{"version":3,"file":"index.mjs","sources":["../src/cssSnapshot.ts","../src/localAssetSnapshot.ts","../src/frictionClicks.ts","../src/contrast.ts","../src/utils/logger.ts","../src/retry-queue.ts","../src/persistence.ts","../src/api.ts","../src/redact.ts","../src/utils/property-detector.ts","../src/utils/property-manager.ts","../src/errors/breadcrumbs.ts","../src/errors/dedup.ts","../src/errors/filters.ts","../src/errors/stack-parser.ts","../src/errors/linked-errors.ts","../src/errors/debug-ids.ts","../src/errors/error-payload.ts","../src/errors/capture.ts","../src/errors/redact-network.ts","../src/errors/hydration.ts","../src/tracing.ts","../src/tracker.ts","../src/shared-session.ts","../src/utils/global-tracker.ts","../../../node_modules/web-vitals/dist/web-vitals.js"],"sourcesContent":["// Record-time capture of cross-origin stylesheet text.\n//\n// rrweb's `inlineStylesheet: true` inlines a stylesheet into the snapshot only\n// when the document can read its cssRules. Cross-origin sheets loaded without\n// a `crossorigin` attribute are CORS-opaque, so rrweb can only record their\n// URL — and a URL is not a contract: by the time a replay renders, the origin\n// may serve a different build than the one the user's browser applied (many\n// CDNs ignore cache-buster params). Replaying record-day DOM with current-day\n// CSS produces phantom visual defects the user never saw.\n//\n// This module closes that gap at the only place it can be closed: record\n// time. For each stylesheet the snapshot could NOT inline, it fetches the CSS\n// text (cache-first, so it prefers the exact bytes the user's browser already\n// downloaded) and emits it as an `hb-css-snapshot` custom event. Replay-side\n// consumers serve these bodies via network interception, rebuilding the page\n// from record-time truth.\n//\n// Invariants (matching the error-capture subsystem): never throws, never\n// blocks the recording path (all work is async), bounded output (per-sheet\n// and per-session byte caps), and each href is captured at most once per\n// session.\n\nexport const CSS_SNAPSHOT_TAG = 'hb-css-snapshot';\n\nexport interface CssSnapshotPayload {\n    href: string;\n    cssText: string;\n}\n\n// A sheet bigger than this is skipped: it would bloat the event stream, and\n// pathological sizes usually indicate something other than a real stylesheet.\nconst MAX_SHEET_BYTES = 1_500_000;\n// Total budget per session across all captured sheets.\nconst MAX_TOTAL_BYTES = 5_000_000;\n// Re-scan delay after each trigger: stylesheets referenced by a fresh\n// navigation may still be loading when the snapshot fires.\nconst RESCAN_DELAY_MS = 3_000;\n\ntype FetchLike = (input: string, init?: RequestInit) => Promise<{ ok: boolean; text(): Promise<string> }>;\n\n// Hrefs of stylesheets in the document that rrweb could NOT have inlined:\n// present in document.styleSheets but with CORS-opaque cssRules.\nexport function unreadableStylesheetHrefs(doc: Document): string[] {\n    const out: string[] = [];\n    let sheets: StyleSheetList;\n    try {\n        sheets = doc.styleSheets;\n    } catch {\n        return out;\n    }\n    for (let i = 0; i < sheets.length; i++) {\n        const sheet = sheets[i] as CSSStyleSheet;\n        let href: string | null = null;\n        try {\n            href = sheet.href;\n        } catch {\n            continue;\n        }\n        if (!href || href.startsWith('data:') || href.startsWith('blob:')) continue;\n        try {\n            // Readable rules mean rrweb inlined it already — nothing to do.\n            void sheet.cssRules;\n        } catch {\n            out.push(href);\n        }\n    }\n    return out;\n}\n\nexport class CssSnapshotCapture {\n    private captured = new Set<string>();\n    private bytesSent = 0;\n    private rescanTimer: ReturnType<typeof setTimeout> | null = null;\n\n    constructor(\n        private emit: (payload: CssSnapshotPayload) => void,\n        private fetchImpl?: FetchLike,\n    ) {}\n\n    // Capture every currently-unreadable stylesheet, then schedule one\n    // re-scan for late-loading sheets. Safe to call repeatedly (navigation\n    // snapshots): already-captured hrefs are skipped.\n    captureFromDocument(doc: Document): void {\n        try {\n            void this.capture(unreadableStylesheetHrefs(doc));\n            if (this.rescanTimer) clearTimeout(this.rescanTimer);\n            this.rescanTimer = setTimeout(() => {\n                this.rescanTimer = null;\n                try {\n                    void this.capture(unreadableStylesheetHrefs(doc));\n                } catch {\n                    /* capture is best-effort */\n                }\n            }, RESCAN_DELAY_MS);\n        } catch {\n            /* never throw into the recording path */\n        }\n    }\n\n    // Fetch + emit the given hrefs, deduped and byte-capped. Exposed for\n    // tests; production entry is captureFromDocument.\n    async capture(hrefs: string[]): Promise<void> {\n        const fetcher: FetchLike | undefined =\n            this.fetchImpl ?? (typeof fetch !== 'undefined' ? fetch.bind(globalThis) : undefined);\n        if (!fetcher) return;\n\n        for (const href of hrefs) {\n            if (this.captured.has(href)) continue;\n            if (this.bytesSent >= MAX_TOTAL_BYTES) return;\n            // Mark before fetching: a failed fetch will not succeed on a\n            // rescan seconds later, and retry loops are worse than a gap the\n            // replay-side fidelity gate already handles.\n            this.captured.add(href);\n            try {\n                // force-cache: prefer the bytes the user's browser already\n                // downloaded for this page — the exact record-time build.\n                const res = await fetcher(href, {\n                    mode: 'cors',\n                    credentials: 'omit',\n                    cache: 'force-cache',\n                });\n                if (!res.ok) continue;\n                const cssText = await res.text();\n                if (!cssText || cssText.length > MAX_SHEET_BYTES) continue;\n                if (this.bytesSent + cssText.length > MAX_TOTAL_BYTES) continue;\n                this.bytesSent += cssText.length;\n                this.emit({ href, cssText });\n            } catch {\n                // CORS-denied or network failure: cannot capture. The\n                // replay-side fidelity gate covers this sheet instead.\n            }\n        }\n    }\n\n    dispose(): void {\n        if (this.rescanTimer) clearTimeout(this.rescanTimer);\n        this.rescanTimer = null;\n    }\n}\n","// Localhost Verify fidelity: ship same-origin image + font bytes in-band.\n//\n// Cloud archivers cannot fetch `http://localhost:...` (SSRF + wrong host).\n// During onboarding Verify the customer's app is often on loopback, so the\n// live rrweb preview shows broken images / Times fallbacks even though the\n// browser already has the bytes. Read cache-first from the page, emit a\n// custom event, let the Verify player rewrite URLs to data: URLs.\n//\n// Strictly loopback-only. Public HTTPS origins keep using server-side asset\n// archival — no COGS hit here. Never throws into the recording path.\n\nexport const LOCAL_ASSET_TAG = 'hb-local-asset';\n\nexport interface LocalAssetPayload {\n    href: string;\n    contentType: string;\n    bodyBase64: string;\n}\n\nconst MAX_ASSET_BYTES = 400_000;\nconst MAX_TOTAL_BYTES = 2_000_000;\n// A couple heroes + next/font woff2 files (Inter / display).\nconst MAX_ASSETS = 12;\nconst RESCAN_DELAY_MS = 2_000;\n\nconst CSS_URL_RE = /url\\(\\s*(['\"]?)([^'\")]+)\\1\\s*\\)/gi;\n\ntype FetchLike = (\n    input: string,\n    init?: RequestInit,\n) => Promise<{\n    ok: boolean;\n    headers: { get(name: string): string | null };\n    arrayBuffer(): Promise<ArrayBuffer>;\n}>;\n\nexport function isLocalVerifyOrigin(href: string): boolean {\n    try {\n        const u = new URL(href);\n        if (u.protocol !== 'http:' && u.protocol !== 'https:') return false;\n        const h = u.hostname.replace(/^\\[|\\]$/g, '').toLowerCase();\n        return h === 'localhost' || h.endsWith('.localhost') || h === '127.0.0.1' || h === '::1';\n    } catch {\n        return false;\n    }\n}\n\nfunction guessContentType(href: string, header: string | null): string {\n    const fromHeader = (header ?? '').split(';')[0].trim().toLowerCase();\n    if (fromHeader.startsWith('image/') || fromHeader.startsWith('font/')) return fromHeader;\n    if (fromHeader === 'application/font-woff' || fromHeader === 'application/font-woff2') {\n        return fromHeader.includes('woff2') ? 'font/woff2' : 'font/woff';\n    }\n    if (/\\.svg(\\?|#|$)/i.test(href)) return 'image/svg+xml';\n    if (/\\.png(\\?|#|$)/i.test(href)) return 'image/png';\n    if (/\\.(jpe?g)(\\?|#|$)/i.test(href)) return 'image/jpeg';\n    if (/\\.gif(\\?|#|$)/i.test(href)) return 'image/gif';\n    if (/\\.webp(\\?|#|$)/i.test(href)) return 'image/webp';\n    if (/\\.avif(\\?|#|$)/i.test(href)) return 'image/avif';\n    if (/\\.woff2(\\?|#|$)/i.test(href)) return 'font/woff2';\n    if (/\\.woff(\\?|#|$)/i.test(href)) return 'font/woff';\n    if (/\\.ttf(\\?|#|$)/i.test(href)) return 'font/ttf';\n    if (/\\.otf(\\?|#|$)/i.test(href)) return 'font/otf';\n    return fromHeader || 'application/octet-stream';\n}\n\nfunction isAllowedContentType(contentType: string, href: string): boolean {\n    if (contentType.startsWith('image/') || contentType.startsWith('font/')) return true;\n    // Some Next/static hosts serve woff2 as octet-stream.\n    if (\n        contentType === 'application/octet-stream' &&\n        /\\.(woff2?|ttf|otf)(\\?|#|$)/i.test(href)\n    ) {\n        return true;\n    }\n    return false;\n}\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n    let binary = '';\n    const chunk = 0x8000;\n    for (let i = 0; i < bytes.length; i += chunk) {\n        binary += String.fromCharCode(...bytes.subarray(i, i + chunk));\n    }\n    return btoa(binary);\n}\n\n/** Collapse Next.js `/_next/image?url=...&w=N` variants to one key per source url. */\nfunction assetDedupeKey(absHref: string): string {\n    try {\n        const u = new URL(absHref);\n        if (u.pathname.includes('/_next/image')) {\n            const inner = u.searchParams.get('url');\n            if (inner) return `next:${inner}`;\n        }\n        u.searchParams.delete('w');\n        u.searchParams.delete('q');\n        u.searchParams.delete('width');\n        return u.origin + u.pathname + (u.searchParams.toString() ? `?${u.searchParams}` : '');\n    } catch {\n        return absHref;\n    }\n}\n\nfunction collectCssUrls(\n    cssText: string,\n    cssBaseHref: string,\n    add: (raw: string, baseHref: string) => void,\n): void {\n    CSS_URL_RE.lastIndex = 0;\n    let match: RegExpExecArray | null;\n    while ((match = CSS_URL_RE.exec(cssText))) {\n        const raw = (match[2] || '').trim();\n        if (!raw || raw.startsWith('data:') || raw.startsWith('blob:')) continue;\n        // Fonts (and the occasional css-referenced image).\n        if (\n            /\\.(woff2?|ttf|otf|eot|png|jpe?g|gif|webp|svg|avif)(\\?|#|$)/i.test(raw) ||\n            raw.includes('/_next/static/media/') ||\n            raw.includes('/media/')\n        ) {\n            // Resolve relative to the stylesheet URL, not the page path —\n            // `url(../media/x.woff2)` from `/_next/static/css/...` must become\n            // `/_next/static/media/x.woff2`, not `/courses/media/...`.\n            add(raw, cssBaseHref);\n        }\n    }\n}\n\n// Image + font URLs currently referenced (absolute). Prefer currentSrc for\n// images; pull @font-face / next/font files from stylesheets + preload links.\nexport function localAssetHrefs(doc: Document, pageHref: string): string[] {\n    if (!isLocalVerifyOrigin(pageHref)) return [];\n    const out: string[] = [];\n    const seenKey = new Set<string>();\n    const add = (raw: string | null | undefined, baseHref: string = pageHref) => {\n        if (!raw || raw.startsWith('data:') || raw.startsWith('blob:')) return;\n        let abs: string;\n        try {\n            abs = new URL(raw, baseHref).href;\n        } catch {\n            return;\n        }\n        if (!isLocalVerifyOrigin(abs)) return;\n        const key = assetDedupeKey(abs);\n        if (seenKey.has(key)) return;\n        seenKey.add(key);\n        out.push(abs);\n    };\n\n    try {\n        const imgs = doc.querySelectorAll('img');\n        for (let i = 0; i < imgs.length; i++) {\n            const el = imgs[i] as HTMLImageElement;\n            add(el.currentSrc || el.getAttribute('src'));\n        }\n        const others = doc.querySelectorAll(\n            'video[poster], image[href], image[xlink\\\\:href], link[rel=\"preload\"][as=\"font\"]',\n        );\n        for (let i = 0; i < others.length; i++) {\n            const el = others[i];\n            add(el.getAttribute('poster'));\n            add(el.getAttribute('href') || el.getAttribute('xlink:href'));\n        }\n\n        // @font-face src from readable stylesheets (next/font self-host).\n        const sheets = doc.styleSheets;\n        for (let i = 0; i < sheets.length; i++) {\n            const sheet = sheets[i] as CSSStyleSheet;\n            const sheetBase = sheet.href || pageHref;\n            let rules: CSSRuleList;\n            try {\n                rules = sheet.cssRules;\n            } catch {\n                // Opaque cross-origin sheet — skip (loopback same-origin is fine).\n                if (sheet.href) add(sheet.href, pageHref);\n                continue;\n            }\n            for (let j = 0; j < rules.length; j++) {\n                const text = (rules[j] as CSSRule).cssText || '';\n                // Duck-type: jsdom often lacks CSSFontFaceRule. collectCssUrls\n                // already filters to font/image-looking urls.\n                if (text.includes('url(')) collectCssUrls(text, sheetBase, add);\n            }\n        }\n\n        // Inline <style> blocks (next/font often injects here).\n        // Prefer document URL; next/font urls are usually root-absolute.\n        const styles = doc.querySelectorAll('style');\n        for (let i = 0; i < styles.length; i++) {\n            const text = styles[i].textContent;\n            if (text) collectCssUrls(text, pageHref, add);\n        }\n    } catch {\n        return out;\n    }\n    return out;\n}\n\n/** @deprecated use localAssetHrefs */\nexport function localImageHrefs(doc: Document, pageHref: string): string[] {\n    return localAssetHrefs(doc, pageHref);\n}\n\nexport class LocalAssetSnapshotCapture {\n    private captured = new Set<string>();\n    private bytesSent = 0;\n    private count = 0;\n    private rescanTimer: ReturnType<typeof setTimeout> | null = null;\n\n    constructor(\n        private emit: (payload: LocalAssetPayload) => void,\n        private fetchImpl?: FetchLike,\n        private encode: (bytes: Uint8Array) => string = bytesToBase64,\n    ) {}\n\n    captureFromDocument(doc: Document, pageHref: string): void {\n        try {\n            if (!isLocalVerifyOrigin(pageHref)) return;\n            void this.capture(localAssetHrefs(doc, pageHref));\n            if (this.rescanTimer) clearTimeout(this.rescanTimer);\n            this.rescanTimer = setTimeout(() => {\n                this.rescanTimer = null;\n                try {\n                    void this.capture(localAssetHrefs(doc, pageHref));\n                } catch {\n                    /* best-effort */\n                }\n            }, RESCAN_DELAY_MS);\n        } catch {\n            /* never throw into the recording path */\n        }\n    }\n\n    async capture(hrefs: string[]): Promise<void> {\n        const fetcher: FetchLike | undefined =\n            this.fetchImpl ?? (typeof fetch !== 'undefined' ? (fetch.bind(globalThis) as FetchLike) : undefined);\n        if (!fetcher) return;\n\n        for (const href of hrefs) {\n            if (this.captured.has(href)) continue;\n            if (this.count >= MAX_ASSETS) return;\n            if (this.bytesSent >= MAX_TOTAL_BYTES) return;\n            this.captured.add(href);\n            try {\n                const res = await fetcher(href, {\n                    mode: 'cors',\n                    credentials: 'omit',\n                    cache: 'force-cache',\n                });\n                if (!res.ok) continue;\n                const buf = new Uint8Array(await res.arrayBuffer());\n                if (!buf.length || buf.length > MAX_ASSET_BYTES) continue;\n                if (this.bytesSent + buf.length > MAX_TOTAL_BYTES) continue;\n                let contentType = guessContentType(href, res.headers.get('content-type'));\n                if (!isAllowedContentType(contentType, href)) continue;\n                if (contentType === 'application/octet-stream') {\n                    contentType = guessContentType(href, null);\n                }\n                const bodyBase64 = this.encode(buf);\n                this.bytesSent += buf.length;\n                this.count++;\n                this.emit({ href, contentType, bodyBase64 });\n            } catch {\n                /* CORS / network: leave gap */\n            }\n        }\n    }\n\n    dispose(): void {\n        if (this.rescanTimer) clearTimeout(this.rescanTimer);\n        this.rescanTimer = null;\n    }\n}\n","/**\n * Rage-click / dead-click detection, live in the browser.\n *\n * This is a deliberate port of the server-side detectors in\n * `trace-compiler/src/detectors.ts` (humanbehavior-v2). Both must answer\n * \"was this click dead?\" the same way, because the raw `$rageclick` /\n * `$deadclick` events this file emits feed the dashboard Health Signals, the\n * issues dashboard, the replay inspector, the visitor timeline and\n * `hb_search_replays`, while the compiled `dead_click` / `rage_click` trace\n * rows feed Issues and `hb_find_friction`. When the two disagree, the product\n * contradicts itself about the same session.\n *\n * Thresholds live in FRICTION below and are asserted against the\n * trace-compiler constants by a parity test in the monorepo\n * (`tests/trace-compiler/sdk-friction-parity.test.ts`). Change one, change both.\n *\n * Two divergences from trace-compiler remain, and are not fixable from either\n * side alone:\n *   1. trace-compiler suppresses \"ambient\" mutations (tickers, clocks, ad\n *      rotation) that fire with no user interaction, so they don't mask a dead\n *      click. Doing that live would mean scoring every MutationRecord on the\n *      main thread of the customer's app; we accept the false negatives.\n *   2. `selectionchange` is deliberately NOT treated as a page reaction. The\n *      old SDK rule used it to suppress text-selection false positives;\n *      trace-compiler has no selection signal at all, and the interactive-only\n *      gate plus the two-gesture rule cover those cases instead.\n\n * A clickable <div> whose `cursor: pointer` comes from a stylesheet rather than\n * an inline style is invisible to BOTH sides: trace-compiler only has the rrweb\n * snapshot's inline styles, and reading the computed style here would break\n * target resolution (see hasInlinePointerCursor).\n */\n\n// Mirror of the constants in trace-compiler/src/detectors.ts. Declared as plain\n// consts, not object properties, so the minifier can inline them — the browser\n// bundle sits against a hard size-limit budget.\n\n/** Clicks on one target within RAGE_WINDOW_MS needed to call it rage. */\nconst RAGE_MIN_CLICKS = 4;\nconst RAGE_WINDOW_MS = 2_000;\n/** How long the page has to react before a click counts as dead. */\nconst DEAD_CLICK_REACTION_MS = 1_500;\n/** One unreacted click is a shrug; the same control ignoring the user twice is broken. */\nconst DEAD_CLICK_MIN_OCCURRENCES = 2;\n/** Clicks closer than this on one target are a single gesture (double/triple-click). */\nconst DEAD_CLICK_GESTURE_MS = 700;\n/** Duplicate capture: two clicks on one target closer than this are one click. */\nconst MIN_CLICK_SPACING_MS = 30;\n\n/** Re-exported as one object for tests and for the cross-repo parity guard. */\nexport const FRICTION = {\n    RAGE_MIN_CLICKS,\n    RAGE_WINDOW_MS,\n    DEAD_CLICK_REACTION_MS,\n    DEAD_CLICK_MIN_OCCURRENCES,\n    DEAD_CLICK_GESTURE_MS,\n    MIN_CLICK_SPACING_MS,\n} as const;\n\n// Mirror of INTERACTIVE_TAGS / INTERACTIVE_ROLES in trace-compiler/src/vdom.ts.\nconst INTERACTIVE_TAGS = new Set('button a input select textarea label summary option'.split(' '));\nconst INTERACTIVE_ROLES = new Set(\n    'button link checkbox radio tab menuitem switch combobox option slider textbox'.split(' '),\n);\n\n// Form controls where a click legitimately produces no DOM mutation (clicking\n// into a text field, toggling focus). Never dead-click OR rage candidates.\nconst FORM_CONTROL_TAGS = new Set('input select textarea label option'.split(' '));\n\n// Copy-to-clipboard controls: the click's effect is a clipboard write, which\n// leaves no DOM trace in many implementations. Silence is expected.\nconst CLIPBOARD_TARGET = /\\bcopy\\b/i;\n\n// ARIA states that mark a control as already selected/on, where a click is a\n// no-op by design (the selected tab, the pressed toggle, the checked option).\nconst SELECTED_STATE_ATTRS = ['aria-selected', 'aria-pressed', 'aria-checked'] as const;\n\n// Hop budget when walking from the event target up to the clicked control.\nconst RESOLVE_MAX_HOPS = 30;\n\n// Bound on retained per-target state, so a long session on a page that mints\n// new target keys forever (virtualised lists) cannot grow without limit.\nconst MAX_TRACKED_TARGETS = 500;\n\n/** What kind of page response a signal represents. */\nexport type ReactionKind =\n    // DOM mutation or navigation. Counts as a reaction for BOTH detectors.\n    | 'dom'\n    // Scroll, or typing. Counts for dead clicks only — trace-compiler's rage\n    // detector reads reactionTs alone, which excludes these.\n    | 'soft';\n\nexport interface FrictionClickInfo {\n    /** The resolved control (the interactive ancestor), not the raw event target. */\n    node: Element;\n    /** clientX/clientY of the first click in the group. */\n    x: number;\n    y: number;\n    /** Timestamp of the first click in the group. */\n    tsMs: number;\n    /** Rage: clicks in the burst. Dead: unreacted clicks recorded on this target. */\n    clickCount: number;\n    /** Dead: distinct gestures (double-clicks collapsed). Undefined for rage. */\n    occurrences?: number;\n    /** Rage: burst span. Dead: 0. */\n    durationMs: number;\n}\n\nexport interface FrictionClickOptions {\n    /** Called when a rage or dead click is confirmed. */\n    emit: (kind: 'rage' | 'dead', info: FrictionClickInfo) => void;\n    /** Injectable for tests. */\n    now?: () => number;\n    setTimeout?: (fn: () => void, ms: number) => number;\n    clearTimeout?: (id: number) => void;\n    /** Injectable for tests; defaults to window.location.href. */\n    currentUrl?: () => string;\n}\n\n/**\n * Walk up from a (possibly deeply nested) event target to the control the user\n * semantically clicked. `interactive: false` means we found no clickable\n * ancestor — the click landed on plain content.\n */\nexport function resolveInteractive(target: Element | null): { node: Element; interactive: boolean } | null {\n    let cur: Element | null = target;\n    let hops = 0;\n    let firstElement: Element | null = null;\n    while (cur && hops < RESOLVE_MAX_HOPS) {\n        if (cur.nodeType === 1 && cur.tagName) {\n            if (!firstElement) firstElement = cur;\n            const tag = cur.tagName.toLowerCase();\n            const role = (cur.getAttribute('role') ?? '').toLowerCase();\n            if (\n                INTERACTIVE_TAGS.has(tag) ||\n                INTERACTIVE_ROLES.has(role) ||\n                cur.getAttribute('onclick') !== null ||\n                (cur as unknown as { onclick?: unknown }).onclick != null ||\n                cur.getAttribute('tabindex') !== null ||\n                hasInlinePointerCursor(cur)\n            ) {\n                return { node: cur, interactive: true };\n            }\n        }\n        cur = cur.parentElement;\n        hops++;\n    }\n    return firstElement ? { node: firstElement, interactive: false } : null;\n}\n\n/**\n * Inline `cursor: pointer` — the visual affordance signal a React-style\n * clickable <div> carries when it has no semantic markup.\n *\n * Deliberately NOT getComputedStyle: `cursor` inherits, so every child of a\n * clickable div computes to `pointer` and the walk would stop at whichever\n * span or icon the click happened to land on. That splits one control into\n * several target keys, so its gestures never group. Inline-only is also\n * exactly what trace-compiler can see in the rrweb snapshot.\n */\nfunction hasInlinePointerCursor(el: Element): boolean {\n    return /cursor\\s*:\\s*pointer/i.test(el.getAttribute('style') ?? '');\n}\n\n/** True when a click on this element takes effect OUTSIDE the recorded tab. */\nexport function opensElsewhere(node: Element, currentUrl: string): boolean {\n    if ((node.tagName || '').toLowerCase() !== 'a') return false;\n    if ((node.getAttribute('target') ?? '').toLowerCase() === '_blank') return true;\n    if (node.getAttribute('download') !== null) return true;\n    const href = node.getAttribute('href') ?? '';\n    if (/^(mailto:|tel:|sms:|blob:|javascript:void)/i.test(href)) return true;\n    if (/^https?:\\/\\//i.test(href)) {\n        try {\n            return new URL(href).origin !== new URL(currentUrl).origin;\n        } catch {\n            return false;\n        }\n    }\n    return false;\n}\n\n/** True when the control is already selected/on, so a click is a no-op by design. */\nexport function alreadySelected(node: Element): boolean {\n    return SELECTED_STATE_ATTRS.some((attr) => (node.getAttribute(attr) ?? '').toLowerCase() === 'true');\n}\n\n/**\n * Stable per-control identity. Mirror of VDom.targetKey in trace-compiler, so a\n * React re-render that swaps the DOM node still groups with its earlier clicks.\n */\nexport function targetKey(node: Element): string {\n    const tag = (node.tagName || 'element').toLowerCase();\n    const id = node.getAttribute('id');\n    if (id) return `${tag}#${stableIdKey(id)}`;\n    const testid = node.getAttribute('data-testid');\n    if (testid) return `${tag}[testid=${testid}]`;\n    const name = node.getAttribute('name');\n    if (name) return `${tag}[name=${name}]`;\n    const aria = node.getAttribute('aria-label')?.trim();\n    const text = (aria || (node.textContent ?? '').slice(0, 40))\n        .toLowerCase()\n        .replace(/\\d+/g, '*')\n        .replace(/\\s+/g, ' ')\n        .trim();\n    if (text) return `${tag}[text=${text.slice(0, 40)}]`;\n    const className = node.getAttribute('class') ?? '';\n    const cls = className\n        .split(/\\s+/)\n        .filter((c) => c && !/\\d/.test(c))\n        .slice(0, 2)\n        .join('.');\n    return cls ? `${tag}.${cls}` : tag;\n}\n\n// Generated ids (React useId, CSS-in-JS) differ per render; collapse tokens\n// carrying entropy so one control doesn't fingerprint into thousands.\nfunction stableIdKey(id: string): string {\n    return id\n        .split(/[-_:]/)\n        .map((part) => (/\\d/.test(part) ? '*' : part))\n        .join('-');\n}\n\n/**\n * The shared eligibility gate. Returns null when this click can never be rage\n * or dead. Same five checks, in the same order, as both trace-compiler\n * detectors.\n */\nexport function frictionEligible(\n    target: Element | null,\n    currentUrl: string,\n): { node: Element; key: string } | null {\n    if (!target || target.nodeType !== 1 || !target.tagName) return null;\n    const resolved = resolveInteractive(target);\n    if (!resolved || !resolved.interactive) return null;\n    const node = resolved.node;\n    if (opensElsewhere(node, currentUrl)) return null;\n    if (alreadySelected(node)) return null;\n    if (FORM_CONTROL_TAGS.has((node.tagName || '').toLowerCase())) return null;\n    return { node, key: targetKey(node) };\n}\n\ninterface PendingDeadClick {\n    node: Element;\n    key: string;\n    x: number;\n    y: number;\n    tsMs: number;\n    timer: number;\n}\n\ninterface TargetState {\n    /** Timestamps of clicks on this target, pruned to the rage window. */\n    burst: number[];\n    /** Scheduled rage verdict for the current burst, if any. */\n    rageTimer: number | null;\n    /** First/last click of the burst under evaluation, and its full size. */\n    burstFirstTs: number;\n    burstLastTs: number;\n    burstCount: number;\n    /** Where the burst under evaluation started. */\n    burstOrigin: { node: Element; x: number; y: number } | null;\n    /** Timestamp of the last click we accepted (30ms duplicate-capture dedupe). */\n    lastClickTs: number;\n    /** Unreacted-click bookkeeping for the dead-click occurrence rule. */\n    unreactedGestures: number;\n    unreactedClicks: number;\n    lastUnreactedTs: number;\n    /** The click that opened the current unreacted streak. */\n    firstUnreacted: PendingDeadClick | null;\n    /** One dead-click event per target per session, matching one compiled row. */\n    deadEmitted: boolean;\n}\n\nfunction newTargetState(): TargetState {\n    return {\n        burst: [],\n        rageTimer: null,\n        burstFirstTs: 0,\n        burstLastTs: 0,\n        burstCount: 0,\n        burstOrigin: null,\n        lastClickTs: Number.NEGATIVE_INFINITY,\n        unreactedGestures: 0,\n        unreactedClicks: 0,\n        lastUnreactedTs: Number.NEGATIVE_INFINITY,\n        firstUnreacted: null,\n        deadEmitted: false,\n    };\n}\n\n/**\n * Live rage/dead click detector. Feed it clicks and page reactions; it calls\n * `emit` when a verdict is reached. Holds no DOM listeners of its own — the\n * tracker owns those.\n */\nexport class FrictionClickDetector {\n    private readonly emit: FrictionClickOptions['emit'];\n    private readonly now: () => number;\n    private readonly schedule: (fn: () => void, ms: number) => number;\n    private readonly unschedule: (id: number) => void;\n    private readonly currentUrl: () => string;\n\n    /** Reaction timestamps, sorted, pruned to a bounded recent window. */\n    private domReactions: number[] = [];\n    private softReactions: number[] = [];\n\n    private targets = new Map<string, TargetState>();\n    private pendingDead = new Map<number, PendingDeadClick>();\n    private nextPendingId = 1;\n\n    constructor(options: FrictionClickOptions) {\n        this.emit = options.emit;\n        this.now = options.now ?? (() => Date.now());\n        this.schedule = options.setTimeout ?? ((fn, ms) => setTimeout(fn, ms) as unknown as number);\n        this.unschedule = options.clearTimeout ?? ((id) => clearTimeout(id as unknown as ReturnType<typeof setTimeout>));\n        this.currentUrl = options.currentUrl ?? (() => (typeof window !== 'undefined' ? window.location.href : ''));\n    }\n\n    /** Record a page response. Call on mutation/navigation ('dom') or scroll/typing ('soft'). */\n    onReaction(kind: ReactionKind, tsMs: number = this.now()): void {\n        const list = kind === 'dom' ? this.domReactions : this.softReactions;\n        list.push(tsMs);\n        // Reactions only matter until every click that could see them has\n        // settled, so old entries are dead weight. Keep a bounded tail.\n        if (list.length > 256) list.splice(0, list.length - 128);\n    }\n\n    /** Feed every click. Ineligible targets are dropped here. */\n    onClick(target: Element | null, x: number, y: number, tsMs: number = this.now()): void {\n        const eligible = frictionEligible(target, this.currentUrl());\n        if (!eligible) return;\n        const { node, key } = eligible;\n        const state = this.stateFor(key);\n\n        // Duplicate capture (double-registered listeners) produces bursts no\n        // human can perform. One physical click.\n        if (tsMs - state.lastClickTs < MIN_CLICK_SPACING_MS) return;\n        state.lastClickTs = tsMs;\n\n        this.trackRage(state, node, key, x, y, tsMs);\n        this.trackDead(state, node, key, x, y, tsMs);\n    }\n\n    /** Drop all pending verdicts (page unload, session end). */\n    reset(): void {\n        for (const pending of this.pendingDead.values()) this.unschedule(pending.timer);\n        this.pendingDead.clear();\n        for (const state of this.targets.values()) {\n            if (state.rageTimer !== null) this.unschedule(state.rageTimer);\n        }\n        this.targets.clear();\n        this.domReactions = [];\n        this.softReactions = [];\n    }\n\n    private stateFor(key: string): TargetState {\n        let state = this.targets.get(key);\n        if (!state) {\n            if (this.targets.size >= MAX_TRACKED_TARGETS) {\n                // Evict the oldest insertion; Map preserves insertion order. A\n                // rage timer left pointing at it settles into a no-op lookup.\n                const oldest = this.targets.keys().next();\n                if (!oldest.done) this.targets.delete(oldest.value);\n            }\n            state = newTargetState();\n            this.targets.set(key, state);\n        }\n        return state;\n    }\n\n    // ---- rage ------------------------------------------------------------\n\n    private trackRage(state: TargetState, node: Element, key: string, x: number, y: number, tsMs: number): void {\n        // A burst is already awaiting its verdict: greedily absorb this click\n        // if the gap is inside the window, so one frustrated flurry is one\n        // event even when it runs longer than RAGE_WINDOW_MS.\n        if (state.rageTimer !== null) {\n            if (tsMs - state.burstLastTs <= RAGE_WINDOW_MS) {\n                state.burstCount++;\n                state.burstLastTs = tsMs;\n                this.unschedule(state.rageTimer);\n                state.rageTimer = this.scheduleRageVerdict(key);\n            }\n            return;\n        }\n\n        state.burst.push(tsMs);\n        // Sliding window anchored on the burst, exactly as the compiler does.\n        while (state.burst.length > 0 && tsMs - state.burst[0] > RAGE_WINDOW_MS) {\n            state.burst.shift();\n        }\n        if (state.burst.length < RAGE_MIN_CLICKS) return;\n\n        state.burstFirstTs = state.burst[0];\n        state.burstLastTs = tsMs;\n        state.burstCount = state.burst.length;\n        state.burstOrigin = { node, x, y };\n        // Rage requires the page to have IGNORED the burst, and both a\n        // continuation click and a late reaction can still arrive — so the\n        // verdict waits out the window plus the reaction grace.\n        state.rageTimer = this.scheduleRageVerdict(key);\n    }\n\n    private scheduleRageVerdict(key: string): number {\n        return this.schedule(\n            () => this.settleRage(key),\n            RAGE_WINDOW_MS + DEAD_CLICK_REACTION_MS,\n        );\n    }\n\n    private settleRage(key: string): void {\n        const state = this.targets.get(key);\n        if (!state || state.rageTimer === null) return;\n        state.rageTimer = null;\n        const count = state.burstCount;\n        const reacted = hasReactionInRange(\n            this.domReactions,\n            state.burstFirstTs,\n            state.burstLastTs + DEAD_CLICK_REACTION_MS,\n        );\n        if (!reacted && count >= RAGE_MIN_CLICKS && state.burstOrigin) {\n            this.emit('rage', {\n                node: state.burstOrigin.node,\n                x: state.burstOrigin.x,\n                y: state.burstOrigin.y,\n                tsMs: state.burstFirstTs,\n                clickCount: count,\n                durationMs: state.burstLastTs - state.burstFirstTs,\n            });\n        }\n        // Emitted or not, this burst is spent: later clicks start a fresh one.\n        state.burst = [];\n        state.burstCount = 0;\n        state.burstOrigin = null;\n    }\n\n    // ---- dead ------------------------------------------------------------\n\n    private trackDead(state: TargetState, node: Element, key: string, x: number, y: number, tsMs: number): void {\n        if (state.deadEmitted) return;\n        // Copy-to-clipboard controls show no DOM feedback by design.\n        if (CLIPBOARD_TARGET.test(node.getAttribute('aria-label') ?? '') ||\n            CLIPBOARD_TARGET.test((node.textContent ?? '').slice(0, 60))) {\n            return;\n        }\n        const id = this.nextPendingId++;\n        const timer = this.schedule(() => this.settleDead(id), DEAD_CLICK_REACTION_MS);\n        this.pendingDead.set(id, { node, key, x, y, tsMs, timer });\n    }\n\n    private settleDead(id: number): void {\n        const pending = this.pendingDead.get(id);\n        if (!pending) return;\n        this.pendingDead.delete(id);\n        const state = this.targets.get(pending.key);\n        if (!state || state.deadEmitted) return;\n\n        // Any DOM change, navigation, scroll or keystroke after the click means\n        // the click did something.\n        if (\n            hasReactionAfter(this.domReactions, pending.tsMs, DEAD_CLICK_REACTION_MS) ||\n            hasReactionAfter(this.softReactions, pending.tsMs, DEAD_CLICK_REACTION_MS)\n        ) {\n            return;\n        }\n\n        // Collapse double/triple-clicks: one gesture is one attempt.\n        if (pending.tsMs - state.lastUnreactedTs > DEAD_CLICK_GESTURE_MS) {\n            state.unreactedGestures++;\n        }\n        state.lastUnreactedTs = pending.tsMs;\n        state.unreactedClicks++;\n        if (!state.firstUnreacted) state.firstUnreacted = pending;\n        if (state.unreactedGestures < DEAD_CLICK_MIN_OCCURRENCES) return;\n\n        state.deadEmitted = true;\n        this.emit('dead', {\n            node: pending.node,\n            x: state.firstUnreacted.x,\n            y: state.firstUnreacted.y,\n            tsMs: state.firstUnreacted.tsMs,\n            clickCount: state.unreactedClicks,\n            occurrences: state.unreactedGestures,\n            durationMs: 0,\n        });\n    }\n}\n\n/** True when a reaction landed strictly after `afterTs`, within `windowMs`. */\nfunction hasReactionAfter(sorted: number[], afterTs: number, windowMs: number): boolean {\n    const idx = upperBound(sorted, afterTs);\n    return idx < sorted.length && sorted[idx] <= afterTs + windowMs;\n}\n\n/** True when a reaction landed strictly after `fromTs` and at or before `toTs`. */\nfunction hasReactionInRange(sorted: number[], fromTs: number, toTs: number): boolean {\n    const idx = upperBound(sorted, fromTs);\n    return idx < sorted.length && sorted[idx] <= toTs;\n}\n\nfunction upperBound(sorted: number[], target: number): number {\n    let lo = 0;\n    let hi = sorted.length;\n    while (lo < hi) {\n        const mid = (lo + hi) >> 1;\n        if (sorted[mid] <= target) lo = mid + 1;\n        else hi = mid;\n    }\n    return lo;\n}\n","// Record-time capture of invisible / near-invisible text.\n//\n// The deterministic trace compiler can read INLINE color/background off the\n// recorded DOM, but most real apps color text with classes, CSS-in-JS, or\n// theming — none of which are visible without a browser. This module closes\n// that gap at the only place it can be closed: record time. It walks the live\n// DOM, reads the browser's already-resolved getComputedStyle() foreground and\n// background for text elements, and — for any whose colors effectively match\n// (white-on-white validation errors, invisible prices) — emits an\n// `hb-contrast` custom event carrying the rrweb node id + the two colors.\n// The compiler recomputes the contrast ratio deterministically from those.\n//\n// Invariants (matching the CSS-snapshot / error-capture subsystems): never\n// throws into the recording path, bounded work (element + emit + scan caps),\n// and each node is reported at most once per session.\n\nexport const HB_CONTRAST_TAG = 'hb-contrast';\n// Clipped/cut-off text: content overflowing an `overflow:hidden` box with no\n// ellipsis (a truncated total, a spilled label). Same record-time sweep as\n// contrast; the compiler emits a clipped_content row.\nexport const HB_CLIP_TAG = 'hb-clip';\n// A failed asset load (broken <img>/<script>/<link>/media). Emitted by the\n// tracker's capture-phase error listener (it owns the rrweb mirror).\nexport const HB_BROKEN_ASSET_TAG = 'hb-broken-asset';\n// Two text elements whose rendered glyphs collide (content printed on top of\n// content) due to a layout bug — a price chip over a title, a button dragged\n// over an input. Same record-time sweep as contrast; the compiler emits a\n// content_overlap row. Intentional layering (modals, dropdowns, toasts, FABs,\n// sticky bars) is excluded at detection time so only unintended collisions fire.\nexport const HB_OVERLAP_TAG = 'hb-overlap';\n// A table whose cells in one column do not share a left edge — a column\n// misalignment bug (a shifted value column, numbers under the wrong headers).\n// The compiler emits a column_misalign row.\nexport const HB_MISALIGN_TAG = 'hb-misalign';\n\nexport interface ContrastPayload {\n    // rrweb mirror id of the text-bearing element.\n    id: number;\n    // getComputedStyle colors, as the browser resolved them (e.g. \"rgb(255, 255, 255)\").\n    fg: string;\n    bg: string;\n    // Precomputed WCAG ratio (the compiler recomputes and gates independently).\n    ratio: number;\n    // Short text sample, for the trace detail line.\n    sample: string;\n}\n\nexport interface ClipPayload {\n    // rrweb mirror id of the clipped element.\n    id: number;\n    // Which axis overflowed its clipped box.\n    axis: 'x' | 'y';\n    // Short text sample of the clipped content.\n    sample: string;\n}\n\nexport interface OverlapPayload {\n    // rrweb mirror id of the element painted ON TOP (later in document order).\n    id: number;\n    // rrweb mirror id of the element it collides with underneath.\n    id2: number;\n    // Short text samples of both colliding elements.\n    sample: string;\n    sample2: string;\n}\n\nexport interface BrokenAssetPayload {\n    // rrweb mirror id of the failed element.\n    id: number;\n    // Element kind (img/script/link/...), for the trace label.\n    tag: string;\n    // The URL that failed to load.\n    url: string;\n}\n\nexport interface MisalignPayload {\n    // rrweb mirror id of the misaligned <table>.\n    id: number;\n    // Human-readable description of the misaligned column.\n    sample: string;\n}\n\n// Ratio at or below this means the text is effectively the same color as its\n// background. Matches the compiler's INVISIBLE_CONTRAST_MAX. Far below the\n// 4.5:1 accessibility bar: we hunt invisible text, not merely low-contrast.\nconst INVISIBLE_CONTRAST_MAX = 1.25;\n// Bounds so a pathological page cannot stall recording or flood the stream.\nconst MAX_ELEMENTS_PER_SCAN = 4000;\nconst MAX_EMITS_PER_SESSION = 50;\nconst MAX_SCANS = 15;\nconst SCAN_INTERVAL_MS = 1500;\nconst MIN_TEXT_LEN = 2;\nconst MAX_SAMPLE_LEN = 100;\n// Content must exceed its clipped box by more than this (px) to count — guards\n// against sub-pixel rounding noise.\nconst CLIP_SLOP_PX = 2;\n// Overlap detection bounds. Pairwise text-rect comparison is O(n²), so cap the\n// candidate set; real collisions involve a handful of headline/price/label\n// elements, not a whole paragraph corpus.\nconst MAX_OVERLAP_CANDIDATES = 250;\nconst MAX_TEXT_LINES = 12;\n// A collision counts only when two text LINE rects intersect by at least this\n// fraction of the smaller line AND by at least this many px on each axis —\n// half a line of text covered is unambiguous; a 1px touch of adjacent text is\n// not. Precision-first: better to miss a marginal overlap than flag normal\n// adjacency.\nconst OVERLAP_MIN_FRACTION = 0.5;\nconst OVERLAP_MIN_PX = 3;\n// Deliberate stacking: a positioned element with a z-index at or above this is\n// an intentional overlay (menu, popover, sticky header), not a layout bug.\nconst OVERLAY_ZINDEX = 5;\n// A table column whose cell left-edges span more than this many px is\n// misaligned. Well past padding/border sub-pixel noise (table layout pins every\n// cell in a column to the same left edge), so only a real shift trips it.\nconst MISALIGN_MIN_PX = 12;\nconst MAX_TABLES_PER_SCAN = 50;\n// Only flag a failed image big enough to be a visible defect — skip 1x1\n// tracking pixels and tiny decorative glyphs that happen to 404.\nconst MIN_BROKEN_IMG_PX = 24;\nconst MAX_IMAGES_PER_SCAN = 300;\n\ninterface RGBA {\n    r: number;\n    g: number;\n    b: number;\n    a: number;\n}\n\nfunction parseRgb(input: string): RGBA | null {\n    const s = input.trim().toLowerCase();\n    if (!s || s === 'transparent') return null;\n    const open = s.indexOf('(');\n    const close = s.indexOf(')');\n    if (open === -1 || close === -1) return null;\n    const parts = s.slice(open + 1, close).split(/[,/\\s]+/).filter(Boolean);\n    if (parts.length < 3) return null;\n    const r = parseFloat(parts[0]);\n    const g = parseFloat(parts[1]);\n    const b = parseFloat(parts[2]);\n    const a = parts.length >= 4 ? parseFloat(parts[3]) : 1;\n    if ([r, g, b, a].some((n) => Number.isNaN(n))) return null;\n    return { r, g, b, a };\n}\n\nfunction compositeOver(fg: RGBA, bg: RGBA): RGBA {\n    const a = fg.a;\n    return {\n        r: fg.r * a + bg.r * (1 - a),\n        g: fg.g * a + bg.g * (1 - a),\n        b: fg.b * a + bg.b * (1 - a),\n        a: 1,\n    };\n}\n\nfunction channelLuminance(c: number): number {\n    const s = c / 255;\n    return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);\n}\n\nfunction relativeLuminance(c: RGBA): number {\n    return 0.2126 * channelLuminance(c.r) + 0.7152 * channelLuminance(c.g) + 0.0722 * channelLuminance(c.b);\n}\n\nexport function contrastRatio(fg: RGBA, bg: RGBA): number {\n    const effectiveFg = fg.a < 1 ? compositeOver(fg, bg) : fg;\n    const l1 = relativeLuminance(effectiveFg);\n    const l2 = relativeLuminance(bg);\n    return (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05);\n}\n\n// The element's OWN direct text (not descendants), so contrast is attributed to\n// the element that actually paints the glyphs.\nfunction directText(el: Element): string {\n    let out = '';\n    const nodes = el.childNodes;\n    for (let i = 0; i < nodes.length; i++) {\n        const n = nodes[i];\n        if (n.nodeType === 3 /* Text */) out += n.textContent ?? '';\n    }\n    return out.trim();\n}\n\n// Resolve the effective background by walking ancestors for the first opaque\n// background-color. Returns null when a background image/gradient is in play\n// (its color is not knowable from computed style) so we never guess.\nfunction resolveBackground(el: Element, win: Window): RGBA | null {\n    let cur: Element | null = el;\n    let hops = 0;\n    while (cur && hops < 50) {\n        const cs = win.getComputedStyle(cur);\n        if (cs.backgroundImage && cs.backgroundImage !== 'none') return null;\n        const bg = parseRgb(cs.backgroundColor);\n        if (bg && bg.a >= 0.5) return bg;\n        cur = cur.parentElement;\n        hops++;\n    }\n    // Nothing opaque up the chain and no image: the canvas shows through as the\n    // browser default (white).\n    return { r: 255, g: 255, b: 255, a: 1 };\n}\n\nfunction isVisible(el: Element, win: Window): boolean {\n    const cs = win.getComputedStyle(el);\n    if (cs.visibility === 'hidden' || cs.visibility === 'collapse' || cs.display === 'none') return false;\n    if (parseFloat(cs.opacity || '1') === 0) return false;\n    // getClientRects is empty for elements not laid out (display:none ancestors,\n    // detached, etc.).\n    return (el as HTMLElement).getClientRects().length > 0;\n}\n\n// Screen-reader-only / visually-hidden a11y text (Tailwind `.sr-only`, Bootstrap\n// `.visually-hidden`, etc.). These boxes are intentionally 1×1 + overflow:hidden\n// (or clipped to nothing) so assistive tech still gets a heading while sighted\n// users see decorative ASCII / icons. That pattern ALWAYS trips scrollWidth >\n// clientWidth — it is not a product clip bug. Real clipped-content defects are\n// visible-sized containers truncating text.\nfunction isVisuallyHidden(el: Element, cs: CSSStyleDeclaration): boolean {\n    const h = el as HTMLElement;\n    if (\n        h.clientWidth > 0 &&\n        h.clientWidth <= 2 &&\n        h.clientHeight > 0 &&\n        h.clientHeight <= 2\n    ) {\n        return true;\n    }\n    // Legacy clip: rect(0, 0, 0, 0)\n    const clip = cs.clip || '';\n    if (/rect\\s*\\(\\s*0(px)?\\s*,\\s*0(px)?\\s*,\\s*0(px)?\\s*,\\s*0(px)?\\s*\\)/i.test(clip)) {\n        return true;\n    }\n    // Modern Tailwind sr-only: clip-path: inset(50%)\n    const clipPath =\n        (cs as CSSStyleDeclaration & { clipPath?: string }).clipPath ||\n        (typeof cs.getPropertyValue === 'function' ? cs.getPropertyValue('clip-path') : '') ||\n        '';\n    if (/inset\\s*\\(\\s*50%/i.test(clipPath)) {\n        return true;\n    }\n    return false;\n}\n\n// Detect content clipped by an `overflow:hidden`/`clip` box (a truncated total,\n// a label spilling out) that has NO ellipsis affordance — deliberate ellipsis\n// truncation is not a defect. Returns the overflowing axis or null.\nfunction clippedAxis(el: Element, cs: CSSStyleDeclaration): 'x' | 'y' | null {\n    if (cs.textOverflow === 'ellipsis') return null;\n    // CSS line-clamp (Tailwind `line-clamp-*`) is deliberate multi-line\n    // truncation: it sets overflow:hidden and always overflows vertically.\n    const lineClamp =\n        (cs as CSSStyleDeclaration & { webkitLineClamp?: string }).webkitLineClamp ||\n        (typeof cs.getPropertyValue === 'function'\n            ? cs.getPropertyValue('-webkit-line-clamp') || cs.getPropertyValue('line-clamp')\n            : '');\n    if (lineClamp && lineClamp !== 'none') return null;\n    if (isVisuallyHidden(el, cs)) return null;\n    const h = el as HTMLElement;\n    const clipsX = cs.overflowX === 'hidden' || cs.overflowX === 'clip';\n    const clipsY = cs.overflowY === 'hidden' || cs.overflowY === 'clip';\n    if (clipsX && h.clientWidth > 0 && h.scrollWidth > h.clientWidth + CLIP_SLOP_PX) return 'x';\n    if (clipsY && h.clientHeight > 0 && h.scrollHeight > h.clientHeight + CLIP_SLOP_PX) return 'y';\n    return null;\n}\n\n// The tight rectangles of an element's OWN direct text (one per rendered line),\n// via a Range over each direct text node. Using glyph rects — not the element\n// box — means a child positioned over its parent's text collides on the actual\n// text, and a parent that merely CONTAINS a child box does not.\nfunction directTextLineRects(el: Element, doc: Document): DOMRect[] {\n    const out: DOMRect[] = [];\n    const nodes = el.childNodes;\n    for (let i = 0; i < nodes.length && out.length < MAX_TEXT_LINES; i++) {\n        const n = nodes[i];\n        if (n.nodeType !== 3 /* Text */) continue;\n        if (!(n.textContent ?? '').trim()) continue;\n        try {\n            const range = doc.createRange();\n            range.selectNodeContents(n);\n            const rects = range.getClientRects();\n            for (let k = 0; k < rects.length && out.length < MAX_TEXT_LINES; k++) {\n                const r = rects[k];\n                if (r.width > 0 && r.height > 0) out.push(r);\n            }\n        } catch {\n            /* Range over a detached/odd node — skip it. */\n        }\n    }\n    return out;\n}\n\n// True when any line rect of A intersects any line rect of B past both the\n// per-axis and area thresholds.\nfunction textLinesCollide(a: DOMRect[], b: DOMRect[]): boolean {\n    for (let i = 0; i < a.length; i++) {\n        for (let j = 0; j < b.length; j++) {\n            const r1 = a[i];\n            const r2 = b[j];\n            const ix = Math.min(r1.right, r2.right) - Math.max(r1.left, r2.left);\n            const iy = Math.min(r1.bottom, r2.bottom) - Math.max(r1.top, r2.top);\n            if (ix <= OVERLAP_MIN_PX || iy <= OVERLAP_MIN_PX) continue;\n            const inter = ix * iy;\n            const smaller = Math.min(r1.width * r1.height, r2.width * r2.height);\n            if (smaller > 0 && inter / smaller >= OVERLAP_MIN_FRACTION) return true;\n        }\n    }\n    return false;\n}\n\n// Deliberate overlay context: the element (or an ancestor) is viewport-pinned\n// (fixed/sticky), a dialog/menu/tooltip role, or positioned with a real\n// z-index. Layering such content on top of the page is intended — never a bug.\nfunction isFloatingOverlay(el: Element, win: Window): boolean {\n    let cur: Element | null = el;\n    let hops = 0;\n    while (cur && hops < 12) {\n        const cs = win.getComputedStyle(cur);\n        if (cs.position === 'fixed' || cs.position === 'sticky') return true;\n        if (cs.position === 'absolute' || cs.position === 'relative') {\n            const z = parseInt(cs.zIndex, 10);\n            if (Number.isFinite(z) && z >= OVERLAY_ZINDEX) return true;\n        }\n        const role = cur.getAttribute ? cur.getAttribute('role') ?? '' : '';\n        if (/^(dialog|alertdialog|tooltip|menu|listbox|combobox)$/.test(role)) return true;\n        if (cur.tagName === 'DIALOG') return true;\n        cur = cur.parentElement;\n        hops++;\n    }\n    return false;\n}\n\ninterface OverlapCandidate {\n    id: number;\n    order: number;\n    floating: boolean;\n    lines: DOMRect[];\n    sample: string;\n}\n\nexport class ContrastCapture {\n    private emittedIds = new Set<number>();\n    private clippedIds = new Set<number>();\n    private overlapPairs = new Set<string>();\n    private misalignedIds = new Set<number>();\n    private brokenAssetUrls = new Set<string>();\n    private emitCount = 0;\n    private scanCount = 0;\n    private timer: ReturnType<typeof setInterval> | null = null;\n\n    constructor(\n        private emit: (payload: ContrastPayload) => void,\n        private getId: (node: Node) => number,\n        private emitClip?: (payload: ClipPayload) => void,\n        private emitOverlap?: (payload: OverlapPayload) => void,\n        private emitMisalign?: (payload: MisalignPayload) => void,\n        private emitBrokenAsset?: (payload: BrokenAssetPayload) => void,\n    ) {}\n\n    // Initial scan + a bounded periodic re-scan: invisible text often appears\n    // only after interaction (a validation error on submit), so one pass is not\n    // enough. Per-node dedupe keeps repeated scans cheap.\n    start(doc: Document, win: Window): void {\n        try {\n            this.scan(doc, win);\n            // Resource-settled rescan: `load` fires once every image/script/etc\n            // has loaded OR failed. It is the deterministic moment to catch\n            // assets that 404'd during initial load — the error listener may\n            // have attached too late to hear them, and the periodic timer may\n            // not tick between settle and the next captured moment.\n            if (doc.readyState !== 'complete') {\n                win.addEventListener(\n                    'load',\n                    () => {\n                        try {\n                            this.scan(doc, win);\n                        } catch {\n                            /* scanning is best-effort */\n                        }\n                    },\n                    { once: true },\n                );\n            }\n            if (this.timer) clearInterval(this.timer);\n            this.timer = setInterval(() => {\n                try {\n                    if (this.scanCount >= MAX_SCANS || this.emitCount >= MAX_EMITS_PER_SESSION) {\n                        this.dispose();\n                        return;\n                    }\n                    this.scan(doc, win);\n                } catch {\n                    /* scanning is best-effort */\n                }\n            }, SCAN_INTERVAL_MS);\n        } catch {\n            /* never throw into the recording path */\n        }\n    }\n\n    // Walk text-bearing elements and emit invisible ones. Exposed for tests.\n    scan(doc: Document, win: Window): void {\n        this.scanCount++;\n        const root = doc.body ?? doc.documentElement;\n        if (!root) return;\n        const all = root.querySelectorAll('*');\n        const limit = Math.min(all.length, MAX_ELEMENTS_PER_SCAN);\n        const overlapCandidates: OverlapCandidate[] = [];\n        for (let i = 0; i < limit; i++) {\n            if (this.emitCount >= MAX_EMITS_PER_SESSION) break;\n            const el = all[i];\n            const text = directText(el);\n            if (text.length < MIN_TEXT_LEN || !/[\\p{L}\\p{N}]/u.test(text)) continue;\n\n            const id = this.getId(el);\n            if (id < 0) continue;\n            if (!isVisible(el, win)) continue;\n\n            const cs = win.getComputedStyle(el);\n\n            // Content-collision candidate: any visible element with its own text.\n            // Collected here (before the contrast/clip early-continues) so every\n            // text element is considered for overlap.\n            if (this.emitOverlap && overlapCandidates.length < MAX_OVERLAP_CANDIDATES) {\n                const lines = directTextLineRects(el, doc);\n                if (lines.length > 0) {\n                    overlapCandidates.push({\n                        id,\n                        order: i,\n                        floating: isFloatingOverlay(el, win),\n                        lines,\n                        sample: text.slice(0, MAX_SAMPLE_LEN),\n                    });\n                }\n            }\n\n            // Clipped/cut-off content (independent of contrast).\n            if (this.emitClip && !this.clippedIds.has(id)) {\n                const axis = clippedAxis(el, cs);\n                if (axis) {\n                    this.clippedIds.add(id);\n                    this.emitCount++;\n                    this.emitClip({ id, axis, sample: text.slice(0, MAX_SAMPLE_LEN) });\n                }\n            }\n\n            if (this.emittedIds.has(id)) continue;\n            const fg = parseRgb(cs.color);\n            if (!fg) continue;\n            const bg = resolveBackground(el, win);\n            if (!bg) continue;\n\n            const ratio = contrastRatio(fg, bg);\n            if (ratio > INVISIBLE_CONTRAST_MAX) continue;\n\n            this.emittedIds.add(id);\n            this.emitCount++;\n            this.emit({\n                id,\n                fg: `rgb(${Math.round(fg.r)}, ${Math.round(fg.g)}, ${Math.round(fg.b)})`,\n                bg: `rgb(${Math.round(bg.r)}, ${Math.round(bg.g)}, ${Math.round(bg.b)})`,\n                ratio,\n                sample: text.slice(0, MAX_SAMPLE_LEN),\n            });\n        }\n\n        if (this.emitOverlap) this.detectOverlaps(overlapCandidates);\n        if (this.emitMisalign) this.detectMisalignedTables(doc, win);\n        if (this.emitBrokenAsset) this.detectBrokenImages(doc, win);\n    }\n\n    // Catch-up scan for failed images. The tracker's capture-phase `error`\n    // listener only hears failures that happen AFTER it attaches, so any image\n    // that 404'd during initial load (the hero, above-the-fold art) is missed —\n    // the error already fired. This polls the live DOM instead: a loaded-but-\n    // zero-size image is a definitively broken one, regardless of when it failed.\n    private detectBrokenImages(doc: Document, win: Window): void {\n        if (!this.emitBrokenAsset) return;\n        const imgs = doc.querySelectorAll('img');\n        const limit = Math.min(imgs.length, MAX_IMAGES_PER_SCAN);\n        for (let i = 0; i < limit; i++) {\n            if (this.emitCount >= MAX_EMITS_PER_SESSION) return;\n            const img = imgs[i] as HTMLImageElement;\n            const url = img.currentSrc || img.src || img.getAttribute('src') || '';\n            if (!url || this.brokenAssetUrls.has(url)) continue;\n            // complete + naturalWidth 0 = the load finished and failed (a\n            // still-loading image has complete === false).\n            if (!img.complete || img.naturalWidth !== 0) continue;\n            if (!isVisible(img, win)) continue;\n            // A broken image often COLLAPSES to its alt-text size (the browser\n            // ignores the width/height attrs once the decode fails), so the\n            // rendered box understates the real image. Judge intent by the max\n            // of the rendered box and the declared width/height — still small\n            // enough to skip 1x1 tracking pixels, but keeps a collapsed hero.\n            const rect = img.getBoundingClientRect();\n            const attrW = parseInt(img.getAttribute('width') || '', 10);\n            const attrH = parseInt(img.getAttribute('height') || '', 10);\n            const w = Math.max(rect.width, Number.isFinite(attrW) ? attrW : 0);\n            const h = Math.max(rect.height, Number.isFinite(attrH) ? attrH : 0);\n            if (w < MIN_BROKEN_IMG_PX || h < MIN_BROKEN_IMG_PX) continue;\n            // Emit even when the rrweb mirror has not yet assigned an id (id = -1\n            // early in the session). The failed URL is the identity that matters\n            // for the broken_asset signal; the compiler tolerates a missing id.\n            const id = this.getId(img);\n            this.brokenAssetUrls.add(url);\n            this.emitCount++;\n            this.emitBrokenAsset({ id, tag: 'img', url });\n        }\n    }\n\n    // Column-misalignment pass: for each visible <table>, the cells in a given\n    // column should share a left edge (table layout guarantees it). When one\n    // column's cell left-edges span more than the threshold, a positioning bug\n    // shifted that column. Cells with colspan/rowspan are skipped (merges break\n    // alignment legitimately), and irregular tables are ignored entirely.\n    private detectMisalignedTables(doc: Document, win: Window): void {\n        if (!this.emitMisalign) return;\n        const tables = doc.querySelectorAll('table');\n        const limit = Math.min(tables.length, MAX_TABLES_PER_SCAN);\n        for (let t = 0; t < limit; t++) {\n            if (this.emitCount >= MAX_EMITS_PER_SESSION) return;\n            const table = tables[t];\n            const id = this.getId(table);\n            if (id < 0 || this.misalignedIds.has(id)) continue;\n            if (!isVisible(table, win)) continue;\n\n            const rows = table.querySelectorAll('tr');\n            if (rows.length < 2) continue;\n\n            const colLefts = new Map<number, number[]>();\n            let irregular = false;\n            for (let r = 0; r < rows.length && !irregular; r++) {\n                const cells = rows[r].children;\n                for (let c = 0; c < cells.length; c++) {\n                    const cell = cells[c] as HTMLElement;\n                    if (cell.tagName !== 'TD' && cell.tagName !== 'TH') continue;\n                    const colspan = parseInt(cell.getAttribute('colspan') || '1', 10);\n                    const rowspan = parseInt(cell.getAttribute('rowspan') || '1', 10);\n                    if (colspan !== 1 || rowspan !== 1) { irregular = true; break; }\n                    const rect = cell.getBoundingClientRect();\n                    if (rect.width <= 0 || rect.height <= 0) continue;\n                    const arr = colLefts.get(c) ?? [];\n                    arr.push(rect.left);\n                    colLefts.set(c, arr);\n                }\n            }\n            if (irregular) continue;\n\n            let worst = 0;\n            let worstCol = -1;\n            for (const [c, xs] of colLefts) {\n                if (xs.length < 2) continue;\n                const spread = Math.max(...xs) - Math.min(...xs);\n                if (spread > worst) { worst = spread; worstCol = c; }\n            }\n            if (worst >= MISALIGN_MIN_PX && worstCol >= 0) {\n                this.misalignedIds.add(id);\n                this.emitCount++;\n                this.emitMisalign({ id, sample: `column ${worstCol + 1} cells misaligned by ${Math.round(worst)}px` });\n            }\n        }\n    }\n\n    // Pairwise text-collision pass over the scan's candidates. A pair fires only\n    // when neither element is a deliberate overlay and their glyph rects\n    // genuinely intersect. The element later in document order is reported as\n    // the one painted on top.\n    private detectOverlaps(candidates: OverlapCandidate[]): void {\n        if (!this.emitOverlap) return;\n        for (let i = 0; i < candidates.length; i++) {\n            for (let j = i + 1; j < candidates.length; j++) {\n                if (this.emitCount >= MAX_EMITS_PER_SESSION) return;\n                const a = candidates[i];\n                const b = candidates[j];\n                // Intentional layering (either side an overlay) is not a bug.\n                if (a.floating || b.floating) continue;\n                if (!textLinesCollide(a.lines, b.lines)) continue;\n                const key = a.id < b.id ? `${a.id}-${b.id}` : `${b.id}-${a.id}`;\n                if (this.overlapPairs.has(key)) continue;\n                this.overlapPairs.add(key);\n                this.emitCount++;\n                const top = a.order >= b.order ? a : b;\n                const other = top === a ? b : a;\n                this.emitOverlap({ id: top.id, id2: other.id, sample: top.sample, sample2: other.sample });\n            }\n        }\n    }\n\n    dispose(): void {\n        if (this.timer) clearInterval(this.timer);\n        this.timer = null;\n    }\n}\n","export enum LogLevel {\n  NONE = 0,\n  ERROR = 1,\n  WARN = 2,\n  INFO = 3,\n  DEBUG = 4\n}\n\nexport interface LoggerConfig {\n  level: LogLevel;\n  enableConsole: boolean;\n  enableStorage: boolean;\n}\n\nclass Logger {\n  private config: LoggerConfig = {\n    level: LogLevel.ERROR, // Default to only errors in production\n    enableConsole: true,\n    enableStorage: false\n  };\n\n  private isBrowser = typeof window !== 'undefined';\n\n  constructor(config?: Partial<LoggerConfig>) {\n    if (config) {\n      this.config = { ...this.config, ...config };\n    }\n  }\n\n  setConfig(config: Partial<LoggerConfig>): void {\n    this.config = { ...this.config, ...config };\n  }\n\n  private shouldLog(level: LogLevel): boolean {\n    return level <= this.config.level;\n  }\n\n  private formatMessage(level: string, message: string, ...args: any[]): string {\n    const timestamp = new Date().toISOString();\n    return `[HumanBehavior ${level}] ${timestamp}: ${message}`;\n  }\n\n  error(message: string, ...args: any[]): void {\n    if (!this.shouldLog(LogLevel.ERROR)) return;\n    \n    const formattedMessage = this.formatMessage('ERROR', message);\n    \n    if (this.config.enableConsole) {\n      console.error(formattedMessage, ...args);\n    }\n    \n    if (this.config.enableStorage && this.isBrowser) {\n      this.logToStorage(formattedMessage, args);\n    }\n  }\n\n  warn(message: string, ...args: any[]): void {\n    if (!this.shouldLog(LogLevel.WARN)) return;\n    \n    const formattedMessage = this.formatMessage('WARN', message);\n    \n    if (this.config.enableConsole) {\n      console.warn(formattedMessage, ...args);\n    }\n    \n    if (this.config.enableStorage && this.isBrowser) {\n      this.logToStorage(formattedMessage, args);\n    }\n  }\n\n  info(message: string, ...args: any[]): void {\n    if (!this.shouldLog(LogLevel.INFO)) return;\n    \n    const formattedMessage = this.formatMessage('INFO', message);\n    \n    if (this.config.enableConsole) {\n      console.log(formattedMessage, ...args);\n    }\n    \n    if (this.config.enableStorage && this.isBrowser) {\n      this.logToStorage(formattedMessage, args);\n    }\n  }\n\n  debug(message: string, ...args: any[]): void {\n    if (!this.shouldLog(LogLevel.DEBUG)) return;\n    \n    const formattedMessage = this.formatMessage('DEBUG', message);\n    \n    if (this.config.enableConsole) {\n      console.log(formattedMessage, ...args);\n    }\n    \n    if (this.config.enableStorage && this.isBrowser) {\n      this.logToStorage(formattedMessage, args);\n    }\n  }\n\n  private logToStorage(message: string, args: any[]): void {\n    try {\n      const logs = JSON.parse(localStorage.getItem('human_behavior_logs') || '[]');\n      const logEntry = {\n        message,\n        args: args.length > 0 ? args : undefined,\n        timestamp: Date.now()\n      };\n      logs.push(logEntry);\n      \n      // Keep only last 1000 logs to prevent storage bloat\n      if (logs.length > 1000) {\n        logs.splice(0, logs.length - 1000);\n      }\n      \n      localStorage.setItem('human_behavior_logs', JSON.stringify(logs));\n    } catch (e) {\n      // Silently fail if storage is not available\n    }\n  }\n\n  getLogs(): any[] {\n    if (!this.isBrowser) return [];\n    \n    try {\n      return JSON.parse(localStorage.getItem('human_behavior_logs') || '[]');\n    } catch (e) {\n      return [];\n    }\n  }\n\n  clearLogs(): void {\n    if (this.isBrowser) {\n      localStorage.removeItem('human_behavior_logs');\n    }\n  }\n}\n\n// Create singleton instance\nexport const logger = new Logger();\n\n// Global flag to track if SDK is currently logging (prevents self-tracking)\nlet sdkLoggingInProgress = false;\n\n// Export getter for tracker to check\nexport const isSDKLogging = (): boolean => sdkLoggingInProgress;\n\n// Export convenience methods with SDK logging flag\nexport const logError = (message: string, ...args: any[]) => {\n    sdkLoggingInProgress = true;\n    try {\n        logger.error(message, ...args);\n    } finally {\n        sdkLoggingInProgress = false;\n    }\n};\n\nexport const logWarn = (message: string, ...args: any[]) => {\n    sdkLoggingInProgress = true;\n    try {\n        logger.warn(message, ...args);\n    } finally {\n        sdkLoggingInProgress = false;\n    }\n};\n\nexport const logInfo = (message: string, ...args: any[]) => {\n    sdkLoggingInProgress = true;\n    try {\n        logger.info(message, ...args);\n    } finally {\n        sdkLoggingInProgress = false;\n    }\n};\n\nexport const logDebug = (message: string, ...args: any[]) => {\n    sdkLoggingInProgress = true;\n    try {\n        logger.debug(message, ...args);\n    } finally {\n        sdkLoggingInProgress = false;\n    }\n}; ","import { logWarn, logError, logDebug } from './utils/logger';\n\nconst THIRTY_MINUTES = 30 * 60 * 1000;\nconst KEEP_ALIVE_THRESHOLD = 64 * 1024 * 0.8; // 64KB * 0.8 for safety margin\n\n/**\n * Generates a jittered exponential backoff delay in milliseconds\n * \n * The base value is 3 seconds, which is doubled with each retry\n * up to the maximum of 30 minutes\n * \n * Each value then has +/- 50% jitter\n * \n * Giving a range of 3 seconds up to 45 minutes\n */\nexport function pickNextRetryDelay(retriesPerformedSoFar: number): number {\n    const rawBackoffTime = 3000 * 2 ** retriesPerformedSoFar;\n    const minBackoff = rawBackoffTime / 2;\n    const cappedBackoffTime = Math.min(THIRTY_MINUTES, rawBackoffTime);\n    const jitterFraction = Math.random() - 0.5; // A random number between -0.5 and 0.5\n    const jitter = jitterFraction * (cappedBackoffTime - minBackoff);\n    return Math.ceil(cappedBackoffTime + jitter);\n}\n\nexport interface RetriableRequestOptions {\n    url: string;\n    method?: string;\n    headers?: Record<string, string>;\n    body?: string | Blob;\n    retriesPerformedSoFar?: number;\n    estimatedSize?: number;\n    callback?: (response: { statusCode: number; text: string; json?: any }) => void;\n}\n\ninterface RetryQueueElement {\n    retryAt: number;\n    requestOptions: RetriableRequestOptions;\n}\n\nexport class RetryQueue {\n    private _isPolling: boolean = false;\n    private _poller: ReturnType<typeof setTimeout> | undefined;\n    private _pollIntervalMs: number = 3000;\n    private _queue: RetryQueueElement[] = [];\n    private _areWeOnline: boolean;\n    private _sendRequest: (options: RetriableRequestOptions) => Promise<void>;\n\n    constructor(sendRequest: (options: RetriableRequestOptions) => Promise<void>) {\n        this._queue = [];\n        this._areWeOnline = true;\n        this._sendRequest = sendRequest;\n\n        if (typeof window !== 'undefined' && 'onLine' in window.navigator) {\n            this._areWeOnline = window.navigator.onLine;\n\n            window.addEventListener('online', () => {\n                this._areWeOnline = true;\n                this._flush();\n            });\n\n            window.addEventListener('offline', () => {\n                this._areWeOnline = false;\n            });\n        }\n    }\n\n    get length(): number {\n        return this._queue.length;\n    }\n\n    async retriableRequest(options: RetriableRequestOptions): Promise<void> {\n        const retriesPerformedSoFar = options.retriesPerformedSoFar || 0;\n        \n        // Add retry count to URL if retrying\n        if (retriesPerformedSoFar > 0) {\n            const url = new URL(options.url);\n            url.searchParams.set('retry_count', retriesPerformedSoFar.toString());\n            options.url = url.toString();\n        }\n\n        try {\n            await this._sendRequest(options);\n        } catch (error: any) {\n            // Check if we should retry\n            const shouldRetry = this._shouldRetry(error, retriesPerformedSoFar);\n            \n            if (shouldRetry && retriesPerformedSoFar < 10) {\n                this._enqueue(options);\n                return;\n            }\n\n            // Call callback with error if provided\n            if (options.callback) {\n                options.callback({\n                    statusCode: error.status || 0,\n                    text: error.message || 'Request failed'\n                });\n            }\n        }\n    }\n\n    private _shouldRetry(error: any, retriesPerformedSoFar: number): boolean {\n        // Don't retry on client errors (4xx) except for 408, 429\n        if (error.status >= 400 && error.status < 500) {\n            return error.status === 408 || error.status === 429;\n        }\n        \n        // Retry on server errors (5xx) and network errors\n        return error.status >= 500 || !error.status;\n    }\n\n    private _enqueue(requestOptions: RetriableRequestOptions): void {\n        const retriesPerformedSoFar = requestOptions.retriesPerformedSoFar || 0;\n        requestOptions.retriesPerformedSoFar = retriesPerformedSoFar + 1;\n\n        const msToNextRetry = pickNextRetryDelay(retriesPerformedSoFar);\n        const retryAt = Date.now() + msToNextRetry;\n\n        this._queue.push({ retryAt, requestOptions });\n\n        let logMessage = `Enqueued failed request for retry in ${Math.round(msToNextRetry / 1000)}s`;\n        if (typeof navigator !== 'undefined' && !navigator.onLine) {\n            logMessage += ' (Browser is offline)';\n        }\n        logWarn(logMessage);\n\n        if (!this._isPolling) {\n            this._isPolling = true;\n            this._poll();\n        }\n    }\n\n    private _poll(): void {\n        if (this._poller) {\n            clearTimeout(this._poller);\n        }\n        this._poller = setTimeout(() => {\n            if (this._areWeOnline && this._queue.length > 0) {\n                this._flush();\n            }\n            this._poll();\n        }, this._pollIntervalMs);\n    }\n\n    private _flush(): void {\n        const now = Date.now();\n        const notToFlush: RetryQueueElement[] = [];\n        const toFlush = this._queue.filter((item) => {\n            if (item.retryAt < now) {\n                return true;\n            }\n            notToFlush.push(item);\n            return false;\n        });\n\n        this._queue = notToFlush;\n\n        if (toFlush.length > 0) {\n            for (const { requestOptions } of toFlush) {\n                this.retriableRequest(requestOptions).catch((error) => {\n                    logError('Failed to retry request:', error);\n                });\n            }\n        }\n    }\n\n    unload(): void {\n        if (this._poller) {\n            clearTimeout(this._poller);\n            this._poller = undefined;\n        }\n\n        for (const { requestOptions } of this._queue) {\n            try {\n                // Use sendBeacon for unload to ensure requests are sent\n                this._sendBeaconRequest(requestOptions);\n            } catch (e) {\n                logError('Failed to send request via sendBeacon on unload:', e);\n            }\n        }\n        this._queue = [];\n    }\n\n    private _sendBeaconRequest(options: RetriableRequestOptions): void {\n        if (typeof navigator === 'undefined' || !navigator.sendBeacon) {\n            return;\n        }\n\n        try {\n            const url = new URL(options.url);\n            url.searchParams.set('beacon', '1');\n\n            let body: Blob | null = null;\n            if (options.body) {\n                if (typeof options.body === 'string') {\n                    // text/plain: CORS-simple; JSON blobs preflight and die on `*`.\n                    body = new Blob([options.body], { type: 'text/plain' });\n                } else if (options.body instanceof Blob) {\n                    body = options.body;\n                }\n            }\n\n            const success = navigator.sendBeacon(url.toString(), body);\n            if (!success) {\n                logWarn('sendBeacon returned false for unload request');\n            }\n        } catch (error) {\n            logError('Error sending beacon request:', error);\n        }\n    }\n}\n\n","import { logDebug, logWarn } from './utils/logger';\n\nconst STORAGE_KEY_PREFIX = 'human_behavior_';\n\nexport interface QueuedEvent {\n    sessionId: string;\n    events: any[];\n    endUserId?: string | null;\n    windowId?: string;\n    automaticProperties?: any;\n    timestamp: number;\n}\n\nexport class EventPersistence {\n    private storageKey: string;\n    private maxQueueSize: number;\n\n    constructor(apiKey: string, maxQueueSize: number = 1000) {\n        this.storageKey = `${STORAGE_KEY_PREFIX}queue`;\n        this.maxQueueSize = maxQueueSize;\n    }\n\n    /**\n     * Get persisted events from storage\n     */\n    getQueue(): QueuedEvent[] {\n        if (typeof window === 'undefined' || !window.localStorage) {\n            return [];\n        }\n\n        try {\n            const stored = window.localStorage.getItem(this.storageKey);\n            if (!stored) {\n                return [];\n            }\n\n            const queue = JSON.parse(stored);\n            if (!Array.isArray(queue)) {\n                return [];\n            }\n\n            return queue;\n        } catch (error) {\n            logWarn('Failed to read persisted queue:', error);\n            return [];\n        }\n    }\n\n    /**\n     * Save events to storage\n     */\n    setQueue(queue: QueuedEvent[]): void {\n        if (typeof window === 'undefined' || !window.localStorage) {\n            return;\n        }\n\n        try {\n            // Limit queue size\n            const limitedQueue = queue.slice(-this.maxQueueSize);\n            window.localStorage.setItem(this.storageKey, JSON.stringify(limitedQueue));\n            logDebug(`Persisted ${limitedQueue.length} events to storage`);\n        } catch (error: any) {\n            // Handle quota exceeded errors gracefully\n            if (error.name === 'QuotaExceededError' || error.code === 22) {\n                logWarn('Storage quota exceeded, clearing old events');\n                try {\n                    // Try to save a smaller queue\n                    const smallerQueue = queue.slice(-Math.floor(this.maxQueueSize / 2));\n                    window.localStorage.setItem(this.storageKey, JSON.stringify(smallerQueue));\n                } catch (e) {\n                    logWarn('Failed to save smaller queue, clearing storage');\n                    this.clearQueue();\n                }\n            } else {\n                logWarn('Failed to persist queue:', error);\n            }\n        }\n    }\n\n    /**\n     * Add event to persisted queue\n     */\n    addToQueue(event: QueuedEvent): void {\n        const queue = this.getQueue();\n        queue.push(event);\n\n        // Remove oldest events if queue is too large\n        if (queue.length > this.maxQueueSize) {\n            queue.shift();\n            logDebug('Queue is full, the oldest event is dropped.');\n        }\n\n        this.setQueue(queue);\n    }\n\n    /**\n     * Remove events from queue (after successful send)\n     */\n    removeFromQueue(count: number): void {\n        const queue = this.getQueue();\n        queue.splice(0, count);\n        this.setQueue(queue);\n    }\n\n    /**\n     * Clear persisted queue\n     */\n    clearQueue(): void {\n        if (typeof window === 'undefined' || !window.localStorage) {\n            return;\n        }\n\n        try {\n            window.localStorage.removeItem(this.storageKey);\n        } catch (error) {\n            logWarn('Failed to clear persisted queue:', error);\n        }\n    }\n\n    /**\n     * Get queue length\n     */\n    getQueueLength(): number {\n        return this.getQueue().length;\n    }\n}\n\n","import { logError, logInfo, logDebug, logWarn } from './utils/logger';\nimport { v1 as uuidv1 } from 'uuid';\nimport { RetryQueue, RetriableRequestOptions } from './retry-queue';\nimport { EventPersistence, QueuedEvent } from './persistence';\nimport type { ErrorReport } from './errors/error-payload';\n\n// SDK version will be replaced at build time by Rollup replace plugin\nconst SDK_VERSION = '__SDK_VERSION__';\n\nexport const MAX_CHUNK_SIZE_BYTES = 1024 * 1024; // 1MB chunk size - more conservative\nconst KEEP_ALIVE_THRESHOLD = 64 * 1024 * 0.8; // 64KB * 0.8 for safety margin\nconst REQUEST_TIMEOUT_MS = 10000; // 10 seconds default timeout\n// CORS-simple type. application/json forces a preflight sendBeacon cannot\n// complete against Access-Control-Allow-Origin: *.\nconst BEACON_MIME = 'text/plain';\n\n/**\n * Fire-and-forget POST that survives page unload, without navigator.sendBeacon's\n * spec-mandated `credentials: include` — which browsers reject outright against\n * this SDK's wildcard-origin CORS policy (arbitrary customer domains, no cookies\n * needed since auth travels in the body as apiKey). `fetch` + `keepalive` gives\n * the same unload-survival guarantee while letting us omit credentials.\n * Falls back to sendBeacon in environments without fetch/keepalive support.\n */\nfunction sendKeepaliveBeacon(url: string, blob: Blob): boolean {\n    if (typeof fetch === 'function') {\n        fetch(url, {\n            method: 'POST',\n            body: blob,\n            keepalive: true,\n            credentials: 'omit',\n        }).catch(() => {\n            // Fire-and-forget: failures are silent, same as sendBeacon.\n        });\n        return true;\n    }\n    if (typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function') {\n        return navigator.sendBeacon(url, blob);\n    }\n    return false;\n}\n\n// One encoder for the process instead of one per call — the old code allocated\n// a TextEncoder per event, on the main thread, for no benefit.\n//\n// Created lazily rather than at module scope on purpose: this module is also\n// imported in SSR/bundler contexts, and a module-level `new TextEncoder()` would\n// turn a missing global into an import-time crash instead of the call-time one\n// the previous code had. Same failure semantics, one allocation.\nlet textEncoder: TextEncoder | null = null;\n\nfunction encodedByteLength(value: any): number {\n    if (!textEncoder) textEncoder = new TextEncoder();\n    return textEncoder.encode(safeJsonStringify(value)).length;\n}\n\n/**\n * Running byte total for the chunk being assembled.\n *\n * This replaces a per-event `isChunkSizeExceeded(currentChunk, event)` that\n * re-serialized and re-UTF8-encoded the ENTIRE accumulated chunk to decide\n * whether one more event fit. That made chunking O(N^2) in both time and\n * allocation for a chunk of N events, on the main thread, on every flush.\n * Measured on realistic rrweb DOM-mutation events (~424 B each):\n *\n *     events |  before |   after\n *        200 |    55 ms |  1.5 ms\n *        500 |   358 ms |  3.8 ms\n *       1000 |  1374 ms |  7.0 ms\n *       2000 |  5504 ms | 13.8 ms\n *\n * The queue flushes at ~800-1000 events (see MAX_QUEUE_SIZE in tracker.ts), and\n * p95 sessions carry ~10k rrweb events, so the 1000-event row was the steady\n * state on a busy page. Every flush was a long task by Google's 50 ms\n * definition, which lands on INP — a metric this SDK also reports.\n *\n * Byte-exactness matters, because the chunk boundaries this picks must match\n * what the old full-measure produced or payload sizes shift. The total is\n * therefore built from the same pieces JSON.stringify emits:\n *\n *   - `envelopeBytes` — `{\"sessionId\":\"…\",\"events\":[]}` with an empty array.\n *   - each event's own encoded length.\n *   - one byte for the `,` separator before every event after the first.\n *\n * Verified against the previous implementation across 5 event-shape scenarios\n * (uniform small, uniform medium, mixed, few-huge, exact-boundary): identical\n * chunk boundaries in every case, no chunk over MAX_CHUNK_SIZE_BYTES. See\n * `__tests__/chunk-size.test.ts`, which pins that equivalence.\n */\nexport function createChunkByteCounter(sessionId: string) {\n    const envelopeBytes = encodedByteLength({ sessionId, events: [] });\n    let totalBytes = envelopeBytes;\n    let eventCount = 0;\n\n    // The caller always asks `wouldExceed(e)` and then commits the same `e` via\n    // add() or reset([e]), so without a memo every event is serialized twice.\n    // One slot, keyed by object identity — exact, and it falls back to a fresh\n    // measure for any other call order.\n    let lastEvent: any = null;\n    let lastEventBytes = 0;\n\n    function eventBytes(event: any): number {\n        if (event !== lastEvent) {\n            lastEvent = event;\n            lastEventBytes = encodedByteLength(event);\n        }\n        return lastEventBytes;\n    }\n\n    return {\n        /** Would adding `event` push the serialized chunk past the cap? */\n        wouldExceed(event: any): boolean {\n            const separator = eventCount > 0 ? 1 : 0;\n            return totalBytes + eventBytes(event) + separator > MAX_CHUNK_SIZE_BYTES;\n        },\n        /** Account for an event that has been pushed onto the chunk. */\n        add(event: any): void {\n            totalBytes += eventBytes(event) + (eventCount > 0 ? 1 : 0);\n            eventCount++;\n        },\n        /** Start a fresh chunk. */\n        reset(events: any[] = []): void {\n            totalBytes = envelopeBytes;\n            eventCount = 0;\n            for (const event of events) this.add(event);\n        },\n    };\n}\n\nexport function validateSingleEventSize(event: any, sessionId: string): void {\n    const singleEventSize = encodedByteLength({ sessionId, events: [event] });\n\n    if (singleEventSize > MAX_CHUNK_SIZE_BYTES) {\n        // Instead of throwing, log a warning and suggest reducing event size\n        logWarn(`Single event size (${singleEventSize} bytes) exceeds maximum chunk size (${MAX_CHUNK_SIZE_BYTES} bytes). Consider reducing event data size.`);\n    }\n}\n\n\n\n\n\n/**\n * Safe JSON stringify that handles BigInt values\n */\nfunction safeJsonStringify(data: any): string {\n    return JSON.stringify(data, (_, value) => {\n        if (typeof value === 'bigint') {\n            return value.toString();\n        }\n        return value;\n    });\n}\n\nexport function splitLargeEvent(event: any, sessionId: string): any[] {\n    // ✅ SIMPLE VALIDATION\n    if (!event || typeof event !== 'object') {\n        return [];\n    }\n    \n    const eventSize = encodedByteLength({ sessionId, events: [event] });\n\n    if (eventSize <= MAX_CHUNK_SIZE_BYTES) {\n        return [event];\n    }\n\n    // If event is too large, try to split it by removing large properties\n    const simplifiedEvent = { ...event };\n    \n    // Remove potentially large properties\n    const largeProperties = ['screenshot', 'html', 'dom', 'fullText', 'innerHTML', 'outerHTML'];\n    largeProperties.forEach(prop => {\n        if (simplifiedEvent[prop]) {\n            delete simplifiedEvent[prop];\n        }\n    });\n\n    // Check if simplified event is now small enough\n    const simplifiedSize = encodedByteLength({ sessionId, events: [simplifiedEvent] });\n\n    if (simplifiedSize <= MAX_CHUNK_SIZE_BYTES) {\n        return [simplifiedEvent];\n    }\n\n    // If still too large, create a minimal event\n    const minimalEvent = {\n        type: event.type,\n        timestamp: event.timestamp,\n        url: event.url,\n        pathname: event.pathname,\n        // Keep only essential properties\n        ...Object.fromEntries(\n            Object.entries(event).filter(([key, value]) => \n                !largeProperties.includes(key) && \n                typeof value !== 'object' && \n                typeof value !== 'string' || \n                (typeof value === 'string' && value.length < 1000)\n            )\n        )\n    };\n\n    return [minimalEvent];\n}\n\nexport class HumanBehaviorAPI {\n    private apiKey: string;\n    private baseUrl: string;\n    private monthlyLimitReached: boolean = false;\n    private throttledUntil: number = 0;\n    private sessionId: string = '';\n    private endUserId: string | null = null;\n    private cspBlocked: boolean = false; // Track if CSP is blocking requests\n    // Probation counter for the sticky beacon-only mode: reset on any\n    // successful fetch, so one transient failure never degrades the session.\n    private consecutiveFetchFailures: number = 0;\n    private retryQueue: RetryQueue;\n    private persistence: EventPersistence;\n    private requestTimeout: number = REQUEST_TIMEOUT_MS;\n    private currentBatchSize: number = 100; // Dynamic batch size for 413 handling\n    private _isDrainingPersisted: boolean = false; // Guards against overlapping persisted-queue drains\n\n    constructor({ apiKey, ingestionUrl }: { apiKey: string, ingestionUrl: string }) {\n        this.apiKey = apiKey;\n        this.baseUrl = ingestionUrl;\n        this.persistence = new EventPersistence(apiKey);\n        this.retryQueue = new RetryQueue((options) => this._sendRequestInternal(options));\n\n        // Drain any events persisted by a previous session/tab, and again\n        // whenever connectivity returns. The tracker also drives periodic\n        // in-session drains via flushPersistedEvents() so a failed batch is not\n        // stranded in storage until the next page load.\n        this.flushPersistedEvents();\n        if (typeof window !== 'undefined' && typeof window.addEventListener === 'function') {\n            window.addEventListener('online', () => {\n                this.flushPersistedEvents();\n            });\n        }\n    }\n\n    /**\n     * Set session and user IDs for tracking context\n     */\n    public setTrackingContext(sessionId: string, endUserId: string | null): void {\n        this.sessionId = sessionId;\n        this.endUserId = endUserId;\n    }\n\n    /**\n     * Drain the durable (localStorage) event queue: resend each persisted batch\n     * oldest-first and remove it only once the server confirms receipt.\n     *\n     * Safe to call repeatedly — a concurrency guard prevents overlapping drains,\n     * and a batch is removed only after a confirmed send, so nothing is lost on\n     * failure. We stop at the first failure to preserve ordering and to back off\n     * instead of hammering a still-down server (the caller retries on the next\n     * flush tick / `online` event). Unlike sendEventsChunked, the send here never\n     * re-persists on failure — the batch is already durable in storage.\n     */\n    public async flushPersistedEvents(): Promise<void> {\n        if (this._isDrainingPersisted) {\n            return;\n        }\n        if (!this.checkMonthlyLimit()) {\n            return;\n        }\n        const persistedQueue = this.persistence.getQueue();\n        if (persistedQueue.length === 0) {\n            return;\n        }\n\n        this._isDrainingPersisted = true;\n        try {\n            logDebug(`Draining ${persistedQueue.length} persisted event batch(es) from storage`);\n            for (const queuedEvent of persistedQueue) {\n                const sent = await this._sendPersistedBatch(queuedEvent);\n                if (!sent) {\n                    // Leave this (and everything after it) in storage for a later drain.\n                    break;\n                }\n                this.persistence.removeFromQueue(1);\n            }\n        } finally {\n            this._isDrainingPersisted = false;\n        }\n    }\n\n    /**\n     * Send a single persisted batch exactly once. Returns true only when the\n     * server confirms receipt. Never re-persists on failure (the batch is\n     * already durable in storage) — the caller keeps it queued for the next\n     * drain, so this cannot duplicate the localStorage entry.\n     */\n    private async _sendPersistedBatch(item: QueuedEvent): Promise<boolean> {\n        if (this.isThrottled()) {\n            return false; // stays queued; drained after the throttle window\n        }\n        try {\n            const validEvents = (item.events || []).filter((e) => e && typeof e === 'object');\n            if (validEvents.length === 0) {\n                return true; // nothing to send — treat as drained\n            }\n            const response = await this.trackedFetch(`${this.baseUrl}/api/ingestion/events`, {\n                method: 'POST',\n                headers: {\n                    'Content-Type': 'application/json',\n                    'Authorization': `Bearer ${this.apiKey}`\n                },\n                body: safeJsonStringify({\n                    sessionId: item.sessionId,\n                    events: validEvents,\n                    endUserId: item.endUserId ?? null,\n                    windowId: item.windowId,\n                    automaticProperties: item.automaticProperties,\n                    sdkVersion: SDK_VERSION\n                })\n            });\n\n            if (!response.ok) {\n                if (response.status === 429) {\n                    await this._apply429(response);\n                }\n                return false;\n            }\n\n            try {\n                const responseJson = await response.json();\n                if (responseJson && responseJson.monthlyLimitReached === true) {\n                    this.monthlyLimitReached = true;\n                }\n            } catch {\n                // Non-JSON success body — fine, the send was accepted.\n            }\n            return true;\n        } catch {\n            return false;\n        }\n    }\n\n    /**\n     * Internal method to send request (used by retry queue)\n     */\n    private async _sendRequestInternal(options: RetriableRequestOptions): Promise<void> {\n        const controller = typeof AbortController !== 'undefined' ? new AbortController() : null;\n        let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n        if (controller) {\n            timeoutId = setTimeout(() => {\n                controller!.abort();\n            }, this.requestTimeout);\n        }\n\n        try {\n            const estimatedSize = options.estimatedSize || 0;\n            const useKeepalive = options.method === 'POST' && estimatedSize < KEEP_ALIVE_THRESHOLD;\n\n            const response = await fetch(options.url, {\n                method: options.method || 'GET',\n                headers: options.headers || {},\n                body: options.body,\n                signal: controller?.signal,\n                keepalive: useKeepalive\n            });\n\n            if (timeoutId) {\n                clearTimeout(timeoutId);\n            }\n\n            const responseText = await response.text();\n            let responseJson: any = null;\n            \n            try {\n                responseJson = JSON.parse(responseText);\n            } catch {\n                // Not JSON, ignore\n            }\n\n            if (options.callback) {\n                options.callback({\n                    statusCode: response.status,\n                    text: responseText,\n                    json: responseJson\n                });\n            }\n\n            if (!response.ok) {\n                throw { status: response.status, message: responseText };\n            }\n        } catch (error: any) {\n            if (timeoutId) {\n                clearTimeout(timeoutId);\n            }\n\n            if (error.name === 'AbortError') {\n                throw { status: 0, message: 'Request timeout' };\n            }\n            throw error;\n        }\n    }\n\n    /**\n     * Handle unload - send pending retries via sendBeacon\n     */\n    public unload(): void {\n        this.retryQueue.unload();\n    }\n\n    private checkMonthlyLimit(): boolean {\n        if (this.monthlyLimitReached) {\n            return false;\n        }\n        return true;\n    }\n\n    private isThrottled(): boolean {\n        return Date.now() < this.throttledUntil;\n    }\n\n    /**\n     * A 429 is rate-limit throttling unless the body explicitly says the\n     * monthly limit was hit. Throttling pauses sends for the server-provided\n     * Retry-After window; persisted/queued events drain once it passes.\n     */\n    private async _apply429(response: Response): Promise<void> {\n        let body: any = null;\n        try {\n            body = await response.clone().json();\n        } catch {\n            // Non-JSON body — treat as plain throttling.\n        }\n        if (body && body.monthlyLimitReached === true) {\n            this.monthlyLimitReached = true;\n            return;\n        }\n        const headerValue = response.headers?.get?.('Retry-After');\n        const seconds = Number(headerValue ?? body?.retryAfterSeconds);\n        const waitSeconds = Number.isFinite(seconds) && seconds > 0 ? Math.min(seconds, 300) : 30;\n        this.throttledUntil = Date.now() + waitSeconds * 1000;\n        logInfo(`Rate limited (429), pausing sends for ${waitSeconds}s`);\n    }\n\n    public async init(sessionId: string, userId: string | null) {\n        // Check if monthly limit is already reached - silently skip if so\n        if (!this.checkMonthlyLimit()) {\n            // Silently return success to avoid any errors\n            return {\n                sessionId: sessionId,\n                endUserId: userId\n            };\n        }\n\n        // Get current page URL and referrer if in browser environment\n        let entryURL = null;\n        let referrer = null;\n        \n        if (typeof window !== 'undefined') {\n            entryURL = window.location.href;\n            referrer = document.referrer;\n        }\n\n        logInfo('API init called with:', { sessionId, userId, entryURL, referrer, baseUrl: this.baseUrl });\n\n        try {\n        const response = await this.trackedFetch(`${this.baseUrl}/api/ingestion/init`, {\n            method: 'POST',\n            headers: {\n                'Content-Type': 'application/json',\n                'Authorization': `Bearer ${this.apiKey}`,\n                'Referer': referrer || ''\n            },\n            body: safeJsonStringify({\n                sessionId: sessionId,\n                endUserId: userId,\n                entryURL: entryURL,\n                referrer: referrer,\n                sdkVersion: SDK_VERSION // Include SDK version for tracking\n            })\n        });\n\n            logInfo('API init response status:', response.status);\n\n        if (!response.ok) {\n            if (response.status === 429) {\n                await this._apply429(response);\n                // Silently return success to avoid any errors\n                return {\n                    sessionId: sessionId,\n                    endUserId: userId\n                };\n            }\n            const errorText = await response.text();\n            logError('API init failed:', response.status, errorText);\n            throw new Error(`Failed to initialize ingestion: ${response.statusText} - ${errorText}`);\n        } \n\n        const responseJson = await response.json();\n        \n        // Check for monthly limit flag in successful response\n        if (responseJson.monthlyLimitReached === true) {\n            this.monthlyLimitReached = true;\n            logInfo('Monthly limit reached detected from server response');\n        }\n        \n        logInfo('API init success:', responseJson);\n        return {\n            sessionId: responseJson.sessionId,\n            endUserId: responseJson.endUserId\n        };\n        } catch (error) {\n            logError('API init error:', error);\n            throw error;\n        }\n    }\n\n    /**\n     * Server detects IP from HTTP requests automatically\n     */\n\n    async sendEvents(events: any[], sessionId: string, userId: string) {\n        // ✅ SIMPLE VALIDATION FOR ALL EVENTS\n        const validEvents = events.filter(event => event && typeof event === 'object');\n        \n        const response = await this.trackedFetch(`${this.baseUrl}/api/ingestion/events`, {\n            method: 'POST',\n            headers: {\n                'Content-Type': 'application/json',\n                'Authorization': `Bearer ${this.apiKey}`\n            },\n            body: safeJsonStringify({\n                sessionId,\n                events: validEvents,\n                endUserId: userId,\n                sdkVersion: SDK_VERSION // Include SDK version for tracking\n            })\n        });\n        \n        if (!response.ok) {\n            if (response.status === 429) {\n                await this._apply429(response);\n                throw new Error(`429: rate limited`);\n            }\n            throw new Error(`Failed to send events: ${response.statusText}`);\n        }\n        \n        // Check for monthly limit flag in successful response\n        const responseJson = await response.json();\n        if (responseJson.monthlyLimitReached === true) {\n            this.monthlyLimitReached = true;\n            logInfo('Monthly limit reached detected from events response');\n        }\n    }\n    \n    async sendEventsChunked(events: any[], sessionId: string, userId?: string, windowId?: string, automaticProperties?: any) {\n        // Check if monthly limit is already reached - silently skip if so\n        if (!this.checkMonthlyLimit()) {\n            // Silently return success to avoid any errors\n            return [];\n        }\n        try {\n            const results = [];\n            let currentChunk: any[] = [];\n            // Tracks currentChunk's serialized size incrementally. Must be kept\n            // in step with every mutation of currentChunk below — add() on\n            // push, reset() on cut.\n            const chunkBytes = createChunkByteCounter(sessionId);\n\n            for (const event of events) {\n                // ✅ SIMPLE VALIDATION FOR ALL EVENTS\n                if (!event || typeof event !== 'object') {\n                    continue;\n                }\n\n                if (chunkBytes.wouldExceed(event)) {\n                    // If current chunk is not empty, send it first\n                    if (currentChunk.length > 0) {\n                        logDebug(`[SDK] Sending chunk with ${currentChunk.length} events`);\n                        const response = await this.trackedFetch(`${this.baseUrl}/api/ingestion/events`, {\n                            method: 'POST',\n                            headers: {\n                                'Content-Type': 'application/json',\n                                'Authorization': `Bearer ${this.apiKey}`\n                            },\n                            body: safeJsonStringify({\n                                sessionId,\n                                events: currentChunk,\n                                endUserId: userId,\n                                windowId: windowId,\n                                automaticProperties: automaticProperties, // Include automatic properties for user creation\n                                sdkVersion: SDK_VERSION // Include SDK version for tracking\n                            })\n                        });\n                        \n                        if (!response.ok) {\n                            if (response.status === 429) {\n                                await this._apply429(response);\n                                // Throw so the outer catch persists the events for retry\n                                throw new Error(`429: rate limited`);\n                            }\n                            throw new Error(`Failed to send events: ${response.statusText}`);\n                        }\n                        \n                        const responseJson = await response.json();\n                        \n                        // Check for monthly limit flag in successful response\n                        if (responseJson.monthlyLimitReached === true) {\n                            this.monthlyLimitReached = true;\n                            logInfo('Monthly limit reached detected from chunked events response');\n                        }\n                        \n                        results.push(responseJson);\n                        currentChunk = [];\n                    }\n\n                    // Handle large events by splitting them\n                    const splitEvents = splitLargeEvent(event, sessionId);\n\n                    // Start new chunk with the split events. Note this is the\n                    // split result, not [event] — and as before, an oversized\n                    // splitEvents is NOT re-split, it just becomes the chunk.\n                    // Preserved deliberately: changing it would change payloads.\n                    currentChunk = splitEvents;\n                    chunkBytes.reset(splitEvents);\n                } else {\n                    // Add event to current chunk\n                    currentChunk.push(event);\n                    chunkBytes.add(event);\n                }\n            }\n            \n            // Send any remaining events\n            if (currentChunk.length > 0) {\n                const result = await this._sendChunkWithRetry(\n                    currentChunk,\n                    sessionId,\n                    userId,\n                    windowId,\n                    automaticProperties || currentChunk[0]?.automaticProperties\n                );\n                if (result) {\n                    results.push(result);\n                }\n            }\n            \n            return results.flat();\n        } catch (error) {\n            logError('Error sending events:', error);\n            // Persist failed events for retry\n            this._persistEvents(events, sessionId, userId, windowId, automaticProperties);\n            throw error;\n        }\n    }\n\n    /**\n     * Send a chunk of events with retry logic and 413 handling\n     */\n    private async _sendChunkWithRetry(\n        chunk: any[],\n        sessionId: string,\n        userId?: string,\n        windowId?: string,\n        automaticProperties?: any\n    ): Promise<any | null> {\n        let batchSize = Math.min(this.currentBatchSize, chunk.length);\n        let startIndex = 0;\n\n        while (startIndex < chunk.length) {\n            const batch = chunk.slice(startIndex, startIndex + batchSize);\n            const payload = {\n                sessionId,\n                events: batch,\n                endUserId: userId,\n                windowId: windowId,\n                automaticProperties: automaticProperties,\n                sdkVersion: SDK_VERSION // Include SDK version for tracking\n            };\n\n            const bodyString = safeJsonStringify(payload);\n            const estimatedSize = new TextEncoder().encode(bodyString).length;\n\n            try {\n                const response = await this.trackedFetch(`${this.baseUrl}/api/ingestion/events`, {\n                    method: 'POST',\n                    headers: {\n                        'Content-Type': 'application/json',\n                        'Authorization': `Bearer ${this.apiKey}`\n                    },\n                    body: bodyString\n                }, estimatedSize);\n                \n                if (!response.ok) {\n                    if (response.status === 429) {\n                        await this._apply429(response);\n                        // Persist remaining events\n                        this._persistEvents(chunk.slice(startIndex), sessionId, userId, windowId, automaticProperties);\n                        return null;\n                    }\n                    \n                    if (response.status === 413) {\n                        // Content too large - reduce batch size and retry\n                        logWarn(`413 error: reducing batch size from ${batchSize} to ${Math.max(1, Math.floor(batchSize / 2))}`);\n                        this.currentBatchSize = Math.max(1, Math.floor(batchSize / 2));\n                        batchSize = this.currentBatchSize;\n                        // Retry with smaller batch\n                        continue;\n                    }\n\n                    // For other errors, use retry queue\n                    await this.retryQueue.retriableRequest({\n                        url: `${this.baseUrl}/api/ingestion/events`,\n                        method: 'POST',\n                        headers: {\n                            'Content-Type': 'application/json',\n                            'Authorization': `Bearer ${this.apiKey}`\n                        },\n                        body: bodyString,\n                        estimatedSize: estimatedSize,\n                        callback: (response) => {\n                            if (response.statusCode === 200 && response.json) {\n                                if (response.json.monthlyLimitReached === true) {\n                                    this.monthlyLimitReached = true;\n                                }\n                            }\n                        }\n                    });\n                    \n                    // Persist for retry\n                    this._persistEvents(chunk.slice(startIndex), sessionId, userId, windowId, automaticProperties);\n                    return null;\n                }\n                \n                const responseJson = await response.json();\n                \n                // Check for monthly limit flag in successful response\n                if (responseJson.monthlyLimitReached === true) {\n                    this.monthlyLimitReached = true;\n                    logInfo('Monthly limit reached detected from chunked events response');\n                }\n                \n                startIndex += batchSize;\n                \n                // If we successfully sent a batch, return the result\n                if (startIndex >= chunk.length) {\n                    return responseJson;\n                }\n            } catch (error: any) {\n                // Network error - use retry queue\n                logWarn('Network error sending chunk, adding to retry queue:', error);\n                await this.retryQueue.retriableRequest({\n                    url: `${this.baseUrl}/api/ingestion/events`,\n                    method: 'POST',\n                    headers: {\n                        'Content-Type': 'application/json',\n                        'Authorization': `Bearer ${this.apiKey}`\n                    },\n                    body: bodyString,\n                    estimatedSize: estimatedSize,\n                    callback: (response) => {\n                        if (response.statusCode === 200 && response.json) {\n                            if (response.json.monthlyLimitReached === true) {\n                                this.monthlyLimitReached = true;\n                            }\n                        }\n                    }\n                });\n                \n                // Persist for retry\n                this._persistEvents(chunk.slice(startIndex), sessionId, userId, windowId, automaticProperties);\n                return null;\n            }\n        }\n\n        return null;\n    }\n\n    /**\n     * Persist events to storage for retry\n     */\n    private _persistEvents(\n        events: any[],\n        sessionId: string,\n        userId?: string,\n        windowId?: string,\n        automaticProperties?: any\n    ): void {\n        if (events.length === 0) {\n            return;\n        }\n\n        this.persistence.addToQueue({\n            sessionId,\n            events,\n            endUserId: userId,\n            windowId,\n            automaticProperties,\n            timestamp: Date.now()\n        });\n    }\n\n    async sendUserData(\n        userId: string,\n        userData: Record<string, any>,\n        sessionId: string,\n        identityToken?: string | null\n    ) {\n        try {\n            const payload: Record<string, any> = {\n                userId: userId,\n                userAttributes: userData,\n                sessionId: sessionId,\n                posthogName: userData.email || userData.name || null // Update user name with email\n            };\n            // Signed by the customer's backend for a user it has already\n            // authenticated. The API key only proves the project, so this is\n            // what lets the server trust an identity claim.\n            if (identityToken) {\n                payload.identityToken = identityToken;\n            }\n            \n            logDebug('Sending user data to server:', { ...payload, identityToken: identityToken ? '[redacted]' : undefined });\n            \n            const response = await this.trackedFetch(`${this.baseUrl}/api/ingestion/user`, {\n                method: 'POST',\n                headers: {\n                    'Content-Type': 'application/json',\n                    'Authorization': `Bearer ${this.apiKey}`\n                },\n                body: safeJsonStringify(payload)\n            });\n            \n            if (!response.ok) {\n                // Identity verification rejections carry the actionable detail\n                // (\"requires a signed identityToken\", \"expired\", ...). Surfacing\n                // only the status text left customers with a bare \"Forbidden\".\n                const detail = await response.text().then(\n                    (text) => {\n                        try {\n                            return JSON.parse(text)?.error ?? text;\n                        } catch {\n                            return text;\n                        }\n                    },\n                    () => ''\n                );\n                throw new Error(\n                    `Failed to send user data: ${response.status} ${response.statusText}${detail ? ` — ${detail}` : ''}`\n                );\n            }\n            \n            const result = await response.json();\n            logDebug('Server response:', result);\n            return result;\n        } catch (error) {\n            logError('Error sending user data:', error);\n            throw error;\n        }\n    }\n\n    /**\n     * Fire a tiny, fire-and-forget beacon to evict this session from the\n     * dashboard's live-presence set. Called from the SDK's `pagehide`\n     * handler. Pass the current `endUserId` (when known) so the server can\n     * evict the user-keyed entry — multi-device/multi-tab presence is\n     * folded onto a single entry on the server side.\n     *\n     * We intentionally do NOT await or check the response: the page is\n     * unloading and the indicator is best-effort. If the beacon never\n     * fires (older browsers, mobile force-quit, browser crash), the entry\n     * still ages out of the live set within ~LIVE_WINDOW_MS on the server.\n     */\n    public sendSessionEndBeacon(sessionId: string, endUserId?: string | null): boolean {\n        try {\n            const payload = {\n                sessionId,\n                endUserId: endUserId || null,\n                apiKey: this.apiKey,\n            };\n            const blob = new Blob([safeJsonStringify(payload)], {\n                type: BEACON_MIME,\n            });\n            return sendKeepaliveBeacon(`${this.baseUrl}/api/ingestion/session-end`, blob);\n        } catch {\n            return false;\n        }\n    }\n\n    /**\n     * Periodic presence ping. Fires from a setInterval inside the SDK\n     * tracker (~30s while the tab is visible) so an idle tab — no DOM\n     * mutations, no input, empty rrweb queue — still refreshes its score\n     * in the dashboard's live set. Without this, a perfectly static page\n     * with a still user would drop out at LIVE_WINDOW_MS even though the\n     * tab is open. Fire-and-forget; failures are silent.\n     */\n    public sendHeartbeatBeacon(sessionId: string, endUserId?: string | null): boolean {\n        try {\n            const payload = {\n                sessionId,\n                endUserId: endUserId || null,\n                apiKey: this.apiKey,\n            };\n            const blob = new Blob([safeJsonStringify(payload)], {\n                type: BEACON_MIME,\n            });\n            return sendKeepaliveBeacon(`${this.baseUrl}/api/ingestion/heartbeat`, blob);\n        } catch {\n            return false;\n        }\n    }\n\n    public sendBeaconEvents(events: any[], sessionId: string, userId?: string, windowId?: string, automaticProperties?: any, groups?: Record<string, string>) {\n        // Create JSON payload that matches the server's expected format\n        // ✅ FIX: Include all fields that sendEventsChunked includes\n        // This ensures sendBeacon requests are processed identically to regular HTTP requests\n        const payload = {\n            sessionId: sessionId,\n            events: events,\n            endUserId: userId || null, // ✅ FIX: Use actual userId instead of hardcoded null\n            windowId: windowId, // ✅ FIX: Include windowId if available\n            automaticProperties: automaticProperties, // ✅ FIX: Include automatic properties for user creation\n            sdkVersion: SDK_VERSION, // ✅ FIX: Include SDK version for tracking\n            apiKey: this.apiKey // Include API key in body since beacon can't use headers\n        };\n\n        // Convert to Blob for sendBeacon\n            const blob = new Blob([safeJsonStringify(payload)], {\n            type: BEACON_MIME\n        });\n\n        const success = navigator.sendBeacon(\n            `${this.baseUrl}/api/ingestion/events`, \n            blob\n        );\n\n        return success;\n    }\n\n    async sendCustomEvent(sessionId: string, eventName: string, eventProperties?: Record<string, any>, endUserId?: string | null, eventId?: string) {\n        logInfo('[SDK] Sending custom event', { sessionId, eventName, eventProperties, endUserId });\n        try {\n            const response = await this.trackedFetch(`${this.baseUrl}/api/ingestion/customEvent`, {\n                method: 'POST',\n                headers: {\n                    'Content-Type': 'application/json',\n                    'Authorization': `Bearer ${this.apiKey}`\n                },\n                body: safeJsonStringify({\n                    sessionId: sessionId,\n                    eventName: eventName,\n                    eventProperties: eventProperties || {},\n                    endUserId: endUserId || null,\n                    // Idempotency ID (same one the batch attempt carried), so\n                    // the server drops this if the batch was already accepted.\n                    ...(eventId ? { eventId } : {})\n                })\n            });\n            \n            logInfo('[SDK] Custom event response', { status: response.status, statusText: response.statusText });\n            \n            if (!response.ok) {\n                const errorText = await response.text();\n                logError('[SDK] Failed to send custom event', { status: response.status, statusText: response.statusText, errorText });\n                throw new Error(`Failed to send custom event: ${response.status} ${response.statusText} - ${errorText}`);\n            }\n            \n            const json = await response.json();\n            logDebug('[SDK] Custom event success', json);\n            return json;\n        } catch (error) {\n            logError('[SDK] Error sending custom event', error, { sessionId, eventName, eventProperties });\n            throw error;\n        }\n    }\n\n    async sendCustomEventBatch(sessionId: string, events: Array<{ eventName: string; eventProperties?: Record<string, any>; eventId?: string }>, endUserId?: string | null) {\n        const body = safeJsonStringify({\n            sessionId: sessionId,\n            events: events,\n            endUserId: endUserId || null\n        });\n        try {\n            const response = await this.trackedFetch(`${this.baseUrl}/api/ingestion/customEvent/batch`, {\n                method: 'POST',\n                headers: {\n                    'Content-Type': 'application/json',\n                    'Authorization': `Bearer ${this.apiKey}`\n                },\n                body\n            });\n            \n            if (!response.ok) {\n                throw new Error(`Failed to send custom event batch: ${response.statusText}`);\n            }\n            \n            return await response.json();\n        } catch (error) {\n            // No retry-queue here on purpose: the tracker already falls back\n            // to per-event sends (and ultimately the durable rrweb queue) when\n            // the batch fails; adding a queued retry would double-send.\n            logError('Error sending custom event batch:', error);\n            throw error;\n        }\n    }\n\n    public sendCustomEventBatchBeacon(\n        sessionId: string,\n        events: Array<{ eventName: string; eventProperties?: Record<string, any>; eventId?: string }>,\n        endUserId?: string | null\n    ): boolean {\n        if (typeof navigator === 'undefined' || typeof navigator.sendBeacon !== 'function') {\n            return false;\n        }\n        try {\n            const payload = {\n                sessionId,\n                events,\n                endUserId: endUserId || null,\n                apiKey: this.apiKey\n            };\n            const blob = new Blob([safeJsonStringify(payload)], {\n                type: BEACON_MIME\n            });\n            return navigator.sendBeacon(\n                `${this.baseUrl}/api/ingestion/customEvent/batch`,\n                blob\n            );\n        } catch {\n            return false;\n        }\n    }\n\n    /**\n     * Send console log (warn/error) to ingestion server\n     */\n    async sendLog(logData: {\n        eventId?: string;\n        level: 'warn' | 'error';\n        message: string;\n        stack?: string;\n        url: string;\n        environment?: string | null;\n        timestampMs: number;\n        sessionId: string;\n        endUserId: string | null;\n        automaticProperties?: Record<string, unknown>;\n    }): Promise<void> {\n        try {\n            logDebug('[SDK] Sending log to server:', { level: logData.level, message: logData.message.substring(0, 50), sessionId: logData.sessionId });\n            \n            if (!this.baseUrl) {\n                return;\n            }\n            \n            if (!logData.sessionId) {\n                return;\n            }\n\n            // Routed through the retry queue (like sendError) so a transient\n            // network failure / 5xx retries with backoff instead of silently\n            // dropping the log. Uses plain fetch internally, so this request\n            // is never re-captured as a network error.\n            const body = safeJsonStringify(logData);\n            await this.retryQueue.retriableRequest({\n                url: `${this.baseUrl}/api/ingestion/logs`,\n                method: 'POST',\n                headers: {\n                    'Content-Type': 'application/json',\n                    'Authorization': `Bearer ${this.apiKey}`\n                },\n                body,\n                estimatedSize: typeof body === 'string' ? body.length : 0\n            });\n        } catch (error) {\n            // Silent fail - don't break app if logging fails\n            logWarn('[SDK] Failed to send log to server:', error);\n        }\n    }\n\n    /**\n     * Send network error to ingestion server\n     */\n    async sendNetworkError(errorData: {\n        requestId: string;\n        url: string;\n        method: string;\n        status: number | null;\n        statusText: string | null;\n        duration: number;\n        timestampMs: number;\n        sessionId: string;\n        endUserId: string | null;\n        errorType: string;\n        errorMessage: string | null;\n        errorName?: string | null;\n        // New span fields\n        startTimeMs?: number;\n        spanName?: string;\n        spanStatus?: 'error' | 'success' | 'slow';\n        attributes?: Record<string, any>;\n        automaticProperties?: Record<string, unknown>;\n    }): Promise<void> {\n        try {\n            logDebug('[SDK] Sending network error to server:', { errorType: errorData.errorType, url: errorData.url.substring(0, 50), sessionId: errorData.sessionId });\n            \n            if (!this.baseUrl) {\n                return;\n            }\n            \n            if (!errorData.sessionId) {\n                return;\n            }\n            \n            // Use regular fetch (not trackedFetch) since this is SDK's own request\n            const response = await fetch(`${this.baseUrl}/api/ingestion/network`, {\n                method: 'POST',\n                headers: {\n                    'Content-Type': 'application/json',\n                    'Authorization': `Bearer ${this.apiKey}`\n                },\n                body: safeJsonStringify(errorData)\n            });\n            \n            if (!response.ok) {\n                logWarn('[SDK] Failed to send network error to server:', response.status, response.statusText);\n            } else {\n                logDebug('[SDK] Network error sent successfully');\n            }\n        } catch (error) {\n            // Silent fail - don't break app if tracking fails\n            logWarn('[SDK] Failed to send network error to server:', error);\n        }\n    }\n\n    /**\n     * Send a batch of tracing spans to the ingestion server. Fire-and-forget,\n     * fails silently. Uses a plain `fetch` (the SDK's own request must not be\n     * re-captured) and posts to the spans batch endpoint.\n     */\n    async sendSpans(\n        spans: unknown[],\n        ctx: { sessionId: string | null; endUserId: string | null; automaticProperties?: Record<string, unknown> },\n    ): Promise<void> {\n        try {\n            if (!this.baseUrl || !ctx.sessionId || !spans || spans.length === 0) return;\n            // Retry queue (like sendError): a transient failure must not drop\n            // a whole span batch, which would silently distort every Traces /\n            // Insights aggregate built on it.\n            const body = safeJsonStringify({\n                sessionId: ctx.sessionId,\n                endUserId: ctx.endUserId,\n                automaticProperties: ctx.automaticProperties,\n                spans,\n            });\n            await this.retryQueue.retriableRequest({\n                url: `${this.baseUrl}/api/ingestion/spans/batch`,\n                method: 'POST',\n                headers: {\n                    'Content-Type': 'application/json',\n                    'Authorization': `Bearer ${this.apiKey}`,\n                },\n                body,\n                estimatedSize: typeof body === 'string' ? body.length : 0,\n            });\n        } catch (error) {\n            logWarn('[SDK] Failed to send spans:', error);\n        }\n    }\n\n    /** sendBeacon variant of `sendSpans` for page unload (synchronous). */\n    public sendSpansBeacon(\n        spans: unknown[],\n        ctx: { sessionId: string | null; endUserId: string | null; automaticProperties?: Record<string, unknown> },\n    ): boolean {\n        if (typeof navigator === 'undefined' || typeof navigator.sendBeacon !== 'function') return false;\n        if (!this.baseUrl || !ctx.sessionId || !spans || spans.length === 0) return false;\n        try {\n            const blob = new Blob(\n                [\n                    safeJsonStringify({\n                        sessionId: ctx.sessionId,\n                        endUserId: ctx.endUserId,\n                        automaticProperties: ctx.automaticProperties,\n                        spans,\n                        apiKey: this.apiKey,\n                    }),\n                ],\n                { type: BEACON_MIME },\n            );\n            return navigator.sendBeacon(`${this.baseUrl}/api/ingestion/spans/batch`, blob);\n        } catch {\n            return false;\n        }\n    }\n\n    /**\n     * Send a captured crash/error report to the ingestion server.\n     *\n     * Routed through the retry queue (not a bare fire-and-forget `fetch`) so a\n     * transient 5xx / network failure is retried with backoff instead of losing\n     * the crash — and anything still queued is flushed via sendBeacon on unload.\n     * The queue does not retry 4xx (except 408/429), which is the correct\n     * behaviour for a malformed report. Retrying a duplicate is safe: the SDK\n     * dedups within a window and the server groups by fingerprint. The queue\n     * uses a plain `fetch` internally, so this request is never re-captured as a\n     * network error. Best-effort and never throws into the host app.\n     */\n    async sendError(report: ErrorReport): Promise<void> {\n        try {\n            logDebug('[SDK] Sending error to server:', {\n                exceptionType: report.exceptionType,\n                mechanism: report.mechanism,\n                sessionId: report.sessionId,\n            });\n\n            if (!this.baseUrl || !report.sessionId) {\n                return;\n            }\n\n            const body = safeJsonStringify(report);\n            await this.retryQueue.retriableRequest({\n                url: `${this.baseUrl}/api/ingestion/errors`,\n                method: 'POST',\n                headers: {\n                    'Content-Type': 'application/json',\n                    'Authorization': `Bearer ${this.apiKey}`\n                },\n                body,\n                estimatedSize: typeof body === 'string' ? body.length : 0\n            });\n        } catch (error) {\n            // Silent fail - don't break app if error reporting fails\n            logWarn('[SDK] Failed to send error to server:', error);\n        }\n    }\n\n    /**\n     * Trigger server-side GeoIP enrichment. Server resolves the IP from request\n     * headers; result is published as a $geoip analytics event that updates\n     * raw_sessions.country/city/region in ClickHouse.\n     */\n    async sendIpInfo(sessionId: string, endUserId: string | null): Promise<void> {\n        try {\n            if (!this.baseUrl || !sessionId || !endUserId) return;\n\n            const response = await fetch(`${this.baseUrl}/api/ingestion/ip-info`, {\n                method: 'POST',\n                headers: {\n                    'Content-Type': 'application/json',\n                    'Authorization': `Bearer ${this.apiKey}`\n                },\n                body: safeJsonStringify({\n                    sessionId,\n                    endUserId,\n                    ipDetectionMethod: 'server_header'\n                })\n            });\n\n            if (!response.ok) {\n                logWarn('[SDK] Failed to send ip-info:', response.status, response.statusText);\n            }\n        } catch (error) {\n            logWarn('[SDK] Failed to send ip-info:', error);\n        }\n    }\n\n    /**\n     * Wrapper for fetch that tracks network errors and falls back to sendBeacon on CSP violations\n     * Skips tracking for SDK's own requests to ingestion server\n     */\n    private async trackedFetch(url: string, options: RequestInit, estimatedSize?: number): Promise<Response> {\n        const requestStartTime = Date.now();\n        const requestId = uuidv1();\n        \n        // ✅ SKIP TRACKING: Don't track SDK's own requests to ingestion server\n        const shouldSkipTracking = this.shouldSkipNetworkTracking(url);\n        \n        // If CSP is already known to be blocking, use sendBeacon directly for POST requests\n        if (this.cspBlocked && options.method === 'POST' && typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function') {\n            return this.trackedFetchWithBeaconFallback(url, options, shouldSkipTracking);\n        }\n        \n        try {\n            const controller = typeof AbortController !== 'undefined' ? new AbortController() : null;\n            let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n            if (controller) {\n                timeoutId = setTimeout(() => {\n                    controller!.abort();\n                }, this.requestTimeout);\n            }\n\n            const useKeepalive = options.method === 'POST' && estimatedSize !== undefined && estimatedSize < KEEP_ALIVE_THRESHOLD;\n\n            const response = await fetch(url, {\n                ...options,\n                signal: controller?.signal,\n                keepalive: useKeepalive\n            });\n\n            if (timeoutId) {\n                clearTimeout(timeoutId);\n            }\n            const requestDuration = Date.now() - requestStartTime;\n            this.consecutiveFetchFailures = 0; // fetch works; clear CSP probation\n            \n            // Track failed requests (4xx, 5xx) AND skip SDK requests\n            if (!response.ok && !shouldSkipTracking) {\n                await this.sendNetworkError({\n                    requestId,\n                    url,\n                    method: options.method || 'GET',\n                    status: response.status,\n                    statusText: response.statusText,\n                    duration: requestDuration,\n                    timestampMs: Date.now(),\n                    sessionId: this.sessionId,\n                    endUserId: this.endUserId,\n                    errorType: this.classifyHttpError(response.status),\n                    errorMessage: response.statusText,\n                    // New span fields\n                    startTimeMs: requestStartTime,\n                    spanName: `${options.method || 'GET'} ${url}`,\n                    spanStatus: 'error',\n                    attributes: {\n                        'http.status_code': response.status,\n                        'http.status_text': response.statusText,\n                    }\n                }).catch(() => {}); // Non-blocking\n            }\n            \n            return response;\n        } catch (error: any) {\n            const requestDuration = Date.now() - requestStartTime;\n            \n            // Handle timeout errors\n            if (error.name === 'AbortError') {\n                const timeoutError: any = new Error('Request timeout');\n                timeoutError.name = 'TimeoutError';\n                error = timeoutError;\n            }\n            \n            // Check if this is a CSP violation\n            const isCSPViolation = this.isCSPViolation(error);\n\n            if (isCSPViolation && options.method === 'POST' && typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function') {\n                // \"Failed to fetch\" is indistinguishable from a transient\n                // network blip, so the STICKY beacon-only mode needs\n                // probation: only flip it after consecutive failures.\n                // Flipping on the first failure permanently degrades the\n                // session to fire-and-forget beacons (no retry, no server\n                // confirmation, 64KB unload quota) — observed to drop\n                // CLS/INP at page close after a single injected failure.\n                this.consecutiveFetchFailures += 1;\n                if (this.consecutiveFetchFailures >= 2) {\n                    this.cspBlocked = true;\n                    logWarn('[SDK] Repeated fetch failures (likely CSP), falling back to sendBeacon for future requests');\n                }\n                // One-shot beacon fallback for THIS request either way, so a\n                // genuine CSP block still delivers.\n                return this.trackedFetchWithBeaconFallback(url, options, shouldSkipTracking);\n            }\n            \n            // Track network errors BUT skip SDK requests\n            if (!shouldSkipTracking) {\n                await this.sendNetworkError({\n                    requestId,\n                    url,\n                    method: options.method || 'GET',\n                    status: null,\n                    statusText: null,\n                    duration: requestDuration,\n                    timestampMs: Date.now(),\n                    sessionId: this.sessionId,\n                    endUserId: this.endUserId,\n                    errorType: this.classifyNetworkError(error),\n                    errorMessage: error.message,\n                    errorName: error.name,\n                    // New span fields\n                    startTimeMs: requestStartTime,\n                    spanName: `${options.method || 'GET'} ${url}`,\n                    spanStatus: 'error',\n                    attributes: {\n                        'error.name': error.name,\n                        'error.message': error.message,\n                    }\n                }).catch(() => {}); // Non-blocking\n            }\n            \n            throw error; // Re-throw to maintain existing error handling\n        }\n    }\n\n    /**\n     * Fallback to sendBeacon when CSP blocks fetch\n     * sendBeacon bypasses CSP connect-src restrictions\n     * Note: sendBeacon is synchronous and fire-and-forget, so we can't await it\n     */\n    private async trackedFetchWithBeaconFallback(url: string, options: RequestInit, shouldSkipTracking: boolean): Promise<Response> {\n        try {\n            // Extract and parse body synchronously (sendBeacon is synchronous)\n            let bodyJson: any = null;\n            let bodyString: string = '';\n            \n            if (options.body) {\n                if (typeof options.body === 'string') {\n                    try {\n                        bodyJson = JSON.parse(options.body);\n                        bodyString = options.body;\n                    } catch {\n                        // If not JSON, use as-is\n                        bodyString = options.body;\n                    }\n                } else if (options.body instanceof Blob) {\n                    // Can't read Blob synchronously, so we'll need to handle this differently\n                    // For now, we'll add apiKey to URL and send the blob as-is\n                    logWarn('[SDK] Cannot extract apiKey from Blob body for sendBeacon, using URL param');\n                    url = `${url}${url.includes('?') ? '&' : '?'}apiKey=${encodeURIComponent(this.apiKey)}`;\n                    const success = navigator.sendBeacon(url, options.body);\n                    // JSON body required: callers `await response.json()` (see below).\n                    return success ? new Response('{\"ok\":true,\"transport\":\"sendBeacon\"}', { status: 200, statusText: 'OK', headers: new Headers({ 'Content-Type': 'application/json' }) })\n                                   : new Response('{\"ok\":false}', { status: 500, statusText: 'sendBeacon failed', headers: new Headers({ 'Content-Type': 'application/json' }) });\n                } else {\n                    // For other types, try to stringify\n                    bodyString = safeJsonStringify(options.body);\n                    bodyJson = options.body;\n                }\n            }\n            \n            // sendBeacon doesn't support headers, so we need to include auth in body or URL\n            // For ingestion endpoints, include apiKey in the body JSON\n            if (url.includes('/api/ingestion/')) {\n                if (bodyJson && typeof bodyJson === 'object') {\n                    // Add API key to body JSON\n                    bodyJson.apiKey = this.apiKey;\n                    bodyString = safeJsonStringify(bodyJson);\n                } else if (bodyString) {\n                    // Try to parse and add apiKey\n                    try {\n                        const parsed = JSON.parse(bodyString);\n                        parsed.apiKey = this.apiKey;\n                        bodyString = safeJsonStringify(parsed);\n                    } catch {\n                        // If parsing fails, append apiKey as query param\n                        url = `${url}${url.includes('?') ? '&' : '?'}apiKey=${encodeURIComponent(this.apiKey)}`;\n                    }\n                } else {\n                    // No body, add apiKey to URL\n                    url = `${url}${url.includes('?') ? '&' : '?'}apiKey=${encodeURIComponent(this.apiKey)}`;\n                }\n            } else {\n                // For non-ingestion endpoints, add apiKey to URL\n                url = `${url}${url.includes('?') ? '&' : '?'}apiKey=${encodeURIComponent(this.apiKey)}`;\n            }\n            \n            // Create Blob with proper Content-Type for sendBeacon\n            const blob = bodyString \n                ? new Blob([bodyString], { type: BEACON_MIME })\n                : null;\n            \n            // Use sendBeacon (synchronous, fire-and-forget)\n            const success = navigator.sendBeacon(url, blob);\n\n            // The synthetic Response MUST carry a JSON body: callers do\n            // `await response.json()`, and a null body makes that throw,\n            // which callers interpret as a failed send and re-send the same\n            // payload through their fallback path — double-delivering every\n            // event in the batch (double-counting bug caught by the chaos\n            // harness).\n            const syntheticBody = '{\"ok\":true,\"transport\":\"sendBeacon\"}';\n            if (success) {\n                logDebug('[SDK] Successfully sent request via sendBeacon (CSP fallback)');\n                return new Response(syntheticBody, {\n                    status: 200,\n                    statusText: 'OK',\n                    headers: new Headers({ 'Content-Type': 'application/json' })\n                });\n            } else {\n                logWarn('[SDK] sendBeacon returned false - browser may be throttling');\n                // Return success anyway since sendBeacon is best-effort\n                return new Response(syntheticBody, {\n                    status: 200,\n                    statusText: 'OK (sendBeacon best-effort)',\n                    headers: new Headers({ 'Content-Type': 'application/json' })\n                });\n            }\n        } catch (error: any) {\n            logError('[SDK] sendBeacon fallback failed:', error);\n            // Return a mock error response\n            return new Response(null, {\n                status: 500,\n                statusText: 'Failed to send via sendBeacon',\n                headers: new Headers()\n            });\n        }\n    }\n\n    /**\n     * Detect if an error is a CSP violation\n     */\n    private isCSPViolation(error: any): boolean {\n        const errorMessage = (error?.message || '').toLowerCase();\n        const errorName = (error?.name || '').toLowerCase();\n        \n        // CSP violations typically manifest as:\n        // 1. TypeError with \"Failed to fetch\" and CSP-related text\n        // 2. Network errors that mention CSP or Content Security Policy\n        // 3. Errors that mention \"violates\" and \"Content Security Policy\"\n        \n        return (\n            (errorName === 'typeerror' && errorMessage.includes('failed to fetch')) ||\n            errorMessage.includes('content security policy') ||\n            errorMessage.includes('csp') ||\n            errorMessage.includes('violates') ||\n            // Check for common CSP violation patterns\n            (errorMessage.includes('refused to connect') && errorMessage.includes('violates'))\n        );\n    }\n\n    /**\n     * Check if network request should be skipped (SDK's own requests)\n     */\n    private shouldSkipNetworkTracking(url: string): boolean {\n        // Skip tracking if URL matches ingestion server base URL\n        if (!url || !this.baseUrl) {\n            return false;\n        }\n        \n        try {\n            const urlObj = new URL(url);\n            const baseUrlObj = new URL(this.baseUrl);\n            \n            // Skip if same origin (same protocol, host, port)\n            if (urlObj.origin === baseUrlObj.origin) {\n                // Also check if it's an ingestion endpoint\n                if (urlObj.pathname.startsWith('/api/ingestion/')) {\n                    return true;\n                }\n            }\n            \n            // Also check string matching as fallback\n            if (url.includes(this.baseUrl)) {\n                return true;\n            }\n            \n            return false;\n        } catch (error) {\n            // If URL parsing fails, do simple string check\n            return url.includes(this.baseUrl);\n        }\n    }\n\n    private classifyHttpError(status: number): string {\n        if (status >= 400 && status < 500) {\n            return 'client_error';\n        }\n        if (status >= 500) {\n            return 'server_error';\n        }\n        return 'unknown_error';\n    }\n\n    private classifyNetworkError(error: any): string {\n        const errorMessage = error.message || '';\n        const errorName = error.name || '';\n        \n        // Check for CSP violations first\n        if (this.isCSPViolation(error)) {\n            return 'csp_violation';\n        }\n        \n        // Check for blocked requests (ad blockers, browser extensions, etc.)\n        // This includes ERR_BLOCKED_BY_CLIENT, ERR_BLOCKED_BY_RESPONSE, and other blocked variants\n        if (errorMessage.includes('ERR_BLOCKED_BY_CLIENT') || \n            errorMessage.includes('ERR_BLOCKED_BY_RESPONSE') ||\n            errorMessage.includes('blocked:other') ||\n            errorMessage.includes('net::ERR_BLOCKED_BY_CLIENT') ||\n            errorMessage.includes('net::ERR_BLOCKED_BY_RESPONSE') ||\n            (errorName === 'TypeError' && errorMessage.includes('Failed to fetch') && \n             (errorMessage.includes('blocked') || errorMessage.includes('ERR_BLOCKED')))) {\n            return 'blocked_by_client';\n        }\n        if (errorMessage.includes('CORS') || errorMessage.includes('Access-Control')) {\n            return 'cors_error';\n        }\n        if (errorMessage.includes('timeout') || errorName === 'TimeoutError') {\n            return 'timeout_error';\n        }\n        if (errorMessage.includes('Failed to fetch') || errorMessage.includes('NetworkError')) {\n            return 'network_error';\n        }\n        return 'unknown_error';\n    }\n}","// Simplified redaction functionality for HumanBehavior SDK\n// Since rrweb auto-redacts all input fields by default, this module only handles\n// selectively unredacting specific fields (except passwords which remain protected)\n\nimport { logDebug, logWarn } from './utils/logger';\n\n// Check if we're in a browser environment\nconst isBrowser = typeof window !== 'undefined';\n\n/**\n * Credential-bearing URL parameter names, dropped from every captured URL.\n *\n * Captured URLs previously went through verbatim, query string and hash\n * included, into session rows, spans, logs and network events. That means a\n * password-reset link, a magic link, an OAuth `?code=`, an implicit-flow\n * `#access_token=`, or a pre-signed S3 URL landed in our store as a live\n * credential, and a replay viewer could read it.\n */\nconst SENSITIVE_URL_PARAM_NAMES = new Set([\n    'access_token',\n    'id_token',\n    'refresh_token',\n    'token',\n    'code',\n    'auth',\n    'authorization',\n    'session',\n    'sid',\n    'key',\n    'secret',\n    'password',\n    'passwd',\n    'pwd',\n    'otp',\n    'signature',\n    'sig',\n    'state',\n    'invite',\n]);\n\n/** Substrings that make a parameter sensitive regardless of exact spelling. */\nconst SENSITIVE_URL_PARAM_PATTERNS = [\n    'token',\n    'secret',\n    'password',\n    'passwd',\n    'signature',\n    'apikey',\n    'api_key',\n    'auth',\n];\n\nconst URL_REDACTED = 'REDACTED';\n\nfunction isSensitiveParamName(name: string): boolean {\n    const lower = name.toLowerCase();\n    if (SENSITIVE_URL_PARAM_NAMES.has(lower)) return true;\n    return SENSITIVE_URL_PARAM_PATTERNS.some((p) => lower.includes(p));\n}\n\nfunction redactParams(search: URLSearchParams): boolean {\n    let changed = false;\n    for (const name of Array.from(search.keys())) {\n        if (!isSensitiveParamName(name)) continue;\n        search.set(name, URL_REDACTED);\n        changed = true;\n    }\n    return changed;\n}\n\n/**\n * Replace credential-bearing query and hash parameters with `REDACTED`.\n *\n * Returns the input untouched when nothing matched, so a URL that carries no\n * secrets is never reformatted by `URL` normalization.\n */\nexport function sanitizeUrl(raw: string | null | undefined): string {\n    if (!raw) return raw ?? '';\n    try {\n        const base = isBrowser ? window.location.href : 'http://localhost/';\n        const url = new URL(raw, base);\n        let changed = redactParams(url.searchParams);\n\n        // Implicit-flow tokens arrive in the fragment, formatted like a query.\n        const fragment = url.hash.startsWith('#') ? url.hash.slice(1) : url.hash;\n        if (fragment && /[=&]/.test(fragment)) {\n            const hashParams = new URLSearchParams(fragment);\n            if (redactParams(hashParams)) {\n                url.hash = `#${hashParams.toString()}`;\n                changed = true;\n            }\n        }\n\n        if (!changed) return raw;\n        // Relative inputs must stay relative.\n        return /^[a-z][a-z0-9+.-]*:|^\\/\\//i.test(raw)\n            ? url.toString()\n            : `${url.pathname}${url.search}${url.hash}`;\n    } catch {\n        return raw;\n    }\n}\n\nexport interface RedactionOptions {\n    redactedText?: string;\n    excludeSelectors?: string[];\n    userFields?: string[]; // Fields that the user wants to unredact (legacy)\n    redactionStrategy?: {\n        mode: 'privacy-first' | 'visibility-first';\n        unredactFields?: string[]; // Fields to make visible (when mode: 'privacy-first')\n        redactFields?: string[];   // Fields to hide (when mode: 'visibility-first')\n    };\n    legacyRedactFields?: string[]; // For backward compatibility\n}\n\nexport class RedactionManager {\n    private redactedText: string = '[REDACTED]';\n    private unredactedFields: Set<string> = new Set(); // Fields that user wants to unredact\n    private redactedFields: Set<string> = new Set(); // Fields that user wants to redact\n    private redactionMode: 'privacy-first' | 'visibility-first' = 'privacy-first';\n    private excludeSelectors: string[] = [\n        '[data-no-redact=\"true\"]',\n        '.human-behavior-no-redact'\n    ];\n\n    constructor(options?: RedactionOptions) {\n        if (options?.redactedText) {\n            this.redactedText = options.redactedText;\n        }\n        if (options?.excludeSelectors) {\n            this.excludeSelectors = [...this.excludeSelectors, ...options.excludeSelectors];\n        }\n        \n        // Handle new redaction strategy\n        if (options?.redactionStrategy) {\n            this.redactionMode = options.redactionStrategy.mode;\n\n            if (this.redactionMode === 'privacy-first') {\n                // Privacy-first: everything redacted by default, unredact specific fields\n                if (options.redactionStrategy.unredactFields) {\n                    this.setFieldsToUnredact(options.redactionStrategy.unredactFields);\n                }\n            } else {\n                // Visibility-first: everything visible by default, redact specific fields\n                // Default to only redacting passwords if no specific fields provided\n                // Support zero-config authoring: allow data-hb-redact=\"true\" marks\n                const defaultMarks = ['input[type=\"password\"]', '[data-hb-redact=\"true\"]'];\n                const fieldsToRedact = options.redactionStrategy.redactFields && options.redactionStrategy.redactFields.length > 0\n                    ? options.redactionStrategy.redactFields\n                    : defaultMarks;\n                this.setFieldsToRedact(fieldsToRedact);\n            }\n        }\n        \n        // Handle legacy redactFields (backward compatibility)\n        if (options?.legacyRedactFields) {\n            this.setFieldsToUnredact(options.legacyRedactFields);\n        }\n        \n        // Handle legacy userFields\n        if (options?.userFields) {\n            this.setFieldsToUnredact(options.userFields);\n        }\n    }\n\n    /**\n     * Set specific fields to be redacted (for visibility-first mode)\n     * @param fields Array of CSS selectors for fields to redact\n     */\n    public setFieldsToRedact(fields: string[]): void {\n        this.redactedFields.clear();\n        \n        // Always include password fields in redacted list\n        const passwordFields = [\n            'input[type=\"password\"]',\n            'input[type=\"password\" i]',\n            '[type=\"password\"]',\n            '[type=\"password\" i]'\n        ];\n        \n        // Add password fields and user-specified fields\n        [...passwordFields, ...fields].forEach(field => {\n            this.redactedFields.add(field);\n        });\n        \n        if (this.redactedFields.size > 0) {\n            logDebug(`Redaction: Active for ${this.redactedFields.size} field(s):`, Array.from(this.redactedFields));\n        } else {\n            logDebug('Redaction: No fields to redact');\n        }\n        \n        this.applyRedactionClasses();\n    }\n\n    /**\n     * Set specific fields to be unredacted (everything else stays redacted by rrweb)\n     * @param fields Array of CSS selectors for fields to unredact\n     */\n    public setFieldsToUnredact(fields: string[]): void {\n        this.unredactedFields.clear();\n        \n        // Filter out password fields (they cannot be unredacted)\n        const validFields = fields.filter(field => {\n            const isPasswordField = this.isPasswordSelector(field);\n            if (isPasswordField) {\n                logWarn(`Cannot unredact password field: ${field} - Password fields are always protected`);\n                return false;\n            }\n            return true;\n        });\n        \n        validFields.forEach(field => this.unredactedFields.add(field));\n        \n        if (validFields.length > 0) {\n            logDebug(`Unredaction: Active for ${validFields.length} field(s):`, validFields);\n        } else {\n            logDebug('Unredaction: No valid fields to unredact');\n        }\n        \n        this.applyUnredactionClasses();\n    }\n\n    /**\n     * Remove specific fields from unredaction (they become redacted again)\n     * @param fields Array of CSS selectors for fields to redact\n     */\n    public redactFields(fields: string[]): void {\n        fields.forEach(field => {\n            this.unredactedFields.delete(field);\n        });\n        \n        if (this.unredactedFields.size > 0) {\n            logDebug(`Unredaction: Removed ${fields.length} field(s), ${this.unredactedFields.size} remaining:`, Array.from(this.unredactedFields));\n        } else {\n            logDebug('Unredaction: All fields redacted');\n        }\n        \n        this.applyUnredactionClasses();\n    }\n\n    /**\n     * Clear all unredacted fields (everything becomes redacted again)\n     */\n    public clearUnredactedFields(): void {\n        this.unredactedFields.clear();\n        logDebug('Unredaction: All fields cleared, everything redacted');\n        \n        this.removeUnredactionClasses();\n    }\n\n    /**\n     * Check if any fields are currently unredacted\n     */\n    public hasUnredactedFields(): boolean {\n        return this.unredactedFields.size > 0;\n    }\n\n    /**\n     * Get the current redaction mode\n     */\n    public getRedactionMode(): 'privacy-first' | 'visibility-first' {\n        return this.redactionMode;\n    }\n\n    /**\n     * Get the currently unredacted fields\n     */\n    public getUnredactedFields(): string[] {\n        return Array.from(this.unredactedFields);\n    }\n\n    /**\n     * Get CSS selectors for rrweb masking configuration\n     * Returns null if no fields are unredacted (everything stays redacted)\n     */\n    public getMaskTextSelector(): string | null {\n        if (this.redactionMode === 'privacy-first') {\n            // Privacy-first: mask everything except unredacted fields\n            if (this.unredactedFields.size === 0) {\n                return null; // Everything stays redacted\n            }\n            return Array.from(this.unredactedFields).join(',');\n        } else {\n            // Visibility-first: mask only redacted fields\n            if (this.redactedFields.size === 0) {\n                return null; // Nothing to redact\n            }\n            return Array.from(this.redactedFields).join(',');\n        }\n    }\n\n    /**\n     * Apply redaction classes to DOM elements (for visibility-first mode)\n     * Adds 'rr-mask' class to elements that should be redacted\n     */\n    public applyRedactionClasses(): void {\n        if (this.redactedFields.size === 0) {\n            return;\n        }\n\n        // Check if DOM is ready\n        if (typeof document === 'undefined' || document.readyState === 'loading') {\n            logDebug('DOM not ready, deferring redaction class application');\n            return;\n        }\n\n        // Add 'rr-mask' class to redacted elements\n        this.redactedFields.forEach(selector => {\n            try {\n                const elements = document.querySelectorAll(selector);\n                elements.forEach(element => {\n                    if (element && element.classList) {\n                        element.classList.add('rr-mask');\n                    }\n                });\n                logDebug(`Added rr-mask class to ${elements.length} element(s) for selector: ${selector}`);\n            } catch (e) {\n                logWarn(`Invalid selector: ${selector}`);\n            }\n        });\n    }\n\n    /**\n     * Apply unredaction classes to DOM elements\n     * Removes 'rr-mask' class from elements that should be unredacted\n     */\n    public applyUnredactionClasses(): void {\n        if (this.unredactedFields.size === 0) {\n            return;\n        }\n\n        // Check if DOM is ready\n        if (typeof document === 'undefined' || document.readyState === 'loading') {\n            logDebug('DOM not ready, deferring unredaction class application');\n            return;\n        }\n\n        // Remove 'rr-mask' class from unredacted elements\n        this.unredactedFields.forEach(selector => {\n            try {\n                const elements = document.querySelectorAll(selector);\n                elements.forEach(element => {\n                    if (element && element.classList) {\n                        element.classList.remove('rr-mask');\n                    }\n                });\n                logDebug(`Removed rr-mask class from ${elements.length} element(s) for selector: ${selector}`);\n            } catch (e) {\n                logWarn(`Invalid selector: ${selector}`);\n            }\n        });\n    }\n\n    /**\n     * Remove all unredaction classes from DOM elements\n     */\n    public removeUnredactionClasses(): void {\n        // Note: This doesn't add 'rr-mask' classes back - rrweb handles that automatically\n        logDebug('Unredaction classes removed');\n    }\n\n    /**\n     * Check if a selector represents a password field\n     */\n    private isPasswordSelector(selector: string): boolean {\n        const passwordPatterns = [\n            'input[type=\"password\"]',\n            'input[type=\"password\" i]',\n            '[type=\"password\"]',\n            '[type=\"password\" i]'\n        ];\n        \n        return passwordPatterns.some(pattern => \n            selector.toLowerCase().includes(pattern.toLowerCase().replace(/[\\[\\]]/g, ''))\n        );\n    }\n\n    /**\n     * Get the original value of an element (for debugging)\n     */\n    public getOriginalValue(element: HTMLElement): string | undefined {\n        if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {\n            return element.value;\n        }\n        return undefined;\n    }\n\n    /**\n     * Check if an element is currently unredacted\n     */\n    public isElementUnredacted(element: HTMLElement): boolean {\n        return this.shouldUnredactElement(element);\n    }\n\n    /**\n     * Check if an element should be unredacted\n     */\n    public shouldUnredactElement(element: HTMLElement): boolean {\n        // Fail closed: any unexpected error masks the element rather than\n        // leaking it. \"Unredact\" is the privileged decision, so when in doubt\n        // we say no.\n        try {\n            if (this.redactionMode === 'privacy-first') {\n                // Privacy-first: only elements that positively match an\n                // allowlisted selector are shown; everything else stays masked.\n                if (this.unredactedFields.size === 0) {\n                    return false; // Nothing unredacted\n                }\n\n                for (const selector of this.unredactedFields) {\n                    try {\n                        if (element.matches(selector)) {\n                            return true;\n                        }\n                    } catch (e) {\n                        // Can't evaluate this allowlist selector — do NOT unredact on it.\n                        logWarn(`Invalid selector: ${selector}`);\n                    }\n                }\n                return false;\n            } else {\n                // Visibility-first: elements are shown unless they match a\n                // redacted selector.\n                if (this.redactedFields.size === 0) {\n                    return true; // Nothing redacted, everything visible\n                }\n\n                for (const selector of this.redactedFields) {\n                    try {\n                        if (element.matches(selector)) {\n                            return false; // Element is redacted\n                        }\n                    } catch (e) {\n                        // Can't evaluate a redact selector — fail closed and mask\n                        // this element rather than risk exposing a field that was\n                        // meant to be hidden.\n                        logWarn(`Invalid selector: ${selector}`);\n                        return false;\n                    }\n                }\n                return true; // Element is not redacted\n            }\n        } catch {\n            return false;\n        }\n    }\n}\n\n// Export a default instance\nexport const redactionManager = new RedactionManager();\n\n// Export the class for custom instances\nexport default RedactionManager; ","/**\n * Automatic Property Detection for HumanBehavior SDK\n * Captures device type, location, and initial referrer information\n */\n\n// Check if we're in a browser environment\nconst isBrowser = typeof window !== 'undefined';\n\nexport interface DeviceInfo {\n    device_type: string;\n    browser: string;\n    browser_version: string;\n    os: string;\n    os_version: string;\n    device_model?: string;\n    screen_resolution: string;\n    viewport_size: string;\n    color_depth: number;\n    timezone: string;\n    language: string;\n    languages: string[];\n    raw_user_agent?: string;\n}\n\nexport interface LocationInfo {\n    current_url: string;\n    pathname: string;\n    search: string;\n    hash: string;\n    title: string;\n    referrer: string;\n    referrer_domain: string;\n    initial_referrer: string;\n    initial_referrer_domain: string;\n    initial_host?: string;\n    utm_source?: string;\n    utm_medium?: string;\n    utm_campaign?: string;\n    utm_term?: string;\n    utm_content?: string;\n}\n\nexport interface AutomaticProperties extends DeviceInfo, LocationInfo {}\n\n/**\n * Detect device type based on user agent and screen size\n */\nfunction detectDeviceType(): string {\n    if (!isBrowser) return 'unknown';\n    \n    const userAgent = navigator.userAgent.toLowerCase();\n    const screenWidth = window.screen.width;\n    const screenHeight = window.screen.height;\n    \n    // Mobile detection\n    if (/mobile|android|iphone|ipad|ipod|blackberry|windows phone/i.test(userAgent)) {\n        if (/ipad/i.test(userAgent) || (screenWidth >= 768 && screenHeight >= 1024)) {\n            return 'tablet';\n        }\n        return 'mobile';\n    }\n    \n    // Desktop detection\n    if (/windows|macintosh|linux/i.test(userAgent)) {\n        return 'desktop';\n    }\n    \n    return 'unknown';\n}\n\n/**\n * Extract browser information from user agent\n */\nfunction detectBrowser(): { browser: string; browser_version: string } {\n    if (!isBrowser) return { browser: 'unknown', browser_version: 'unknown' };\n    \n    const userAgent = navigator.userAgent;\n    \n    // Chrome\n    if (/chrome/i.test(userAgent) && !/edge/i.test(userAgent)) {\n        const match = userAgent.match(/chrome\\/(\\d+)/i);\n        return {\n            browser: 'chrome',\n            browser_version: match ? match[1] : 'unknown'\n        };\n    }\n    \n    // Firefox\n    if (/firefox/i.test(userAgent)) {\n        const match = userAgent.match(/firefox\\/(\\d+)/i);\n        return {\n            browser: 'firefox',\n            browser_version: match ? match[1] : 'unknown'\n        };\n    }\n    \n    // Safari\n    if (/safari/i.test(userAgent) && !/chrome/i.test(userAgent)) {\n        const match = userAgent.match(/version\\/(\\d+)/i);\n        return {\n            browser: 'safari',\n            browser_version: match ? match[1] : 'unknown'\n        };\n    }\n    \n    // Edge\n    if (/edge/i.test(userAgent)) {\n        const match = userAgent.match(/edge\\/(\\d+)/i);\n        return {\n            browser: 'edge',\n            browser_version: match ? match[1] : 'unknown'\n        };\n    }\n    \n    // Internet Explorer\n    if (/msie|trident/i.test(userAgent)) {\n        const match = userAgent.match(/msie (\\d+)/i) || userAgent.match(/rv:(\\d+)/i);\n        return {\n            browser: 'ie',\n            browser_version: match ? match[1] : 'unknown'\n        };\n    }\n    \n    return { browser: 'unknown', browser_version: 'unknown' };\n}\n\n/**\n * Extract operating system information from user agent\n */\nfunction detectOS(): { os: string; os_version: string } {\n    if (!isBrowser) return { os: 'unknown', os_version: 'unknown' };\n    \n    const userAgent = navigator.userAgent;\n    \n    // Windows\n    if (/windows/i.test(userAgent)) {\n        const match = userAgent.match(/windows nt (\\d+\\.\\d+)/i);\n        let version = 'unknown';\n        if (match) {\n            const versionNum = parseFloat(match[1]);\n            if (versionNum === 10.0) version = '10';\n            else if (versionNum === 6.3) version = '8.1';\n            else if (versionNum === 6.2) version = '8';\n            else if (versionNum === 6.1) version = '7';\n            else version = match[1];\n        }\n        return { os: 'windows', os_version: version };\n    }\n    \n    // iOS first. iPhone/iPad UAs also contain \"like Mac OS X\"; matching\n    // Macintosh first labeled every iOS Safari session as macos.\n    if (/iphone|ipad|ipod/i.test(userAgent)) {\n        const match = userAgent.match(/os (\\d+[._]\\d+)/i);\n        return {\n            os: 'ios',\n            os_version: match ? match[1].replace('_', '.') : 'unknown'\n        };\n    }\n\n    // macOS\n    if (/macintosh|mac os x/i.test(userAgent)) {\n        const match = userAgent.match(/mac os x (\\d+[._]\\d+)/i);\n        return {\n            os: 'macos',\n            os_version: match ? match[1].replace('_', '.') : 'unknown'\n        };\n    }\n    \n    // Android\n    if (/android/i.test(userAgent)) {\n        const match = userAgent.match(/android (\\d+\\.\\d+)/i);\n        return {\n            os: 'android',\n            os_version: match ? match[1] : 'unknown'\n        };\n    }\n    \n    // Linux\n    if (/linux/i.test(userAgent)) {\n        return { os: 'linux', os_version: 'unknown' };\n    }\n    \n    return { os: 'unknown', os_version: 'unknown' };\n}\n\n/**\n * Extract UTM parameters from URL\n */\nfunction extractUTMParams(url: string): Record<string, string> {\n    const urlObj = new URL(url);\n    const utmParams: Record<string, string> = {};\n    \n    const utmKeys = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'];\n    \n    utmKeys.forEach(key => {\n        const value = urlObj.searchParams.get(key);\n        if (value) {\n            utmParams[key] = value;\n        }\n    });\n    \n    return utmParams;\n}\n\n/**\n * Extract domain from URL\n */\nfunction extractDomain(url: string): string {\n    try {\n        const urlObj = new URL(url);\n        return urlObj.hostname;\n    } catch {\n        return '';\n    }\n}\n\n/**\n * Get device information\n */\nexport function getDeviceInfo(): DeviceInfo {\n    if (!isBrowser) {\n        return {\n            device_type: 'unknown',\n            browser: 'unknown',\n            browser_version: 'unknown',\n            os: 'unknown',\n            os_version: 'unknown',\n            screen_resolution: 'unknown',\n            viewport_size: 'unknown',\n            color_depth: 0,\n            timezone: 'unknown',\n            language: 'unknown',\n            languages: []\n        };\n    }\n    \n    const { browser, browser_version } = detectBrowser();\n    const { os, os_version } = detectOS();\n    \n    return {\n        device_type: detectDeviceType(),\n        browser,\n        browser_version,\n        os,\n        os_version,\n        screen_resolution: `${window.screen.width}x${window.screen.height}`,\n        viewport_size: `${window.innerWidth}x${window.innerHeight}`,\n        color_depth: window.screen.colorDepth,\n        timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n        language: navigator.language,\n        languages: [...(navigator.languages || [navigator.language])],\n        raw_user_agent: navigator.userAgent\n    };\n}\n\n/**\n * Get location information\n */\nexport function getLocationInfo(): LocationInfo {\n    if (!isBrowser) {\n        return {\n            current_url: '',\n            pathname: '',\n            search: '',\n            hash: '',\n            title: '',\n            referrer: '',\n            referrer_domain: '',\n            initial_referrer: '',\n            initial_referrer_domain: ''\n        };\n    }\n    \n    const currentUrl = window.location.href;\n    const referrer = document.referrer;\n    const utmParams = extractUTMParams(currentUrl);\n    \n    return {\n        current_url: currentUrl,\n        pathname: window.location.pathname,\n        search: window.location.search,\n        hash: window.location.hash,\n        title: document.title,\n        referrer,\n        referrer_domain: extractDomain(referrer),\n        initial_referrer: referrer,\n        initial_referrer_domain: extractDomain(referrer),\n        initial_host: window.location.hostname,\n        ...utmParams\n    };\n}\n\n/**\n * Get all automatic properties\n */\nexport function getAutomaticProperties(): AutomaticProperties {\n    return {\n        ...getDeviceInfo(),\n        ...getLocationInfo()\n    };\n}\n\n/**\n * Get initial properties that should be captured once per session\n */\nexport function getInitialProperties(): Record<string, any> {\n    if (!isBrowser) return {};\n    \n    const locationInfo = getLocationInfo();\n    \n    return {\n        initial_referrer: locationInfo.initial_referrer,\n        initial_referrer_domain: locationInfo.initial_referrer_domain,\n        initial_url: locationInfo.current_url,\n        initial_pathname: locationInfo.pathname,\n        initial_utm_source: locationInfo.utm_source,\n        initial_utm_medium: locationInfo.utm_medium,\n        initial_utm_campaign: locationInfo.utm_campaign,\n        initial_utm_term: locationInfo.utm_term,\n        initial_utm_content: locationInfo.utm_content\n    };\n}\n\n/**\n * Get current page properties (changes with navigation)\n */\nexport function getCurrentPageProperties(): Record<string, any> {\n    if (!isBrowser) return {};\n    \n    const locationInfo = getLocationInfo();\n    \n    return {\n        current_url: locationInfo.current_url,\n        pathname: locationInfo.pathname,\n        search: locationInfo.search,\n        hash: locationInfo.hash,\n        title: locationInfo.title,\n        referrer: locationInfo.referrer,\n        referrer_domain: locationInfo.referrer_domain,\n        utm_source: locationInfo.utm_source,\n        utm_medium: locationInfo.utm_medium,\n        utm_campaign: locationInfo.utm_campaign,\n        utm_term: locationInfo.utm_term,\n        utm_content: locationInfo.utm_content\n    };\n}\n","/**\n * Property Manager for HumanBehavior SDK\n * Handles automatic properties, session properties, and user properties\n */\n\nimport { getAutomaticProperties, getInitialProperties, getCurrentPageProperties, AutomaticProperties } from './property-detector';\n\nexport interface Properties {\n    [key: string]: any;\n}\n\nexport interface PropertyManagerConfig {\n    enableAutomaticProperties?: boolean;\n    enableSessionProperties?: boolean;\n    enableUserProperties?: boolean;\n    propertyDenylist?: string[];\n}\n\nexport class PropertyManager {\n    private config: PropertyManagerConfig;\n    private automaticProperties: AutomaticProperties;\n    private sessionProperties: Properties = {};\n    private userProperties: Properties = {};\n    private initialProperties: Properties = {};\n    private isInitialized: boolean = false;\n\n    constructor(config: PropertyManagerConfig = {}) {\n        this.config = {\n            enableAutomaticProperties: true,\n            enableSessionProperties: true,\n            enableUserProperties: true,\n            propertyDenylist: [],\n            ...config\n        };\n        \n        this.automaticProperties = getAutomaticProperties();\n        this.initialize();\n    }\n\n    /**\n     * Initialize the property manager\n     */\n    private initialize(): void {\n        if (this.isInitialized) return;\n        \n        // Capture initial properties once\n        this.initialProperties = getInitialProperties();\n        \n        // Load session properties from sessionStorage\n        this.loadSessionProperties();\n        \n        this.isInitialized = true;\n    }\n\n    /**\n     * Get all properties for an event\n     */\n    public getEventProperties(eventProperties: Properties = {}): Properties {\n        // Auto/session/user/initial property merges happen on top of the\n        // caller-provided eventProperties, but we want USER-provided values\n        // to win when there's a key collision (intuitive override semantics).\n        // We achieve that by merging into `properties` from the bottom up\n        // and re-applying eventProperties last.\n        const properties: Properties = {};\n\n        // When `enableAutomaticProperties` is false we short-circuit the\n        // entire enrichment chain (auto/session/user/initial) instead of\n        // just the auto leg. Pre-0.7 turning this flag off only at construction\n        // had no observable effect at call time because session+user props\n        // still ballooned the payload.\n        if (this.config.enableAutomaticProperties) {\n            Object.assign(properties, this.getAutomaticProperties());\n\n            if (this.config.enableSessionProperties) {\n                Object.assign(properties, this.sessionProperties);\n            }\n\n            if (this.config.enableUserProperties) {\n                Object.assign(properties, this.userProperties);\n            }\n\n            if (!this.sessionProperties['$initial_properties_captured']) {\n                Object.assign(properties, this.initialProperties);\n                this.setSessionProperty('$initial_properties_captured', true);\n            }\n        }\n\n        // User-provided keys win over auto/session/user/initial.\n        Object.assign(properties, eventProperties);\n\n        // Apply denylist\n        this.applyDenylist(properties);\n\n        return properties;\n    }\n\n    /**\n     * Get automatic properties\n     */\n    public getAutomaticProperties(): Properties {\n        return {\n            ...this.automaticProperties,\n            ...getCurrentPageProperties() // Always get fresh page properties\n        };\n    }\n\n    /**\n     * Get automatic properties with GeoIP data merged in\n     */\n    public getAutomaticPropertiesWithGeoIP(geoIPProperties: Record<string, any> = {}): Properties {\n        return {\n            ...this.automaticProperties,\n            ...getCurrentPageProperties(), // Always get fresh page properties\n            ...geoIPProperties\n        };\n    }\n\n    /**\n     * Set a session property\n     */\n    public setSessionProperty(key: string, value: any): void {\n        this.sessionProperties[key] = value;\n        this.saveSessionProperties();\n    }\n\n    /**\n     * Set multiple session properties\n     */\n    public setSessionProperties(properties: Properties): void {\n        Object.assign(this.sessionProperties, properties);\n        this.saveSessionProperties();\n    }\n\n    /**\n     * Get a session property\n     */\n    public getSessionProperty(key: string): any {\n        return this.sessionProperties[key];\n    }\n\n    /**\n     * Remove a session property\n     */\n    public removeSessionProperty(key: string): void {\n        delete this.sessionProperties[key];\n        this.saveSessionProperties();\n    }\n\n    /**\n     * Set a user property\n     */\n    public setUserProperty(key: string, value: any): void {\n        this.userProperties[key] = value;\n    }\n\n    /**\n     * Set multiple user properties\n     */\n    public setUserProperties(properties: Properties): void {\n        Object.assign(this.userProperties, properties);\n    }\n\n    /**\n     * Get a user property\n     */\n    public getUserProperty(key: string): any {\n        return this.userProperties[key];\n    }\n\n    /**\n     * Get a shallow copy of all user properties.\n     */\n    public getUserProperties(): Properties {\n        return { ...this.userProperties };\n    }\n\n    /**\n     * Get a shallow copy of all session properties.\n     */\n    public getSessionProperties(): Properties {\n        return { ...this.sessionProperties };\n    }\n\n    /**\n     * Remove a user property\n     */\n    public removeUserProperty(key: string): void {\n        delete this.userProperties[key];\n    }\n\n    /**\n     * Set a property only if it hasn't been set before\n     */\n    public setOnce(key: string, value: any, scope: 'session' | 'user' = 'user'): void {\n        if (scope === 'session') {\n            if (!(key in this.sessionProperties)) {\n                this.setSessionProperty(key, value);\n            }\n        } else {\n            if (!(key in this.userProperties)) {\n                this.setUserProperty(key, value);\n            }\n        }\n    }\n\n    /**\n     * Clear all session properties\n     */\n    public clearSessionProperties(): void {\n        this.sessionProperties = {};\n        this.saveSessionProperties();\n    }\n\n    /**\n     * Clear all user properties\n     */\n    public clearUserProperties(): void {\n        this.userProperties = {};\n    }\n\n    /**\n     * Reset all properties\n     */\n    public reset(): void {\n        this.clearSessionProperties();\n        this.clearUserProperties();\n        this.initialProperties = {};\n        this.isInitialized = false;\n        this.initialize();\n    }\n\n    /**\n     * Load session properties from sessionStorage\n     */\n    private loadSessionProperties(): void {\n        if (typeof sessionStorage === 'undefined') return;\n        \n        try {\n            const stored = sessionStorage.getItem('hb_session_properties');\n            if (stored) {\n                this.sessionProperties = JSON.parse(stored);\n            }\n        } catch (error) {\n            console.warn('Failed to load session properties:', error);\n        }\n    }\n\n    /**\n     * Save session properties to sessionStorage\n     */\n    private saveSessionProperties(): void {\n        if (typeof sessionStorage === 'undefined') return;\n        \n        try {\n            sessionStorage.setItem('hb_session_properties', JSON.stringify(this.sessionProperties));\n        } catch (error) {\n            console.warn('Failed to save session properties:', error);\n        }\n    }\n\n    /**\n     * Apply property denylist\n     */\n    private applyDenylist(properties: Properties): void {\n        if (!this.config.propertyDenylist || this.config.propertyDenylist.length === 0) {\n            return;\n        }\n\n        this.config.propertyDenylist.forEach(deniedKey => {\n            delete properties[deniedKey];\n        });\n    }\n\n    /**\n     * Update automatic properties (call when page changes)\n     */\n    public updateAutomaticProperties(): void {\n        this.automaticProperties = getAutomaticProperties();\n    }\n\n    /**\n     * Get all properties for debugging\n     */\n    public getAllProperties(): {\n        automatic: Properties;\n        session: Properties;\n        user: Properties;\n        initial: Properties;\n    } {\n        return {\n            automatic: this.getAutomaticProperties(),\n            session: { ...this.sessionProperties },\n            user: { ...this.userProperties },\n            initial: { ...this.initialProperties }\n        };\n    }\n}\n","/**\n * A small, fixed-size rolling log of \"what the user did just before a crash\".\n *\n * The tracker pushes a breadcrumb whenever it detects a click, navigation,\n * console error, or network error. When an error is captured we attach a\n * snapshot of the buffer so each report carries its lead-up context.\n *\n * Pure and dependency-free; covered by __tests__/breadcrumbs.test.ts.\n */\n\nexport type BreadcrumbType = 'click' | 'navigation' | 'console' | 'network';\n\nexport interface Breadcrumb {\n    type: BreadcrumbType;\n    message: string;\n    timestampMs: number;\n    data?: Record<string, unknown>;\n}\n\nconst DEFAULT_MAX_BREADCRUMBS = 50;\n\nexport class BreadcrumbBuffer {\n    private readonly max: number;\n    private items: Breadcrumb[] = [];\n\n    constructor(max: number = DEFAULT_MAX_BREADCRUMBS) {\n        this.max = max > 0 ? max : DEFAULT_MAX_BREADCRUMBS;\n    }\n\n    /** Append a breadcrumb, evicting the oldest once the buffer is full. */\n    add(crumb: Breadcrumb): void {\n        this.items.push(crumb);\n        if (this.items.length > this.max) {\n            this.items.splice(0, this.items.length - this.max);\n        }\n    }\n\n    /** Return a shallow copy so callers can't mutate the live buffer. */\n    snapshot(): Breadcrumb[] {\n        return this.items.map((c) => ({ ...c }));\n    }\n\n    clear(): void {\n        this.items = [];\n    }\n\n    get size(): number {\n        return this.items.length;\n    }\n}\n","/**\n * Client-side de-duplication for error reports, matching Sentry's\n * `dedupeIntegration` semantics: an event is dropped when it is identical to\n * the immediately-previous captured event (same type, value, and stack\n * frames — encoded in the dedupe key), with no time window. Only the single\n * previous event is remembered, so alternating errors are all reported while\n * a crash loop repeating the same error reports once.\n *\n * Pure and dependency-free; covered by __tests__/dedup.test.ts.\n */\n\nexport class ErrorDeduper {\n    private previousKey: string | undefined;\n\n    /**\n     * Returns true if this key should be reported now, false if it is\n     * identical to the previous event's key.\n     */\n    shouldReport(key: string): boolean {\n        if (this.previousKey !== undefined && key === this.previousKey) {\n            return false;\n        }\n        this.previousKey = key;\n        return true;\n    }\n}\n","/**\n * Client-side event filtering: default ignore rules plus host-configurable\n * `ignoreErrors` / `denyUrls` / `allowUrls`, matching Sentry's semantics so\n * customers migrating from Sentry can reuse their existing filter config.\n *\n * Portions adapted from sentry-javascript (packages/core/src/integrations/\n * eventFilters.ts and packages/core/src/utils/string.ts), MIT License,\n * Copyright (c) 2019-present Functional Software, Inc. dba Sentry.\n *\n * Pure and dependency-free; covered by __tests__/filters.test.ts.\n */\n\nimport { StackFrame } from './stack-parser';\n\n/** A filter pattern: substring match when a string, regex test when a RegExp. */\nexport type FilterPattern = string | RegExp;\n\nexport interface ErrorFilterOptions {\n    /**\n     * Error messages (or `Type: message` pairs) to drop client-side. Strings\n     * match as substrings; RegExps test the full message. Merged with the\n     * built-in defaults unless `disableErrorDefaults` is set.\n     */\n    ignoreErrors?: FilterPattern[];\n    /** Drop errors whose originating script URL matches any pattern. */\n    denyUrls?: FilterPattern[];\n    /** When set, only keep errors whose originating script URL matches. */\n    allowUrls?: FilterPattern[];\n    /** Opt out of the built-in default ignore list. */\n    disableErrorDefaults?: boolean;\n}\n\n/**\n * Sentry's curated default ignore list (browser noise nobody can act on),\n * plus HumanBehavior's own transport/self noise.\n */\nexport const DEFAULT_IGNORE_ERRORS: FilterPattern[] = [\n    // From sentry-javascript DEFAULT_IGNORE_ERRORS (MIT):\n    /^Script error\\.?$/,\n    /^Javascript error: Script error\\.? on line 0$/,\n    /^ResizeObserver loop completed with undelivered notifications.$/,\n    /^Cannot redefine property: googletag$/,\n    /^Can't find variable: gmo$/,\n    /^undefined is not an object \\(evaluating 'a\\.[A-Z]'\\)$/,\n    /can't redefine non-configurable property \"solana\"/,\n    /vv\\(\\)\\.getRestrictions is not a function/,\n    /Can't find variable: _AutofillCallbackHandler/,\n    /Object Not Found Matching Id:\\d+, MethodName:simulateEvent/,\n    /^Java exception was raised during method invocation$/,\n    // HumanBehavior SDK's own surfaced noise (kept in sync with the console\n    // suppression list in tracker.ts) so we never report ourselves.\n    /HumanBehavior error/i,\n    /Failed to send events/i,\n    // Ingest transport blips: flush persists + retries; surfacing these as\n    // Runtime Issues just mirrors our own outbound failures.\n    /request timeout/i,\n    /^TimeoutError\\b/,\n];\n\n/** Substring match for strings, `.test()` for RegExps (Sentry semantics). */\nexport function isMatchingPattern(value: string, pattern: FilterPattern): boolean {\n    if (typeof value !== 'string') {\n        return false;\n    }\n    if (pattern instanceof RegExp) {\n        return pattern.test(value);\n    }\n    return value.includes(pattern);\n}\n\nexport function stringMatchesSomePattern(value: string, patterns: FilterPattern[] = []): boolean {\n    return patterns.some((pattern) => isMatchingPattern(value, pattern));\n}\n\n/** The message forms an ignore rule can match against (Sentry checks both). */\nfunction possibleMessages(exceptionType: string, value: string): string[] {\n    const messages: string[] = [];\n    if (value) {\n        messages.push(value);\n    }\n    if (exceptionType && value) {\n        messages.push(`${exceptionType}: ${value}`);\n    }\n    return messages;\n}\n\n/**\n * The URL to filter on: the last stack frame with a real (non-anonymous)\n * filename — the script the error actually originated from (Sentry's rule).\n */\nexport function reportUrlForFiltering(frames: StackFrame[]): string | null {\n    for (let i = frames.length - 1; i >= 0; i -= 1) {\n        const file = frames[i]?.file;\n        if (file && file !== '<anonymous>' && file !== '[native code]') {\n            return file;\n        }\n    }\n    return null;\n}\n\nexport interface FilterableReport {\n    exceptionType: string;\n    value: string;\n    stackFrames: StackFrame[];\n}\n\nexport interface DropReason {\n    reason: 'ignoreErrors' | 'denyUrls' | 'allowUrls';\n    matched: string;\n}\n\n/**\n * Returns why a report should be dropped, or null to keep it. Applies, in\n * Sentry's order: ignore-message patterns, denied URLs, then the allow-list.\n */\nexport function getDropReason(report: FilterableReport, options: ErrorFilterOptions = {}): DropReason | null {\n    const ignore = [\n        ...(options.ignoreErrors || []),\n        ...(options.disableErrorDefaults ? [] : DEFAULT_IGNORE_ERRORS),\n    ];\n    for (const message of possibleMessages(report.exceptionType, report.value)) {\n        if (stringMatchesSomePattern(message, ignore)) {\n            return { reason: 'ignoreErrors', matched: message };\n        }\n    }\n\n    const url = reportUrlForFiltering(report.stackFrames);\n    if (options.denyUrls?.length && url && stringMatchesSomePattern(url, options.denyUrls)) {\n        return { reason: 'denyUrls', matched: url };\n    }\n    if (options.allowUrls?.length && url && !stringMatchesSomePattern(url, options.allowUrls)) {\n        return { reason: 'allowUrls', matched: url };\n    }\n    return null;\n}\n","/**\n * Dependency-free stack-trace parser.\n *\n * Turns a raw `Error.stack` string into structured frames. Handles the two\n * shapes we see across our supported browsers:\n *   - V8 / Chrome / Edge:  \"    at fnName (file:line:col)\"  /  \"    at file:line:col\"\n *   - Gecko / Safari:      \"fnName@file:line:col\"           /  \"@file:line:col\"\n *\n * Lines that don't carry a usable location (e.g. the leading message line, or\n * \"at <anonymous>\") are skipped. This is intentionally small and is covered by\n * fixture tests in __tests__/stack-parser.test.ts.\n */\n\nexport interface StackFrame {\n    function: string | null;\n    file: string | null;\n    line: number | null;\n    column: number | null;\n}\n\nconst CHROME_WITH_FN = /^\\s*at\\s+(.+?)\\s+\\((.+?):(\\d+):(\\d+)\\)\\s*$/;\nconst CHROME_NO_FN = /^\\s*at\\s+(.+?):(\\d+):(\\d+)\\s*$/;\nconst GECKO = /^\\s*(.*?)@(.+?):(\\d+)(?::(\\d+))?\\s*$/;\n\nfunction parseChromeLine(line: string): StackFrame | null {\n    let m = CHROME_WITH_FN.exec(line);\n    if (m) {\n        return { function: m[1], file: m[2], line: Number(m[3]), column: Number(m[4]) };\n    }\n    m = CHROME_NO_FN.exec(line);\n    if (m) {\n        return { function: null, file: m[1], line: Number(m[2]), column: Number(m[3]) };\n    }\n    return null;\n}\n\nfunction parseGeckoLine(line: string): StackFrame | null {\n    const m = GECKO.exec(line);\n    if (m) {\n        return {\n            function: m[1] ? m[1] : null,\n            file: m[2],\n            line: Number(m[3]),\n            column: m[4] ? Number(m[4]) : null,\n        };\n    }\n    return null;\n}\n\n/**\n * Parse an Error.stack string into frames. Returns [] for missing/empty input.\n */\nexport function parseStack(stack: string | null | undefined): StackFrame[] {\n    if (!stack || typeof stack !== 'string') {\n        return [];\n    }\n\n    const frames: StackFrame[] = [];\n    for (const rawLine of stack.split('\\n')) {\n        if (!rawLine.trim()) {\n            continue;\n        }\n        const frame = parseChromeLine(rawLine) || parseGeckoLine(rawLine);\n        if (frame) {\n            frames.push(frame);\n        }\n    }\n    return frames;\n}\n","/**\n * Cause-chain capture: walks `Error.cause` (and `AggregateError.errors`) so a\n * wrapped error ships with the underlying root cause, the way Sentry's\n * linkedErrors integration does.\n *\n * Adapted from sentry-javascript (packages/core/src/utils/aggregate-errors.ts),\n * MIT License, Copyright (c) 2019-present Functional Software, Inc. dba Sentry.\n *\n * Pure and dependency-free; covered by __tests__/linked-errors.test.ts.\n */\n\nimport { parseStack, StackFrame } from './stack-parser';\n\nexport interface LinkedException {\n    exceptionType: string;\n    value: string;\n    stackFrames: StackFrame[];\n    /** How this exception is linked to its parent: 'cause' or 'errors[i]'. */\n    source: string;\n}\n\nconst DEFAULT_LIMIT = 5;\n\nfunction isErrorLike(value: unknown): value is Error {\n    return (\n        value instanceof Error ||\n        (typeof value === 'object' &&\n            value !== null &&\n            typeof (value as { message?: unknown }).message === 'string' &&\n            typeof (value as { name?: unknown }).name === 'string')\n    );\n}\n\nfunction toLinkedException(error: Error, source: string): LinkedException {\n    return {\n        exceptionType: error.name || 'Error',\n        value: error.message || '',\n        stackFrames: parseStack(typeof error.stack === 'string' ? error.stack : null),\n        source,\n    };\n}\n\n/**\n * Collect the chain of linked exceptions hanging off a thrown value, in\n * outermost-cause-first order. Cycle-safe and bounded by `limit`. Returns []\n * for non-Error values or errors with no cause.\n */\nexport function collectLinkedErrors(error: unknown, limit: number = DEFAULT_LIMIT): LinkedException[] {\n    const out: LinkedException[] = [];\n    if (!isErrorLike(error)) {\n        return out;\n    }\n    const seen = new Set<unknown>([error]);\n\n    const visit = (parent: Error): void => {\n        if (out.length >= limit) {\n            return;\n        }\n        const cause = (parent as Error & { cause?: unknown }).cause;\n        if (isErrorLike(cause) && !seen.has(cause)) {\n            seen.add(cause);\n            out.push(toLinkedException(cause, 'cause'));\n            visit(cause);\n        }\n        const children = (parent as Error & { errors?: unknown }).errors;\n        if (Array.isArray(children)) {\n            for (let i = 0; i < children.length && out.length < limit; i += 1) {\n                const child = children[i];\n                if (isErrorLike(child) && !seen.has(child)) {\n                    seen.add(child);\n                    out.push(toLinkedException(child, `errors[${i}]`));\n                    visit(child);\n                }\n            }\n        }\n    };\n\n    visit(error);\n    return out;\n}\n","/**\n * Debug-ID discovery: bundler plugins (Sentry's and the emerging standard)\n * inject a global map of stack-trace snippets → debug IDs into every built\n * chunk. Resolving those to filenames lets the backend pick the exact source\n * map for a frame with no release/dist coordination.\n *\n * Adapted from sentry-javascript (packages/core/src/utils/debug-ids.ts),\n * MIT License, Copyright (c) 2019-present Functional Software, Inc. dba Sentry.\n *\n * Covered by __tests__/debug-ids.test.ts.\n */\n\nimport { parseStack, StackFrame } from './stack-parser';\n\ntype DebugIdContainer = {\n    _debugIds?: Record<string, string>;\n    _sentryDebugIds?: Record<string, string>;\n};\n\ninterface CacheEntry {\n    map: Record<string, string>;\n    debugIdKeyCount: number;\n    sentryDebugIdKeyCount: number;\n}\n\nlet cache: CacheEntry | undefined;\n\n/** Test hook: reset the memoized filename→debugId map. */\nexport function resetDebugIdCache(): void {\n    cache = undefined;\n}\n\nfunction buildFilenameMap(byStackKey: Record<string, string>, into: Record<string, string>): void {\n    for (const stackKey of Object.keys(byStackKey)) {\n        const frames = parseStack(stackKey);\n        for (let i = frames.length - 1; i >= 0; i -= 1) {\n            const file = frames[i]?.file;\n            if (file) {\n                into[file] = byStackKey[stackKey];\n                break;\n            }\n        }\n    }\n}\n\n/**\n * The current filename → debug-ID map, derived from the global registries.\n * Memoized on registry sizes (chunks load over time, so the map can grow).\n */\nexport function getFilenameDebugIdMap(): Record<string, string> {\n    const g = globalThis as DebugIdContainer;\n    const debugIds = g._debugIds;\n    const sentryDebugIds = g._sentryDebugIds;\n    if (!debugIds && !sentryDebugIds) {\n        return {};\n    }\n\n    const debugIdKeyCount = debugIds ? Object.keys(debugIds).length : 0;\n    const sentryDebugIdKeyCount = sentryDebugIds ? Object.keys(sentryDebugIds).length : 0;\n    if (cache && cache.debugIdKeyCount === debugIdKeyCount && cache.sentryDebugIdKeyCount === sentryDebugIdKeyCount) {\n        return cache.map;\n    }\n\n    const map: Record<string, string> = {};\n    if (sentryDebugIds) {\n        buildFilenameMap(sentryDebugIds, map);\n    }\n    // Native _debugIds wins over _sentryDebugIds for the same file.\n    if (debugIds) {\n        buildFilenameMap(debugIds, map);\n    }\n    cache = { map, debugIdKeyCount, sentryDebugIdKeyCount };\n    return map;\n}\n\n/**\n * The subset of the debug-ID map covering the files in a report's frames.\n * Returns undefined when nothing matches, so reports stay unchanged on apps\n * without a debug-ID-injecting bundler plugin.\n */\nexport function getDebugIdMapForFrames(frames: StackFrame[]): Record<string, string> | undefined {\n    const full = getFilenameDebugIdMap();\n    let out: Record<string, string> | undefined;\n    for (const frame of frames) {\n        const file = frame.file;\n        if (file && full[file]) {\n            if (!out) {\n                out = {};\n            }\n            out[file] = full[file];\n        }\n    }\n    return out;\n}\n","/**\n * Builds the error report payload — the contract the SDK sends to the backend\n * (POST /api/ingestion/errors). This is intentionally the single place that\n * defines the shape, so the unit test for it doubles as the living spec that\n * the backend repo can build against.\n *\n * Pure and dependency-free; covered by __tests__/error-payload.test.ts.\n */\n\nimport { parseStack, StackFrame } from './stack-parser';\nimport { Breadcrumb } from './breadcrumbs';\nimport { collectLinkedErrors, LinkedException } from './linked-errors';\nimport { getDebugIdMapForFrames } from './debug-ids';\n\n/**\n * Random 32-hex idempotency ID (kept dependency-free to match this module).\n * Falls back to Math.random outside secure contexts; uniqueness (not\n * unpredictability) is all dedup needs.\n */\nfunction mintEventId(): string {\n    if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {\n        const bytes = new Uint8Array(16);\n        crypto.getRandomValues(bytes);\n        return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n    }\n    let out = '';\n    for (let i = 0; i < 32; i += 1) out += Math.floor(Math.random() * 16).toString(16);\n    return out;\n}\n\nexport type ErrorMechanism =\n    | 'onerror'\n    | 'onunhandledrejection'\n    | 'resource'\n    | 'csp'\n    | 'react'\n    | 'console'\n    | 'captureException';\n\n/** Connectivity at the moment the error fired — helps reproduce offline/flaky bugs. */\nexport interface NetworkState {\n    online: boolean;\n    effectiveType: string | null;\n}\n\n/**\n * The most recent error-correlated network request (a failed fetch/XHR), with\n * bodies/headers redacted. Bodies/headers are only present when the host opted\n * in via `captureRequestBodies`.\n */\nexport interface RequestContext {\n    url: string;\n    method: string;\n    status: number | null;\n    errorType?: string;\n    durationMs?: number;\n    requestHeaders?: Record<string, string>;\n    requestBody?: string;\n    responseBody?: string;\n}\n\nexport interface ErrorReportInput {\n    /** The thrown value: an Error, a string, or anything else. */\n    error: unknown;\n    mechanism: ErrorMechanism;\n    handled: boolean;\n    sessionId: string;\n    endUserId: string | null;\n    url: string;\n    breadcrumbs: Breadcrumb[];\n    release?: string | null;\n    environment?: string | null;\n    /** Git commit SHA the build was cut from — turns a culprit frame into a GitHub blob link. */\n    commitSha?: string | null;\n    /** Build/dist discriminator (e.g. build id) — pairs with `release` to locate the right source map. */\n    dist?: string | null;\n    /** React component stack (set by the error boundary). */\n    componentStack?: string;\n    /** Connectivity snapshot at error time. */\n    networkState?: NetworkState;\n    /** Last error-correlated network request (redacted). */\n    requestContext?: RequestContext;\n    /** Epoch ms the replay session started — lets the backend compute replay offset. */\n    sessionStartTimestampMs?: number;\n    automaticProperties?: Record<string, unknown>;\n    userProperties?: Record<string, unknown>;\n    sessionProperties?: Record<string, unknown>;\n    timestampMs?: number;\n}\n\nexport interface ErrorReport {\n    /**\n     * Client-minted idempotency ID. Set once at capture time; retries reuse\n     * the same serialized body, so the server can drop duplicate deliveries\n     * without touching occurrence counts.\n     */\n    eventId: string;\n    exceptionType: string;\n    value: string;\n    stackFrames: StackFrame[];\n    mechanism: ErrorMechanism;\n    handled: boolean;\n    release: string | null;\n    environment: string | null;\n    commitSha: string | null;\n    dist: string | null;\n    componentStack?: string;\n    networkState?: NetworkState;\n    requestContext?: RequestContext;\n    sessionStartTimestampMs?: number;\n    breadcrumbs: Breadcrumb[];\n    sessionId: string;\n    endUserId: string | null;\n    url: string;\n    timestampMs: number;\n    automaticProperties?: Record<string, unknown>;\n    userProperties?: Record<string, unknown>;\n    sessionProperties?: Record<string, unknown>;\n    /** Cause chain (`Error.cause` / `AggregateError.errors`), outermost first. */\n    linkedErrors?: LinkedException[];\n    /** filename → debug ID for the frames' files (from bundler-injected globals). */\n    debugIdMap?: Record<string, string>;\n}\n\n/**\n * Normalize an arbitrary thrown value into a type/message/stack triple.\n */\nexport function describeError(error: unknown): { type: string; value: string; stack: string | null } {\n    if (error instanceof Error) {\n        return {\n            type: error.name || 'Error',\n            value: error.message || '',\n            stack: error.stack || null,\n        };\n    }\n    if (typeof error === 'string') {\n        return { type: 'Error', value: error, stack: null };\n    }\n    if (error == null) {\n        return { type: 'Error', value: String(error), stack: null };\n    }\n    // Some libraries reject with a plain object that carries name/message.\n    if (typeof error === 'object') {\n        const obj = error as Record<string, unknown>;\n        const type = typeof obj.name === 'string' && obj.name ? obj.name : 'Error';\n        let value: string;\n        if (typeof obj.message === 'string') {\n            value = obj.message;\n        } else {\n            try {\n                value = JSON.stringify(error);\n            } catch {\n                value = String(error);\n            }\n        }\n        const stack = typeof obj.stack === 'string' ? obj.stack : null;\n        return { type, value, stack };\n    }\n    return { type: 'Error', value: String(error), stack: null };\n}\n\nexport function buildErrorReport(input: ErrorReportInput): ErrorReport {\n    const described = describeError(input.error);\n    const stackFrames = parseStack(described.stack);\n    const linkedErrors = collectLinkedErrors(input.error);\n    const linkedFrames: StackFrame[] = [];\n    for (const linked of linkedErrors) {\n        linkedFrames.push(...linked.stackFrames);\n    }\n    const debugIdMap = getDebugIdMapForFrames(\n        linkedFrames.length ? stackFrames.concat(linkedFrames) : stackFrames,\n    );\n    return {\n        eventId: mintEventId(),\n        exceptionType: described.type,\n        value: described.value,\n        stackFrames,\n        mechanism: input.mechanism,\n        handled: input.handled,\n        release: input.release ?? null,\n        environment: input.environment ?? null,\n        commitSha: input.commitSha ?? null,\n        dist: input.dist ?? null,\n        componentStack: input.componentStack,\n        networkState: input.networkState,\n        requestContext: input.requestContext,\n        sessionStartTimestampMs: input.sessionStartTimestampMs,\n        breadcrumbs: input.breadcrumbs,\n        sessionId: input.sessionId,\n        endUserId: input.endUserId,\n        url: input.url,\n        timestampMs: input.timestampMs ?? Date.now(),\n        automaticProperties: input.automaticProperties,\n        userProperties: input.userProperties,\n        sessionProperties: input.sessionProperties,\n        linkedErrors: linkedErrors.length ? linkedErrors : undefined,\n        debugIdMap,\n    };\n}\n\n/**\n * Key used for client-side de-dup. Built from the error type, message, and\n * every stack frame's location+function (Sentry's dedupe compares the full\n * stack), so distinct code paths that share a top frame are NOT collapsed\n * while the identical crash repeated in a burst is.\n */\nexport function dedupeKey(report: ErrorReport): string {\n    const frames = report.stackFrames.length\n        ? report.stackFrames\n              .map((f) => `${f.function ?? '?'}@${f.file ?? '?'}:${f.line ?? '?'}:${f.column ?? '?'}`)\n              .join('|')\n        : 'no-stack';\n    return `${report.exceptionType}|${report.value}|${frames}`;\n}\n","/**\n * The (thin) browser glue for crash detection.\n *\n * Listens for the two most common crash sources — uncaught errors\n * (`window.onerror`) and unhandled promise rejections (`unhandledrejection`) —\n * then runs the pure helpers (stack parse, breadcrumbs, dedup) and hands a\n * finished report to `send`.\n *\n * Everything here is wrapped so error capture can never throw back into the\n * host app. The interesting logic lives in the pure modules; this file is kept\n * deliberately small and is exercised by the Playwright browser tests.\n */\n\nimport { BreadcrumbBuffer } from './breadcrumbs';\nimport { ErrorDeduper } from './dedup';\nimport { ErrorFilterOptions, getDropReason } from './filters';\nimport {\n    buildErrorReport,\n    dedupeKey,\n    describeError,\n    ErrorMechanism,\n    ErrorReport,\n    NetworkState,\n    RequestContext,\n} from './error-payload';\n\n/** Read connectivity at error time (online + connection type when available). */\nfunction readNetworkState(): NetworkState | undefined {\n    if (typeof navigator === 'undefined') {\n        return undefined;\n    }\n    const conn = (navigator as Navigator & { connection?: { effectiveType?: string } }).connection;\n    return {\n        online: typeof navigator.onLine === 'boolean' ? navigator.onLine : true,\n        effectiveType: conn && typeof conn.effectiveType === 'string' ? conn.effectiveType : null,\n    };\n}\n\nexport interface ErrorCaptureContext {\n    sessionId: string;\n    endUserId: string | null;\n    url: string;\n    release?: string | null;\n    environment?: string | null;\n    commitSha?: string | null;\n    dist?: string | null;\n    sessionStartTimestampMs?: number;\n    requestContext?: RequestContext;\n    automaticProperties?: Record<string, unknown>;\n    userProperties?: Record<string, unknown>;\n    sessionProperties?: Record<string, unknown>;\n}\n\nexport interface ErrorCaptureOptions {\n    send: (report: ErrorReport) => void;\n    getContext: () => ErrorCaptureContext;\n    breadcrumbs: BreadcrumbBuffer;\n    /** Host-configurable ignoreErrors / denyUrls / allowUrls (Sentry semantics). */\n    filters?: ErrorFilterOptions;\n    /**\n     * Capture failed loads of third-party resources (other origins' scripts,\n     * images, ads, trackers). Off by default: adblockers make cross-origin\n     * resource failures overwhelmingly noise the app author cannot fix, so\n     * only first-party (same-origin) resource failures are reported.\n     */\n    captureThirdPartyResourceErrors?: boolean;\n}\n\n/** Same-origin check for resource URLs; relative URLs count as first-party. */\nfunction isFirstPartyUrl(url: string): boolean {\n    try {\n        if (typeof location === 'undefined') {\n            return true;\n        }\n        return new URL(url, location.href).origin === location.origin;\n    } catch {\n        return true;\n    }\n}\n\nexport class ErrorCapture {\n    private readonly opts: ErrorCaptureOptions;\n    private readonly deduper: ErrorDeduper;\n    private installed = false;\n    private onError?: (event: ErrorEvent) => void;\n    private onRejection?: (event: PromiseRejectionEvent) => void;\n    private onResourceError?: (event: Event) => void;\n    private onCsp?: (event: SecurityPolicyViolationEvent) => void;\n\n    constructor(opts: ErrorCaptureOptions) {\n        this.opts = opts;\n        this.deduper = new ErrorDeduper();\n    }\n\n    install(): void {\n        if (this.installed || typeof window === 'undefined') {\n            return;\n        }\n        this.installed = true;\n\n        this.onError = (event: ErrorEvent) => {\n            // Prefer the real Error object; fall back to the message string for\n            // browsers/cases that don't populate `event.error`.\n            const thrown = event && event.error != null ? event.error : (event && event.message) || 'Unknown error';\n            this.capture(thrown, 'onerror', false);\n        };\n        window.addEventListener('error', this.onError);\n\n        this.onRejection = (event: PromiseRejectionEvent) => {\n            const reason = event && 'reason' in event ? event.reason : event;\n            this.capture(reason, 'onunhandledrejection', false);\n        };\n        window.addEventListener('unhandledrejection', this.onRejection);\n\n        // Resource-load failures (404 img/script/link, failed JS chunk loads)\n        // don't bubble, so they only surface to a CAPTURE-phase listener on\n        // window. Script errors (target === window) are left to the bubble-phase\n        // `onError` above to avoid double-reporting.\n        this.onResourceError = (event: Event) => {\n            const target = event.target as (HTMLElement & { src?: string; href?: string }) | null;\n            if (!target || target === (window as unknown as HTMLElement) || !target.tagName) {\n                return;\n            }\n            const tag = target.tagName.toLowerCase();\n            const url = target.src || target.href || '';\n            if (!url) {\n                return;\n            }\n            if (!this.opts.captureThirdPartyResourceErrors && !isFirstPartyUrl(url)) {\n                return;\n            }\n            this.capture(\n                { name: 'ResourceLoadError', message: `Failed to load ${tag}: ${url}` },\n                'resource',\n                false,\n            );\n        };\n        window.addEventListener('error', this.onResourceError, true);\n\n        // Content-Security-Policy violations: cheap, high-signal security events.\n        // Extension-injected scripts constantly trip host CSP; those are not the\n        // app author's defect and drown the Issues feed.\n        this.onCsp = (event: SecurityPolicyViolationEvent) => {\n            const source = event.sourceFile || '';\n            if (\n                source.startsWith('chrome-extension:') ||\n                source.startsWith('moz-extension:') ||\n                source.startsWith('safari-extension:') ||\n                source.startsWith('safari-web-extension:')\n            ) {\n                return;\n            }\n            const directive = event.effectiveDirective || event.violatedDirective || 'unknown';\n            const blocked = event.blockedURI || 'inline';\n            this.capture(\n                { name: 'SecurityPolicyViolation', message: `Blocked ${blocked} (${directive})` },\n                'csp',\n                false,\n            );\n        };\n        window.addEventListener('securitypolicyviolation', this.onCsp);\n    }\n\n    uninstall(): void {\n        if (!this.installed || typeof window === 'undefined') {\n            return;\n        }\n        if (this.onError) {\n            window.removeEventListener('error', this.onError);\n        }\n        if (this.onRejection) {\n            window.removeEventListener('unhandledrejection', this.onRejection);\n        }\n        if (this.onResourceError) {\n            window.removeEventListener('error', this.onResourceError, true);\n        }\n        if (this.onCsp) {\n            window.removeEventListener('securitypolicyviolation', this.onCsp);\n        }\n        this.installed = false;\n    }\n\n    /**\n     * Build and send a report for a thrown value. Public so the manual\n     * `captureException` path can reuse it later. `extra.componentStack` carries\n     * the React component stack from the error boundary. Never throws.\n     */\n    capture(\n        error: unknown,\n        mechanism: ErrorMechanism,\n        handled: boolean,\n        extra?: { componentStack?: string },\n    ): void {\n        try {\n            const described = describeError(error);\n            // Cheap pre-build drop for ignored messages; URL filters need the\n            // parsed stack and run again in getDropReason on the full report.\n            if (\n                getDropReason(\n                    { exceptionType: described.type, value: described.value, stackFrames: [] },\n                    this.opts.filters,\n                )\n            ) {\n                return;\n            }\n\n            const ctx = this.opts.getContext();\n            const report = buildErrorReport({\n                error,\n                mechanism,\n                handled,\n                sessionId: ctx.sessionId,\n                endUserId: ctx.endUserId,\n                url: ctx.url,\n                release: ctx.release ?? null,\n                environment: ctx.environment ?? null,\n                commitSha: ctx.commitSha ?? null,\n                dist: ctx.dist ?? null,\n                componentStack: extra?.componentStack,\n                networkState: readNetworkState(),\n                requestContext: ctx.requestContext,\n                sessionStartTimestampMs: ctx.sessionStartTimestampMs,\n                breadcrumbs: this.opts.breadcrumbs.snapshot(),\n                automaticProperties: ctx.automaticProperties,\n                userProperties: ctx.userProperties,\n                sessionProperties: ctx.sessionProperties,\n            });\n\n            if (getDropReason(report, this.opts.filters)) {\n                return;\n            }\n\n            if (!this.deduper.shouldReport(dedupeKey(report))) {\n                return;\n            }\n\n            this.opts.send(report);\n        } catch {\n            // Error capture must never break the host application.\n        }\n    }\n}\n","/**\n * Redaction for error-correlated network request/response context.\n *\n * The SDK's main `redact.ts` only handles rrweb DOM-field masking, so it can't\n * scrub request/response payloads. This module is a small, pure, dependency-free\n * helper that redacts well-known sensitive keys/headers and truncates bodies so\n * the opt-in `requestContext` on an error report never leaks secrets or bloats\n * the payload. Covered by __tests__/redact-network.test.ts.\n */\n\nconst REDACTED = '[REDACTED]';\nconst DEFAULT_MAX_BODY_LEN = 2048;\n\n// Substring match (case-insensitive) against object keys / header names.\nconst SENSITIVE_KEY_PATTERNS = [\n    'password',\n    'passwd',\n    'secret',\n    'token',\n    'apikey',\n    'api_key',\n    'authorization',\n    'auth',\n    'cookie',\n    'session',\n    'creditcard',\n    'credit_card',\n    'cardnumber',\n    'card_number',\n    'cvv',\n    'cvc',\n    'ssn',\n];\n\nfunction isSensitiveKey(key: string): boolean {\n    const k = key.toLowerCase();\n    return SENSITIVE_KEY_PATTERNS.some((p) => k.includes(p));\n}\n\nfunction redactJsonValue(value: unknown): unknown {\n    if (Array.isArray(value)) {\n        return value.map(redactJsonValue);\n    }\n    if (value && typeof value === 'object') {\n        const out: Record<string, unknown> = {};\n        for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n            out[k] = isSensitiveKey(k) ? REDACTED : redactJsonValue(v);\n        }\n        return out;\n    }\n    return value;\n}\n\nfunction truncate(text: string, maxLen: number): string {\n    if (text.length <= maxLen) {\n        return text;\n    }\n    return `${text.slice(0, maxLen)}...[truncated ${text.length - maxLen} chars]`;\n}\n\n/**\n * Redact a request/response body. JSON bodies get their sensitive keys replaced\n * with [REDACTED]; non-JSON bodies are passed through (truncated). Always\n * truncated to `maxLen`.\n */\nexport function redactBodyString(body: string, maxLen: number = DEFAULT_MAX_BODY_LEN): string {\n    if (!body) {\n        return body;\n    }\n    try {\n        const parsed = JSON.parse(body);\n        return truncate(JSON.stringify(redactJsonValue(parsed)), maxLen);\n    } catch {\n        return truncate(body, maxLen);\n    }\n}\n\n/** Redact sensitive header values (Authorization, Cookie, etc.). */\nexport function redactHeaders(headers: Record<string, string>): Record<string, string> {\n    const out: Record<string, string> = {};\n    for (const [k, v] of Object.entries(headers)) {\n        out[k] = isSensitiveKey(k) ? REDACTED : v;\n    }\n    return out;\n}\n","/**\n * Detects React/Next.js hydration-mismatch errors from console output.\n *\n * Hydration mismatches surface as `console.error` calls (not thrown errors), so\n * they never reach `window.onerror`. This pure matcher lets the tracker promote\n * the well-known hydration messages into structured error reports.\n *\n * Pure and dependency-free; covered by __tests__/hydration.test.ts.\n */\n\n// Substring patterns React/Next emit for hydration mismatches (lowercased).\nconst HYDRATION_PATTERNS = [\n    'hydration failed because',\n    'text content does not match server-rendered html',\n    'there was an error while hydrating',\n    'hydration completed but contains mismatches',\n    \"did not match. server:\",\n    'while hydrating the component',\n];\n\n// React minified hydration error codes (https://react.dev/errors).\nconst HYDRATION_MINIFIED_CODES = ['418', '421', '422', '423', '425'];\n\nexport function isHydrationError(message: string): boolean {\n    if (!message || typeof message !== 'string') {\n        return false;\n    }\n    const lower = message.toLowerCase();\n    if (HYDRATION_PATTERNS.some((p) => lower.includes(p))) {\n        return true;\n    }\n    // \"Minified React error #418\" / \".../errors/418\"\n    return HYDRATION_MINIFIED_CODES.some(\n        (code) => lower.includes(`minified react error #${code}`) || lower.includes(`/errors/${code}`),\n    );\n}\n","// Distributed tracing for the browser SDK (Sentry-parity).\n//\n// Captures spans that describe how a page load / interaction unfolds and groups\n// them into a trace:\n//   - a root `pageload` transaction (Navigation Timing),\n//   - child spans for the navigation phases (request / response / dom),\n//   - a `resource.<type>` child per Resource Timing entry (scripts, css, img,\n//     fetch/xhr, fonts, …) so the trace reads as a granular waterfall,\n//   - any custom spans the host app opens via `startSpan` / `startInactiveSpan`.\n//\n// All spans in a page load share a W3C-style `traceId`; children point at their\n// `parentSpanId`. Outgoing same-origin requests get a `traceparent` /\n// `sentry-trace` header so a backend that understands them can continue the\n// trace (the \"distributed\" part). The module is self-contained and best-effort:\n// any failure is swallowed so it can never break the host app.\n\nimport { logDebug, logWarn } from './utils/logger';\nimport { sanitizeUrl } from './redact';\n\nconst isBrowser = typeof window !== 'undefined';\n\nexport interface HBSpan {\n    traceId: string;\n    spanId: string;\n    parentSpanId: string; // '' for a root span\n    name: string;\n    op: string;\n    startTimeMs: number; // epoch ms\n    durationMs: number;\n    status: 'ok' | 'error' | 'cancelled';\n    attributes?: Record<string, unknown>;\n}\n\nexport interface TracingSessionContext {\n    sessionId: string | null;\n    endUserId: string | null;\n    automaticProperties?: Record<string, unknown>;\n    release?: string | null;\n    environment?: string | null;\n}\n\nexport interface TracingConfig {\n    getSession: () => TracingSessionContext;\n    // Batch flush. `useBeacon` is true on page unload (synchronous transport).\n    sendSpans: (spans: HBSpan[], ctx: TracingSessionContext, useBeacon: boolean) => void;\n    // Predicate for SDK-owned/ignored URLs (reuses the tracker's network skip).\n    shouldSkipUrl?: (url: string) => boolean;\n    // How long after `load` to collect resource spans before flushing the\n    // page-load trace. Late resources are still captured by the observer.\n    pageLoadFlushDelayMs?: number;\n}\n\n// Active, mutable span the host app is timing.\nexport interface InactiveSpan {\n    setAttribute(key: string, value: unknown): void;\n    setStatus(status: 'ok' | 'error' | 'cancelled'): void;\n    end(): void;\n}\n\nconst HEX = '0123456789abcdef';\nfunction randHex(bytes: number): string {\n    const len = bytes * 2;\n    // Prefer crypto for unbiased ids; fall back to Math.random.\n    try {\n        const arr = new Uint8Array(bytes);\n        (globalThis.crypto || (globalThis as any).msCrypto).getRandomValues(arr);\n        let out = '';\n        for (let i = 0; i < bytes; i++) out += arr[i].toString(16).padStart(2, '0');\n        return out;\n    } catch {\n        let out = '';\n        for (let i = 0; i < len; i++) out += HEX[(Math.random() * 16) | 0];\n        return out;\n    }\n}\n\nconst newTraceId = () => randHex(16); // 32 hex chars\nconst newSpanId = () => randHex(8); //  16 hex chars\n\n// Map a Resource Timing initiatorType to a span op.\nfunction resourceOp(initiatorType: string): string {\n    switch (initiatorType) {\n        case 'fetch':\n        case 'xmlhttprequest':\n            return 'http.client';\n        case 'script':\n            return 'resource.script';\n        case 'css':\n        case 'link':\n            return 'resource.css';\n        case 'img':\n        case 'image':\n            return 'resource.img';\n        case 'font':\n            return 'resource.font';\n        default:\n            return initiatorType ? `resource.${initiatorType}` : 'resource.other';\n    }\n}\n\n// Short, readable resource name: method-less path + filename.\nfunction resourceName(url: string): string {\n    try {\n        const u = new URL(url, isBrowser ? window.location.href : undefined);\n        const file = u.pathname.split('/').filter(Boolean).pop() || u.pathname || u.hostname;\n        return file;\n    } catch {\n        return url.slice(0, 120);\n    }\n}\n\nexport class Tracing {\n    private cfg: TracingConfig;\n    private traceId: string = newTraceId();\n    private rootSpanId: string = newSpanId();\n    private buffer: HBSpan[] = [];\n    private flushTimer: ReturnType<typeof setTimeout> | null = null;\n    private resourceObserver: PerformanceObserver | null = null;\n    private started = false;\n    private originalFetch: typeof fetch | null = null;\n\n    constructor(cfg: TracingConfig) {\n        this.cfg = cfg;\n    }\n\n    start(): void {\n        if (!isBrowser || this.started) return;\n        this.started = true;\n        try {\n            this.installTracePropagation();\n            this.observeResources();\n            if (document.readyState === 'complete') {\n                this.capturePageLoad();\n            } else {\n                window.addEventListener('load', () => this.capturePageLoad(), { once: true });\n            }\n        } catch (e) {\n            logWarn('[SDK] tracing start failed:', e);\n        }\n    }\n\n    /** Begin a fresh trace (e.g. an SPA route change). */\n    startNewTrace(): void {\n        this.traceId = newTraceId();\n        this.rootSpanId = newSpanId();\n    }\n\n    /** W3C + Sentry trace-context headers for the current trace. */\n    getTraceHeaders(): Record<string, string> {\n        const childId = newSpanId();\n        return {\n            traceparent: `00-${this.traceId}-${childId}-01`,\n            'sentry-trace': `${this.traceId}-${childId}-1`,\n        };\n    }\n\n    /** Time a synchronous or async callback as a child span. */\n    startSpan<T>(\n        opts: { name: string; op?: string; attributes?: Record<string, unknown> },\n        callback: () => T,\n    ): T {\n        const span = this.startInactiveSpan(opts);\n        try {\n            const result = callback();\n            if (result instanceof Promise) {\n                return result\n                    .then((v) => {\n                        span.end();\n                        return v;\n                    })\n                    .catch((err) => {\n                        span.setStatus('error');\n                        span.end();\n                        throw err;\n                    }) as unknown as T;\n            }\n            span.end();\n            return result;\n        } catch (err) {\n            span.setStatus('error');\n            span.end();\n            throw err;\n        }\n    }\n\n    /** Open a span the caller ends manually. */\n    startInactiveSpan(opts: {\n        name: string;\n        op?: string;\n        attributes?: Record<string, unknown>;\n    }): InactiveSpan {\n        const startTimeMs = Date.now();\n        const spanId = newSpanId();\n        const traceId = this.traceId;\n        const parentSpanId = this.rootSpanId;\n        const attributes: Record<string, unknown> = { ...(opts.attributes || {}) };\n        let status: 'ok' | 'error' | 'cancelled' = 'ok';\n        let ended = false;\n        const record = this.record.bind(this);\n        return {\n            setAttribute(key, value) {\n                attributes[key] = value;\n            },\n            setStatus(next) {\n                status = next;\n            },\n            end() {\n                if (ended) return;\n                ended = true;\n                record({\n                    traceId,\n                    spanId,\n                    parentSpanId,\n                    name: opts.name,\n                    op: opts.op || 'custom',\n                    startTimeMs,\n                    durationMs: Math.max(Date.now() - startTimeMs, 0),\n                    status,\n                    attributes,\n                });\n            },\n        };\n    }\n\n    /** Flush buffered spans now. Called on unload (beacon) by the tracker. */\n    flush(useBeacon = false): void {\n        if (this.buffer.length === 0) return;\n        const spans = this.buffer;\n        this.buffer = [];\n        if (this.flushTimer) {\n            clearTimeout(this.flushTimer);\n            this.flushTimer = null;\n        }\n        try {\n            this.cfg.sendSpans(spans, this.cfg.getSession(), useBeacon);\n        } catch (e) {\n            logWarn('[SDK] tracing flush failed:', e);\n        }\n    }\n\n    stop(): void {\n        try {\n            this.resourceObserver?.disconnect();\n        } catch {\n            /* ignore */\n        }\n        if (this.originalFetch && typeof window !== 'undefined') {\n            window.fetch = this.originalFetch;\n        }\n        this.flush(false);\n    }\n\n    // ── internals ────────────────────────────────────────────────────────────\n\n    private record(span: HBSpan): void {\n        // Stamp release/environment onto every span so Monitoring env filters\n        // can scope Performance without a separate join.\n        try {\n            const ctx = this.cfg.getSession();\n            const attrs = { ...(span.attributes || {}) };\n            if (ctx.release && attrs.release == null) attrs.release = ctx.release;\n            if (ctx.environment && attrs.environment == null) {\n                attrs.environment = ctx.environment;\n            }\n            span = { ...span, attributes: attrs };\n        } catch {\n            /* best-effort */\n        }\n        this.buffer.push(span);\n        // Coalesce sends; cap buffer so a chatty page can't grow it unbounded.\n        if (this.buffer.length >= 100) {\n            this.flush(false);\n            return;\n        }\n        if (!this.flushTimer) {\n            this.flushTimer = setTimeout(() => this.flush(false), 4000);\n        }\n    }\n\n    private capturePageLoad(): void {\n        if (typeof performance === 'undefined') return;\n        try {\n            const nav = performance.getEntriesByType(\n                'navigation',\n            )[0] as PerformanceNavigationTiming | undefined;\n            if (!nav) return;\n            const origin = performance.timeOrigin;\n            const startTimeMs = origin + nav.fetchStart;\n            const endRel = nav.loadEventEnd || nav.domComplete || nav.responseEnd;\n            const durationMs = Math.max(endRel - nav.fetchStart, 0);\n            const pathname = isBrowser ? window.location.pathname : '/';\n\n            // Root transaction.\n            this.record({\n                traceId: this.traceId,\n                spanId: this.rootSpanId,\n                parentSpanId: '',\n                name: `pageload ${pathname}`,\n                op: 'pageload',\n                startTimeMs,\n                durationMs,\n                status: 'ok',\n                attributes: {\n                    'page.url': isBrowser ? sanitizeUrl(window.location.href) : '',\n                    'page.route': pathname,\n                    'http.response.transfer_size': nav.transferSize,\n                    'navigation.type': nav.type,\n                },\n            });\n\n            // Standard navigation-phase child spans (only emit non-zero ones).\n            const phase = (\n                name: string,\n                op: string,\n                startRel: number,\n                endRelTime: number,\n                attrs?: Record<string, unknown>,\n            ) => {\n                const dur = endRelTime - startRel;\n                if (!(dur > 0) || !(startRel >= 0)) return;\n                this.record({\n                    traceId: this.traceId,\n                    spanId: newSpanId(),\n                    parentSpanId: this.rootSpanId,\n                    name,\n                    op,\n                    startTimeMs: origin + startRel,\n                    durationMs: dur,\n                    status: 'ok',\n                    attributes: attrs,\n                });\n            };\n            phase('DNS lookup', 'browser.dns', nav.domainLookupStart, nav.domainLookupEnd);\n            phase('TCP connect', 'browser.connect', nav.connectStart, nav.connectEnd);\n            phase('Request', 'http.request', nav.requestStart, nav.responseStart, {\n                'http.method': 'GET',\n            });\n            phase('Response', 'http.response', nav.responseStart, nav.responseEnd);\n            phase(\n                'DOM processing',\n                'browser.dom',\n                nav.domInteractive || nav.responseEnd,\n                nav.domContentLoadedEventEnd || nav.domComplete,\n            );\n            phase('Resource load', 'browser.load', nav.domContentLoadedEventEnd, nav.loadEventEnd);\n\n            // Resources already buffered before `load`.\n            const resources = performance.getEntriesByType(\n                'resource',\n            ) as PerformanceResourceTiming[];\n            for (const r of resources) this.recordResource(r, origin);\n\n            // Flush the page-load trace shortly after, letting late resources land.\n            const delay = this.cfg.pageLoadFlushDelayMs ?? 2500;\n            setTimeout(() => this.flush(false), delay);\n        } catch (e) {\n            logWarn('[SDK] capturePageLoad failed:', e);\n        }\n    }\n\n    private observeResources(): void {\n        if (typeof PerformanceObserver === 'undefined') return;\n        try {\n            const origin = performance.timeOrigin;\n            this.resourceObserver = new PerformanceObserver((list) => {\n                for (const entry of list.getEntries()) {\n                    this.recordResource(entry as PerformanceResourceTiming, origin);\n                }\n            });\n            this.resourceObserver.observe({ type: 'resource', buffered: false });\n        } catch (e) {\n            logDebug('[SDK] resource observer unavailable:', e);\n        }\n    }\n\n    private recordResource(r: PerformanceResourceTiming, origin: number): void {\n        const url = r.name;\n        if (!url) return;\n        if (this.cfg.shouldSkipUrl && this.cfg.shouldSkipUrl(url)) return;\n        const durationMs = Math.max(r.responseEnd - r.startTime, 0);\n        if (!(durationMs >= 0)) return;\n        this.record({\n            traceId: this.traceId,\n            spanId: newSpanId(),\n            parentSpanId: this.rootSpanId,\n            name: resourceName(url),\n            op: resourceOp(r.initiatorType),\n            startTimeMs: origin + r.startTime,\n            durationMs,\n            status: 'ok',\n            attributes: {\n                'resource.url': url.slice(0, 300),\n                'resource.initiator': r.initiatorType,\n                'resource.transfer_size': r.transferSize,\n                'resource.encoded_size': r.encodedBodySize,\n                'resource.decoded_size': r.decodedBodySize,\n            },\n        });\n    }\n\n    // Inject trace-context headers into same-origin fetch requests so a backend\n    // can continue the trace. Best-effort: never blocks or alters the request\n    // semantics, and chains to whatever `window.fetch` currently is (composes\n    // with the network-error wrapper).\n    private installTracePropagation(): void {\n        if (typeof window === 'undefined' || typeof window.fetch !== 'function') return;\n        this.originalFetch = window.fetch.bind(window);\n        const self = this;\n        window.fetch = function (\n            input: RequestInfo | URL,\n            init?: RequestInit,\n        ): Promise<Response> {\n            try {\n                const url =\n                    typeof input === 'string'\n                        ? input\n                        : input instanceof URL\n                          ? input.toString()\n                          : input.url;\n                const sameOrigin = (() => {\n                    try {\n                        return new URL(url, window.location.href).origin === window.location.origin;\n                    } catch {\n                        return false;\n                    }\n                })();\n                const skip = self.cfg.shouldSkipUrl ? self.cfg.shouldSkipUrl(url) : false;\n                if (sameOrigin && !skip) {\n                    const headers = new Headers(\n                        init?.headers || (typeof input !== 'string' && !(input instanceof URL) ? input.headers : undefined),\n                    );\n                    const trace = self.getTraceHeaders();\n                    if (!headers.has('traceparent')) headers.set('traceparent', trace.traceparent);\n                    if (!headers.has('sentry-trace')) headers.set('sentry-trace', trace['sentry-trace']);\n                    const nextInit: RequestInit = { ...(init || {}), headers };\n                    return self.originalFetch!(input, nextInit);\n                }\n            } catch {\n                /* fall through to the unmodified request */\n            }\n            return self.originalFetch!(input, init);\n        } as typeof fetch;\n    }\n}\n","import { record } from '@rrweb/record';\nimport type { listenerHandler } from '@rrweb/types';\nimport { CssSnapshotCapture, CSS_SNAPSHOT_TAG } from './cssSnapshot';\nimport { LocalAssetSnapshotCapture, LOCAL_ASSET_TAG } from './localAssetSnapshot';\nimport { FrictionClickDetector, type FrictionClickInfo } from './frictionClicks';\nimport { ContrastCapture, HB_CONTRAST_TAG, HB_CLIP_TAG, HB_BROKEN_ASSET_TAG, HB_OVERLAP_TAG, HB_MISALIGN_TAG } from './contrast';\nimport { v1 as uuidv1 } from 'uuid';\nimport { HumanBehaviorAPI } from './api';\nimport { RedactionManager, RedactionOptions, sanitizeUrl } from './redact';\nimport { logger, logError, logWarn, logInfo, logDebug, isSDKLogging } from './utils/logger';\nimport { PropertyManager, Properties } from './utils/property-manager';\nimport { BreadcrumbBuffer, BreadcrumbType } from './errors/breadcrumbs';\nimport { ErrorCapture } from './errors/capture';\nimport { ErrorFilterOptions } from './errors/filters';\nimport { ErrorMechanism, RequestContext } from './errors/error-payload';\nimport { redactBodyString, redactHeaders } from './errors/redact-network';\nimport { isHydrationError } from './errors/hydration';\nimport { Tracing, type HBSpan, type InactiveSpan } from './tracing';\nimport { isAdoptableSharedSession } from './shared-session';\n\n// Check if we're in a browser environment\nconst isBrowser = typeof window !== 'undefined';\n\n// Global declarations moved to browser-tracker.ts to avoid conflicts\n\nexport class HumanBehaviorTracker {\n    private eventQueue: any[] = []; // Unified queue for all events (regular + recordings)\n    private pendingCustomEvents: Array<{ eventName: string; properties: any; timestamp: number; eventId: string }> = [];\n    private pendingLogs: Array<{ logData: any; timestamp: number }> = [];\n    private pendingNetworkErrors: Array<{ errorData: any; timestamp: number }> = [];\n\n    /**\n     * Outbound custom-event batch queue. Debounced flush at\n     * CUSTOM_EVENT_FLUSH_MS or hard-flush at CUSTOM_EVENT_BATCH_MAX. Mirrors\n     * PostHog's 100ms / 50-event default. Preserves insertion order.\n     */\n    private customEventBatch: Array<{ eventName: string; eventProperties: any; eventId: string }> = [];\n    private customEventBatchTimer: ReturnType<typeof setTimeout> | null = null;\n    private readonly CUSTOM_EVENT_FLUSH_MS = 100;\n    private readonly CUSTOM_EVENT_BATCH_MAX = 50;\n    \n    private sessionId!: string;\n    private windowId!: string; // Window ID for multi-window tracking\n    // In-memory session state (source of truth during session)\n    private _sessionActivityTimestamp: number | null = null;\n    private _sessionStartTimestamp: number | null = null;\n    private userProperties: Record<string, any> = {};\n    private isProcessing: boolean = false;\n    \n    private flushInterval: number | null = null;\n    // Two-tier flush cadence. Normally we batch every IDLE_FLUSH_INTERVAL_MS to\n    // keep request volume low. When the server reports that a dashboard live\n    // viewer is actively watching this project (via the `liveViewerActive` flag\n    // on the /events response), we temporarily drop to LIVE_FLUSH_INTERVAL_MS so\n    // the live replay feels real-time, then decay back after\n    // LIVE_FLUSH_GRACE_MS without another positive signal. Dormant by default:\n    // until a server sends the flag, behaviour is identical to the old fixed 3s.\n    private readonly IDLE_FLUSH_INTERVAL_MS = 3000;\n    private readonly LIVE_FLUSH_INTERVAL_MS = 150;\n    private readonly LIVE_FLUSH_GRACE_MS = 6000;\n    private flushTier: 'idle' | 'live' = 'idle';\n    private liveFlushDeadline = 0;\n    private readonly MAX_QUEUE_SIZE: number; // Configurable queue size\n\n    // Fixed-cadence presence ping so the dashboard's \"live now\" indicator\n    // counts idle pages too — flushEvents() short-circuits on an empty\n    // queue, so without this an inert tab (no DOM mutations, no input)\n    // would silently drop out of the live set after ~60s. 30s is well\n    // under the server's LIVE_WINDOW_MS so a single missed beacon is\n    // tolerated.\n    private heartbeatInterval: number | null = null;\n    private readonly HEARTBEAT_INTERVAL_MS = 30_000;\n\n    // Idle window after which the SDK rotates `sessionId`.\n    //\n    // Session-continuity invariant across the three services (keep in sync):\n    //   SDK idle rotation (here)          = 15 min\n    //   ingestion stitch window           = 15 min  (SESSION_INACTIVE_MINUTES,\n    //                                        ingestion-server/src/utils/sessionManager.ts)\n    //   archiver finalize delay           = 20 min  (SESSION_INACTIVE_MINUTES,\n    //                                        humanbehavior-session-archiver/src/archiver.ts)\n    //\n    // This value MUST EQUAL the ingestion stitch window so a returning user\n    // within the window reuses the same sessionId on BOTH sides — rrweb chunks\n    // and analytics events stitch onto one session and the replay covers the\n    // full span. If the two drift, the dashboard shows a session \"lasting 1h\"\n    // with a 20s replay: one side keeps the old id while the other starts a new\n    // one, splitting rrweb from analytics. The archiver MUST trail both (20 > 15)\n    // so it never finalizes a recording ingestion still considers live and then\n    // drops the returning half as \"already archived\". (Tighter than the 30-min\n    // GA/Mixpanel/Amplitude default — a deliberate trade to keep replay-archive\n    // latency low.)\n    private readonly SESSION_IDLE_TIMEOUT_MS = 15 * 60 * 1000;\n    private readonly SESSION_MAX_LENGTH_MS = 24 * 60 * 60 * 1000;\n\n    /** Only emit `$focus` blurred after blur lasts this long (quick alt-tab → no chop). */\n    private focusBlurGraceTimeout: number | null = null;\n    private readonly FOCUS_BLUR_GRACE_MS = 2_000;\n    /** Last `$focus` marker we pushed (`null` = never blurred in this session tree). */\n    private lastEmittedFocusState: 'focused' | 'blurred' | null = null;\n\n    private api!: HumanBehaviorAPI;\n    private endUserId: string | null = null;\n    private apiKey!: string;\n    private ingestionUrl!: string;\n    private initialized: boolean = false;\n    public initializationPromise: Promise<void> | null = null;\n    private monthlyLimitReached: boolean = false;\n    private redactionManager!: RedactionManager;\n    private propertyManager!: PropertyManager;\n    \n    private isDomReady: boolean = false;\n    private requestQueue: any[] = [];\n    private domReadyHandlers: Array<() => void> = [];\n    \n    // Console tracking properties\n    private originalConsole: {\n        log: typeof console.log;\n        warn: typeof console.warn;\n        error: typeof console.error;\n    } | null = null;\n    private consoleTrackingEnabled: boolean = false;\n\n    // Network tracking properties\n    private originalFetch: typeof fetch | null = null;\n    private networkTrackingEnabled: boolean = false;\n    private captureRequestBodiesFlag: boolean = false;\n    // Most recent network request (redacted), attached to the next error report\n    // when it occurred within ERROR_REQUEST_WINDOW_MS. Holds failures (with\n    // bodies/headers when opted in) and successes (metadata only).\n    private lastRequestContext?: RequestContext;\n    private lastRequestContextAt = 0; // epoch ms the lastRequestContext was recorded\n    // Only correlate a request to an error if it happened within this window.\n    private readonly ERROR_REQUEST_WINDOW_MS = 10_000;\n    \n    // Tracking configuration flags\n    private enableConsoleTrackingFlag: boolean = true; // Default: enabled (opt-out)\n    private enableNetworkTrackingFlag: boolean = true; // Default: enabled (opt-out)\n    private enableErrorTrackingFlag: boolean = true; // Enable crash/error capture (default: true, set to false to opt-out)\n    private enableWebVitalsFlag: boolean = true; // Enable Core Web Vitals tracking (FCP/LCP/CLS/INP/TTFB) (default: true, set to false to opt-out)\n    private enableTracingFlag: boolean = true; // Enable distributed tracing (page-load + resource + custom spans) (default: true, opt-out)\n    private tracing: Tracing | null = null;\n\n    // Error (crash) capture properties\n    private errorCapture: ErrorCapture | null = null;\n    private errorFilterOptions: ErrorFilterOptions = {};\n    private captureThirdPartyResourceErrorsFlag = false; // Default: first-party resource failures only\n    private readonly breadcrumbs: BreadcrumbBuffer = new BreadcrumbBuffer();\n    private release: string | null = null; // App version stamped on each error report\n    private environment: string | null = null; // e.g. 'production' / 'staging'\n    private commitSha: string | null = null; // Git commit SHA → GitHub blob links for issues\n    private dist: string | null = null; // Build/dist discriminator → pairs with release to locate source maps\n\n    // Navigation tracking properties\n    public navigationTrackingEnabled: boolean = false;\n    private currentUrl: string = '';\n    private previousUrl: string = '';\n    private originalPushState: typeof history.pushState | null = null;\n    private originalReplaceState: typeof history.replaceState | null = null;\n    private navigationListeners: Array<() => void> = [];\n    /**\n     * Timestamp of the last `pushState` call. Used by the `hashchange`\n     * listener to suppress the dup pageview when a `pushState('/foo#bar')`\n     * also fires `hashchange` synchronously.\n     */\n    private lastPushStateAt: number = 0;\n    /** Window for hashchange-after-pushState dedupe. */\n    private readonly NAVIGATION_DEDUPE_MS = 100;\n    private _connectionBlocked: boolean = false;\n    private recordInstance: listenerHandler | null = null;\n    private sessionStartTime: number = Date.now();\n    private rrwebRecord: any = null;\n    private fullSnapshotTimeout: number | null = null;\n    private cssSnapshotCapture: CssSnapshotCapture | null = null;\n    private localAssetSnapshotCapture: LocalAssetSnapshotCapture | null = null;\n    private contrastCapture: ContrastCapture | null = null;\n    private brokenAssetHandler: ((e: Event) => void) | null = null;\n    private brokenAssetSeen: Set<string> = new Set();\n    private recordCanvas: boolean = false; // Store canvas recording preference\n    private isStarted: boolean = false; // Guard against multiple start() calls\n    private minimumDurationMilliseconds: number = 5000; // Default: 5 seconds minimum (configurable via init)\n    \n    // WindowId tracking properties\n    private readonly _window_id_storage_key: string;\n    private readonly _primary_window_exists_storage_key: string;\n    \n    // Idle detection properties\n    private _isIdle: boolean | 'unknown' = 'unknown'; // Idle state: true, false, or 'unknown' (initial)\n    private _lastActivityTimestamp: number = Date.now(); // Last user interaction timestamp\n    private readonly IDLE_THRESHOLD_MS = 5 * 60 * 1000; // 5 minutes\n    \n    // Rage-click / dead-click detection. All thresholds and eligibility rules\n    // live in frictionClicks.ts, which is a port of trace-compiler's\n    // detectors so the raw $rageclick/$deadclick stream agrees with the\n    // compiled rage_click/dead_click trace rows for the same session.\n    private frictionClicks: FrictionClickDetector | null = null;\n    private frictionMutationObserver: MutationObserver | null = null;\n    \n    /**\n     * Check if the tracker has been started\n     */\n    public get isTrackerStarted(): boolean {\n        return this.isStarted;\n    }\n\n    /**\n     * DOM ready detection - more aggressive\n     */\n    private setupDomReadyHandler(): void {\n        if (!isBrowser) {\n            this.onDomReady();\n            return;\n        }\n\n        // More aggressive DOM ready detection\n        if (document.readyState === 'complete' || document.readyState === 'interactive') {\n            // DOM is ready enough\n            this.onDomReady();\n        } else if (document.addEventListener) {\n            // Wait for DOMContentLoaded, but also check periodically\n            const checkDomReady = () => {\n                if (document.readyState === 'interactive' || document.readyState === 'complete') {\n                    this.onDomReady();\n                }\n            };\n            \n            document.addEventListener('DOMContentLoaded', () => this.onDomReady(), { capture: false });\n            \n            // Also check periodically for faster response\n            const interval = setInterval(() => {\n                if (document.readyState === 'interactive' || document.readyState === 'complete') {\n                    clearInterval(interval);\n                    this.onDomReady();\n                }\n            }, 10); // Check every 10ms\n            \n            // Clear interval after 5 seconds to avoid infinite checking\n            setTimeout(() => clearInterval(interval), 5000);\n        } else {\n            // Fallback for older browsers\n            this.onDomReady();\n        }\n    }\n\n    /**\n     * Called when DOM is ready - processes queued requests\n     */\n    private onDomReady(): void {\n        if (this.isDomReady) return; // Prevent multiple calls\n        \n        this.isDomReady = true;\n        logDebug('🎯 DOM is ready, processing queued requests');\n        \n        // Process queued requests\n        this.requestQueue.forEach(request => {\n            this.processRequest(request);\n        });\n        this.requestQueue = [];\n        \n        // Call registered handlers\n        this.domReadyHandlers.forEach(handler => handler());\n        this.domReadyHandlers = [];\n    }\n\n    /**\n     * Queue a request until DOM is ready\n     */\n    private queueRequest(request: any): void {\n        if (this.isDomReady) {\n            this.processRequest(request);\n        } else {\n            this.requestQueue.push(request);\n        }\n    }\n\n    /**\n     * Process a request (called after DOM is ready)\n     */\n    private async processRequest(request: any): Promise<void> {\n        logDebug('Processing queued request:', request);\n        \n        switch (request.type) {\n            case 'addEvent':\n                await this.addEvent(request.event);\n                break;\n            case 'identifyUser':\n                await this.identifyUser(request.userProperties, { identityToken: request.identityToken });\n                break;\n            case 'trackPageView':\n                this.trackPageView();\n                break;\n            default:\n                logWarn('Unknown request type:', request.type);\n        }\n    }\n\n    /**\n     * Register a handler to be called when DOM is ready\n     */\n    private registerDomReadyHandler(handler: () => void): void {\n        if (this.isDomReady) {\n            handler();\n        } else {\n            this.domReadyHandlers.push(handler);\n        }\n    }\n\n    /**\n     * Initialize the HumanBehavior tracker\n     * This is the main entry point - call this once per page\n     */\n    public static init(apiKey: string, options?: {\n        ingestionUrl?: string;\n        logLevel?: 'none' | 'error' | 'warn' | 'info' | 'debug';\n        redactFields?: string[]; // DEPRECATED: Use redactionStrategy instead\n        redactionStrategy?: {\n            mode: 'privacy-first' | 'visibility-first'; // Default: 'privacy-first'\n            unredactFields?: string[]; // Fields to make visible (when mode: 'privacy-first')\n            redactFields?: string[];   // Fields to hide (when mode: 'visibility-first')\n        };\n        enableAutomaticTracking?: boolean;\n        suppressConsoleErrors?: boolean; // New option to control error suppression\n        recordCanvas?: boolean; // Enable canvas recording with protection\n        enableAutomaticProperties?: boolean; // Enable automatic property detection\n        propertyDenylist?: string[]; // Properties to exclude from tracking\n        automaticTrackingOptions?: {\n            trackButtons?: boolean;\n            trackLinks?: boolean;\n            trackForms?: boolean;\n            includeText?: boolean;\n            includeClasses?: boolean;\n        };\n        maxQueueSize?: number; // Configurable queue size\n        enableConsoleTracking?: boolean; // Enable console warn/error tracking (default: true, set to false to opt-out)\n        enableNetworkTracking?: boolean; // Enable network error tracking (default: true, set to false to opt-out)\n        enableErrorTracking?: boolean; // Enable crash/error capture (default: true, set to false to opt-out)\n        ignoreErrors?: (string | RegExp)[]; // Error messages to drop client-side (substring or RegExp; Sentry semantics)\n        denyUrls?: (string | RegExp)[]; // Drop errors originating from matching script URLs\n        allowUrls?: (string | RegExp)[]; // When set, only keep errors originating from matching script URLs\n        captureThirdPartyResourceErrors?: boolean; // Report failed loads of cross-origin resources (default: false, first-party only)\n        release?: string; // App version/release stamped on each error report\n        environment?: string; // Deployment environment (e.g. 'production')\n        commitSha?: string; // Git commit SHA stamped on each error report (enables GitHub blob links)\n        dist?: string; // Build/dist discriminator stamped on each error report (pairs with release for source-map lookup)\n        captureRequestBodies?: boolean; // Opt-in: attach redacted request/response bodies + headers to error-correlated requests (default: false)\n        enableWebVitals?: boolean; // Enable Core Web Vitals tracking (FCP/LCP/CLS/INP/TTFB) (default: true, set to false to opt-out)\n        enableTracing?: boolean; // Enable distributed tracing (page-load/resource/custom spans) (default: true, set to false to opt-out)\n        minimumDurationMilliseconds?: number; // Min session duration before events are sent (default: 5000). Set 0 to send immediately.\n    }): HumanBehaviorTracker {\n        // ✅ SUPPRESS COMMON RRWEB ERRORS FOR CLEAN CONSOLE\n        if (isBrowser && options?.suppressConsoleErrors !== false) {\n            // Suppress canvas security errors and network errors\n            // Only patterns that are unambiguously OURS. The generic network and\n            // CORS strings that used to be here ('CORS', 'Cross-Origin',\n            // 'Failed to fetch', 'Failed to load resource',\n            // 'Access-Control-Allow-Origin', 'net::ERR_BLOCKED_BY_CLIENT',\n            // 'NetworkError…') are substrings of the HOST app's own messages, so\n            // installing this SDK silently deleted a customer's console output:\n            // someone debugging a production CORS incident saw an empty console\n            // and concluded their code was fine. Our own failures already carry a\n            // HumanBehavior marker, so nothing of ours needs the broad match.\n            const originalConsoleError = console.error;\n            console.error = (...args: any[]) => {\n                const message = args.join(' ');\n                if (\n                    message.includes('SecurityError: Failed to execute \\'toDataURL\\'') ||\n                    message.includes('Tainted canvases may not be exported') ||\n                    message.includes('Cannot inline img src=') ||\n                    message.includes('HumanBehavior ERROR') ||\n                    message.includes('Failed to track custom event') ||\n                    message.includes('Error sending custom event')\n                ) {\n                    // Silently suppress these common errors\n                    return;\n                }\n                originalConsoleError.apply(console, args);\n            };\n\n            // Suppress console.warn for similar issues\n            const originalConsoleWarn = console.warn;\n            console.warn = (...args: any[]) => {\n                const message = args.join(' ');\n                if (\n                    message.includes('Cannot inline img src=') ||\n                    message.includes('Custom event network error') ||\n                    message.includes('Request blocked by ad blocker')\n                ) {\n                    // Silently suppress these common warnings\n                    return;\n                }\n                originalConsoleWarn.apply(console, args);\n            };\n\n            // Add global error handler for any remaining rrweb errors.\n            // INTENTIONAL: this listener only swallows the narrow rrweb/canvas\n            // console-noise messages matched below. It is SEPARATE from Issue\n            // Detection's crash capture (ErrorCapture, installed in\n            // setupErrorCapture()), which runs on its own `error` listener — so\n            // genuine app crashes are still captured even though preventDefault()\n            // is called here. Only fires when suppressConsoleErrors !== false.\n            //\n            // Narrowed for the same reason as the console patches above: matching\n            // 'CORS' / 'NetworkError' / 'Failed to fetch' here also cancelled the\n            // host app's own error events, hiding real failures from the customer.\n            window.addEventListener('error', (event) => {\n                const message = event.message || '';\n                if (\n                    message.includes('SecurityError') ||\n                    message.includes('Tainted canvases') ||\n                    message.includes('toDataURL')\n                ) {\n                    event.preventDefault();\n                    return false;\n                }\n            });\n        }\n        // Return existing instance if already initialized\n        if (isBrowser && (window as any).__humanBehaviorGlobalTracker) {\n            logDebug('Tracker already initialized, returning existing instance');\n            return (window as any).__humanBehaviorGlobalTracker;\n        }\n\n        // Configure logging if specified\n        if (options?.logLevel) {\n            // Class referenced explicitly so `init` also works when detached\n            // from the class (e.g. aliased on a global object).\n            HumanBehaviorTracker.configureLogging({ level: options.logLevel });\n        }\n\n        // Create new tracker instance\n        const tracker = new HumanBehaviorTracker(apiKey, options?.ingestionUrl, {\n            enableAutomaticProperties: options?.enableAutomaticProperties,\n            propertyDenylist: options?.propertyDenylist,\n            redactionStrategy: options?.redactionStrategy,\n            redactFields: options?.redactFields,\n            maxQueueSize: options?.maxQueueSize,\n            enableConsoleTracking: options?.enableConsoleTracking,\n            enableNetworkTracking: options?.enableNetworkTracking,\n            enableErrorTracking: options?.enableErrorTracking,\n            ignoreErrors: options?.ignoreErrors,\n            denyUrls: options?.denyUrls,\n            allowUrls: options?.allowUrls,\n            captureThirdPartyResourceErrors: options?.captureThirdPartyResourceErrors,\n            release: options?.release,\n            environment: options?.environment,\n            commitSha: options?.commitSha,\n            dist: options?.dist,\n            captureRequestBodies: options?.captureRequestBodies,\n            enableWebVitals: options?.enableWebVitals,\n            enableTracing: options?.enableTracing,\n            minimumDurationMilliseconds: options?.minimumDurationMilliseconds\n        });\n        \n        // Store canvas recording preference\n        tracker.recordCanvas = options?.recordCanvas ?? false;\n        \n        // Set unredacted fields if specified (legacy support)\n        if (options?.redactFields) {\n            tracker.setUnredactedFields(options.redactFields);\n        }\n\n        // Handle new redaction strategy - this is now handled in the constructor\n        // The redactionManager is created with the correct redactionStrategy in the constructor\n\n        // Setup automatic tracking if enabled\n        if (options?.enableAutomaticTracking !== false) {\n            tracker.setupAutomaticTracking(options?.automaticTrackingOptions);\n        }\n\n        // Start tracking\n        tracker.start();\n        \n        return tracker;\n    }\n\n    constructor(apiKey: string | undefined, ingestionUrl?: string, options?: {\n        enableAutomaticProperties?: boolean;\n        propertyDenylist?: string[];\n        redactionStrategy?: {\n            mode: 'privacy-first' | 'visibility-first';\n            unredactFields?: string[];\n            redactFields?: string[];\n        };\n        redactFields?: string[]; // Legacy support\n        maxQueueSize?: number; // Configurable queue size\n        enableConsoleTracking?: boolean; // Enable console warn/error tracking (default: true, set to false to opt-out)\n        enableNetworkTracking?: boolean; // Enable network error tracking (default: true, set to false to opt-out)\n        enableErrorTracking?: boolean; // Enable crash/error capture (default: true, set to false to opt-out)\n        ignoreErrors?: (string | RegExp)[]; // Error messages to drop client-side (substring or RegExp; Sentry semantics)\n        denyUrls?: (string | RegExp)[]; // Drop errors originating from matching script URLs\n        allowUrls?: (string | RegExp)[]; // When set, only keep errors originating from matching script URLs\n        captureThirdPartyResourceErrors?: boolean; // Report failed loads of cross-origin resources (default: false, first-party only)\n        release?: string; // App version/release stamped on each error report\n        environment?: string; // Deployment environment (e.g. 'production')\n        commitSha?: string; // Git commit SHA stamped on each error report (enables GitHub blob links)\n        dist?: string; // Build/dist discriminator stamped on each error report (pairs with release for source-map lookup)\n        captureRequestBodies?: boolean; // Opt-in: attach redacted request/response bodies + headers to error-correlated requests (default: false)\n        enableWebVitals?: boolean; // Enable Core Web Vitals tracking (FCP/LCP/CLS/INP/TTFB) (default: true, set to false to opt-out)\n        enableTracing?: boolean; // Enable distributed tracing (page-load/resource/custom spans) (default: true, set to false to opt-out)\n        minimumDurationMilliseconds?: number; // Min session duration before events are sent (default: 5000). Set 0 to send immediately.\n    }) {\n        if (!apiKey) {\n            throw new Error('Human Behavior API Key is required');\n        }\n        \n        // Initialize API\n        //const defaultIngestionUrl = 'http://3.137.217.33:3000'; // AWS Development Server\n        //const defaultIngestionUrl = 'http://ingestion-server-alb-1823866402.us-east-2.elb.amazonaws.com'; // ALB\n        const defaultIngestionUrl = 'https://ingest.humanbehavior.co'; // HTTPS ALB\n        const finalIngestionUrl = ingestionUrl || defaultIngestionUrl;\n        this.api = new HumanBehaviorAPI({ \n            apiKey: apiKey,\n            ingestionUrl: finalIngestionUrl\n        });\n        this.apiKey = apiKey;\n        this.ingestionUrl = finalIngestionUrl;\n        \n        // Initialize queue size (default 1000)\n        this.MAX_QUEUE_SIZE = options?.maxQueueSize ?? 1000;\n        \n        // Store tracking configuration flags (default: enabled, opt-out by setting to false)\n        this.enableConsoleTrackingFlag = options?.enableConsoleTracking !== false; // Default: true (opt-out)\n        this.enableNetworkTrackingFlag = options?.enableNetworkTracking !== false; // Default: true (opt-out)\n        this.enableErrorTrackingFlag = options?.enableErrorTracking !== false; // Default: true (opt-out)\n        this.errorFilterOptions = {\n            ignoreErrors: options?.ignoreErrors,\n            denyUrls: options?.denyUrls,\n            allowUrls: options?.allowUrls,\n        };\n        this.captureThirdPartyResourceErrorsFlag = options?.captureThirdPartyResourceErrors === true;\n        this.enableWebVitalsFlag = options?.enableWebVitals !== false; // Default: true (opt-out)\n        this.enableTracingFlag = options?.enableTracing !== false; // Default: true (opt-out)\n\n        // Optional version/environment context stamped onto every error report\n        // and (via session properties below) onto analytics events / spans.\n        this.release = options?.release ?? null;\n        this.environment = options?.environment ?? null;\n        this.commitSha = options?.commitSha ?? null;\n        this.dist = options?.dist ?? null;\n        // Opt-in: capture redacted request/response bodies for error-correlated requests\n        this.captureRequestBodiesFlag = options?.captureRequestBodies === true;\n\n        // Minimum session duration (ms) before buffered events are sent. Lets\n        // callers (and tests) opt into immediate delivery with 0.\n        if (typeof options?.minimumDurationMilliseconds === 'number' && options.minimumDurationMilliseconds >= 0) {\n            this.minimumDurationMilliseconds = options.minimumDurationMilliseconds;\n        }\n\n        this.redactionManager = new RedactionManager({\n            redactionStrategy: options?.redactionStrategy,\n            legacyRedactFields: options?.redactFields // For backward compatibility\n        });\n        \n        // Initialize property manager\n        this.propertyManager = new PropertyManager({\n            enableAutomaticProperties: options?.enableAutomaticProperties !== false,\n            propertyDenylist: options?.propertyDenylist || []\n        });\n        // Stamp release/environment onto every analytics event so Monitoring\n        // env filters (Releases CFR, Performance, Web Vitals) stay honest.\n        if (this.release) {\n            this.propertyManager.setSessionProperty('release', this.release);\n        }\n        if (this.environment) {\n            this.propertyManager.setSessionProperty('environment', this.environment);\n        }\n        \n        // DOM ready handling removed - using simpler approach\n\n        // ✅ CLIENT-SIDE ID GENERATION\n        // Generate endUserId locally (no server dependency)\n        if (isBrowser) {\n            const endUserIdKey = `human_behavior_end_user_id`;\n            const existingEndUserId = this.getCookie(endUserIdKey);\n            this.endUserId = existingEndUserId || uuidv1();\n            if (!existingEndUserId) {\n                this.setCookie(endUserIdKey, this.endUserId, 365);\n                logDebug(`Generated new endUserId: ${this.endUserId}`);\n            } else {\n                logDebug(`Reusing existing endUserId: ${this.endUserId}`);\n                }\n        } else {\n            this.endUserId = uuidv1();\n        }\n\n        // ✅ CLIENT-SIDE SESSION MANAGEMENT\n        // Generate sessionId with timeout checking\n        if (isBrowser) {\n            // Initialize windowId storage keys\n            const persistenceName = this.apiKey || 'default';\n            this._window_id_storage_key = `human_behavior_${persistenceName}_window_id`;\n            this._primary_window_exists_storage_key = `human_behavior_${persistenceName}_primary_window_exists`;\n            \n            this.sessionId = this.getOrCreateSessionId();\n            this.windowId = this.getOrCreateWindowId(); // Multi-window tracking\n            this.currentUrl = sanitizeUrl(window.location.href);\n            (window as any).__humanBehaviorGlobalTracker = this;\n            \n            // Setup beforeunload listener to clear primary_window_exists flag\n            this.setupWindowUnloadListener();\n        } else {\n            this._window_id_storage_key = '';\n            this._primary_window_exists_storage_key = '';\n            this.sessionId = uuidv1();\n            this.windowId = uuidv1();\n        }\n\n        // ✅ SET TRACKING CONTEXT: Set session and user IDs for network error tracking\n        this.api.setTrackingContext(this.sessionId, this.endUserId);\n\n        // ✅ INITIALIZATION: Setup handlers immediately (no server call needed)\n        this.initializationPromise = this.init().catch(error => {\n            logError('Initialization failed:', error);\n        });\n    }\n\n    private async init(): Promise<void> {\n        try {\n            // Mark initialized BEFORE wiring navigation tracking so the\n            // synchronous initial-pageload `trackNavigationEvent('pageLoad')`\n            // call doesn't get gated by the `if (!this.initialized) return`\n            // guard. The previous order silently dropped every cold-start\n            // pageview.\n            this.initialized = true;\n\n            // Setup handlers immediately\n            if (isBrowser) {\n                this.setupPageUnloadHandler();\n                this.setupNavigationTracking();\n            } else {\n                logInfo('HumanBehaviorTracker initialized in server environment. Session tracking is disabled.');\n            }\n\n            logInfo(`HumanBehaviorTracker initialized with sessionId: ${this.sessionId}, endUserId: ${this.endUserId}`);\n        } catch (error: any) {\n            // Handle initialization errors gracefully - don't throw\n            logError('Failed to initialize HumanBehaviorTracker:', error);\n            this.initialized = true; // Allow tracker to work locally even if init fails\n        }\n    }\n\n    /**\n     * ✅ FIXED: Wait for Kafka-based initialization to complete\n     */\n    private async ensureInitialized(): Promise<void> {\n        if (this.initializationPromise) {\n            await this.initializationPromise;\n        }\n    }\n\n    /**\n     * Setup navigation event tracking for SPA navigation\n     */\n    private setupNavigationTracking(): void {\n        if (!isBrowser || this.navigationTrackingEnabled) return;\n        \n        this.navigationTrackingEnabled = true;\n        logDebug('Setting up navigation tracking');\n\n        // Store original history methods\n        this.originalPushState = history.pushState;\n        this.originalReplaceState = history.replaceState;\n\n        // Override pushState to capture programmatic navigation\n        history.pushState = (...args) => {\n            this.previousUrl = this.currentUrl;\n            // Apply the original first so window.location reflects the new URL\n            // before we read it (matches what listeners would observe).\n            this.originalPushState!.apply(history, args);\n            this.currentUrl = sanitizeUrl(window.location.href);\n            this.lastPushStateAt = Date.now();\n\n            this.trackNavigationEvent('pushState', this.previousUrl, this.currentUrl);\n            this.takeFullSnapshot();\n        };\n\n        // Override replaceState to capture programmatic navigation\n        history.replaceState = (...args) => {\n            this.previousUrl = this.currentUrl;\n            this.originalReplaceState!.apply(history, args);\n            this.currentUrl = sanitizeUrl(window.location.href);\n            this.lastPushStateAt = Date.now();\n\n            this.trackNavigationEvent('replaceState', this.previousUrl, this.currentUrl);\n            this.takeFullSnapshot();\n        };\n\n        // Listen for popstate events (back/forward navigation)\n        const popstateListener = () => {\n            this.previousUrl = this.currentUrl;\n            this.currentUrl = sanitizeUrl(window.location.href);\n            this.trackNavigationEvent('popstate', this.previousUrl, this.currentUrl);\n            \n            // Take FullSnapshot on navigation\n            this.takeFullSnapshot();\n        };\n        \n        window.addEventListener('popstate', popstateListener);\n        this.navigationListeners.push(() => {\n            window.removeEventListener('popstate', popstateListener);\n        });\n\n        // Listen for hashchange events. Suppress when a pushState fired in\n        // the last NAVIGATION_DEDUPE_MS — that's the same logical\n        // navigation (`pushState('/foo#bar')` triggers both).\n        const hashchangeListener = () => {\n            const now = Date.now();\n            if (now - this.lastPushStateAt < this.NAVIGATION_DEDUPE_MS) {\n                this.previousUrl = this.currentUrl;\n                this.currentUrl = sanitizeUrl(window.location.href);\n                return;\n            }\n            this.previousUrl = this.currentUrl;\n            this.currentUrl = sanitizeUrl(window.location.href);\n            this.trackNavigationEvent('hashchange', this.previousUrl, this.currentUrl);\n        };\n        \n        window.addEventListener('hashchange', hashchangeListener);\n        this.navigationListeners.push(() => {\n            window.removeEventListener('hashchange', hashchangeListener);\n        });\n\n        // Track initial page load\n        this.trackNavigationEvent('pageLoad', '', this.currentUrl);\n    }\n\n    /**\n     * Track navigation events and send custom events\n     */\n    public async trackNavigationEvent(type: string, fromUrl: string, toUrl: string): Promise<void> {\n        if (!this.initialized) return;\n\n        // Same-URL dedupe — ignore programmatic SPA \"navigations\" that don't\n        // actually change the URL (modal close handlers, focus listeners,\n        // etc. routinely re-push the same path). PostHog dedupes by URL\n        // across pushState/replaceState; we mirror that. `pageLoad` is\n        // exempt because it's the canonical first pageview.\n        if (type !== 'pageLoad' && fromUrl === toUrl) {\n            logDebug(`Navigation dedupe: ${type} same URL (${toUrl})`);\n            return;\n        }\n\n        try {\n            // Send $pageview custom event for page loads and navigation.\n            // Note: we used to also push a separate Type-5 rrweb \"navigation\"\n            // event but that produced double accounting in dashboards that\n            // counted both channels. The single $pageview is the source of\n            // truth.\n            if (type === 'pageLoad' || type === 'pushState' || type === 'replaceState' || type === 'popstate' || type === 'hashchange') {\n                const pageViewProperties = {\n                    // Full current URL minus credential-bearing params (see sanitizeUrl).\n                    url: sanitizeUrl(window.location.href),\n                    fromUrl: fromUrl,\n                    // Per PostHog convention, expose the navigation cause\n                    // as $navigation_type so dashboards can opt-out of\n                    // shallow `replaceState` updates without losing the\n                    // signal entirely. Keep `navigationType` as an alias\n                    // for back-compat with anyone consuming the pre-0.7\n                    // payload.\n                    $navigation_type: type,\n                    navigationType: type,\n                    pathname: window.location.pathname,\n                    search: window.location.search,\n                    hash: window.location.hash,\n                    referrer: document.referrer,\n                    timestamp: Date.now()\n                };\n\n                await this.customEvent('$pageview', pageViewProperties);\n            }\n\n            logDebug(`Navigation tracked: ${type} from ${fromUrl} to ${toUrl}`);\n        } catch (error) {\n            logError('Failed to track navigation event:', error);\n        }\n    }\n\n    public async trackPageView(url?: string): Promise<void> {\n        if (!this.initialized) return;\n\n        // Update automatic properties for new page\n        this.propertyManager.updateAutomaticProperties();\n\n        try {\n            const pageViewData = {\n                url: sanitizeUrl(url || window.location.href),\n                pathname: window.location.pathname,\n                search: window.location.search,\n                hash: window.location.hash,\n                referrer: document.referrer,\n                timestamp: new Date().toISOString()\n            };\n\n            // Get enhanced properties with automatic properties\n            const enhancedProperties = this.propertyManager.getEventProperties(pageViewData);\n\n            // Add pageview event to the main event stream\n            await this.addEvent({\n                type: 5, // Custom event type\n                data: {\n                    payload: {\n                        eventType: 'pageview',\n                        ...enhancedProperties\n                    }\n                },\n                timestamp: Date.now()\n            });\n            \n            logDebug(`Pageview tracked: ${pageViewData.url}`);\n        } catch (error) {\n            logError('Failed to track pageview event:', error);\n        }\n    }\n\n    public async customEvent(eventName: string, properties?: Record<string, any>): Promise<void> {\n        // Reject empty/whitespace/null/undefined names client-side, matching\n        // PostHog/Mixpanel. Sending these to the server only pollutes the\n        // events table and downstream funnel queries.\n        if (\n            eventName == null ||\n            typeof eventName !== 'string' ||\n            eventName.trim() === ''\n        ) {\n            logWarn('customEvent() called with empty or invalid name; skipping');\n            return;\n        }\n\n        // ✅ NON-BLOCKING: endUserId is always available (generated locally)\n        // No need to wait for server initialization\n        if (!this.endUserId) {\n            // This should never happen, but fallback to anonymous if it does\n            logWarn(`endUserId not available, using anonymous ID for event: ${eventName}`);\n            this.endUserId = uuidv1();\n        }\n\n        // ✅ CHECK SESSION TIMEOUT before sending custom event (creates new session if expired)\n        if (isBrowser) {\n            this.checkAndRefreshSession();\n        }\n\n        // Feed click/navigation breadcrumbs so crash reports carry the\n        // lead-up context. These two events are the chokepoint for user\n        // clicks ($click) and SPA navigations ($pageview).\n        if (eventName === '$click') {\n            const label = properties?.text || properties?.elementText || properties?.tag || 'element';\n            this.addBreadcrumb('click', String(label), {\n                tag: properties?.tag,\n                id: properties?.id,\n                page: properties?.page\n            });\n        } else if (eventName === '$pageview') {\n            this.addBreadcrumb('navigation', String(properties?.url || ''), {\n                fromUrl: properties?.fromUrl\n            });\n        }\n\n        // Get enhanced properties with automatic properties\n        const enhancedProperties = this.propertyManager.getEventProperties(properties);\n\n        // ✅ Check minimum duration - queue if below, send if above\n        if (this.shouldSkipDueToMinimumDuration()) {\n            logDebug(`Custom event '${eventName}' queued due to session duration below minimum`);\n            this.pendingCustomEvents.push({\n                eventName,\n                properties: enhancedProperties,\n                timestamp: Date.now(),\n                // Idempotency ID minted at capture time so every delivery\n                // attempt (batch, per-event fallback, beacon, retry) carries\n                // the same ID and the server can drop duplicates.\n                eventId: uuidv1()\n            });\n            return;\n        }\n\n        // ✅ Flush any pending custom events first (everything before 5 seconds)\n        await this.flushPendingCustomEvents();\n\n        // Route through the batch queue. PostHog batches up to 50 events per\n        // 100ms; this is the same shape. Preserves insertion order.\n        this.queueCustomEvent(eventName, enhancedProperties);\n        logDebug(`Custom event queued: ${eventName}`, enhancedProperties);\n    }\n\n    /** Append to the outbound batch and arm/refresh the flush timer. */\n    private queueCustomEvent(eventName: string, eventProperties: any, eventId?: string): void {\n        // The idempotency ID is minted here (or inherited from the pending\n        // queue) so retries and fallback transports all re-send the same ID.\n        this.customEventBatch.push({ eventName, eventProperties, eventId: eventId ?? uuidv1() });\n\n        // Page teardown path: events queued while the page is hidden (e.g.\n        // CLS/INP, which the web-vitals library only finalizes on\n        // visibilitychange/pagehide) would arm a debounce timer that never\n        // fires — the page is gone before 100ms elapse. Our own pagehide\n        // flush has usually already run by the time web-vitals reports, so\n        // ship immediately via sendBeacon (survives navigation) instead of\n        // batching. Without this, CLS and INP are silently lost on every\n        // tab close / navigation.\n        if (isBrowser && document.visibilityState === 'hidden') {\n            this.flushCustomEventBatchBeacon();\n            return;\n        }\n\n        if (this.customEventBatch.length >= this.CUSTOM_EVENT_BATCH_MAX) {\n            void this.flushCustomEventBatch();\n            return;\n        }\n        if (this.customEventBatchTimer == null) {\n            this.customEventBatchTimer = setTimeout(() => {\n                this.customEventBatchTimer = null;\n                void this.flushCustomEventBatch();\n            }, this.CUSTOM_EVENT_FLUSH_MS);\n        }\n    }\n\n    /**\n     * Teardown-path flush of the outbound custom-event batch: synchronous\n     * sendBeacon (survives navigation), falling back to a keepalive fetch\n     * when the beacon is unavailable or refuses the payload. Never awaited —\n     * callers are unload/hidden handlers that cannot block.\n     */\n    private flushCustomEventBatchBeacon(): void {\n        if (this.customEventBatchTimer != null) {\n            clearTimeout(this.customEventBatchTimer);\n            this.customEventBatchTimer = null;\n        }\n        if (this.customEventBatch.length === 0) return;\n        const batch = this.customEventBatch;\n        this.customEventBatch = [];\n        const sent = this.api?.sendCustomEventBatchBeacon(\n            this.sessionId,\n            batch,\n            this.endUserId\n        );\n        if (!sent) {\n            // Beacon unavailable/refused: fall back to the keepalive path.\n            this.api?.sendCustomEventBatch(this.sessionId, batch, this.endUserId).catch(() => {\n                /* best-effort during teardown */\n            });\n        }\n    }\n\n    /**\n     * Drain the sub-minimum-duration buffer into the outbound batch. The\n     * minimum-duration gate is a delivery *delay*, not a filter — every event\n     * in `pendingCustomEvents` is sent verbatim once the session crosses the\n     * threshold. FullSnapshots bypass that gate entirely, so the replay\n     * already exists server-side; a teardown that drops this buffer is what\n     * produces replays with zero analytics events. Called from the hidden/\n     * pagehide handlers right before the batch is beaconed out.\n     */\n    private drainPendingCustomEventsForTeardown(): void {\n        if (this.pendingCustomEvents.length === 0) return;\n        const pending = this.pendingCustomEvents;\n        this.pendingCustomEvents = [];\n        for (const ev of pending) {\n            this.customEventBatch.push({\n                eventName: ev.eventName,\n                eventProperties: ev.properties,\n                eventId: ev.eventId\n            });\n        }\n    }\n\n    /**\n     * Flush the outbound custom-event batch. Falls back to per-event\n     * `sendCustomEvent` (and finally to the rrweb event-stream Type-5\n     * payload) when the batch endpoint errors — this is the same fallback\n     * behaviour pre-0.7 had for individual sends.\n     */\n    private async flushCustomEventBatch(): Promise<void> {\n        if (this.customEventBatchTimer != null) {\n            clearTimeout(this.customEventBatchTimer);\n            this.customEventBatchTimer = null;\n        }\n        if (this.customEventBatch.length === 0) return;\n        const batch = this.customEventBatch;\n        this.customEventBatch = [];\n\n        try {\n            await this.api.sendCustomEventBatch(this.sessionId, batch, this.endUserId);\n            logDebug(`Custom event batch flushed: ${batch.length} event(s)`);\n            return;\n        } catch (error: any) {\n            logError('Failed to flush custom event batch, falling back per-event:', error);\n        }\n\n        // Fallback path — try sending each event individually so a single\n        // bad event doesn't drop the whole batch. Then fall further back to\n        // the rrweb queue if that also fails.\n        for (const ev of batch) {\n            try {\n                await this.api.sendCustomEvent(this.sessionId, ev.eventName, ev.eventProperties, this.endUserId, ev.eventId);\n            } catch (perEventError: any) {\n                logError('Per-event fallback also failed:', perEventError);\n                try {\n                    await this.addEvent({\n                        type: 5,\n                        data: {\n                            payload: {\n                                eventType: 'custom',\n                                eventName: ev.eventName,\n                                properties: ev.eventProperties || {},\n                                timestamp: new Date().toISOString(),\n                                url: isBrowser ? sanitizeUrl(window.location.href) : '',\n                                pathname: isBrowser ? window.location.pathname : ''\n                            }\n                        },\n                        timestamp: Date.now()\n                    });\n                } catch (fallbackError) {\n                    logError('Failed to add custom event to event stream as fallback:', fallbackError);\n                }\n            }\n        }\n    }\n\n    /**\n     * Setup automatic tracking for buttons, links, and forms\n     */\n    private setupAutomaticTracking(options?: {\n        trackButtons?: boolean;\n        trackLinks?: boolean;\n        trackForms?: boolean;\n        includeText?: boolean;\n        includeClasses?: boolean;\n    }): void {\n        if (!isBrowser) return;\n\n        const config = {\n            trackButtons: options?.trackButtons !== false,\n            trackLinks: false, // Always disabled - only buttons and forms\n            trackForms: options?.trackForms !== false,\n            includeText: options?.includeText !== false,\n            includeClasses: options?.includeClasses || false\n        };\n\n        logDebug('Setting up automatic tracking with config:', config);\n\n        // Single autocapture handler replaces the old $click + $button_clicked\n        // double-emission. Matches PostHog/Mixpanel autotrack: one $autocapture\n        // event per user click, with element metadata as properties.\n        this.setupAutocapture(config);\n\n        // Setup form tracking\n        if (config.trackForms) {\n            this.setupAutomaticFormTracking(config);\n        }\n\n        this.setupFrictionClickDetection();\n    }\n\n    /**\n     * PostHog/Mixpanel-style autocapture. One `$click` event per user click.\n     * Walks up to the nearest interactive ancestor so a click on a <span>\n     * inside a <button> is attributed to the button. The event name stays\n     * constant (no per-label fragmentation) and text/role/etc. live as\n     * properties.\n     *\n     * Note: the dashboard's hard-coded event name is `$click`, so we keep\n     * that as the public event name. The shape is what changed (single\n     * event, richer properties) — semantically this is \"autocapture\".\n     */\n    private setupAutocapture(config: {\n        includeText?: boolean;\n        includeClasses?: boolean;\n    }): void {\n        const INTERACTIVE_SELECTOR =\n            'button, a, input, select, textarea, label, [role=\"button\"], [role=\"link\"], [role=\"tab\"], [role=\"menuitem\"]';\n\n        document.addEventListener('click', async (event) => {\n            const target = event.target as Element | null;\n            // Robustness: clicks dispatched on `document` itself, on TextNodes\n            // (e.g. inside contenteditable), or anywhere `target` is not an\n            // Element would otherwise crash `.closest(...)` / `.tagName`.\n            if (!target || target.nodeType !== 1 || typeof (target as any).closest !== 'function') {\n                return;\n            }\n\n            const interactiveParent = (target as Element).closest(INTERACTIVE_SELECTOR);\n            const element = (interactiveParent || target) as Element;\n            const tagName = (element.tagName || '').toLowerCase();\n\n            const properties: Record<string, any> = {\n                tag: tagName,\n                x: event.clientX,\n                y: event.clientY,\n                page: window.location.pathname,\n                path: this.buildDomPath(element),\n                timestamp: Date.now()\n            };\n\n            if (element.id) {\n                properties.id = element.id;\n                // Keep elementId for back-compat with anyone consuming the\n                // pre-0.7 property names.\n                properties.elementId = element.id;\n            }\n\n            const role = element.getAttribute && element.getAttribute('role');\n            if (role) properties.role = role;\n\n            const type = (element as HTMLInputElement).type;\n            if (type && (tagName === 'input' || tagName === 'button')) {\n                properties.type = type;\n            }\n\n            const href = (element as HTMLAnchorElement).href;\n            if (href) properties.href = sanitizeUrl(href);\n\n            if (config.includeText !== false) {\n                const text = (element.textContent || '').replace(/\\s+/g, ' ').trim();\n                if (text) {\n                    properties.text = text.substring(0, 200);\n                    // Keep elementText for back-compat.\n                    properties.elementText = text.substring(0, 100);\n                }\n            }\n\n            const className = (element as HTMLElement).className;\n            if (config.includeClasses && typeof className === 'string' && className) {\n                properties.class = className;\n                properties.elementClass = className;\n            }\n\n            await this.customEvent('$click', properties);\n        });\n    }\n\n    /**\n     * Build a short DOM path like `body > div#root > button.cta`. Capped at\n     * 8 ancestors so deeply-nested DOMs don't blow up the payload.\n     */\n    private buildDomPath(el: Element): string {\n        const parts: string[] = [];\n        let node: Element | null = el;\n        let depth = 0;\n        while (node && node.nodeType === 1 && depth < 8) {\n            const tag = (node.tagName || '').toLowerCase();\n            if (!tag) break;\n            let segment = tag;\n            if (node.id) segment += '#' + node.id;\n            else if (typeof (node as HTMLElement).className === 'string' && (node as HTMLElement).className) {\n                const cls = ((node as HTMLElement).className as string).trim().split(/\\s+/)[0];\n                if (cls) segment += '.' + cls;\n            }\n            parts.unshift(segment);\n            if (tag === 'body' || tag === 'html') break;\n            node = node.parentElement;\n            depth++;\n        }\n        return parts.join(' > ');\n    }\n\n    /**\n     * Wire the friction detectors: one click listener, plus the page-reaction\n     * signals they need (mutations, scroll, typing, navigation).\n     */\n    private setupFrictionClickDetection(): void {\n        if (!isBrowser) return;\n        if (this.frictionClicks) return;\n\n        const detector = new FrictionClickDetector({\n            emit: (kind, info) => {\n                void this.fireFrictionEvent(kind, info);\n            },\n        });\n        this.frictionClicks = detector;\n\n        document.addEventListener('click', (event: MouseEvent) => {\n            const target = event.target as Element | null;\n            // Synthetic clicks dispatched on `document`, on TextNodes, or on\n            // detached nodes must never throw into the customer's handlers.\n            if (!target || target.nodeType !== 1) return;\n            detector.onClick(target, event.clientX, event.clientY);\n        });\n\n        // Any DOM change counts as the page responding. We don't care what\n        // changed, only that something did.\n        this.frictionMutationObserver = new MutationObserver(() => {\n            detector.onReaction('dom');\n        });\n        this.frictionMutationObserver.observe(document, {\n            attributes: true,\n            characterData: true,\n            childList: true,\n            subtree: true\n        });\n\n        // Scroll and typing count for dead clicks only — a click that scrolled\n        // the page (in-page anchor) or focused a field the user then typed into\n        // did something. trace-compiler's rage detector ignores both.\n        window.addEventListener('scroll', () => detector.onReaction('soft'), { capture: true, passive: true });\n        document.addEventListener('input', () => detector.onReaction('soft'), { capture: true });\n\n        // Navigation is the strongest possible reaction.\n        const originalTrackNavigationEvent = this.trackNavigationEvent.bind(this);\n        this.trackNavigationEvent = async (type: string, fromUrl: string, toUrl: string) => {\n            detector.onReaction('dom');\n            return originalTrackNavigationEvent(type, fromUrl, toUrl);\n        };\n        window.addEventListener('popstate', () => detector.onReaction('dom'));\n        window.addEventListener('hashchange', () => detector.onReaction('dom'));\n        window.addEventListener('beforeunload', () => detector.reset());\n    }\n\n    /**\n     * Emit `$rageclick` / `$deadclick`. Property shape is load-bearing: the\n     * dashboard, replay inspector and visitor timeline read `element`,\n     * `elementId`, `elementClass`, `elementText`, `page` and `clickCount`.\n     */\n    private async fireFrictionEvent(kind: 'rage' | 'dead', info: FrictionClickInfo): Promise<void> {\n        const element = info.node;\n        const properties: Record<string, any> = {\n            x: info.x,\n            y: info.y,\n            page: window.location.pathname,\n            element: (element.tagName || '').toLowerCase(),\n            clickCount: info.clickCount,\n            timestamp: info.tsMs\n        };\n        if (info.occurrences !== undefined) {\n            properties.occurrences = info.occurrences;\n        }\n        if (info.durationMs > 0) {\n            properties.durationMs = info.durationMs;\n        }\n        if (element.id) {\n            properties.elementId = element.id;\n        }\n        const className = (element as HTMLElement).className;\n        if (typeof className === 'string' && className) {\n            properties.elementClass = className;\n        }\n        if (element.textContent) {\n            properties.elementText = element.textContent.trim().substring(0, 100);\n        }\n        Object.keys(properties).forEach(key => {\n            if (properties[key] === null || properties[key] === undefined) {\n                delete properties[key];\n            }\n        });\n\n        // Names aligned with the dashboard's `event IN ('$rageclick',\n        // '$deadclick')` queries (dead click was `$dead_click` pre-0.7).\n        await this.customEvent(kind === 'rage' ? '$rageclick' : '$deadclick', properties);\n    }\n\n    /**\n     * Setup automatic link tracking\n     * TEMPORARILY DISABLED: Automatic custom event tracking\n     */\n    private setupAutomaticLinkTracking(config: {\n        includeText?: boolean;\n        includeClasses?: boolean;\n    }): void {\n        // TEMPORARILY DISABLED: Automatic custom event tracking\n        return;\n        \n        // document.addEventListener('click', async (event) => {\n        //     const target = event.target as HTMLElement;\n            \n        //     // Track link clicks\n        //     if (target.tagName === 'A' || target.closest('a')) {\n        //         const link = target.tagName === 'A'\n        //             ? target as HTMLAnchorElement\n        //             : target.closest('a') as HTMLAnchorElement;\n                \n        //         const properties: Record<string, any> = {\n        //             linkUrl: link.href || null,\n        //             linkId: link.id || null,\n        //             linkTarget: link.target || null,\n        //             page: window.location.pathname,\n        //             timestamp: Date.now()\n        //         };\n\n        //         if (config.includeText) {\n        //             properties.linkText = link.textContent?.trim() || null;\n        //         }\n\n        //         if (config.includeClasses) {\n        //             properties.linkClass = link.className || null;\n        //         }\n\n        //         // Remove null values\n        //         Object.keys(properties).forEach(key => {\n        //             if (properties[key] === null) {\n        //                 delete properties[key];\n        //             }\n        //         });\n\n        //         await this.customEvent('link_clicked', properties);\n        //     }\n        // });\n    }\n\n    /**\n     * Setup automatic form tracking\n     */\n    private setupAutomaticFormTracking(config: {\n        includeText?: boolean;\n        includeClasses?: boolean;\n    }): void {\n        document.addEventListener('submit', async (event) => {\n            const form = event.target as HTMLFormElement;\n            const formData = new FormData(form);\n            \n            const properties: Record<string, any> = {\n                formId: form.id || null,\n                formAction: form.action || null,\n                formMethod: form.method || 'get',\n                fields: Array.from(formData.keys()),\n                page: window.location.pathname,\n                timestamp: Date.now()\n            };\n\n            if (config.includeClasses) {\n                properties.formClass = form.className || null;\n            }\n\n            // Remove null values\n            Object.keys(properties).forEach(key => {\n                if (properties[key] === null) {\n                    delete properties[key];\n                }\n            });\n\n            await this.customEvent('$form_submitted', properties);\n        });\n    }\n\n    /**\n     * Cleanup navigation tracking\n     */\n    private cleanupNavigationTracking(): void {\n        if (!this.navigationTrackingEnabled) return;\n\n        // Restore original history methods\n        if (this.originalPushState) {\n            history.pushState = this.originalPushState;\n        }\n        if (this.originalReplaceState) {\n            history.replaceState = this.originalReplaceState;\n        }\n\n        // Remove event listeners\n        this.navigationListeners.forEach(cleanup => cleanup());\n        this.navigationListeners = [];\n\n        this.navigationTrackingEnabled = false;\n        logDebug('Navigation tracking cleaned up');\n    }\n\n    public static logToStorage(message: string) {\n        logInfo(message);\n    }\n\n    /**\n     * Configure logging behavior for the SDK\n     * @param config Logger configuration options\n     */\n    public static configureLogging(config: { level?: 'none' | 'error' | 'warn' | 'info' | 'debug', enableConsole?: boolean, enableStorage?: boolean }) {\n        const levelMap = {\n            'none': 0,\n            'error': 1,\n            'warn': 2,\n            'info': 3,\n            'debug': 4\n        };\n        \n        logger.setConfig({\n            level: levelMap[config.level || 'error'],\n            enableConsole: config.enableConsole !== false,\n            enableStorage: config.enableStorage || false\n        });\n    }\n\n    /**\n     * Enable console event tracking\n     */\n    public enableConsoleTracking(): void {\n        if (!isBrowser || this.consoleTrackingEnabled) return;\n        \n        // Store original console methods\n        this.originalConsole = {\n            log: console.log,\n            warn: console.warn,\n            error: console.error\n        };\n\n        // Override console methods to capture ALL console output (including logger output)\n        console.log = (...args) => {\n            this.trackConsoleEvent('log', args);\n            this.originalConsole!.log(...args);\n        };\n\n        console.warn = (...args) => {\n            this.trackConsoleEvent('warn', args);\n            this.originalConsole!.warn(...args);\n        };\n\n        console.error = (...args) => {\n            this.trackConsoleEvent('error', args);\n            this.originalConsole!.error(...args);\n        };\n\n        this.consoleTrackingEnabled = true;\n        logDebug('Console tracking enabled');\n    }\n\n    /**\n     * Enable network error tracking by intercepting the global fetch API\n     */\n    public enableNetworkTracking(): void {\n        if (!isBrowser || this.networkTrackingEnabled || typeof fetch === 'undefined') return;\n        \n        // Store original fetch\n        this.originalFetch = window.fetch.bind(window);\n        \n        // Override global fetch to track network errors\n        window.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n            const requestStartTime = Date.now();\n            const requestId = uuidv1();\n            const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;\n            const method = (init?.method || (typeof input === 'object' && 'method' in input ? input.method : undefined) || 'GET').toUpperCase();\n            \n            // Check if we should skip tracking (SDK's own requests)\n            const shouldSkipTracking = this.shouldSkipNetworkTracking(url);\n            \n            // Track long-loading requests (>10 seconds)\n            const LONG_LOADING_THRESHOLD_MS = 10000; // 10 seconds\n            let longLoadingTimeoutId: ReturnType<typeof setTimeout> | null = null;\n            let longLoadingTracked = false;\n            \n            // Set up timeout to track long-loading requests\n            if (!shouldSkipTracking) {\n                longLoadingTimeoutId = setTimeout(() => {\n                    const elapsedTime = Date.now() - requestStartTime;\n                    if (!longLoadingTracked) {\n                        longLoadingTracked = true;\n                        const errorData = {\n                            requestId,\n                            url,\n                            method,\n                            status: null, // Request still in progress\n                            statusText: null,\n                            duration: elapsedTime,\n                            timestampMs: Date.now(),\n                            sessionId: this.sessionId,\n                            endUserId: this.endUserId,\n                            errorType: 'long_loading',\n                            errorMessage: `Request took longer than ${LONG_LOADING_THRESHOLD_MS}ms (${elapsedTime}ms elapsed)`,\n                            // New span fields\n                            startTimeMs: requestStartTime,\n                            spanName: `${method} ${url}`,\n                            spanStatus: 'slow' as const,\n                            attributes: {\n                                'http.method': method,\n                                'http.url': url,\n                                'request.duration_ms': elapsedTime,\n                                'request.long_loading_threshold_ms': LONG_LOADING_THRESHOLD_MS,\n                            },\n                            automaticProperties: this.propertyManager.getAutomaticProperties()\n                        };\n                        // ✅ Check minimum duration - queue if below, send if above\n                        if (this.shouldSkipDueToMinimumDuration()) {\n                            logDebug('Long-loading network error queued due to session duration below minimum');\n                            this.pendingNetworkErrors.push({\n                                errorData,\n                                timestamp: Date.now()\n                            });\n                            return;\n                        }\n                        // ✅ Flush any pending network errors first (everything before 5 seconds)\n                        this.flushPendingNetworkErrors();\n                        this.api.sendNetworkError(errorData).catch(() => {}); // Non-blocking\n                        return;\n                    }\n                }, LONG_LOADING_THRESHOLD_MS);\n            }\n            \n            try {\n                const response = await this.originalFetch!(input, init);\n                const requestDuration = Date.now() - requestStartTime;\n                \n                // Clear long-loading timeout if request completed\n                if (longLoadingTimeoutId) {\n                    clearTimeout(longLoadingTimeoutId);\n                }\n                \n                // Track failed requests (4xx, 5xx) AND skip SDK requests\n                if (!response.ok && !shouldSkipTracking) {\n                    const errorData = {\n                        requestId,\n                        url,\n                        method,\n                        status: response.status,\n                        statusText: response.statusText,\n                        duration: requestDuration,\n                        timestampMs: Date.now(),\n                        sessionId: this.sessionId,\n                        endUserId: this.endUserId,\n                        errorType: this.classifyHttpError(response.status),\n                        errorMessage: response.statusText,\n                        // New span fields\n                        startTimeMs: requestStartTime,\n                        spanName: `${method} ${url}`,\n                        spanStatus: 'error' as const,\n                        attributes: {\n                            'http.status_code': response.status,\n                            'http.status_text': response.statusText,\n                        },\n                        automaticProperties: this.propertyManager.getAutomaticProperties()\n                    };\n                    // ✅ Check minimum duration - queue if below, send if above\n                    if (this.shouldSkipDueToMinimumDuration()) {\n                        logDebug('Failed request network error queued due to session duration below minimum');\n                        this.pendingNetworkErrors.push({\n                            errorData,\n                            timestamp: Date.now()\n                        });\n                        return response;\n                    }\n                    this.addBreadcrumb('network', `${errorData.method} ${errorData.url}`, { status: errorData.status, errorType: errorData.errorType });\n                    let responseBodyText: string | undefined;\n                    if (this.captureRequestBodiesFlag) {\n                        try { responseBodyText = await response.clone().text(); } catch { /* body not readable */ }\n                    }\n                    this.lastRequestContext = this.buildRequestContext({\n                        url, method, status: response.status, errorType: errorData.errorType, durationMs: requestDuration,\n                        requestBody: init?.body, requestHeaders: this.headersToObject(init?.headers), responseBody: responseBodyText,\n                    });\n                    this.lastRequestContextAt = Date.now();\n                    // ✅ Flush any pending network errors first (everything before 5 seconds)\n                    this.flushPendingNetworkErrors();\n                    this.api.sendNetworkError(errorData).catch(() => {}); // Non-blocking\n                } else if (response.ok && !shouldSkipTracking) {\n                    // Record the most recent SUCCESSFUL request (metadata only) so an\n                    // error firing shortly after can be correlated to the API call that\n                    // preceded it — e.g. a 200 returning malformed data → TypeError.\n                    this.lastRequestContext = this.buildRequestContext({\n                        url, method, status: response.status, durationMs: requestDuration,\n                    });\n                    this.lastRequestContextAt = Date.now();\n                }\n                \n                return response;\n            } catch (error: any) {\n                const requestDuration = Date.now() - requestStartTime;\n                \n                // Clear long-loading timeout if request failed\n                if (longLoadingTimeoutId) {\n                    clearTimeout(longLoadingTimeoutId);\n                }\n                \n                // Track network errors BUT skip SDK requests\n                if (!shouldSkipTracking) {\n                    const errorData = {\n                        requestId,\n                        url,\n                        method,\n                        status: null,\n                        statusText: null,\n                        duration: requestDuration,\n                        timestampMs: Date.now(),\n                        sessionId: this.sessionId,\n                        endUserId: this.endUserId,\n                        errorType: this.classifyNetworkError(error),\n                        errorMessage: error.message,\n                        errorName: error.name,\n                        // New span fields\n                        startTimeMs: requestStartTime,\n                        spanName: `${method} ${url}`,\n                        spanStatus: 'error' as const,\n                        attributes: {\n                            'error.name': error.name,\n                            'error.message': error.message,\n                        },\n                        automaticProperties: this.propertyManager.getAutomaticProperties()\n                    };\n                    // ✅ Check minimum duration - queue if below, send if above\n                    if (this.shouldSkipDueToMinimumDuration()) {\n                        logDebug('Network error queued due to session duration below minimum');\n                        this.pendingNetworkErrors.push({\n                            errorData,\n                            timestamp: Date.now()\n                        });\n                        throw error; // Re-throw to maintain error propagation\n                    }\n                    this.addBreadcrumb('network', `${errorData.method} ${errorData.url}`, { status: errorData.status, errorType: errorData.errorType });\n                    this.lastRequestContext = this.buildRequestContext({\n                        url, method, status: null, errorType: errorData.errorType, durationMs: requestDuration,\n                        requestBody: init?.body, requestHeaders: this.headersToObject(init?.headers),\n                    });\n                    this.lastRequestContextAt = Date.now();\n                    // ✅ Flush any pending network errors first (everything before 5 seconds)\n                    this.flushPendingNetworkErrors();\n                    this.api.sendNetworkError(errorData).catch(() => {}); // Non-blocking\n                }\n                \n                throw error;\n            }\n        };\n\n        // Mirror the fetch patch for XMLHttpRequest so XHR-based clients (axios's\n        // XHR adapter, legacy code) get the same failed-request breadcrumbs +\n        // sendNetworkError + requestContext. Prototype-patch (not constructor\n        // replacement) to stay compatible with code that reads XHR statics.\n        if (typeof XMLHttpRequest !== 'undefined') {\n            const tracker = this;\n            const xhrProto = XMLHttpRequest.prototype;\n            const originalOpen = xhrProto.open;\n            const originalSend = xhrProto.send;\n            const originalSetRequestHeader = xhrProto.setRequestHeader;\n\n            type HBXhrMeta = { method: string; url: string; headers: Record<string, string>; startTime: number; body?: unknown };\n\n            xhrProto.open = function (this: XMLHttpRequest, method: string, url: string | URL, ...rest: unknown[]) {\n                (this as unknown as { __hb?: HBXhrMeta }).__hb = {\n                    method: String(method || 'GET').toUpperCase(),\n                    url: typeof url === 'string' ? url : url.toString(),\n                    headers: {},\n                    startTime: 0,\n                };\n                return (originalOpen as (...a: unknown[]) => void).apply(this, [method, url, ...rest]);\n            } as typeof xhrProto.open;\n\n            xhrProto.setRequestHeader = function (this: XMLHttpRequest, name: string, value: string) {\n                const meta = (this as unknown as { __hb?: HBXhrMeta }).__hb;\n                if (meta) {\n                    try { meta.headers[name] = value; } catch { /* ignore */ }\n                }\n                return originalSetRequestHeader.apply(this, [name, value] as [string, string]);\n            } as typeof xhrProto.setRequestHeader;\n\n            xhrProto.send = function (this: XMLHttpRequest, body?: Document | XMLHttpRequestBodyInit | null) {\n                const meta = (this as unknown as { __hb?: HBXhrMeta }).__hb;\n                if (meta && !tracker.shouldSkipNetworkTracking(meta.url)) {\n                    meta.startTime = Date.now();\n                    meta.body = body;\n                    this.addEventListener('loadend', function (this: XMLHttpRequest) {\n                        try { tracker.handleXhrComplete(this, meta); } catch { /* never break the app */ }\n                    });\n                }\n                return (originalSend as (...a: unknown[]) => void).apply(this, [body]);\n            } as typeof xhrProto.send;\n        }\n\n        this.networkTrackingEnabled = true;\n        logDebug('Network tracking enabled');\n    }\n\n    /**\n     * Capture Core Web Vitals (FCP, LCP, CLS, INP, TTFB) via Google's\n     * `web-vitals` library. Each metric is reported once with its FINAL value\n     * (LCP/CLS/INP settle at page-hide) and emitted as a `$web_vitals` custom\n     * event, so it flows through the existing batch → /customEvent/batch →\n     * ClickHouse path with no backend change. See ADR-3.\n     *\n     * The library is loaded via dynamic import() so it stays off the initial\n     * bundle and the critical render path. CLS/INP fire their final callback on\n     * visibilitychange/pagehide and ride the SDK's existing batch-flush + beacon\n     * fallback, so no extra unload handling is needed. Best-effort: a failed\n     * import is swallowed, matching the other trackers.\n     */\n    public enableWebVitalsTracking(): void {\n        if (!isBrowser) return;\n\n        // One id per page load: web-vitals v4 reports each metric once for the\n        // initial page load (no soft-nav), so all metrics emitted in this\n        // page's lifetime share this id. A full reload re-runs the SDK and\n        // mints a fresh id. This lets the dashboard stitch the per-metric\n        // events back into a single page load and score it as a whole.\n        const pageLoadId = uuidv1();\n\n        import('web-vitals')\n            .then(({ onFCP, onLCP, onCLS, onINP, onTTFB }) => {\n                const report = (metric: {\n                    name: string;\n                    value: number;\n                    rating: string;\n                    id: string;\n                    navigationType: string;\n                }): void => {\n                    void this.customEvent('$web_vitals', {\n                        $web_vitals_metric: metric.name, // FCP | LCP | CLS | INP | TTFB\n                        $web_vitals_value: metric.value,\n                        [`$web_vitals_${metric.name}_value`]: metric.value,\n                        $web_vitals_rating: metric.rating, // good | needs-improvement | poor\n                        $web_vitals_id: metric.id,\n                        $web_vitals_navigation_type: metric.navigationType,\n                        $web_vitals_pageload_id: pageLoadId,\n                    });\n                };\n\n                onFCP(report);\n                onLCP(report);\n                onCLS(report);\n                onINP(report);\n                onTTFB(report);\n\n                logDebug('Web Vitals tracking enabled');\n            })\n            .catch((error) => {\n                logWarn('Failed to load web-vitals; Web Vitals tracking disabled:', error);\n            });\n    }\n\n    /**\n     * Enable distributed tracing: a page-load transaction with navigation-phase\n     * + resource child spans, custom spans via startSpan/startInactiveSpan, and\n     * trace-context header propagation on same-origin requests. Spans batch\n     * through `api.sendSpans` (and sendBeacon on unload). Best-effort.\n     */\n    public enableTracing(): void {\n        if (!isBrowser || this.tracing) return;\n        this.tracing = new Tracing({\n            getSession: () => ({\n                sessionId: this.sessionId,\n                endUserId: this.endUserId,\n                automaticProperties: this.propertyManager.getAutomaticProperties(),\n                release: this.release,\n                environment: this.environment,\n            }),\n            sendSpans: (spans: HBSpan[], ctx, useBeacon) => {\n                if (useBeacon) {\n                    const ok = this.api.sendSpansBeacon(spans, ctx);\n                    if (!ok) void this.api.sendSpans(spans, ctx);\n                } else {\n                    void this.api.sendSpans(spans, ctx);\n                }\n            },\n            shouldSkipUrl: (url: string) => this.shouldSkipNetworkTracking(url),\n        });\n        this.tracing.start();\n        logDebug('Distributed tracing enabled');\n    }\n\n    /**\n     * Time a synchronous/async callback as a tracing span (child of the current\n     * page-load trace). No-op (still runs the callback) if tracing is disabled.\n     */\n    public startSpan<T>(\n        opts: { name: string; op?: string; attributes?: Record<string, unknown> },\n        callback: () => T,\n    ): T {\n        if (!this.tracing) return callback();\n        return this.tracing.startSpan(opts, callback);\n    }\n\n    /**\n     * Open a tracing span the caller ends manually (`span.end()`). Returns a\n     * no-op span if tracing is disabled.\n     */\n    public startInactiveSpan(opts: {\n        name: string;\n        op?: string;\n        attributes?: Record<string, unknown>;\n    }): InactiveSpan {\n        if (this.tracing) return this.tracing.startInactiveSpan(opts);\n        return {\n            setAttribute() {},\n            setStatus() {},\n            end() {},\n        };\n    }\n\n    /**\n     * Handle a completed (failed) XHR: record a breadcrumb, capture redacted\n     * requestContext, and ship a network-error report — mirroring the fetch path.\n     */\n    private handleXhrComplete(\n        xhr: XMLHttpRequest,\n        meta: { method: string; url: string; headers: Record<string, string>; startTime: number; body?: unknown },\n    ): void {\n        const status = xhr.status;\n        // 2xx/3xx are successes; status 0 is a network error/blocked request.\n        if (status >= 200 && status < 400) {\n            // Record the most recent SUCCESSFUL request (metadata only) for\n            // error correlation, mirroring the fetch success path.\n            this.lastRequestContext = this.buildRequestContext({\n                url: meta.url, method: meta.method, status, durationMs: Date.now() - meta.startTime,\n            });\n            this.lastRequestContextAt = Date.now();\n            return;\n        }\n        const duration = Date.now() - meta.startTime;\n        const isNetworkError = status === 0;\n        const errorType = isNetworkError ? 'network_error' : this.classifyHttpError(status);\n        let responseBody: string | undefined;\n        try {\n            if (typeof xhr.responseText === 'string') {\n                responseBody = xhr.responseText;\n            }\n        } catch { /* responseText throws for non-text responseType */ }\n\n        const errorData = {\n            requestId: uuidv1(),\n            url: meta.url,\n            method: meta.method,\n            status: isNetworkError ? null : status,\n            statusText: xhr.statusText || null,\n            duration,\n            timestampMs: Date.now(),\n            sessionId: this.sessionId,\n            endUserId: this.endUserId,\n            errorType,\n            errorMessage: xhr.statusText || (isNetworkError ? 'Network request failed' : `HTTP ${status}`),\n            startTimeMs: meta.startTime,\n            spanName: `${meta.method} ${meta.url}`,\n            spanStatus: 'error' as const,\n            attributes: {\n                'http.method': meta.method,\n                'http.url': meta.url,\n                'http.status_code': status,\n            },\n            automaticProperties: this.propertyManager.getAutomaticProperties(),\n        };\n\n        if (this.shouldSkipDueToMinimumDuration()) {\n            this.pendingNetworkErrors.push({ errorData, timestamp: Date.now() });\n            return;\n        }\n        this.addBreadcrumb('network', `${meta.method} ${meta.url}`, { status: errorData.status, errorType });\n        this.lastRequestContext = this.buildRequestContext({\n            url: meta.url, method: meta.method, status: errorData.status, errorType, durationMs: duration,\n            requestBody: meta.body, requestHeaders: meta.headers, responseBody,\n        });\n        this.lastRequestContextAt = Date.now();\n        this.flushPendingNetworkErrors();\n        this.api.sendNetworkError(errorData).catch(() => {});\n    }\n\n    /**\n     * Build a redacted requestContext for an error-correlated request. Metadata\n     * (url/method/status) is always included; bodies + headers only when the host\n     * opted in via `captureRequestBodies`.\n     */\n    private buildRequestContext(raw: {\n        url: string;\n        method: string;\n        status: number | null;\n        errorType?: string;\n        durationMs?: number;\n        requestBody?: unknown;\n        requestHeaders?: Record<string, string>;\n        responseBody?: string;\n    }): RequestContext {\n        const ctx: RequestContext = {\n            url: raw.url,\n            method: raw.method,\n            status: raw.status,\n            errorType: raw.errorType,\n            durationMs: raw.durationMs,\n        };\n        if (this.captureRequestBodiesFlag) {\n            if (raw.requestHeaders && Object.keys(raw.requestHeaders).length > 0) {\n                ctx.requestHeaders = redactHeaders(raw.requestHeaders);\n            }\n            if (typeof raw.requestBody === 'string' && raw.requestBody) {\n                ctx.requestBody = redactBodyString(raw.requestBody);\n            }\n            if (typeof raw.responseBody === 'string' && raw.responseBody) {\n                ctx.responseBody = redactBodyString(raw.responseBody);\n            }\n        }\n        return ctx;\n    }\n\n    /** Normalize a fetch HeadersInit into a plain object for redaction. */\n    private headersToObject(headers?: HeadersInit): Record<string, string> | undefined {\n        if (!headers) {\n            return undefined;\n        }\n        const out: Record<string, string> = {};\n        try {\n            if (typeof Headers !== 'undefined' && headers instanceof Headers) {\n                headers.forEach((value, key) => { out[key] = value; });\n            } else if (Array.isArray(headers)) {\n                for (const [key, value] of headers) {\n                    out[key] = value;\n                }\n            } else {\n                for (const [key, value] of Object.entries(headers)) {\n                    out[key] = String(value);\n                }\n            }\n        } catch {\n            return undefined;\n        }\n        return out;\n    }\n\n    /**\n     * Setup crash/error capture.\n     *\n     * Installs listeners for uncaught errors (`window.onerror`) and unhandled\n     * promise rejections, then ships a structured report via `api.sendError`.\n     * The heavy lifting (stack parsing, breadcrumbs, dedup) lives in the pure\n     * helpers under `./errors`; this just supplies the live session context.\n     *\n     * Note: this is separate from the rrweb console-noise `preventDefault()`\n     * filter installed during construction — that filter only suppresses the\n     * SDK's own internal noise from the console and does not stop genuine app\n     * crashes from reaching this handler.\n     */\n    private setupErrorCapture(): void {\n        if (!isBrowser || this.errorCapture) {\n            return;\n        }\n\n        this.errorCapture = new ErrorCapture({\n            send: (report) => {\n                this.api.sendError(report).catch(() => {}); // Non-blocking\n            },\n            getContext: () => ({\n                sessionId: this.sessionId,\n                endUserId: this.endUserId,\n                url: isBrowser ? sanitizeUrl(window.location.href) : '',\n                release: this.release,\n                environment: this.environment,\n                commitSha: this.commitSha,\n                dist: this.dist,\n                sessionStartTimestampMs: this._sessionStartTimestamp ?? undefined,\n                requestContext: Date.now() - this.lastRequestContextAt <= this.ERROR_REQUEST_WINDOW_MS\n                    ? this.lastRequestContext\n                    : undefined,\n                automaticProperties: this.propertyManager.getAutomaticProperties(),\n                userProperties: this.propertyManager.getUserProperties(),\n                sessionProperties: this.propertyManager.getSessionProperties()\n            }),\n            breadcrumbs: this.breadcrumbs,\n            filters: this.errorFilterOptions,\n            captureThirdPartyResourceErrors: this.captureThirdPartyResourceErrorsFlag\n        });\n\n        this.errorCapture.install();\n        logDebug('Error capture enabled');\n    }\n\n    /**\n     * Record a breadcrumb (recent user action) for crash-report context.\n     * Safe to call even when error tracking is disabled — the buffer just\n     * accumulates and is never read.\n     */\n    private addBreadcrumb(type: BreadcrumbType, message: string, data?: Record<string, unknown>): void {\n        try {\n            this.breadcrumbs.add({ type, message, timestampMs: Date.now(), data });\n        } catch {\n            // Breadcrumbs are best-effort context; never break tracking.\n        }\n    }\n\n    /**\n     * Manually report a caught error from a try/catch path. The report is\n     * marked `handled: true` (it didn't crash the app) and otherwise flows\n     * through the same pipeline (breadcrumbs, dedup, send) as uncaught errors.\n     * `options.componentStack` attaches a React component stack; `options.mechanism`\n     * lets the React error boundary mark its reports as `react`.\n     * No-op if error capture isn't active yet (before `start()` or when the\n     * `enableErrorTracking` option is false). Never throws into the host app.\n     */\n    public captureException(\n        error: unknown,\n        options?: { componentStack?: string; mechanism?: ErrorMechanism },\n    ): void {\n        try {\n            this.errorCapture?.capture(\n                error,\n                options?.mechanism ?? 'captureException',\n                true,\n                { componentStack: options?.componentStack },\n            );\n        } catch {\n            // Manual capture must never break the host application.\n        }\n    }\n\n    /**\n     * Flush pending custom events (queued before 5 seconds)\n     */\n    private async flushPendingCustomEvents(): Promise<void> {\n        if (this.pendingCustomEvents.length === 0) {\n            return;\n        }\n\n        const eventsToFlush = [...this.pendingCustomEvents];\n        this.pendingCustomEvents = [];\n\n        logDebug(`Flushing ${eventsToFlush.length} pending custom events`);\n\n        // Route the bridge from \"queued behind the 5s gate\" through the same\n        // batching pipeline so we don't fire N individual requests on\n        // session start.\n        for (const { eventName, properties, eventId } of eventsToFlush) {\n            this.queueCustomEvent(eventName, properties, eventId);\n        }\n    }\n\n    /**\n     * Flush pending logs (queued before 5 seconds)\n     */\n    private async flushPendingLogs(): Promise<void> {\n        if (this.pendingLogs.length === 0) {\n            return;\n        }\n\n        const logsToFlush = [...this.pendingLogs];\n        this.pendingLogs = [];\n\n        logDebug(`Flushing ${logsToFlush.length} pending logs`);\n\n        for (const { logData } of logsToFlush) {\n            try {\n                await this.api.sendLog(logData);\n            } catch (error) {\n                logError('Failed to flush pending log:', error);\n            }\n        }\n    }\n\n    /**\n     * Flush pending network errors (queued before 5 seconds)\n     */\n    private async flushPendingNetworkErrors(): Promise<void> {\n        if (this.pendingNetworkErrors.length === 0) {\n            return;\n        }\n\n        const errorsToFlush = [...this.pendingNetworkErrors];\n        this.pendingNetworkErrors = [];\n\n        logDebug(`Flushing ${errorsToFlush.length} pending network errors`);\n\n        for (const { errorData } of errorsToFlush) {\n            try {\n                await this.api.sendNetworkError(errorData);\n            } catch (error) {\n                logError('Failed to flush pending network error:', error);\n            }\n        }\n    }\n\n    /**\n     * Enable page load tracking - detects heavy page loads (>3 seconds)\n     */\n    public enablePageLoadTracking(): void {\n        if (!isBrowser || typeof window === 'undefined') return;\n        \n        // Track initial page load\n        if (document.readyState === 'complete') {\n            // Page already loaded, check immediately\n            this.trackPageLoad();\n        } else {\n            // Wait for page load\n            window.addEventListener('load', () => {\n                this.trackPageLoad();\n            });\n        }\n        \n        logDebug('Page load tracking enabled');\n    }\n\n    /**\n     * Track heavy page loads using Performance API\n     */\n    private trackPageLoad(): void {\n        if (!isBrowser || typeof performance === 'undefined') return;\n        \n        try {\n            const perfEntry = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming;\n            if (!perfEntry) return;\n            \n            const loadDuration = perfEntry.loadEventEnd - perfEntry.fetchStart;\n            const HEAVY_LOAD_THRESHOLD_MS = 3000; // 3 seconds\n            \n            // Only track if heavy (>3 seconds)\n            if (loadDuration > HEAVY_LOAD_THRESHOLD_MS) {\n                const requestId = uuidv1();\n                const domContentLoaded = perfEntry.domContentLoadedEventEnd - perfEntry.fetchStart;\n                const domComplete = perfEntry.domComplete - perfEntry.fetchStart;\n                \n                const errorData = {\n                    requestId,\n                    url: sanitizeUrl(window.location.href),\n                    method: 'GET',\n                    status: 200, // Page loads are typically successful\n                    statusText: 'OK',\n                    duration: loadDuration,\n                    timestampMs: perfEntry.loadEventEnd + performance.timeOrigin,\n                    sessionId: this.sessionId,\n                    endUserId: this.endUserId,\n                    errorType: 'slow_page_load',\n                    errorMessage: `Page load took ${loadDuration}ms`,\n                    // New span fields\n                    startTimeMs: perfEntry.fetchStart + performance.timeOrigin,\n                    spanName: 'page_load',\n                    spanStatus: 'slow' as const,\n                    attributes: {\n                        'page.url': window.location.href,\n                        'page.load_time': loadDuration,\n                        'page.dom_content_loaded': domContentLoaded,\n                        'page.dom_complete': domComplete,\n                    }\n                };\n                // ✅ Check minimum duration - queue if below, send if above\n                if (this.shouldSkipDueToMinimumDuration()) {\n                    logDebug('Slow page load network error queued due to session duration below minimum');\n                    this.pendingNetworkErrors.push({\n                        errorData,\n                        timestamp: Date.now()\n                    });\n                    return;\n                }\n                // ✅ Flush any pending network errors first (everything before 5 seconds)\n                this.flushPendingNetworkErrors();\n                this.api.sendNetworkError(errorData).catch(() => {}); // Non-blocking\n            }\n        } catch (error) {\n            logWarn('Failed to track page load:', error);\n        }\n    }\n\n    /**\n     * Check if network request should be skipped (SDK's own requests)\n     */\n    private shouldSkipNetworkTracking(url: string): boolean {\n        if (!url || !this.ingestionUrl) {\n            return false;\n        }\n        \n        try {\n            const urlObj = new URL(url);\n            const baseUrlObj = new URL(this.ingestionUrl);\n            \n            // Skip if same origin (same protocol, host, port)\n            if (urlObj.origin === baseUrlObj.origin) {\n                // Also check if it's an ingestion endpoint\n                if (urlObj.pathname.startsWith('/api/ingestion/')) {\n                    return true;\n                }\n            }\n            \n            // Also check string matching as fallback\n            if (url.includes(this.ingestionUrl)) {\n                return true;\n            }\n            \n            return false;\n        } catch (error) {\n            // If URL parsing fails, do simple string check\n            return url.includes(this.ingestionUrl);\n        }\n    }\n\n    /**\n     * Classify HTTP error status codes\n     */\n    private classifyHttpError(status: number): string {\n        if (status >= 400 && status < 500) {\n            return 'client_error';\n        }\n        if (status >= 500) {\n            return 'server_error';\n        }\n        return 'unknown_error';\n    }\n\n    /**\n     * Classify network errors (CORS, timeouts, blocked requests, etc.)\n     */\n    private classifyNetworkError(error: any): string {\n        const errorMessage = error.message || '';\n        const errorName = error.name || '';\n        \n        // Check for blocked requests (ad blockers, browser extensions, etc.)\n        if (\n            errorMessage.includes('blocked') ||\n            errorMessage.includes('ERR_BLOCKED_BY_CLIENT') ||\n            errorMessage.includes('net::ERR_BLOCKED_BY_CLIENT') ||\n            errorName === 'TypeError' && errorMessage.includes('Failed to fetch')\n        ) {\n            return 'blocked_by_client';\n        }\n        \n        // Check for CORS errors\n        if (\n            errorMessage.includes('CORS') ||\n            errorMessage.includes('Cross-Origin') ||\n            errorMessage.includes('Access-Control-Allow-Origin') ||\n            errorName === 'TypeError' && errorMessage.includes('CORS')\n        ) {\n            return 'cors_error';\n        }\n        \n        // Check for network/timeout errors\n        if (\n            errorMessage.includes('timeout') ||\n            errorMessage.includes('TIMEOUT') ||\n            errorMessage.includes('NetworkError') ||\n            errorName === 'NetworkError'\n        ) {\n            return 'network_error';\n        }\n        \n        // Check for abort errors\n        if (\n            errorMessage.includes('abort') ||\n            errorName === 'AbortError'\n        ) {\n            return 'aborted';\n        }\n        \n        return 'unknown_error';\n    }\n\n    /**\n     * Disable console event tracking\n     */\n    public disableConsoleTracking(): void {\n        if (!isBrowser || !this.consoleTrackingEnabled) return;\n\n        // Restore original console methods\n        if (this.originalConsole) {\n            console.log = this.originalConsole.log;\n            console.warn = this.originalConsole.warn;\n            console.error = this.originalConsole.error;\n        }\n\n        this.consoleTrackingEnabled = false;\n        logDebug('Console tracking disabled');\n    }\n\n    private trackConsoleEvent(level: 'log' | 'warn' | 'error', args: any[]): void {\n        if (!this.initialized) {\n            return;\n        }\n\n        // Only track warn and error, skip log\n        if (level === 'log') {\n            // Just call original console.log, don't track\n            if (this.originalConsole) {\n                this.originalConsole.log(...args);\n            }\n            return;\n        }\n\n        try {\n            // ✅ SKIP TRACKING: If SDK logger is currently active, don't track\n            if (isSDKLogging()) {\n                if (this.originalConsole) {\n                    this.originalConsole[level](...args);\n                }\n                return;\n            }\n\n            // ✅ SKIP TRACKING: Check if log originates from SDK code\n            const stack = new Error().stack || '';\n            if (this.isSDKStackFrame(stack)) {\n                // This log came from SDK code, don't track it\n                if (this.originalConsole) {\n                    this.originalConsole[level](...args);\n                }\n                return;\n            }\n\n            const consoleData = {\n                // Idempotency ID: the retry queue re-sends the same serialized\n                // body, so the server can drop duplicate deliveries.\n                eventId: uuidv1(),\n                level: level, // 'warn' or 'error'\n                message: args.map(arg =>\n                    typeof arg === 'object' ? JSON.stringify(arg) : String(arg)\n                ).join(' '),\n                timestampMs: Date.now(),\n                url: isBrowser ? sanitizeUrl(window.location.href) : '',\n                userAgent: isBrowser ? navigator.userAgent : '',\n                stack: stack,\n                environment: this.environment,\n                sessionId: this.sessionId,\n                endUserId: this.endUserId,\n                automaticProperties: this.propertyManager.getAutomaticProperties()\n            };\n\n            // ✅ Check minimum duration - queue if below, send if above\n            if (this.shouldSkipDueToMinimumDuration()) {\n                logDebug(`Console ${level} queued due to session duration below minimum`);\n                this.pendingLogs.push({\n                    logData: consoleData,\n                    timestamp: Date.now()\n                });\n                return;\n            }\n\n            // ✅ Flush any pending logs first (everything before 5 seconds)\n            this.flushPendingLogs();\n\n            // Record a console breadcrumb for crash-report context.\n            this.addBreadcrumb('console', `${consoleData.level}: ${consoleData.message}`.substring(0, 200), { level: consoleData.level });\n\n            // Promote React/Next hydration-mismatch console errors into structured\n            // error reports — they surface as console.error and never reach\n            // window.onerror. Marked handled (React recovers via client render).\n            if (consoleData.level === 'error' && isHydrationError(consoleData.message)) {\n                this.captureException(new Error(consoleData.message), { mechanism: 'react' });\n            }\n\n            // Send to dedicated endpoint for ClickHouse\n            this.api.sendLog(consoleData).catch(err => {\n                // Fallback to event stream if dedicated endpoint fails\n            this.addEvent({\n                type: 5, // Custom event type\n                data: {\n                    payload: {\n                        eventType: 'console',\n                        ...consoleData\n                    }\n                },\n                timestamp: Date.now()\n                }).catch(() => {}); // Silent fail\n            });\n        } catch (error) {\n            logError('Error in trackConsoleEvent:', error);\n        }\n    }\n\n    /**\n     * Check if the actual caller (not SDK wrapper) is from SDK code\n     * Since we intercept console methods, the stack will always include SDK frames.\n     * We need to look at the actual caller frame (the one that called console.error/warn from user code).\n     */\n    private isSDKStackFrame(stack: string): boolean {\n        if (!stack) return false;\n        \n        // SDK file path patterns to check for\n        const sdkPatterns = [\n            'humanbehavior-js',\n            '@humanbehavior/core',\n            '@humanbehavior/browser',\n            'tracker.ts',\n            'api.ts',\n            'logger.ts',\n            'utils/logger',\n            'packages/core',\n            'packages/browser',\n            'index.mjs', // Built SDK bundle\n            'index.js'   // Built SDK bundle\n        ];\n        \n        // Parse stack into lines\n        const stackLines = stack.split('\\n');\n        const stackLower = stack.toLowerCase();\n        \n        // If the ENTIRE stack only contains SDK patterns, it's an SDK-originated log\n        // Otherwise, if there's ANY non-SDK frame, it's user code\n        \n        // Check if ALL frames (excluding the first \"Error\" line) are SDK frames\n        let foundNonSDKFrame = false;\n        \n        for (let i = 0; i < stackLines.length; i++) {\n            const line = stackLines[i].trim().toLowerCase();\n            \n            // Skip empty lines and the first \"Error:\" line\n            if (!line || line === 'error' || line.startsWith('error:')) {\n                continue;\n            }\n            \n            // Check if this line contains SDK patterns\n            const isSDKFrame = sdkPatterns.some(pattern => line.includes(pattern.toLowerCase()));\n            \n            if (!isSDKFrame) {\n                // Found a non-SDK frame - this is user code calling console.error/warn\n                foundNonSDKFrame = true;\n                break;\n            }\n        }\n        \n        // If we found a non-SDK frame, it's user code - don't skip\n        // If all frames are SDK frames, skip tracking\n        return !foundNonSDKFrame;\n    }\n\n    /** Tracks whether unload listeners are already attached on the global\n     *  window so we don't stack them across HMR / re-init cycles. The flag\n     *  lives on `window`, not on `this`, because in dev environments\n     *  (Next.js Turbopack HMR, module duplication) the instance can change\n     *  even when window-level listeners are already in place. Stacking\n     *  listeners means N copies of `pagehide` → N parallel sendBeacons →\n     *  browsers throttle some, and only the first sub-100ms one survives\n     *  reliably. The downstream symptom is a /session-end beacon that\n     *  silently fails to deliver, making the dashboard fall through to the\n     *  75-second age-out path instead of immediate eviction.\n     */\n    private setupPageUnloadHandler() {\n        if (!isBrowser) return;\n        const flag = '__humanBehaviorUnloadAttached';\n        if ((window as any)[flag]) {\n            logDebug('Page unload handlers already attached on window — skipping re-attach');\n            // Update the bound tracker so this instance receives the calls\n            // (the listener closes over `this` via a stable reference).\n            (window as any).__humanBehaviorActiveTracker = this;\n            return;\n        }\n        (window as any)[flag] = true;\n        (window as any).__humanBehaviorActiveTracker = this;\n        \n        logDebug('Setting up page unload handler');\n        \n        // Handle visibility changes for sending events\n        window.addEventListener('visibilitychange', () => {\n            // Industry-standard active-time gating: every other replay tool\n            // (Mixpanel, FullStory, Hotjar, MS Clarity) excludes hidden-tab\n            // time from active duration. We capture the transition as an\n            // rrweb custom event so the archiver and player segment logic\n            // can clip activity at the boundary.\n            //\n            // Push BEFORE flushing on hidden so the marker ships in the\n            // same batch as the events that immediately preceded it —\n            // ensures the archiver sees the close before any trailing\n            // mutations arrive in a later chunk.\n            const state =\n                document.visibilityState === 'hidden' ? 'hidden' : 'visible';\n            this.emitVisibilityMarker(state);\n\n            if (document.visibilityState === 'hidden') {\n                logDebug('Page hidden - sending pending events');\n                // Hidden is the last reliable signal on mobile (pagehide may\n                // never fire when the app is killed): ship the gated analytics\n                // buffer alongside the rrweb flush.\n                this.drainPendingCustomEventsForTeardown();\n                this.flushCustomEventBatchBeacon();\n                this.flushEvents();\n            } else if (document.visibilityState === 'visible') {\n                logDebug('Page visible - taking full snapshot for multi-window replay');\n                this.takeFullSnapshot();\n            }\n        });\n\n        // Window focus: visible tabs on a secondary monitor still get rrweb noise\n        // while blurred; gated with debounced blur so brief alt-tab does not jitter.\n        this.setupWindowFocusTracking();\n\n        // Use pagehide if available (more reliable than beforeunload)\n        // pagehide fires in more cases (navigation, tab close, etc.)\n        const unloadEvent = 'onpagehide' in window ? 'pagehide' : 'beforeunload';\n        \n        window.addEventListener(unloadEvent, () => {\n            // ✅ SYNCHRONOUS UNLOAD HANDLER\n            // Prepare data synchronously (no await) and send via sendBeacon\n            logDebug('Page unloading - sending final events via sendBeacon');\n\n            let sessionEndSent = false;\n            const sendSessionEnd = () => {\n                if (sessionEndSent) return;\n                sessionEndSent = true;\n                try {\n                    this.api?.sendSessionEndBeacon(this.sessionId, this.endUserId);\n                } catch {\n                    // best-effort\n                }\n            };\n\n            // Flush any in-memory customEvent batch BEFORE the minimum-\n            // duration gate below — pagehide is the last chance to ship\n            // these. The 100ms debounce in queueCustomEvent would otherwise\n            // drop them on a fast tab close. `sendCustomEventBatch` uses\n            // `keepalive: true` so the browser commits the in-flight POST\n            // even after the page goes away. Fire-and-forget; we don't\n            // await because that would block unload.\n            try {\n                this.drainPendingCustomEventsForTeardown();\n                this.flushCustomEventBatchBeacon();\n            } catch {\n                // best-effort\n            }\n\n            // Flush buffered tracing spans on unload (sendBeacon, synchronous).\n            try {\n                this.tracing?.flush(true);\n            } catch {\n                // best-effort\n            }\n\n            // ✅ Check minimum duration before sending on unload (default: 5 seconds)\n            const minimumDuration = this.minimumDurationMilliseconds;\n            const sessionDuration = this.getSessionDuration();\n            const isPositiveSessionDuration = sessionDuration !== null && sessionDuration >= 0;\n            const isBelowMinimumDuration = \n                isPositiveSessionDuration && \n                sessionDuration < minimumDuration;\n            \n            if (isBelowMinimumDuration) {\n                // Don't send - buffer stays\n                logDebug(`Session duration (${sessionDuration}ms) below minimum (${minimumDuration}ms), not sending on unload`);\n                sendSessionEnd();\n                return;\n            }\n            \n            // 1. Prepare events synchronously (copy queue immediately)\n            const eventsToSend = [...this.eventQueue];\n            \n            // 2. Include pending snapshots if available (for very short sessions)\n            // This handles cases where a flush is in progress but hasn't completed\n            if (isBrowser && (window as any).__hb_pending_snapshots) {\n                const pendingSnapshots = (window as any).__hb_pending_snapshots;\n                if (Array.isArray(pendingSnapshots) && pendingSnapshots.length > 0) {\n                    logDebug('Including pending FullSnapshot(s) in sendBeacon for short session');\n                    eventsToSend.unshift(...pendingSnapshots); // Add at beginning so snapshot comes first\n                    delete (window as any).__hb_pending_snapshots;\n                }\n            }\n            \n            // 3. Send via sendBeacon synchronously\n            if (eventsToSend.length > 0 && this.api) {\n                try {\n                    // Get automatic properties synchronously\n                    const automaticProperties = this.propertyManager.getAutomaticProperties();\n                    \n                    // Send via sendBeacon (synchronous API - completes before page closes)\n                    this.api.sendBeaconEvents(\n                        eventsToSend, \n                        this.sessionId, \n                        this.endUserId || undefined,\n                        this.windowId,\n                        automaticProperties\n                    );\n                    \n                    // Clear queue after sending\n                    this.eventQueue = [];\n                } catch (error) {\n                    // sendBeacon is best-effort, log but don't throw\n                    logWarn('Failed to send events via sendBeacon on unload:', error);\n                }\n            }\n            \n            // 4. Also handle retry queue (for any failed requests)\n            if (this.api) {\n                this.api.unload();\n            }\n\n            // Evict from the dashboard's live-presence set LAST. Final event\n            // beacons also refresh presence server-side; sending session-end\n            // first lets those trailing writes re-add the user for the full\n            // live-window timeout after a tab close.\n            sendSessionEnd();\n        });\n\n        // Update activity timestamp on user interaction (not on page load)\n        const updateActivity = () => {\n            localStorage.setItem(`human_behavior_last_activity`, Date.now().toString());\n        };\n\n        // Listen for user interactions to update activity timestamp\n        window.addEventListener('click', updateActivity);\n        window.addEventListener('keydown', updateActivity);\n        window.addEventListener('scroll', updateActivity);\n        window.addEventListener('mousemove', updateActivity);\n    }\n\n    public viewLogs() {\n        try {\n            const logs = logger.getLogs();\n            logInfo('HumanBehavior Logs:', logs);\n            logger.clearLogs(); // Clear logs after viewing\n        } catch (e) {\n            logError('Failed to read logs:', e);\n        }\n    }\n\n    /**\n     * Add user identification information to the tracker\n     * If userId is not provided, will use userProperties.email as the userId (if present)\n     */\n    public async identifyUser(\n        userPropertiesOrArg: Record<string, any> | { userProperties: Record<string, any>; identityToken?: string },\n        options?: { identityToken?: string }\n    ): Promise<string> {\n        // Accept either shape — the React wrapper passes\n        // `{ userProperties }`, but tests/users routinely pass the bare\n        // properties object. Both worked pre-0.7 only because the call sites\n        // happened to spread compatibly; this normalises it.\n        const wrapped = (userPropertiesOrArg as any)?.userProperties && typeof (userPropertiesOrArg as any).userProperties === 'object';\n        const userProperties: Record<string, any> = wrapped\n            ? (userPropertiesOrArg as any).userProperties\n            : (userPropertiesOrArg as any) || {};\n\n        // A token minted by the customer's backend, forwarded verbatim. Projects\n        // with identity verification required reject an identify that claims an\n        // externalUserId without one.\n        const identityToken: string | undefined =\n            options?.identityToken\n            ?? (wrapped ? (userPropertiesOrArg as any).identityToken : undefined);\n\n        // ✅ NON-BLOCKING: Don't wait for init, endUserId is already available locally\n        // await this.ensureInitialized(); // Removed - no longer needed\n        \n        // Keep the original endUserId (UUID) - don't change it\n        const originalEndUserId = this.endUserId;\n        \n        // Store user properties on BOTH the local cache and the\n        // PropertyManager so getUserAttributes() and getAllProperties().user\n        // see the same set. Pre-0.7 only the local field was set, which made\n        // identify() effectively write-only.\n        this.userProperties = { ...this.userProperties, ...userProperties };\n        try {\n            this.propertyManager?.setUserProperties(userProperties);\n        } catch {\n            // PropertyManager not yet constructed — ignore (constructor sets\n            // it before init() runs in practice).\n        }\n\n        logDebug('Identifying user:', { userProperties, originalEndUserId, sessionId: this.sessionId });\n        \n        // Get automatic properties and send with user data (only in browser)\n        const automaticProperties = isBrowser ? this.propertyManager.getAutomaticProperties() : {};\n        \n        // Use the API class method which properly handles user name\n        const userResponse = await this.api.sendUserData(\n            originalEndUserId || '',\n            userProperties,\n            this.sessionId,\n            identityToken\n        );\n\n        // If server found a preexisting user, persist the canonical ID for future sessions\n        // but keep using the original anon ID for the current session\n        if (userResponse.actualUserId || userResponse.wasExistingUser) {\n            const canonicalEndUserId = userResponse.actualUserId || originalEndUserId;\n            if (canonicalEndUserId && canonicalEndUserId !== originalEndUserId) {\n                // Persist canonical ID to cookie/localStorage for future sessions\n                const cookieName = `human_behavior_end_user_id`;\n                this.setCookie(cookieName, canonicalEndUserId, 365);\n                // Explicitly set localStorage as well to ensure it's persisted\n                if (isBrowser) {\n                    try {\n                        localStorage.setItem(cookieName, canonicalEndUserId);\n                    } catch (error) {\n                        logDebug('Failed to set canonical endUserId in localStorage:', error);\n                    }\n                }\n                logDebug(`🔗 Preexisting user detected. Future sessions will use canonical ID: ${canonicalEndUserId} (current session stays: ${originalEndUserId})`);\n            }\n        }\n\n        // Keep original endUserId for the entire session (no mid-session switch)\n        return originalEndUserId || '';\n    }\n    /**\n     * Get current user attributes\n     */\n    public getUserAttributes(): Record<string, any> {\n        // Combine the local cache with whatever the PropertyManager has\n        // recorded. setUserProperty() (the public API) only writes to the\n        // PropertyManager; identifyUser() writes to both.\n        const fromManager = (() => {\n            try {\n                return this.propertyManager?.getAllProperties().user || {};\n            } catch {\n                return {};\n            }\n        })();\n        return { ...fromManager, ...this.userProperties };\n    }\n\n    public async start() {\n        // ✅ NON-BLOCKING: Start immediately, don't wait for init\n        // Init continues in background but doesn't block recording\n        if (!isBrowser) return;\n        \n        // Prevent multiple start() calls\n        if (this.isStarted) {\n            logDebug('HumanBehaviorTracker already started, skipping start() call.');\n            return;\n        }\n        this.isStarted = true;\n        \n        // ✅ Initialize idle detection\n        // Sync with session activity timestamp if it exists (from session creation)\n        // Otherwise use current time\n        this._lastActivityTimestamp = this._sessionActivityTimestamp !== null \n            ? this._sessionActivityTimestamp \n            : Date.now();\n        this._isIdle = 'unknown'; // Start as unknown until first interaction\n\n        // Start periodic flushing (unified queue) at the current tier.\n        this.armFlushTimer();\n\n        // Periodic presence ping for the dashboard's live indicator. Skips\n        // when the tab is hidden (browsers throttle setInterval there\n        // anyway) and when the SDK hasn't finished bootstrapping yet\n        // (sessionId not assigned). Best-effort — failures are silent.\n        this.heartbeatInterval = window.setInterval(() => {\n            try {\n                if (typeof document !== 'undefined' && document.visibilityState !== 'visible') {\n                    return;\n                }\n                if (!this.sessionId || !this.api) return;\n                this.api.sendHeartbeatBeacon(this.sessionId, this.endUserId);\n            } catch {\n                // best-effort\n            }\n        }, this.HEARTBEAT_INTERVAL_MS);\n\n        // ✅ Enable console tracking for warn/error logging (if enabled)\n        if (this.enableConsoleTrackingFlag) {\n            this.enableConsoleTracking();\n        }\n        \n        // ✅ Enable network error tracking (if enabled)\n        if (this.enableNetworkTrackingFlag) {\n            this.enableNetworkTracking();\n        }\n\n        // ✅ Enable Core Web Vitals tracking (if enabled)\n        if (this.enableWebVitalsFlag) {\n            this.enableWebVitalsTracking();\n        }\n\n        // ✅ Enable distributed tracing (if enabled)\n        if (this.enableTracingFlag) {\n            this.enableTracing();\n        }\n\n        // ✅ Enable crash/error capture (if enabled)\n        if (this.enableErrorTrackingFlag) {\n            this.setupErrorCapture();\n        }\n        \n        // Enable page load tracking\n        this.enablePageLoadTracking();\n\n        // Trigger server-side GeoIP enrichment once per session (fire-and-forget).\n        // Server resolves IP from request headers and publishes a $geoip event.\n        this.api.sendIpInfo(this.sessionId, this.endUserId);\n\n        // ✅ DOM READY DETECTION\n        // Wait for DOM to be ready before starting recording\n        const startRecording = () => {\n            // Prevent multiple recording instances\n            if (this.recordInstance) {\n                logDebug('🎯 Recording already started, skipping duplicate start');\n                return;\n            }\n            \n            logDebug('🎯 DOM ready, starting session recording');\n            \n            // ✅ HUMANBEHAVIOR RRWEB CONFIGURATION\n            this.rrwebRecord = record;\n            // debug removed\n            const recordInstance = record({\n            emit: (event) => {\n                this.addRecordingEvent(event);\n                \n                // ✅ DEBUG FULLSNAPSHOT GENERATION\n                if (event.type === 2) { // FullSnapshot\n                    logDebug(`🎯 FullSnapshot generated at ${new Date().toISOString()}`);\n                }\n            },\n            // ✅ HUMANBEHAVIOR'S CUSTOM SETTINGS\n            maskTextSelector: this.redactionManager.getMaskTextSelector() || undefined,\n            maskTextFn: undefined,\n            maskAllInputs: this.redactionManager.getRedactionMode() === 'privacy-first', // Configurable based on strategy\n            maskInputOptions: {\n                // Enable rrweb input masking callbacks for all common types\n                password: true,\n                text: true,\n                textarea: true,\n                email: true,\n                number: true,\n                tel: true,\n                url: true,\n                search: true,\n                date: true,\n                time: true,\n                month: true,\n                week: true\n            },\n            // In visibility-first, selectively mask inputs that should be redacted\n            maskInputFn: (text, element) => {\n                const masked = '*'.repeat(text.length || 1);\n                try {\n                    const mode = this.redactionManager.getRedactionMode();\n                    // If we can't positively identify the element, mask it.\n                    if (!(element instanceof HTMLElement)) return masked;\n                    // privacy-first: always mask input values\n                    if (mode === 'privacy-first') return masked;\n                    // visibility-first: show only elements the manager says are safe.\n                    return this.redactionManager.shouldUnredactElement(element) ? text : masked;\n                } catch {\n                    // Fail closed: a thrown redaction decision must never leak the\n                    // raw input value into the recording.\n                    return masked;\n                }\n            },\n            slimDOMOptions: {},\n            // ✅ ERROR SUPPRESSION SETTINGS - Disabled to prevent console noise\n            collectFonts: false, // Disable font collection to reduce errors\n            inlineStylesheet: true, // Keep styles for proper session replay\n            recordCrossOriginIframes: false, // Prevent cross-origin iframe errors\n            \n            // ✅ CANVAS RECORDING - protection against overwhelm\n            recordCanvas: this.recordCanvas, // Opt-in only\n            sampling: this.recordCanvas ? { canvas: 4 } : undefined, // 4 FPS throttle\n            dataURLOptions: this.recordCanvas ? { \n                type: 'image/webp', \n                quality: 0.4 \n            } : undefined, // WebP with 40% quality\n            \n            // ✅ FULLSNAPSHOT GENERATION - No periodic snapshots to avoid animation issues\n            // Rely on initial FullSnapshot + navigation-triggered ones only\n            hooks: {\n                // Extra safety: mask input events selectively using rrweb hook\n                input: (event) => {\n                    try {\n                        const mode = this.redactionManager.getRedactionMode();\n                        // In privacy-first everything is masked already by maskAllInputs\n                        if (mode === 'privacy-first') return;\n                        const node = typeof document !== 'undefined'\n                          ? document.querySelector(`[data-rrweb-id=\"${(event as any).id}\"]`)\n                          : null;\n                        if (node && node instanceof HTMLElement) {\n                            const shouldShow = this.redactionManager.shouldUnredactElement(node);\n                            if (!shouldShow) {\n                                // Mask text payloads in input event\n                                if (typeof (event as any).text !== 'undefined') {\n                                    (event as any).text = '*'.repeat((event as any).text?.length || 1);\n                                }\n                                if (typeof (event as any).value !== 'undefined') {\n                                    (event as any).value = '*'.repeat((event as any).value?.length || 1);\n                                }\n                            }\n                        }\n                    } catch {}\n                }\n            }\n        });\n        \n        // Store the record instance for cleanup \n        this.recordInstance = recordInstance || null;\n\n        // Replay-fidelity: capture the text of stylesheets rrweb could not\n        // inline (CORS-opaque cross-origin sheets) and ship it in-band as\n        // custom events, so replay renders the record-time CSS instead of\n        // whatever the origin serves later. Never throws, fully async.\n        try {\n            this.cssSnapshotCapture?.dispose();\n            this.cssSnapshotCapture = new CssSnapshotCapture((payload) => {\n                try {\n                    if (this.rrwebRecord && typeof this.rrwebRecord.addCustomEvent === 'function') {\n                        this.rrwebRecord.addCustomEvent(CSS_SNAPSHOT_TAG, payload);\n                    }\n                } catch {\n                    // Recording may have stopped between fetch and emit.\n                }\n            });\n            this.cssSnapshotCapture.captureFromDocument(document);\n        } catch {\n            // CSS snapshotting must never break recording.\n        }\n\n        // Localhost Verify fidelity: ship same-origin image bytes in-band so\n        // the cloud live preview can render them (archiver cannot fetch loopback).\n        // Push onto eventQueue directly (same as $visibility markers) — large\n        // base64 payloads are more reliable here than rrweb addCustomEvent.\n        // No-op on public origins. Never throws.\n        try {\n            this.localAssetSnapshotCapture?.dispose();\n            this.localAssetSnapshotCapture = new LocalAssetSnapshotCapture((payload) => {\n                try {\n                    this.eventQueue.push({\n                        type: 5,\n                        data: { tag: LOCAL_ASSET_TAG, payload },\n                        timestamp: Date.now(),\n                    } as any);\n                    // Nudge flush so Verify sees the image soon after paint.\n                    void this.flushEvents();\n                } catch {\n                    // Recording may have stopped between fetch and emit.\n                }\n            });\n            if (typeof location !== 'undefined') {\n                this.localAssetSnapshotCapture.captureFromDocument(document, location.href);\n            }\n        } catch {\n            // Local asset snapshotting must never break recording.\n        }\n\n        // Invisible-text capture: read getComputedStyle colors for text elements\n        // and ship an `hb-contrast` custom event for any whose foreground matches\n        // its background (white-on-white validation errors, invisible prices).\n        // The deterministic trace compiler cannot see class/CSS-in-JS colors, so\n        // this is the only path that catches them. Never throws.\n        try {\n            this.contrastCapture?.dispose();\n            this.contrastCapture = new ContrastCapture(\n                (payload) => {\n                    try {\n                        if (this.rrwebRecord && typeof this.rrwebRecord.addCustomEvent === 'function') {\n                            this.rrwebRecord.addCustomEvent(HB_CONTRAST_TAG, payload);\n                        }\n                    } catch {\n                        // Recording may have stopped between scan and emit.\n                    }\n                },\n                (node) => {\n                    try {\n                        return this.rrwebRecord?.mirror?.getId(node) ?? -1;\n                    } catch {\n                        return -1;\n                    }\n                },\n                (payload) => {\n                    try {\n                        if (this.rrwebRecord && typeof this.rrwebRecord.addCustomEvent === 'function') {\n                            this.rrwebRecord.addCustomEvent(HB_CLIP_TAG, payload);\n                        }\n                    } catch {\n                        // Recording may have stopped between scan and emit.\n                    }\n                },\n                (payload) => {\n                    try {\n                        if (this.rrwebRecord && typeof this.rrwebRecord.addCustomEvent === 'function') {\n                            this.rrwebRecord.addCustomEvent(HB_OVERLAP_TAG, payload);\n                        }\n                    } catch {\n                        // Recording may have stopped between scan and emit.\n                    }\n                },\n                (payload) => {\n                    try {\n                        if (this.rrwebRecord && typeof this.rrwebRecord.addCustomEvent === 'function') {\n                            this.rrwebRecord.addCustomEvent(HB_MISALIGN_TAG, payload);\n                        }\n                    } catch {\n                        // Recording may have stopped between scan and emit.\n                    }\n                },\n                (payload) => {\n                    try {\n                        if (this.rrwebRecord && typeof this.rrwebRecord.addCustomEvent === 'function') {\n                            this.rrwebRecord.addCustomEvent(HB_BROKEN_ASSET_TAG, payload);\n                        }\n                    } catch {\n                        // Recording may have stopped between scan and emit.\n                    }\n                },\n            );\n            if (isBrowser) this.contrastCapture.start(document, window);\n        } catch {\n            // Contrast capture must never break recording.\n        }\n\n        // Broken-asset capture: a failed <img>/<script>/<link>/media load fires a\n        // capture-phase `error` on the element (it does not bubble). Ship it as an\n        // `hb-broken-asset` custom event with the element's rrweb node id so the\n        // deterministic trace compiler can emit a broken_asset row and prime the\n        // visual grader. The error subsystem separately records the runtime issue;\n        // this marker is the replay-stream + element-localized signal. Never throws.\n        try {\n            if (isBrowser) {\n                if (this.brokenAssetHandler) {\n                    window.removeEventListener('error', this.brokenAssetHandler, true);\n                }\n                this.brokenAssetSeen.clear();\n                const ASSET_TAGS = new Set(['IMG', 'SCRIPT', 'LINK', 'SOURCE', 'VIDEO', 'AUDIO', 'IFRAME']);\n                this.brokenAssetHandler = (event: Event) => {\n                    try {\n                        const target = event.target as (Element & { src?: string; href?: string }) | null;\n                        if (!target || !target.tagName || !ASSET_TAGS.has(target.tagName)) return;\n                        const url = target.src || target.href || '';\n                        if (!url || this.brokenAssetSeen.has(url)) return;\n                        if (this.brokenAssetSeen.size >= 100) return;\n                        this.brokenAssetSeen.add(url);\n                        let id = -1;\n                        try {\n                            id = this.rrwebRecord?.mirror?.getId(target) ?? -1;\n                        } catch {\n                            id = -1;\n                        }\n                        if (this.rrwebRecord && typeof this.rrwebRecord.addCustomEvent === 'function') {\n                            this.rrwebRecord.addCustomEvent(HB_BROKEN_ASSET_TAG, {\n                                id,\n                                tag: target.tagName.toLowerCase(),\n                                url,\n                            });\n                        }\n                    } catch {\n                        // Marker emission is best-effort.\n                    }\n                };\n                window.addEventListener('error', this.brokenAssetHandler, true);\n            }\n        } catch {\n            // Broken-asset capture must never break recording.\n        }\n        };\n\n        // ✅ DOM READY DETECTION - More aggressive like previous version\n        logDebug(`🎯 DOM ready state: ${document.readyState}`);\n        if (document.readyState === 'complete' || document.readyState === 'interactive') {\n            // DOM is ready enough, start immediately\n            logDebug(`🎯 DOM ready (${document.readyState}), starting recording immediately`);\n            startRecording();\n        } else {\n            // Wait for DOM to be ready, but also check periodically\n            logDebug('🎯 DOM not ready, waiting for DOMContentLoaded event');\n            \n            const checkDomReady = () => {\n                if (document.readyState === 'interactive' || document.readyState === 'complete') {\n                    logDebug(`🎯 DOM ready (${document.readyState}), starting recording`);\n                    startRecording();\n                    return true;\n                }\n                return false;\n            };\n            \n            // Check immediately in case it changed\n            if (checkDomReady()) return;\n            \n            // Listen for DOMContentLoaded\n            document.addEventListener('DOMContentLoaded', () => {\n                logDebug('🎯 DOMContentLoaded fired, starting recording');\n                startRecording();\n            }, { once: true });\n            \n            // Also check periodically for faster response\n            const interval = setInterval(() => {\n                if (checkDomReady()) {\n                    clearInterval(interval);\n                }\n            }, 10); // Check every 10ms\n            \n            // Clear interval after 5 seconds to avoid infinite checking\n            setTimeout(() => clearInterval(interval), 5000);\n        }\n    }\n\n    /**\n     * Push an rrweb custom event (type 5) that records a tab visibility\n     * transition. The archiver and replay player both consume this marker\n     * to gate active-duration calculations: time when state==='hidden' is\n     * excluded from \"Active Time Spent\" regardless of any rrweb activity\n     * that happens during it (animations, ad rotations, etc).\n     *\n     * Best-effort: failures are silent. The marker is just a hint; the\n     * existing 30s AFK + 2.5s gap rules still bound active time even if\n     * we drop one of these.\n     */\n    private emitVisibilityMarker(state: 'hidden' | 'visible'): void {\n        try {\n            const event = {\n                type: 5, // rrweb EventType.Custom\n                data: { tag: '$visibility', payload: { state } },\n                timestamp: Date.now(),\n            };\n            this.eventQueue.push(event as any);\n        } catch {\n            // best-effort\n        }\n    }\n\n    /** rrweb Custom event for archiver/player: engaged = visible && focused. */\n    private emitFocusMarker(state: 'focused' | 'blurred'): void {\n        try {\n            const event = {\n                type: 5,\n                data: { tag: '$focus', payload: { state } },\n                timestamp: Date.now(),\n            };\n            this.eventQueue.push(event as any);\n        } catch {\n            // best-effort\n        }\n    }\n\n    private setupWindowFocusTracking(): void {\n        if (!isBrowser) return;\n        try {\n            if (typeof document.hasFocus === 'function' && !document.hasFocus()) {\n                this.emitFocusMarker('blurred');\n                this.lastEmittedFocusState = 'blurred';\n            }\n        } catch {\n            // best-effort\n        }\n\n        window.addEventListener('blur', () => this.onWindowBlur(), false);\n        window.addEventListener('focus', () => this.onWindowFocus(), false);\n    }\n\n    private onWindowBlur(): void {\n        if (!isBrowser) return;\n        if (this.focusBlurGraceTimeout !== null) {\n            clearTimeout(this.focusBlurGraceTimeout);\n        }\n        this.focusBlurGraceTimeout = window.setTimeout(() => {\n            this.focusBlurGraceTimeout = null;\n            try {\n                if (typeof document.hasFocus === 'function' && document.hasFocus()) return;\n                if (this.lastEmittedFocusState === 'blurred') return;\n                this.emitFocusMarker('blurred');\n                this.lastEmittedFocusState = 'blurred';\n            } catch {\n                // noop\n            }\n        }, this.FOCUS_BLUR_GRACE_MS);\n    }\n\n    private onWindowFocus(): void {\n        if (!isBrowser) return;\n        if (this.focusBlurGraceTimeout !== null) {\n            clearTimeout(this.focusBlurGraceTimeout);\n            this.focusBlurGraceTimeout = null;\n        }\n        if (this.lastEmittedFocusState !== 'blurred') return;\n        try {\n            this.emitFocusMarker('focused');\n            this.lastEmittedFocusState = 'focused';\n        } catch {\n            // noop\n        }\n    }\n\n    /**\n     * Manually trigger a FullSnapshot (for navigation events)\n     * Delays snapshot to avoid capturing mid-animation states\n     */\n    private takeFullSnapshot(): void {\n        // Clear any existing timeout to avoid multiple snapshots\n        if (this.fullSnapshotTimeout) {\n            clearTimeout(this.fullSnapshotTimeout);\n        }\n\n        // Delay FullSnapshot to let animations settle\n        this.fullSnapshotTimeout = window.setTimeout(() => {\n            // Wait for any pending animations/transitions to complete\n            requestAnimationFrame(() => {\n                requestAnimationFrame(() => {\n                    try {\n                        // Access takeFullSnapshot from the rrweb record function\n                        if (this.rrwebRecord && typeof this.rrwebRecord.takeFullSnapshot === 'function') {\n                            this.rrwebRecord.takeFullSnapshot();\n                            logDebug('✅ FullSnapshot taken (delayed for animations)');\n                            // A navigation may have introduced stylesheets the\n                            // snapshot could not inline; capture their text too.\n                            this.cssSnapshotCapture?.captureFromDocument(document);\n                            if (typeof location !== 'undefined') {\n                                this.localAssetSnapshotCapture?.captureFromDocument(\n                                    document,\n                                    location.href,\n                                );\n                            }\n                        } else {\n                            logWarn('⚠️ takeFullSnapshot not available on record function');\n                        }\n                    } catch (error) {\n                        logError('❌ Failed to take FullSnapshot:', error);\n                    }\n                });\n            });\n        }, 1000); // Wait 1 second for animations to settle\n    }\n\n    public async stop() {\n        await this.ensureInitialized();\n        if (!isBrowser) return;\n        \n        if (this.flushInterval) {\n            clearInterval(this.flushInterval);\n            this.flushInterval = null;\n        }\n\n        if (this.heartbeatInterval) {\n            clearInterval(this.heartbeatInterval);\n            this.heartbeatInterval = null;\n        }\n\n        // Stop rrweb recording\n        if (this.recordInstance) {\n            this.recordInstance();\n            this.recordInstance = null;\n        }\n        \n        // Clear any pending FullSnapshot timeouts\n        if (this.fullSnapshotTimeout) {\n            clearTimeout(this.fullSnapshotTimeout);\n            this.fullSnapshotTimeout = null;\n        }\n\n        if (this.cssSnapshotCapture) {\n            this.cssSnapshotCapture.dispose();\n            this.cssSnapshotCapture = null;\n        }\n\n        if (this.localAssetSnapshotCapture) {\n            this.localAssetSnapshotCapture.dispose();\n            this.localAssetSnapshotCapture = null;\n        }\n\n        if (this.contrastCapture) {\n            this.contrastCapture.dispose();\n            this.contrastCapture = null;\n        }\n\n        if (this.brokenAssetHandler && typeof window !== 'undefined') {\n            window.removeEventListener('error', this.brokenAssetHandler, true);\n            this.brokenAssetHandler = null;\n        }\n\n        if (this.focusBlurGraceTimeout !== null) {\n            clearTimeout(this.focusBlurGraceTimeout);\n            this.focusBlurGraceTimeout = null;\n        }\n\n        this.rrwebRecord = null;\n\n        // Disable console tracking\n        this.disableConsoleTracking();\n\n        // Cleanup navigation tracking\n        this.cleanupNavigationTracking();\n\n        // Cleanup rage/dead click tracking\n        if (this.frictionMutationObserver) {\n            this.frictionMutationObserver.disconnect();\n            this.frictionMutationObserver = null;\n        }\n        this.frictionClicks?.reset();\n    }\n\n    /**\n     * Add an event to the ingestion queue\n     * Events are sent directly without processing to avoid corruption\n     */\n    public async addEvent(event: any) {\n        // ✅ NON-BLOCKING: Events work immediately, no waiting for init\n        // endUserId and sessionId are already available locally\n        \n        // ✅ CHECK SESSION TIMEOUT before adding event (creates new session if expired)\n        if (isBrowser) {\n            this.checkAndRefreshSession();\n        }\n        \n        // ✅ DIRECT EVENT HANDLING - No custom processing to avoid corruption\n        // Events flow directly from rrweb to ingestion server\n        \n        // ✅ EVENT VALIDATION\n        if (!event || typeof event !== 'object') {\n            logDebug('⚠️ Skipping invalid event:', event);\n            return;\n        }\n        \n        // ✅ LOG FULLSNAPSHOT STATUS FOR DEBUGGING\n        if (event.type === 2) { // FullSnapshot\n            const hasData = !!event.data;\n            const hasNode = !!(event.data && event.data.node);\n            \n            if (!hasData || !hasNode) {\n                logDebug(`⚠️ Empty FullSnapshot detected: hasData=${hasData}, hasNode=${hasNode} - continuing session`);\n            } else {\n                logDebug(`✅ Valid FullSnapshot: hasData=${hasData}, hasNode=${hasNode}, dataType=${event.data?.node?.type}`);\n            }\n        }\n        \n        // Queue size management with immediate flushing\n        if (this.eventQueue.length >= this.MAX_QUEUE_SIZE) {\n            // Drop oldest event when queue is full\n            this.eventQueue.shift();\n            logDebug('Queue is full, the oldest event is dropped.');\n        }\n        \n        this.eventQueue.push(event); // Direct event handling\n        \n        // Immediate flush for FullSnapshots (important events)\n        if (event.type === 2) { // FullSnapshot\n            logDebug('FullSnapshot added, triggering immediate flush');\n            this.flushEvents();\n        }\n        // Immediate flush if queue is getting large\n        else if (this.eventQueue.length >= this.MAX_QUEUE_SIZE * 0.8) {\n            logDebug(`Queue at ${this.eventQueue.length}/${this.MAX_QUEUE_SIZE}, triggering immediate flush`);\n            this.flushEvents();\n        }\n    }\n\n    /**\n     * Calculate session duration\n     * Uses most recent event timestamp minus session start timestamp\n     */\n    private getSessionDuration(): number | null {\n        // Use session start timestamp if available, otherwise fall back to sessionStartTime\n        const sessionStart = this._sessionStartTimestamp ?? this.sessionStartTime;\n        if (!sessionStart) {\n            return null;\n        }\n        \n        // Get most recent event timestamp from queue\n        const eventsWithTimestamps = this.eventQueue.filter((e: any) => e && e.timestamp);\n        if (eventsWithTimestamps.length === 0) {\n            return null;\n        }\n        \n        const mostRecentEvent = eventsWithTimestamps.reduce((latest: any, current: any) => {\n            return (!latest || (current.timestamp && current.timestamp > latest.timestamp)) ? current : latest;\n        }, null);\n        \n        if (!mostRecentEvent || !mostRecentEvent.timestamp) {\n            return null;\n        }\n        \n        // Calculate duration (both timestamps should be in milliseconds)\n        const duration = mostRecentEvent.timestamp - sessionStart;\n        return duration >= 0 ? duration : null;\n    }\n\n    /**\n     * Check if session duration is below minimum threshold\n     * Returns true if we should skip sending (session too short)\n     */\n    private shouldSkipDueToMinimumDuration(): boolean {\n        const minimumDuration = this.minimumDurationMilliseconds;\n        const sessionDuration = this.getSessionDuration();\n        const isPositiveSessionDuration = sessionDuration !== null && sessionDuration >= 0;\n        const isBelowMinimumDuration =\n            isPositiveSessionDuration &&\n            sessionDuration < minimumDuration;\n\n        if (isBelowMinimumDuration) {\n            logDebug(`Session duration (${sessionDuration}ms) below minimum (${minimumDuration}ms), skipping send`);\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * Flush events to the ingestion server\n     * Events are sent in chunks to handle large payloads efficiently\n     */\n    /**\n     * (Re)start the periodic flush interval at the cadence for the current\n     * tier. Idempotent: safe to call repeatedly; only the interval handle is\n     * recreated.\n     */\n    private armFlushTimer(): void {\n        if (!isBrowser) return;\n        if (this.flushInterval) {\n            clearInterval(this.flushInterval);\n            this.flushInterval = null;\n        }\n        const intervalMs = this.flushTier === 'live'\n            ? this.LIVE_FLUSH_INTERVAL_MS\n            : this.IDLE_FLUSH_INTERVAL_MS;\n        this.flushInterval = window.setInterval(() => {\n            this.flushEvents();\n            // Also drain anything a previous failed flush persisted to storage,\n            // so durable events are delivered in-session rather than waiting for\n            // the next page load. Re-entrant + no-op when the queue is empty.\n            this.api.flushPersistedEvents();\n        }, intervalMs);\n    }\n\n    /**\n     * React to the server's `liveViewerActive` signal (read off the /events\n     * response). A positive signal pushes the live-tier deadline forward and\n     * switches to the fast cadence; once the grace window lapses with no fresh\n     * positive signal, we fall back to the idle cadence. Only re-arms the timer\n     * when the tier actually changes, so this is cheap to call every flush.\n     */\n    private noteLiveViewerSignal(active: boolean): void {\n        const now = Date.now();\n        if (active) {\n            this.liveFlushDeadline = now + this.LIVE_FLUSH_GRACE_MS;\n        }\n        const nextTier: 'idle' | 'live' = now < this.liveFlushDeadline ? 'live' : 'idle';\n        if (nextTier !== this.flushTier) {\n            this.flushTier = nextTier;\n            this.armFlushTimer();\n        }\n    }\n\n    private async flushEvents() {\n        // Prevent concurrent flushes\n        // ✅ NON-BLOCKING: Don't check initialized - events work immediately\n        if (this.isProcessing) {\n            return;\n        }\n\n        // Don't make requests if monthly limit is reached - silently skip\n        if (this.monthlyLimitReached) {\n            return; // Silently skip without logging\n        }\n        \n        // ✅ IDLE STATE: Don't flush when idle and queue is empty (events are being skipped anyway)\n        // But allow flush if we have events queued (e.g., from before going idle or FullSnapshots)\n        if (this._isIdle === true && this.eventQueue.length === 0) {\n            return;\n        }\n        \n        const hasFullSnapshotQueued = this.eventQueue.some(e => e && e.type === 2);\n\n        // ✅ Check minimum duration before flushing (default: 5 seconds).\n        // FullSnapshots must bypass this gate so live replay can render the\n        // page immediately during install verification and first page load.\n        const minimumDuration = this.minimumDurationMilliseconds;\n        const sessionDuration = this.getSessionDuration();\n        const isPositiveSessionDuration = sessionDuration !== null && sessionDuration >= 0;\n        const isBelowMinimumDuration = \n            isPositiveSessionDuration && \n            sessionDuration < minimumDuration;\n        \n        if (isBelowMinimumDuration && !hasFullSnapshotQueued) {\n            // Don't flush - schedule retry\n            logDebug(`Session duration (${sessionDuration}ms) below minimum (${minimumDuration}ms), buffering`);\n            // Schedule retry after buffer timeout (2 seconds)\n            setTimeout(() => {\n                this.flushEvents();\n            }, 2000);\n            return;\n        }\n\n        this.isProcessing = true;\n        try {\n            // ✅ CRITICAL FIX FOR SHORT SESSIONS:\n            // Swap the current queue with an empty one atomically\n            // BUT: Store a reference to FullSnapshots so they can be re-sent on unload if needed\n            const eventsToProcess = this.eventQueue;\n            const fullSnapshotsInFlush = eventsToProcess.filter(e => e && e.type === 2);\n            this.eventQueue = [];\n            \n            // Store FullSnapshots that are being flushed so they can be sent via sendBeacon on unload\n            // if the HTTP request doesn't complete in time (for very short sessions)\n            if (fullSnapshotsInFlush.length > 0 && isBrowser) {\n                // Store in a way that's accessible during unload\n                (window as any).__hb_pending_snapshots = fullSnapshotsInFlush;\n                // Clear after a delay (long enough for HTTP to complete, short enough to not waste memory)\n                setTimeout(() => {\n                    delete (window as any).__hb_pending_snapshots;\n                }, 5000); // 5 seconds should be enough for HTTP to complete\n            }\n\n            if (eventsToProcess.length > 0) {\n                logDebug('Flushing events:', eventsToProcess);\n                \n                // ✅ LOG FULLSNAPSHOT STATUS FOR MONITORING\n                const fullSnapshots = eventsToProcess.filter(e => e.type === 2);\n                if (fullSnapshots.length > 0) {\n                    logDebug(`[FIXED] Sending ${fullSnapshots.length} FullSnapshot(s) with valid data`);\n                }\n                \n                try {\n                    // ✅ Include all IDs in payload (endUserId, sessionId, windowId)\n                    // ✅ Include automatic properties for user creation on first event\n                    // Server will create user/session on first event if needed\n                    const automaticProperties = this.propertyManager.getAutomaticProperties();\n                    const sendResults = await this.api.sendEventsChunked(\n                        eventsToProcess, \n                        this.sessionId, \n                        this.endUserId!,\n                        this.windowId,\n                        automaticProperties\n                    );\n                    // If a dashboard live viewer is watching this project, the\n                    // server flags it on the events response — speed up the\n                    // flush cadence so the live replay stays near real-time.\n                    const liveViewerActive = Array.isArray(sendResults)\n                        && sendResults.some((r) => r && (r as any).liveViewerActive === true);\n                    this.noteLiveViewerSignal(liveViewerActive);\n                } catch (error: any) {\n                    // sendEventsChunked persists the batch to durable storage\n                    // before it throws, so these are queued for retry (drained\n                    // in-session by flushPersistedEvents + on the next load), not\n                    // lost — except a session that the server has already closed,\n                    // which can no longer accept them.\n                    const msg = String(error?.message ?? error ?? '');\n                    if (msg.includes('ERROR: Session already completed')) {\n                        logWarn('Session already completed on the server; dropping events for the expired session');\n                    } else if (msg.includes('413') || msg.includes('Content Too Large')) {\n                        logWarn('Payload too large; events persisted for retry');\n                    } else if (\n                        msg.includes('ERR_BLOCKED_BY_CLIENT') ||\n                        msg.includes('Failed to fetch') ||\n                        msg.includes('NetworkError') ||\n                        msg.includes('Failed to send events') ||\n                        msg.includes('Request timeout') ||\n                        error?.name === 'TimeoutError' ||\n                        error?.name === 'AbortError'\n                    ) {\n                        logWarn('Request blocked by ad blocker, timed out, or network issue; events persisted for retry');\n                    } else {\n                        throw error;\n                    }\n                }\n            }\n\n            // ✅ Once we hit 5 seconds, flush all pending events (custom events, logs, network errors)\n            // This ensures everything that happened before 5 seconds gets sent\n            // Flush AFTER eventQueue to prioritize session recording events\n            await this.flushPendingCustomEvents();\n            await this.flushPendingLogs();\n            await this.flushPendingNetworkErrors();\n        } finally {\n            this.isProcessing = false;\n        }\n    }\n\n    /**\n     * Check if an event represents user interaction (not background DOM mutations)\n     */\n    private isInteractiveEvent(event: any): boolean {\n        // Event type 3 = IncrementalSnapshot\n        if (event.type !== 3) {\n            return false;\n        }\n        \n        // Active sources that indicate user interaction\n        // Source values from rrweb: 0=DomContentLoaded, 1=MouseMove, 2=MouseInteraction, 3=Scroll, \n        // 4=ViewportResize, 5=Input, 6=MediaInteraction, 7=StyleSheetRule, 8=CanvasMutation, \n        // 9=Font, 10=Log, 11=Drag, 12=StyleDeclaration, 13=Selection, 14=AdoptedStyleSheet, 15=Mutation\n        const ACTIVE_SOURCES = [1, 2, 3, 4, 5, 6, 11]; // MouseMove, MouseInteraction, Scroll, ViewportResize, Input, MediaInteraction, Drag\n        \n        const source = event.data?.source;\n        return ACTIVE_SOURCES.includes(source);\n    }\n    \n    /**\n     * Update idle state based on event activity\n     * Also updates session activity timestamp to prevent premature session expiration\n     */\n    private updateIdleState(event: any): void {\n        const isUserInteraction = this.isInteractiveEvent(event);\n        const currentTime = event.timestamp || Date.now();\n        \n        // Update activity timestamp on user interaction\n        if (isUserInteraction) {\n            const wasIdle = this._isIdle === true;\n            this._lastActivityTimestamp = currentTime;\n            \n            // ✅ CRITICAL: Also update session activity timestamp to prevent session expiration\n            // This ensures the SESSION_IDLE_TIMEOUT_MS session timeout is extended on user interaction\n            // (checkAndRefreshSession will handle persistence, but we update memory here)\n            if (this._sessionActivityTimestamp !== null) {\n                this._sessionActivityTimestamp = currentTime;\n            }\n            \n            // If we were idle and user interacts, exit idle state\n            if (wasIdle) {\n                logDebug('✅ User activity detected, exiting idle state');\n                this._isIdle = false;\n                \n                // Take full snapshot when returning from idle to capture current state\n                if (this.rrwebRecord && typeof this.rrwebRecord.takeFullSnapshot === 'function') {\n                    this.rrwebRecord.takeFullSnapshot();\n                    logDebug('✅ FullSnapshot taken after returning from idle');\n                }\n            } else if (this._isIdle === 'unknown') {\n                // First interaction, mark as active\n                this._isIdle = false;\n            }\n        } else if (this._isIdle !== true) {\n            // Check if we should go idle (no user interaction for threshold time)\n            // Note: This uses 5-minute threshold for idle detection (stops recording)\n            // Session timeout uses SESSION_IDLE_TIMEOUT_MS (ends session completely)\n            const timeSinceLastActivity = currentTime - this._lastActivityTimestamp;\n            if (timeSinceLastActivity > this.IDLE_THRESHOLD_MS) {\n                logDebug(`⏸️ Session idle detected (${Math.round(timeSinceLastActivity / 1000)}s since last activity) - stopping background event recording`);\n                logDebug(`ℹ️ Session will expire after ${Math.round(this.SESSION_IDLE_TIMEOUT_MS / 60000)} minutes of inactivity (${Math.round((this.SESSION_IDLE_TIMEOUT_MS - timeSinceLastActivity) / 1000)}s remaining)`);\n                this._isIdle = true;\n                \n                // Flush buffer when going idle to save what we have\n                this.flushEvents();\n            }\n        }\n    }\n    \n    /**\n     * Add an event to the session recording queue\n     * These are typically FullSnapshots or IncrementalSnapshots\n     */\n    public async addRecordingEvent(event: any) {\n        // ✅ NON-BLOCKING: Recording events work immediately\n        // endUserId and sessionId are already available locally\n        \n        // ✅ CHECK SESSION TIMEOUT before adding event (creates new session if expired)\n        if (isBrowser) {\n            this.checkAndRefreshSession();\n        }\n        \n        // ✅ DIRECT EVENT HANDLING - No custom processing to avoid corruption\n        // Events flow directly from rrweb to ingestion server\n        \n        // ✅ EVENT VALIDATION\n        if (!event || typeof event !== 'object') {\n            logDebug('⚠️ Skipping invalid recording event:', event);\n            return;\n        }\n        \n        // ✅ IDLE DETECTION: Update idle state based on event\n        this.updateIdleState(event);\n        \n        // ✅ IDLE STATE: Skip non-interactive events when idle (save bandwidth)\n        // Always record FullSnapshots and user interactions, but skip background mutations\n        if (this._isIdle === true && event.type === 3 && !this.isInteractiveEvent(event)) {\n            // Skip background DOM mutations while idle\n            return;\n        }\n        \n        // ✅ LOG FULLSNAPSHOT STATUS FOR DEBUGGING\n        if (event.type === 2) { // FullSnapshot\n            const hasData = !!event.data;\n            const hasNode = !!(event.data && event.data.node);\n            \n            if (!hasData || !hasNode) {\n                logDebug(`⚠️ Empty FullSnapshot detected: hasData=${hasData}, hasNode=${hasNode} - continuing session`);\n            } else {\n                logDebug(`✅ Valid FullSnapshot: hasData=${hasData}, hasNode=${hasNode}, dataType=${event.data?.node?.type}`);\n            }\n        }\n        \n        // Use the same unified queue for all events\n        // Queue size management with immediate flushing\n        if (this.eventQueue.length >= this.MAX_QUEUE_SIZE) {\n            // Drop oldest event when queue is full\n            this.eventQueue.shift();\n            logDebug('Queue is full, the oldest event is dropped.');\n        }\n        \n        this.eventQueue.push(event); // Direct event handling\n        \n        // Immediate flush for FullSnapshots (important events)\n        if (event.type === 2) { // FullSnapshot\n            logDebug('FullSnapshot added, triggering immediate flush');\n            this.flushEvents();\n        }\n        // Immediate flush if queue is getting large (but not when idle)\n        else if (this._isIdle !== true && this.eventQueue.length >= this.MAX_QUEUE_SIZE * 0.8) {\n            logDebug(`Queue at ${this.eventQueue.length}/${this.MAX_QUEUE_SIZE}, triggering immediate flush`);\n            this.flushEvents();\n        }\n    }\n\n\n\n    /**\n     * Check if sessionStorage is available and can be used\n     */\n    private _canUseSessionStorage(): boolean {\n        if (!isBrowser) return false;\n        try {\n            const test = '__sessionStorage_test__';\n            sessionStorage.setItem(test, test);\n            sessionStorage.removeItem(test);\n            return true;\n        } catch {\n            return false;\n        }\n    }\n\n    private _canUseLocalStorage(): boolean {\n        if (!isBrowser) return false;\n        try {\n            const test = '__localStorage_test__';\n            localStorage.setItem(test, test);\n            localStorage.removeItem(test);\n            return true;\n        } catch {\n            return false;\n        }\n    }\n\n    /**\n     * Get windowId from sessionStorage\n     * SessionStorage persists across page reloads but is unique per window/tab\n     */\n    private _getWindowIdFromStorage(): string | null {\n        if (!this._canUseSessionStorage()) {\n            return null;\n        }\n        try {\n            return sessionStorage.getItem(this._window_id_storage_key);\n        } catch {\n            return null;\n        }\n    }\n\n    /**\n     * Set windowId in sessionStorage\n     */\n    private _setWindowIdInStorage(windowId: string): void {\n        if (!this._canUseSessionStorage()) {\n            return;\n        }\n        try {\n            sessionStorage.setItem(this._window_id_storage_key, windowId);\n            logDebug(`Stored windowId in sessionStorage: ${windowId}`);\n        } catch (error) {\n            logWarn('Failed to store windowId in sessionStorage:', error);\n        }\n    }\n\n    /**\n     * Remove windowId from sessionStorage\n     */\n    private _removeWindowIdFromStorage(): void {\n        if (!this._canUseSessionStorage()) {\n            return;\n        }\n        try {\n            sessionStorage.removeItem(this._window_id_storage_key);\n        } catch (error) {\n            logWarn('Failed to remove windowId from sessionStorage:', error);\n        }\n    }\n\n    /**\n     * Check if primary_window_exists flag is set in sessionStorage\n     * This flag indicates if a window was opened as a new tab/window (not a reload)\n     */\n    private _getPrimaryWindowExists(): boolean {\n        if (!this._canUseSessionStorage()) {\n            return false;\n        }\n        try {\n            return sessionStorage.getItem(this._primary_window_exists_storage_key) === 'true';\n        } catch {\n            return false;\n        }\n    }\n\n    /**\n     * Set primary_window_exists flag in sessionStorage\n     * This flag is set when DOM loads and cleared on beforeunload\n     */\n    private _setPrimaryWindowExists(value: boolean): void {\n        if (!this._canUseSessionStorage()) {\n            return;\n        }\n        try {\n            if (value) {\n                sessionStorage.setItem(this._primary_window_exists_storage_key, 'true');\n            } else {\n                sessionStorage.removeItem(this._primary_window_exists_storage_key);\n            }\n        } catch (error) {\n            logWarn('Failed to set primary_window_exists flag:', error);\n        }\n    }\n\n    /**\n     * Get or create windowId with multi-window detection\n     * - Reuses windowId on page reload (same tab)\n     * - Creates new windowId for new windows/tabs\n     */\n    private getOrCreateWindowId(): string {\n        if (!isBrowser) {\n            return uuidv1();\n        }\n\n        const lastWindowId = this._getWindowIdFromStorage();\n        const primaryWindowExists = this._getPrimaryWindowExists();\n\n        if (lastWindowId && !primaryWindowExists) {\n            // Page reload: primary_window_exists was cleared on beforeunload\n            // Reuse the windowId from sessionStorage\n            logDebug(`Reusing windowId from previous page load: ${lastWindowId}`);\n            this._setWindowIdInStorage(lastWindowId);\n            this._setPrimaryWindowExists(true);\n            return lastWindowId;\n        } else {\n            // New window/tab: primary_window_exists exists (copied from original window)\n            // OR no previous windowId exists\n            // Create a new windowId\n            const newWindowId = uuidv1();\n            logDebug(`Creating new windowId: ${newWindowId} (new window/tab detected)`);\n            this._setWindowIdInStorage(newWindowId);\n            this._setPrimaryWindowExists(true);\n            return newWindowId;\n        }\n    }\n\n    /**\n     * Setup beforeunload listener to clear primary_window_exists flag\n     * This allows us to distinguish page reloads from new windows/tabs\n     */\n    private setupWindowUnloadListener(): void {\n        if (!isBrowser) {\n            return;\n        }\n\n        // Use beforeunload to clear the flag before page unloads\n        window.addEventListener('beforeunload', () => {\n            if (this._canUseSessionStorage()) {\n                this._setPrimaryWindowExists(false);\n                logDebug('Cleared primary_window_exists flag on beforeunload');\n            }\n        }, { capture: false });\n    }\n\n    // Add helper methods for cookie management with localStorage fallback\n    private setCookie(name: string, value: string, daysToExpire: number) {\n        if (!isBrowser) return;\n        \n        try {\n            // Try to set cookie first\n            const date = new Date();\n            date.setTime(date.getTime() + (daysToExpire * 24 * 60 * 60 * 1000));\n            const expires = `expires=${date.toUTCString()}`;\n            document.cookie = `${name}=${value};${expires};path=/;SameSite=Lax`;\n            \n            // Also store in localStorage as backup\n            localStorage.setItem(name, value);\n            logDebug(`Set cookie and localStorage: ${name}`);\n        } catch (error) {\n            // If cookie fails, use localStorage only\n            try {\n                localStorage.setItem(name, value);\n                logDebug(`Cookie blocked, using localStorage: ${name}`);\n            } catch (localStorageError) {\n                logError('Failed to store user ID in both cookie and localStorage:', localStorageError);\n            }\n        }\n    }\n\n    public getCookie(name: string): string | null {\n        if (!isBrowser) return null;\n        \n        try {\n            // Try to get from cookie first\n            const nameEQ = name + \"=\";\n            const ca = document.cookie.split(';');\n            for (let i = 0; i < ca.length; i++) {\n                let c = ca[i];\n                while (c.charAt(0) === ' ') c = c.substring(1, c.length);\n                if (c.indexOf(nameEQ) === 0) {\n                    const cookieValue = c.substring(nameEQ.length, c.length);\n                    logDebug(`Found cookie: ${name}`);\n                    return cookieValue;\n                }\n            }\n            \n            // If cookie not found, try localStorage\n            const localStorageValue = localStorage.getItem(name);\n            if (localStorageValue) {\n                logDebug(`Cookie not found, using localStorage: ${name}`);\n                return localStorageValue;\n            }\n            \n            return null;\n        } catch (error) {\n            // If cookie access fails, try localStorage\n            try {\n                const localStorageValue = localStorage.getItem(name);\n                if (localStorageValue) {\n                    logDebug(`Cookie access failed, using localStorage: ${name}`);\n                    return localStorageValue;\n                }\n            } catch (localStorageError) {\n                logError('Failed to access both cookie and localStorage:', localStorageError);\n            }\n            return null;\n        }\n    }\n\n    /**\n     * Delete a cookie by setting its expiration date to the past\n     * @param name The name of the cookie to delete\n     */\n    private deleteCookie(name: string) {\n        if (!isBrowser) return;\n        \n        try {\n            // Delete cookie by setting expiration to past\n            document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; SameSite=Lax`;\n            logDebug(`Deleted cookie: ${name}`);\n        } catch (error) {\n            logError(`Failed to delete cookie: ${name}`, error);\n        }\n        \n        // Also remove from localStorage\n        try {\n            localStorage.removeItem(name);\n            logDebug(`Removed from localStorage: ${name}`);\n        } catch (error) {\n            logError(`Failed to remove from localStorage: ${name}`, error);\n        }\n    }\n\n    /**\n     * Clear user data and reset session when user signs out of the site\n     * This should be called when a user logs out of your application to prevent\n     * data contamination between different users\n     */\n    public logout(): void {\n        if (!isBrowser) return;\n        \n        try {      \n            // Clear user ID cookie and localStorage\n            const userIdCookieName = `human_behavior_end_user_id`;\n            this.deleteCookie(userIdCookieName);\n            \n            // Clear the shared session blob (localStorage) plus any per-tab\n            // sessionStorage copy from older SDK builds.\n            const sessionKey = `human_behavior_session`;\n            this._clearStoredSession(sessionKey);\n            \n            // Reset user-related properties\n            this.endUserId = null;\n            this.userProperties = {};\n            \n            // Generate new IDs for the next user\n            this.endUserId = uuidv1();\n            this.setCookie(`human_behavior_end_user_id`, this.endUserId, 365);\n            this.sessionId = this.createNewSession(sessionKey);\n            // Create new windowId for new session (logout = new session)\n            this.windowId = uuidv1();\n            this._setWindowIdInStorage(this.windowId);\n            this.api.setTrackingContext(this.sessionId, this.endUserId);\n            // Emit FullSnapshot so the player can replay this new window\n            this.takeFullSnapshot();\n\n            logInfo('User logged out - cleared all user data and started fresh session');\n        } catch (error) {\n            logError('Error during logout:', error);\n        }\n    }\n\n    /**\n     * Start redaction functionality for sensitive input fields\n     * @param options Optional configuration for redaction behavior\n     */\n    public async redact(options?: RedactionOptions): Promise<void> {\n        await this.ensureInitialized();\n        if (!isBrowser) {\n            logWarn('Redaction is only available in browser environments');\n            return;\n        }\n        \n        // Create a new redaction manager with the provided options\n        this.redactionManager = new RedactionManager(options);\n    }\n\n    /**\n     * Set specific fields to be redacted (for visibility-first mode)\n     * @param fields Array of CSS selectors for fields to redact\n     */\n    public setRedactedFields(fields: string[]): void {\n        this.redactionManager.setFieldsToRedact(fields);\n        \n        // ✅ RESTART RECORDING WITH NEW SETTINGS - Ensures redaction is applied\n        if (this.recordInstance) {\n            this.restartWithNewRedaction();\n        }\n    }\n\n    /**\n     * Set specific fields to be unredacted (everything else stays redacted by rrweb)\n     * @param fields Array of CSS selectors for fields to unredact (e.g., ['#username', '#comment'])\n     */\n    public setUnredactedFields(fields: string[]): void {\n        this.redactionManager.setFieldsToUnredact(fields);\n        \n        // ✅ RESTART RECORDING WITH NEW SETTINGS - Ensures unredaction is applied\n        if (this.recordInstance) {\n            this.restartWithNewRedaction();\n        }\n    }\n\n    private restartWithNewRedaction(): void {\n        if (this.recordInstance) {\n            this.recordInstance(); // Stop current recording\n            this.start(); // Restart with new redaction settings\n        }\n    }\n\n    /**\n     * Check if any fields are currently unredacted\n     */\n    public hasUnredactedFields(): boolean {\n        return this.redactionManager.hasUnredactedFields();\n    }\n\n    /**\n     * Get the currently unredacted fields\n     */\n    public getUnredactedFields(): string[] {\n        return this.redactionManager.getUnredactedFields();\n    }\n\n    /**\n     * Remove specific fields from unredaction (they become redacted again)\n     * @param fields Array of CSS selectors for fields to redact\n     */\n    public redactFields(fields: string[]): void {\n        this.redactionManager.redactFields(fields);\n        \n        // ✅ RESTART RECORDING WITH NEW SETTINGS - Ensures redaction is updated\n        if (this.recordInstance) {\n            this.restartWithNewRedaction();\n        }\n    }\n\n    /**\n     * Clear all unredacted fields (everything becomes redacted again)\n     */\n    public clearUnredactedFields(): void {\n        this.redactionManager.clearUnredactedFields();\n        \n        // ✅ RESTART RECORDING WITH NEW SETTINGS - Ensures redaction is updated\n        if (this.recordInstance) {\n            this.restartWithNewRedaction();\n        }\n    }\n\n    /**\n     * Check and refresh session if expired (called before adding events)\n     * Uses in-memory state as source of truth\n     */\n    private checkAndRefreshSession(): void {\n        if (!isBrowser) return;\n        \n        const sessionKey = `human_behavior_session`;\n        const now = Date.now();\n\n        // Get session data (checks memory first, then localStorage)\n        // getStoredSession() now handles session expiration and creation atomically\n        const stored = this.getStoredSession(sessionKey);\n\n        if (!stored || !stored.sessionId) {\n            // No stored session - create new one\n            this.createNewSession(sessionKey);\n            // New session = new windowId\n            this.windowId = uuidv1();\n            this._setWindowIdInStorage(this.windowId);\n            this.api.setTrackingContext(this.sessionId, this.endUserId);\n            // Emit FullSnapshot so the player can replay this new window\n            this.takeFullSnapshot();\n            logDebug(`Created new session (no stored session): ${this.sessionId}`);\n            return;\n        }\n\n        // Session is valid (getStoredSession() already handled expiration)\n        // Update activity timestamp to extend the session\n        this.updateSessionActivity(sessionKey, now, stored.sessionId, stored.sessionStartTimestamp);\n    }\n\n    /**\n     * Get or create session ID with timeout checking\n     * Called once during initialization\n     * Uses in-memory state as source of truth\n     *\n     * Session identity is shared across browser tabs (localStorage), matching\n     * GA/PostHog: a new tab joins the live session and records under its own\n     * `windowId`; the player groups per-window recordings inside the session.\n     */\n    private getOrCreateSessionId(): string {\n        if (!isBrowser) {\n            return uuidv1();\n        }\n\n        const sessionKey = `human_behavior_session`;\n        const now = Date.now();\n\n        // Get session data (checks memory first, then shared localStorage)\n        const stored = this.getStoredSession(sessionKey);\n\n        if (!stored || !stored.sessionId) {\n            const newSessionId = this.createNewSession(sessionKey);\n            this.api.setTrackingContext(newSessionId, this.endUserId);\n            return newSessionId;\n        }\n\n        const timeSinceActivity = now - stored.lastActivityTimestamp;\n        const sessionAge = now - stored.sessionStartTimestamp;\n\n        if (\n            timeSinceActivity > this.SESSION_IDLE_TIMEOUT_MS ||\n            sessionAge > this.SESSION_MAX_LENGTH_MS\n        ) {\n            logDebug(`Session expired: idle=${timeSinceActivity}ms, age=${sessionAge}ms`);\n            const newSessionId = this.createNewSession(sessionKey);\n            this.api.setTrackingContext(newSessionId, this.endUserId);\n            return newSessionId;\n        }\n\n        // Update activity timestamp (extends session)\n        // Memory is already updated by getStoredSession() if it read from storage\n        this.updateSessionActivity(sessionKey, now, stored.sessionId, stored.sessionStartTimestamp);\n        return stored.sessionId;\n    }\n\n    /**\n     * Get session data (check memory first, then tab-scoped sessionStorage)\n     */\n    private getStoredSession(key: string): { sessionId: string; lastActivityTimestamp: number; sessionStartTimestamp: number } | null {\n        const now = Date.now();\n        const SESSION_IDLE_TIMEOUT_MS = this.SESSION_IDLE_TIMEOUT_MS;\n        const SESSION_MAX_LENGTH_MS = this.SESSION_MAX_LENGTH_MS;\n        \n        // Check in-memory state first (source of truth during session)\n        // BUT: Always validate expiration before returning from memory\n        if (this.sessionId && this._sessionActivityTimestamp !== null && this._sessionStartTimestamp !== null) {\n            const timeSinceActivity = now - this._sessionActivityTimestamp;\n            const sessionAge = now - this._sessionStartTimestamp;\n            \n            // If expired, immediately create new session (atomic operation)\n            if (timeSinceActivity > SESSION_IDLE_TIMEOUT_MS || sessionAge > SESSION_MAX_LENGTH_MS) {\n                logDebug(`Session in memory expired: adopting shared session or creating a new one`);\n                const oldSessionId = this.sessionId;\n                const adopted = this._adoptOrCreateSession(key);\n                if (!adopted) {\n                    // New session = new windowId (an adopted session keeps this\n                    // tab's windowId; the FullSnapshot below re-anchors it).\n                    this.windowId = uuidv1();\n                    this._setWindowIdInStorage(this.windowId);\n                }\n                this.api.setTrackingContext(this.sessionId, this.endUserId);\n                // Emit FullSnapshot so the player can replay this new window\n                this.takeFullSnapshot();\n                logInfo(`🔄 Session timeout (memory): Created new session ${this.sessionId} (previous: ${oldSessionId})`);\n                // Return the new session (createNewSession ensures these are not null)\n                if (this._sessionActivityTimestamp !== null && this._sessionStartTimestamp !== null) {\n                    return {\n                        sessionId: this.sessionId,\n                        lastActivityTimestamp: this._sessionActivityTimestamp,\n                        sessionStartTimestamp: this._sessionStartTimestamp\n                    };\n                }\n            } else {\n                // Session in memory is valid\n                return {\n                    sessionId: this.sessionId,\n                    lastActivityTimestamp: this._sessionActivityTimestamp,\n                    sessionStartTimestamp: this._sessionStartTimestamp\n                };\n            }\n        }\n        \n        // Only read from shared storage if memory is empty (init or reload)\n        try {\n            const stored = this._readStoredSessionRaw(key);\n            if (!stored) return null;\n            const parsed = JSON.parse(stored);\n            \n            // Check expiration BEFORE setting in memory\n            const timeSinceActivity = now - parsed.lastActivityTimestamp;\n            const sessionAge = now - parsed.sessionStartTimestamp;\n            \n            if (timeSinceActivity > SESSION_IDLE_TIMEOUT_MS || sessionAge > SESSION_MAX_LENGTH_MS) {\n                // Session expired - immediately create new session (atomic operation)\n                logDebug(`Stored session expired: idle=${Math.round(timeSinceActivity / 1000 / 60)}min, age=${Math.round(sessionAge / 1000 / 60 / 60)}hrs`);\n                const oldSessionId = parsed.sessionId;\n                this.createNewSession(key);\n                // New session = new windowId\n                this.windowId = uuidv1();\n                this._setWindowIdInStorage(this.windowId);\n                this.api.setTrackingContext(this.sessionId, this.endUserId);\n                // Emit FullSnapshot so the player can replay this new window\n                this.takeFullSnapshot();\n                logInfo(`🔄 Session timeout (storage): Created new session ${this.sessionId} (previous: ${oldSessionId})`);\n                // Return the new session (createNewSession ensures these are not null)\n                if (this._sessionActivityTimestamp !== null && this._sessionStartTimestamp !== null) {\n                    return {\n                        sessionId: this.sessionId,\n                        lastActivityTimestamp: this._sessionActivityTimestamp,\n                        sessionStartTimestamp: this._sessionStartTimestamp\n                    };\n                }\n            }\n            \n            // Session is valid - update memory from storage (for next time)\n            if (parsed.sessionId) {\n                this.sessionId = parsed.sessionId;\n                this._sessionActivityTimestamp = parsed.lastActivityTimestamp;\n                this._sessionStartTimestamp = parsed.sessionStartTimestamp;\n            }\n            \n            return parsed;\n        } catch {\n            return null;\n        }\n    }\n\n    /**\n     * Create a new session (update memory first, then persistence)\n     */\n    private createNewSession(key: string): string {\n        const sessionId = uuidv1();\n        const now = Date.now();\n        \n        // Update memory immediately (source of truth)\n        this.sessionId = sessionId;\n        this._sessionActivityTimestamp = now;\n        this._sessionStartTimestamp = now;\n        \n        // ✅ Sync idle detection timestamp with session activity timestamp\n        // This ensures idle detection (5 min) and session timeout (SESSION_IDLE_TIMEOUT_MS) are aligned\n        this._lastActivityTimestamp = now;\n        \n        // Then write to shared persistence\n        const session = {\n            sessionId,\n            lastActivityTimestamp: now,\n            sessionStartTimestamp: now\n        };\n        this._writeStoredSession(key, session);\n        \n        logDebug(`Created new session: ${sessionId}`);\n        return sessionId;\n    }\n\n    /**\n     * Update session activity timestamp (update memory first, then persistence)\n     * Note: This is called by checkAndRefreshSession() for any event, not just user interactions\n     * For user interactions, updateIdleState() also updates this, keeping them in sync\n     */\n    private updateSessionActivity(key: string, timestamp: number, sessionId: string, sessionStartTimestamp: number): void {\n        // Update memory immediately (source of truth)\n        this.sessionId = sessionId;\n        this._sessionActivityTimestamp = timestamp;\n        this._sessionStartTimestamp = sessionStartTimestamp;\n        \n        // ✅ Note: We don't update _lastActivityTimestamp here because:\n        // - updateIdleState() handles it for user interactions\n        // - Non-interactive events shouldn't reset idle detection (5 min threshold)\n        // - Session timeout (SESSION_IDLE_TIMEOUT_MS) is extended by any event via checkAndRefreshSession()\n        \n        // Then write to shared persistence\n        const session = {\n            sessionId,\n            lastActivityTimestamp: timestamp,\n            sessionStartTimestamp\n        };\n        this._writeStoredSession(key, session);\n    }\n\n    /**\n     * If another tab kept (or rotated) the shared session while this tab's\n     * in-memory copy went stale, adopt it instead of minting a competitor.\n     * Returns true when an existing shared session was adopted.\n     */\n    private _adoptOrCreateSession(key: string): boolean {\n        const now = Date.now();\n        try {\n            const raw = this._readStoredSessionRaw(key);\n            if (raw) {\n                const parsed = JSON.parse(raw);\n                if (\n                    isAdoptableSharedSession(\n                        parsed,\n                        this.sessionId,\n                        now,\n                        this.SESSION_IDLE_TIMEOUT_MS,\n                        this.SESSION_MAX_LENGTH_MS,\n                    )\n                ) {\n                    this.sessionId = parsed.sessionId;\n                    this._sessionActivityTimestamp = parsed.lastActivityTimestamp;\n                    this._sessionStartTimestamp = parsed.sessionStartTimestamp;\n                    logDebug(`Adopted shared session from another tab: ${parsed.sessionId}`);\n                    return true;\n                }\n            }\n        } catch {\n            // fall through to a fresh session\n        }\n        this.createNewSession(key);\n        return false;\n    }\n\n    /** Shared session blob (localStorage). Concurrent tabs share one session. */\n    private _writeStoredSession(\n        key: string,\n        session: { sessionId: string; lastActivityTimestamp: number; sessionStartTimestamp: number },\n    ): void {\n        const raw = JSON.stringify(session);\n        try {\n            if (this._canUseLocalStorage()) {\n                localStorage.setItem(key, raw);\n                return;\n            }\n        } catch (e) {\n            logWarn(`Failed to save session to localStorage: ${e}`);\n        }\n        // Fallback for environments without localStorage (session stays per-tab).\n        try {\n            if (this._canUseSessionStorage()) {\n                sessionStorage.setItem(key, raw);\n            }\n        } catch (e) {\n            logWarn(`Failed to save session to sessionStorage: ${e}`);\n        }\n    }\n\n    private _readStoredSessionRaw(key: string): string | null {\n        try {\n            if (this._canUseLocalStorage()) {\n                const shared = localStorage.getItem(key);\n                if (shared) return shared;\n            }\n        } catch {\n            // fall through\n        }\n        // Per-tab copy written by older SDK builds (or the fallback above).\n        try {\n            if (this._canUseSessionStorage()) {\n                const fromTab = sessionStorage.getItem(key);\n                if (fromTab) return fromTab;\n            }\n        } catch {\n            // fall through\n        }\n        return null;\n    }\n\n    private _clearStoredSession(key: string): void {\n        try {\n            if (this._canUseSessionStorage()) {\n                sessionStorage.removeItem(key);\n            }\n        } catch {\n            // ignore\n        }\n        try {\n            localStorage.removeItem(key);\n        } catch {\n            // ignore\n        }\n    }\n\n    /**\n     * Get the current session ID\n     */\n    public getSessionId(): string {\n        return this.sessionId;\n    }\n\n    /**\n     * Get the current URL being tracked\n     */\n    public getCurrentUrl(): string {\n        // Read the live URL rather than the cached `currentUrl` so callers\n        // see the same value `window.location.href` would return — useful\n        // when the SDK's own navigation tracker hasn't fired yet (e.g.\n        // immediately after a programmatic pushState in user code).\n        if (isBrowser && typeof window !== 'undefined' && window.location) {\n            return window.location.href;\n        }\n        return this.currentUrl;\n    }\n\n    /**\n     * Get current snapshot frequency info\n     * Uses configured values (5 minutes, 1000 events)\n     */\n    public getSnapshotFrequencyInfo(): {\n        sessionDuration: number;\n        currentInterval: number;\n        currentThreshold: number;\n        phase: string;\n    } {\n        const sessionDuration = Date.now() - this.sessionStartTime;\n        \n        return {\n            sessionDuration,\n            currentInterval: 300000, // Configured - 5 minutes\n            currentThreshold: 1000,  // Configured - 1000 events\n            phase: 'configured' // Using explicit configuration\n        };\n    }\n\n    /**\n     * Test if the tracker can reach the ingestion server\n     */\n    public async testConnection(): Promise<{ success: boolean; error?: string }> {\n        try {\n            await this.api.init(this.sessionId, this.endUserId);\n            return { success: true };\n        } catch (error: any) {\n            return { \n                success: false, \n                error: error.message || 'Unknown error' \n            };\n        }\n    }\n\n    /**\n     * Get connection status and recommendations\n     */\n    public getConnectionStatus(): { \n        blocked: boolean; \n        recommendations: string[] \n    } {\n        const recommendations: string[] = [];\n        let blocked = false;\n\n        // Check if we have queued events (might indicate blocking)\n        if (this.eventQueue.length > 0) {\n            blocked = true;\n            recommendations.push('Some requests may be blocked by ad blockers');\n        }\n\n        // Check if connection was blocked during initialization\n        if (this._connectionBlocked) {\n            blocked = true;\n            recommendations.push('Initial connection test failed - ad blocker may be active');\n        }\n\n        // Check if we're in a browser environment\n        if (typeof window === 'undefined') {\n            recommendations.push('Not running in browser environment');\n        }\n\n        // Check if navigator.sendBeacon is available\n        if (typeof navigator.sendBeacon === 'undefined') {\n            recommendations.push('sendBeacon not available, using fetch fallback');\n        }\n\n        return { blocked, recommendations };\n    }\n\n    /**\n     * Check if the current user is a preexisting user\n     * Returns true if the user has an existing endUserId cookie from a previous session\n     */\n    public isPreexistingUser(): boolean {\n        if (!isBrowser) {\n            return false;\n        }\n        \n        // Check if there's an existing endUserId cookie for this API key\n        const existingEndUserId = this.getCookie(`human_behavior_end_user_id`);\n        return existingEndUserId !== null && existingEndUserId !== this.endUserId;\n    }\n\n    /**\n     * Get user information including whether they are preexisting\n     */\n    public getUserInfo(): {\n        endUserId: string | null;\n        sessionId: string;\n        isPreexistingUser: boolean;\n        initialized: boolean;\n    } {\n        return {\n            endUserId: this.endUserId,\n            sessionId: this.sessionId,\n            isPreexistingUser: this.isPreexistingUser(),\n            initialized: this.initialized\n        };\n    }\n\n    // ===== PROPERTY MANAGEMENT METHODS =====\n\n    /**\n     * Set a session property that will be included in all events for this session\n     */\n    public setSessionProperty(key: string, value: any): void {\n        this.propertyManager.setSessionProperty(key, value);\n    }\n\n    /**\n     * Set multiple session properties\n     */\n    public setSessionProperties(properties: Record<string, any>): void {\n        this.propertyManager.setSessionProperties(properties);\n    }\n\n    /**\n     * Get a session property\n     */\n    public getSessionProperty(key: string): any {\n        return this.propertyManager.getSessionProperty(key);\n    }\n\n    /**\n     * Remove a session property\n     */\n    public removeSessionProperty(key: string): void {\n        this.propertyManager.removeSessionProperty(key);\n    }\n\n    /**\n     * Set a user property that will be included in all events\n     */\n    public setUserProperty(key: string, value: any): void {\n        this.propertyManager.setUserProperty(key, value);\n    }\n\n    /**\n     * Set multiple user properties\n     */\n    public setUserProperties(properties: Record<string, any>): void {\n        this.propertyManager.setUserProperties(properties);\n    }\n\n    /**\n     * Get a user property\n     */\n    public getUserProperty(key: string): any {\n        return this.propertyManager.getUserProperty(key);\n    }\n\n    /**\n     * Remove a user property\n     */\n    public removeUserProperty(key: string): void {\n        this.propertyManager.removeUserProperty(key);\n    }\n\n    /**\n     * Set a property only if it hasn't been set before\n     */\n    public setOnce(key: string, value: any, scope: 'session' | 'user' = 'user'): void {\n        this.propertyManager.setOnce(key, value, scope);\n    }\n\n    /**\n     * Clear all session properties\n     */\n    public clearSessionProperties(): void {\n        this.propertyManager.clearSessionProperties();\n    }\n\n    /**\n     * Clear all user properties\n     */\n    public clearUserProperties(): void {\n        this.propertyManager.clearUserProperties();\n    }\n\n    /**\n     * Get all properties for debugging\n     */\n    public getAllProperties(): {\n        automatic: Record<string, any>;\n        session: Record<string, any>;\n        user: Record<string, any>;\n        initial: Record<string, any>;\n    } {\n        return this.propertyManager.getAllProperties();\n    }\n}\n\n// Only expose to window object in browser environments\nif (isBrowser) {\n    (window as any).HumanBehaviorTracker = HumanBehaviorTracker;\n}\n\nexport default HumanBehaviorTracker;\n","export interface StoredSession {\n    sessionId: string;\n    lastActivityTimestamp: number;\n    sessionStartTimestamp: number;\n}\n\n/**\n * Session identity is shared across browser tabs via a localStorage blob\n * (matching GA/PostHog). When a tab's in-memory session goes stale, it must\n * adopt the shared session another tab kept alive instead of minting a\n * competitor. A stored session is adoptable when it is a different, still\n * live (not idle-expired, not over max length) session.\n */\nexport function isAdoptableSharedSession(\n    stored: Partial<StoredSession> | null | undefined,\n    currentSessionId: string | undefined,\n    now: number,\n    idleTimeoutMs: number,\n    maxLengthMs: number,\n): stored is StoredSession {\n    return Boolean(\n        stored &&\n        stored.sessionId &&\n        stored.sessionId !== currentSessionId &&\n        typeof stored.lastActivityTimestamp === 'number' &&\n        typeof stored.sessionStartTimestamp === 'number' &&\n        now - stored.lastActivityTimestamp <= idleTimeoutMs &&\n        now - stored.sessionStartTimestamp <= maxLengthMs,\n    );\n}\n","/**\n * Global tracker utility functions\n * Provides helper functions for accessing the global HumanBehavior tracker instance\n */\n\n/**\n * Identifies a user using the global HumanBehavior tracker\n * @param userProperties - User properties to identify with\n * @param identityToken - Optional token minted by your backend attesting this user\n * @returns Promise<string> - The endUserId if successful, null if tracker not found\n */\nexport function identifyUserGlobally(\n  userProperties: Record<string, any>,\n  identityToken?: string\n): Promise<string> | null {\n  const globalTracker = (globalThis as any).__humanBehaviorGlobalTracker;\n  \n  if (globalTracker?.identifyUser) {\n    return globalTracker.identifyUser({ userProperties, identityToken });\n  } else {\n    console.warn('HumanBehavior tracker not found. Make sure the SDK is initialized.');\n    return null;\n  }\n}\n\n/**\n * Sends an event using the global HumanBehavior tracker.\n *\n * Resolves the singleton from window (where the tracker constructor stashes\n * `this`) with a globalThis fallback so this still works in non-DOM\n * environments. Calls `customEvent`, which is the SDK's public API for\n * named events. (Pre-0.7 there was an alias `track`; it was removed when\n * we unified on `customEvent`.)\n */\nexport function sendEventGlobally(eventName: string, properties?: Record<string, any>): Promise<void> | null {\n  const root: any = typeof window !== 'undefined' ? window : globalThis;\n  const globalTracker = root.__humanBehaviorGlobalTracker;\n\n  if (globalTracker?.customEvent) {\n    return globalTracker.customEvent(eventName, properties);\n  }\n  // Back-compat: if a future build re-introduces `track`, prefer it.\n  if (globalTracker?.track) {\n    return globalTracker.track(eventName, properties);\n  }\n  console.warn('HumanBehavior tracker not found. Make sure the SDK is initialized.');\n  return null;\n}\n\n/**\n * Checks if the global HumanBehavior tracker is available\n * @returns boolean - True if tracker is available\n */\nexport function isGlobalTrackerAvailable(): boolean {\n  const globalTracker = (globalThis as any).__humanBehaviorGlobalTracker;\n  return !!(globalTracker?.identifyUser);\n}\n","var e,n,t,r,i,o=-1,a=function(e){addEventListener(\"pageshow\",(function(n){n.persisted&&(o=n.timeStamp,e(n))}),!0)},c=function(){var e=self.performance&&performance.getEntriesByType&&performance.getEntriesByType(\"navigation\")[0];if(e&&e.responseStart>0&&e.responseStart<performance.now())return e},u=function(){var e=c();return e&&e.activationStart||0},f=function(e,n){var t=c(),r=\"navigate\";o>=0?r=\"back-forward-cache\":t&&(document.prerendering||u()>0?r=\"prerender\":document.wasDiscarded?r=\"restore\":t.type&&(r=t.type.replace(/_/g,\"-\")));return{name:e,value:void 0===n?-1:n,rating:\"good\",delta:0,entries:[],id:\"v4-\".concat(Date.now(),\"-\").concat(Math.floor(8999999999999*Math.random())+1e12),navigationType:r}},s=function(e,n,t){try{if(PerformanceObserver.supportedEntryTypes.includes(e)){var r=new PerformanceObserver((function(e){Promise.resolve().then((function(){n(e.getEntries())}))}));return r.observe(Object.assign({type:e,buffered:!0},t||{})),r}}catch(e){}},d=function(e,n,t,r){var i,o;return function(a){n.value>=0&&(a||r)&&((o=n.value-(i||0))||void 0===i)&&(i=n.value,n.delta=o,n.rating=function(e,n){return e>n[1]?\"poor\":e>n[0]?\"needs-improvement\":\"good\"}(n.value,t),e(n))}},l=function(e){requestAnimationFrame((function(){return requestAnimationFrame((function(){return e()}))}))},p=function(e){document.addEventListener(\"visibilitychange\",(function(){\"hidden\"===document.visibilityState&&e()}))},v=function(e){var n=!1;return function(){n||(e(),n=!0)}},m=-1,h=function(){return\"hidden\"!==document.visibilityState||document.prerendering?1/0:0},g=function(e){\"hidden\"===document.visibilityState&&m>-1&&(m=\"visibilitychange\"===e.type?e.timeStamp:0,T())},y=function(){addEventListener(\"visibilitychange\",g,!0),addEventListener(\"prerenderingchange\",g,!0)},T=function(){removeEventListener(\"visibilitychange\",g,!0),removeEventListener(\"prerenderingchange\",g,!0)},E=function(){return m<0&&(m=h(),y(),a((function(){setTimeout((function(){m=h(),y()}),0)}))),{get firstHiddenTime(){return m}}},C=function(e){document.prerendering?addEventListener(\"prerenderingchange\",(function(){return e()}),!0):e()},b=[1800,3e3],S=function(e,n){n=n||{},C((function(){var t,r=E(),i=f(\"FCP\"),o=s(\"paint\",(function(e){e.forEach((function(e){\"first-contentful-paint\"===e.name&&(o.disconnect(),e.startTime<r.firstHiddenTime&&(i.value=Math.max(e.startTime-u(),0),i.entries.push(e),t(!0)))}))}));o&&(t=d(e,i,b,n.reportAllChanges),a((function(r){i=f(\"FCP\"),t=d(e,i,b,n.reportAllChanges),l((function(){i.value=performance.now()-r.timeStamp,t(!0)}))})))}))},L=[.1,.25],w=function(e,n){n=n||{},S(v((function(){var t,r=f(\"CLS\",0),i=0,o=[],c=function(e){e.forEach((function(e){if(!e.hadRecentInput){var n=o[0],t=o[o.length-1];i&&e.startTime-t.startTime<1e3&&e.startTime-n.startTime<5e3?(i+=e.value,o.push(e)):(i=e.value,o=[e])}})),i>r.value&&(r.value=i,r.entries=o,t())},u=s(\"layout-shift\",c);u&&(t=d(e,r,L,n.reportAllChanges),p((function(){c(u.takeRecords()),t(!0)})),a((function(){i=0,r=f(\"CLS\",0),t=d(e,r,L,n.reportAllChanges),l((function(){return t()}))})),setTimeout(t,0))})))},A=0,I=1/0,P=0,M=function(e){e.forEach((function(e){e.interactionId&&(I=Math.min(I,e.interactionId),P=Math.max(P,e.interactionId),A=P?(P-I)/7+1:0)}))},k=function(){return e?A:performance.interactionCount||0},F=function(){\"interactionCount\"in performance||e||(e=s(\"event\",M,{type:\"event\",buffered:!0,durationThreshold:0}))},D=[],x=new Map,R=0,B=function(){var e=Math.min(D.length-1,Math.floor((k()-R)/50));return D[e]},H=[],q=function(e){if(H.forEach((function(n){return n(e)})),e.interactionId||\"first-input\"===e.entryType){var n=D[D.length-1],t=x.get(e.interactionId);if(t||D.length<10||e.duration>n.latency){if(t)e.duration>t.latency?(t.entries=[e],t.latency=e.duration):e.duration===t.latency&&e.startTime===t.entries[0].startTime&&t.entries.push(e);else{var r={id:e.interactionId,latency:e.duration,entries:[e]};x.set(r.id,r),D.push(r)}D.sort((function(e,n){return n.latency-e.latency})),D.length>10&&D.splice(10).forEach((function(e){return x.delete(e.id)}))}}},O=function(e){var n=self.requestIdleCallback||self.setTimeout,t=-1;return e=v(e),\"hidden\"===document.visibilityState?e():(t=n(e),p(e)),t},N=[200,500],j=function(e,n){\"PerformanceEventTiming\"in self&&\"interactionId\"in PerformanceEventTiming.prototype&&(n=n||{},C((function(){var t;F();var r,i=f(\"INP\"),o=function(e){O((function(){e.forEach(q);var n=B();n&&n.latency!==i.value&&(i.value=n.latency,i.entries=n.entries,r())}))},c=s(\"event\",o,{durationThreshold:null!==(t=n.durationThreshold)&&void 0!==t?t:40});r=d(e,i,N,n.reportAllChanges),c&&(c.observe({type:\"first-input\",buffered:!0}),p((function(){o(c.takeRecords()),r(!0)})),a((function(){R=k(),D.length=0,x.clear(),i=f(\"INP\"),r=d(e,i,N,n.reportAllChanges)})))})))},_=[2500,4e3],z={},G=function(e,n){n=n||{},C((function(){var t,r=E(),i=f(\"LCP\"),o=function(e){n.reportAllChanges||(e=e.slice(-1)),e.forEach((function(e){e.startTime<r.firstHiddenTime&&(i.value=Math.max(e.startTime-u(),0),i.entries=[e],t())}))},c=s(\"largest-contentful-paint\",o);if(c){t=d(e,i,_,n.reportAllChanges);var m=v((function(){z[i.id]||(o(c.takeRecords()),c.disconnect(),z[i.id]=!0,t(!0))}));[\"keydown\",\"click\"].forEach((function(e){addEventListener(e,(function(){return O(m)}),{once:!0,capture:!0})})),p(m),a((function(r){i=f(\"LCP\"),t=d(e,i,_,n.reportAllChanges),l((function(){i.value=performance.now()-r.timeStamp,z[i.id]=!0,t(!0)}))}))}}))},J=[800,1800],K=function e(n){document.prerendering?C((function(){return e(n)})):\"complete\"!==document.readyState?addEventListener(\"load\",(function(){return e(n)}),!0):setTimeout(n,0)},Q=function(e,n){n=n||{};var t=f(\"TTFB\"),r=d(e,t,J,n.reportAllChanges);K((function(){var i=c();i&&(t.value=Math.max(i.responseStart-u(),0),t.entries=[i],r(!0),a((function(){t=f(\"TTFB\",0),(r=d(e,t,J,n.reportAllChanges))(!0)})))}))},U={passive:!0,capture:!0},V=new Date,W=function(e,i){n||(n=i,t=e,r=new Date,Z(removeEventListener),X())},X=function(){if(t>=0&&t<r-V){var e={entryType:\"first-input\",name:n.type,target:n.target,cancelable:n.cancelable,startTime:n.timeStamp,processingStart:n.timeStamp+t};i.forEach((function(n){n(e)})),i=[]}},Y=function(e){if(e.cancelable){var n=(e.timeStamp>1e12?new Date:performance.now())-e.timeStamp;\"pointerdown\"==e.type?function(e,n){var t=function(){W(e,n),i()},r=function(){i()},i=function(){removeEventListener(\"pointerup\",t,U),removeEventListener(\"pointercancel\",r,U)};addEventListener(\"pointerup\",t,U),addEventListener(\"pointercancel\",r,U)}(n,e):W(n,e)}},Z=function(e){[\"mousedown\",\"keydown\",\"touchstart\",\"pointerdown\"].forEach((function(n){return e(n,Y,U)}))},$=[100,300],ee=function(e,r){r=r||{},C((function(){var o,c=E(),u=f(\"FID\"),l=function(e){e.startTime<c.firstHiddenTime&&(u.value=e.processingStart-e.startTime,u.entries.push(e),o(!0))},m=function(e){e.forEach(l)},h=s(\"first-input\",m);o=d(e,u,$,r.reportAllChanges),h&&(p(v((function(){m(h.takeRecords()),h.disconnect()}))),a((function(){var a;u=f(\"FID\"),o=d(e,u,$,r.reportAllChanges),i=[],t=-1,n=null,Z(addEventListener),a=l,i.push(a),X()})))}))};export{L as CLSThresholds,b as FCPThresholds,$ as FIDThresholds,N as INPThresholds,_ as LCPThresholds,J as TTFBThresholds,w as onCLS,S as onFCP,ee as onFID,j as onINP,G as onLCP,Q as onTTFB};\n"],"names":["MAX_TOTAL_BYTES","unreadableStylesheetHrefs","doc","out","sheets","styleSheets","i","length","sheet","href","startsWith","cssRules","push","CssSnapshotCapture","constructor","emit","fetchImpl","this","captured","Set","bytesSent","rescanTimer","captureFromDocument","capture","clearTimeout","setTimeout","hrefs","fetcher","fetch","bind","globalThis","undefined","has","add","res","mode","credentials","cache","ok","cssText","text","dispose","CSS_URL_RE","isLocalVerifyOrigin","u","URL","protocol","h","hostname","replace","toLowerCase","endsWith","guessContentType","header","fromHeader","split","trim","includes","test","isAllowedContentType","contentType","bytesToBase64","bytes","binary","String","fromCharCode","subarray","btoa","collectCssUrls","cssBaseHref","match","lastIndex","exec","raw","localAssetHrefs","pageHref","seenKey","baseHref","abs","key","absHref","pathname","inner","searchParams","get","delete","origin","toString","assetDedupeKey","imgs","querySelectorAll","el","currentSrc","getAttribute","others","sheetBase","rules","j","styles","textContent","LocalAssetSnapshotCapture","encode","count","buf","Uint8Array","arrayBuffer","headers","bodyBase64","RAGE_WINDOW_MS","DEAD_CLICK_REACTION_MS","INTERACTIVE_TAGS","INTERACTIVE_ROLES","FORM_CONTROL_TAGS","CLIPBOARD_TARGET","SELECTED_STATE_ATTRS","hasInlinePointerCursor","targetKey","node","tag","tagName","id","map","part","join","stableIdKey","testid","name","aria","slice","cls","filter","c","frictionEligible","target","currentUrl","nodeType","resolved","cur","hops","firstElement","role","onclick","interactive","parentElement","resolveInteractive","opensElsewhere","some","attr","alreadySelected","FrictionClickDetector","options","domReactions","softReactions","targets","Map","pendingDead","nextPendingId","now","Date","schedule","fn","ms","unschedule","window","location","onReaction","kind","tsMs","list","splice","onClick","x","y","eligible","state","stateFor","lastClickTs","trackRage","trackDead","reset","pending","values","timer","clear","rageTimer","size","oldest","keys","next","done","value","burst","burstFirstTs","burstLastTs","burstCount","burstOrigin","Number","NEGATIVE_INFINITY","unreactedGestures","unreactedClicks","lastUnreactedTs","firstUnreacted","deadEmitted","set","shift","scheduleRageVerdict","settleRage","sorted","fromTs","toTs","idx","upperBound","hasReactionInRange","clickCount","durationMs","settleDead","hasReactionAfter","occurrences","afterTs","windowMs","lo","hi","mid","HB_BROKEN_ASSET_TAG","parseRgb","input","s","open","indexOf","close","parts","Boolean","r","parseFloat","g","b","a","n","isNaN","channelLuminance","Math","pow","relativeLuminance","contrastRatio","fg","bg","effectiveFg","compositeOver","l1","l2","max","min","directText","nodes","childNodes","resolveBackground","win","cs","getComputedStyle","backgroundImage","backgroundColor","isVisible","visibility","display","opacity","getClientRects","clippedAxis","textOverflow","lineClamp","webkitLineClamp","getPropertyValue","clientWidth","clientHeight","clip","clipPath","isVisuallyHidden","clipsX","overflowX","clipsY","overflowY","scrollWidth","scrollHeight","directTextLineRects","range","createRange","selectNodeContents","rects","k","width","height","textLinesCollide","r1","r2","ix","right","left","iy","bottom","top","inter","smaller","isFloatingOverlay","position","z","parseInt","zIndex","isFinite","ContrastCapture","getId","emitClip","emitOverlap","emitMisalign","emitBrokenAsset","emittedIds","clippedIds","overlapPairs","misalignedIds","brokenAssetUrls","emitCount","scanCount","start","scan","readyState","addEventListener","once","clearInterval","setInterval","root","body","documentElement","all","limit","overlapCandidates","lines","order","floating","sample","axis","color","ratio","round","detectOverlaps","detectMisalignedTables","detectBrokenImages","img","url","src","complete","naturalWidth","rect","getBoundingClientRect","attrW","attrH","w","tables","t","table","rows","colLefts","irregular","cells","children","cell","colspan","rowspan","arr","worst","worstCol","xs","spread","candidates","other","id2","sample2","LogLevel","logger","config","level","ERROR","enableConsole","enableStorage","isBrowser","setConfig","shouldLog","formatMessage","message","args","toISOString","error","formattedMessage","console","logToStorage","warn","WARN","info","INFO","log","debug","DEBUG","logs","JSON","parse","localStorage","getItem","logEntry","timestamp","setItem","stringify","e","getLogs","clearLogs","removeItem","sdkLoggingInProgress","isSDKLogging","logError","logWarn","logInfo","logDebug","RetryQueue","sendRequest","_isPolling","_pollIntervalMs","_queue","_areWeOnline","_sendRequest","navigator","onLine","_flush","retriableRequest","retriesPerformedSoFar","_shouldRetry","_enqueue","callback","statusCode","status","requestOptions","msToNextRetry","rawBackoffTime","minBackoff","cappedBackoffTime","jitter","random","ceil","pickNextRetryDelay","retryAt","logMessage","_poll","_poller","notToFlush","toFlush","item","catch","unload","_sendBeaconRequest","sendBeacon","Blob","type","EventPersistence","apiKey","maxQueueSize","storageKey","getQueue","stored","queue","Array","isArray","setQueue","limitedQueue","code","smallerQueue","floor","clearQueue","addToQueue","event","removeFromQueue","getQueueLength","SDK_VERSION","MAX_CHUNK_SIZE_BYTES","KEEP_ALIVE_THRESHOLD","BEACON_MIME","sendKeepaliveBeacon","blob","method","keepalive","textEncoder","encodedByteLength","TextEncoder","safeJsonStringify","data","_","splitLargeEvent","sessionId","events","simplifiedEvent","largeProperties","forEach","prop","Object","fromEntries","entries","HumanBehaviorAPI","ingestionUrl","monthlyLimitReached","throttledUntil","endUserId","cspBlocked","consecutiveFetchFailures","requestTimeout","currentBatchSize","_isDrainingPersisted","baseUrl","persistence","retryQueue","_sendRequestInternal","flushPersistedEvents","setTrackingContext","checkMonthlyLimit","persistedQueue","queuedEvent","_sendPersistedBatch","isThrottled","validEvents","response","trackedFetch","Authorization","windowId","automaticProperties","sdkVersion","_apply429","responseJson","json","controller","AbortController","timeoutId","abort","estimatedSize","useKeepalive","signal","responseText","clone","headerValue","seconds","retryAfterSeconds","waitSeconds","init","userId","entryURL","referrer","document","Referer","errorText","Error","statusText","sendEvents","sendEventsChunked","results","currentChunk","chunkBytes","envelopeBytes","totalBytes","eventCount","lastEvent","lastEventBytes","eventBytes","wouldExceed","separator","createChunkByteCounter","splitEvents","result","_sendChunkWithRetry","flat","_persistEvents","chunk","batchSize","startIndex","bodyString","sendUserData","userData","identityToken","payload","userAttributes","posthogName","email","detail","then","sendSessionEndBeacon","sendHeartbeatBeacon","sendBeaconEvents","groups","sendCustomEvent","eventName","eventProperties","eventId","sendCustomEventBatch","sendCustomEventBatchBeacon","sendLog","logData","substring","sendNetworkError","errorData","errorType","sendSpans","spans","ctx","sendSpansBeacon","sendError","report","exceptionType","mechanism","sendIpInfo","ipDetectionMethod","requestStartTime","requestId","uuidv1","shouldSkipTracking","shouldSkipNetworkTracking","trackedFetchWithBeaconFallback","requestDuration","duration","timestampMs","classifyHttpError","errorMessage","startTimeMs","spanName","spanStatus","attributes","timeoutError","isCSPViolation","classifyNetworkError","errorName","bodyJson","encodeURIComponent","Response","Headers","parsed","success","syntheticBody","urlObj","baseUrlObj","SENSITIVE_URL_PARAM_NAMES","SENSITIVE_URL_PARAM_PATTERNS","isSensitiveParamName","lower","p","redactParams","search","changed","from","sanitizeUrl","base","fragment","hash","hashParams","URLSearchParams","RedactionManager","redactedText","unredactedFields","redactedFields","redactionMode","excludeSelectors","redactionStrategy","unredactFields","setFieldsToUnredact","defaultMarks","fieldsToRedact","redactFields","setFieldsToRedact","legacyRedactFields","userFields","fields","field","applyRedactionClasses","validFields","isPasswordSelector","applyUnredactionClasses","clearUnredactedFields","removeUnredactionClasses","hasUnredactedFields","getRedactionMode","getUnredactedFields","getMaskTextSelector","selector","elements","element","classList","remove","pattern","getOriginalValue","HTMLInputElement","HTMLTextAreaElement","isElementUnredacted","shouldUnredactElement","matches","detectDeviceType","userAgent","screenWidth","screen","screenHeight","extractDomain","getDeviceInfo","device_type","browser","browser_version","os","os_version","screen_resolution","viewport_size","color_depth","timezone","language","languages","detectBrowser","version","versionNum","detectOS","innerWidth","innerHeight","colorDepth","Intl","DateTimeFormat","resolvedOptions","timeZone","raw_user_agent","getLocationInfo","current_url","title","referrer_domain","initial_referrer","initial_referrer_domain","utmParams","extractUTMParams","initial_host","getAutomaticProperties","getInitialProperties","locationInfo","initial_url","initial_pathname","initial_utm_source","utm_source","initial_utm_medium","utm_medium","initial_utm_campaign","utm_campaign","initial_utm_term","utm_term","initial_utm_content","utm_content","getCurrentPageProperties","PropertyManager","sessionProperties","userProperties","initialProperties","isInitialized","enableAutomaticProperties","enableSessionProperties","enableUserProperties","propertyDenylist","initialize","loadSessionProperties","getEventProperties","properties","assign","setSessionProperty","applyDenylist","getAutomaticPropertiesWithGeoIP","geoIPProperties","saveSessionProperties","setSessionProperties","getSessionProperty","removeSessionProperty","setUserProperty","setUserProperties","getUserProperty","getUserProperties","getSessionProperties","removeUserProperty","setOnce","scope","clearSessionProperties","clearUserProperties","sessionStorage","deniedKey","updateAutomaticProperties","getAllProperties","automatic","session","user","initial","BreadcrumbBuffer","items","crumb","snapshot","ErrorDeduper","shouldReport","previousKey","DEFAULT_IGNORE_ERRORS","stringMatchesSomePattern","patterns","RegExp","isMatchingPattern","getDropReason","ignore","ignoreErrors","disableErrorDefaults","messages","possibleMessages","reason","matched","frames","file","reportUrlForFiltering","stackFrames","denyUrls","allowUrls","CHROME_WITH_FN","CHROME_NO_FN","GECKO","parseChromeLine","line","m","function","column","parseGeckoLine","parseStack","stack","rawLine","frame","isErrorLike","toLinkedException","source","buildFilenameMap","byStackKey","into","stackKey","getDebugIdMapForFrames","full","debugIds","_debugIds","sentryDebugIds","_sentryDebugIds","debugIdKeyCount","sentryDebugIdKeyCount","getFilenameDebugIdMap","mintEventId","crypto","getRandomValues","padStart","describeError","obj","buildErrorReport","described","linkedErrors","seen","visit","parent","cause","errors","child","collectLinkedErrors","linkedFrames","linked","debugIdMap","concat","handled","release","environment","commitSha","dist","componentStack","networkState","requestContext","sessionStartTimestampMs","breadcrumbs","readNetworkState","conn","connection","online","effectiveType","ErrorCapture","opts","installed","deduper","install","onError","thrown","onRejection","onResourceError","captureThirdPartyResourceErrors","isFirstPartyUrl","onCsp","sourceFile","directive","effectiveDirective","violatedDirective","blocked","blockedURI","uninstall","removeEventListener","extra","filters","getContext","f","dedupeKey","send","REDACTED","SENSITIVE_KEY_PATTERNS","isSensitiveKey","redactJsonValue","v","truncate","maxLen","redactBodyString","HYDRATION_PATTERNS","HYDRATION_MINIFIED_CODES","randHex","len","msCrypto","newTraceId","newSpanId","resourceOp","initiatorType","resourceName","pop","Tracing","cfg","traceId","rootSpanId","buffer","flushTimer","resourceObserver","started","originalFetch","installTracePropagation","observeResources","capturePageLoad","startNewTrace","getTraceHeaders","childId","traceparent","startSpan","span","startInactiveSpan","Promise","end","err","setStatus","spanId","parentSpanId","ended","record","setAttribute","op","flush","useBeacon","getSession","stop","disconnect","attrs","performance","nav","getEntriesByType","timeOrigin","fetchStart","endRel","loadEventEnd","domComplete","responseEnd","transferSize","phase","startRel","endRelTime","dur","domainLookupStart","domainLookupEnd","connectStart","connectEnd","requestStart","responseStart","domInteractive","domContentLoadedEventEnd","resources","recordResource","delay","pageLoadFlushDelayMs","PerformanceObserver","entry","getEntries","observe","buffered","shouldSkipUrl","startTime","encodedBodySize","decodedBodySize","self","sameOrigin","skip","trace","nextInit","HumanBehaviorTracker","isTrackerStarted","isStarted","setupDomReadyHandler","onDomReady","interval","isDomReady","requestQueue","request","processRequest","domReadyHandlers","handler","queueRequest","addEvent","identifyUser","trackPageView","registerDomReadyHandler","suppressConsoleErrors","originalConsoleError","apply","originalConsoleWarn","preventDefault","__humanBehaviorGlobalTracker","logLevel","configureLogging","tracker","enableConsoleTracking","enableNetworkTracking","enableErrorTracking","captureRequestBodies","enableWebVitals","enableTracing","minimumDurationMilliseconds","recordCanvas","setUnredactedFields","enableAutomaticTracking","setupAutomaticTracking","automaticTrackingOptions","eventQueue","pendingCustomEvents","pendingLogs","pendingNetworkErrors","customEventBatch","customEventBatchTimer","CUSTOM_EVENT_FLUSH_MS","CUSTOM_EVENT_BATCH_MAX","_sessionActivityTimestamp","_sessionStartTimestamp","isProcessing","flushInterval","IDLE_FLUSH_INTERVAL_MS","LIVE_FLUSH_INTERVAL_MS","LIVE_FLUSH_GRACE_MS","flushTier","liveFlushDeadline","heartbeatInterval","HEARTBEAT_INTERVAL_MS","SESSION_IDLE_TIMEOUT_MS","SESSION_MAX_LENGTH_MS","focusBlurGraceTimeout","FOCUS_BLUR_GRACE_MS","lastEmittedFocusState","initialized","initializationPromise","originalConsole","consoleTrackingEnabled","networkTrackingEnabled","captureRequestBodiesFlag","lastRequestContextAt","ERROR_REQUEST_WINDOW_MS","enableConsoleTrackingFlag","enableNetworkTrackingFlag","enableErrorTrackingFlag","enableWebVitalsFlag","enableTracingFlag","tracing","errorCapture","errorFilterOptions","captureThirdPartyResourceErrorsFlag","navigationTrackingEnabled","previousUrl","originalPushState","originalReplaceState","navigationListeners","lastPushStateAt","NAVIGATION_DEDUPE_MS","_connectionBlocked","recordInstance","sessionStartTime","rrwebRecord","fullSnapshotTimeout","cssSnapshotCapture","localAssetSnapshotCapture","contrastCapture","brokenAssetHandler","brokenAssetSeen","_isIdle","_lastActivityTimestamp","IDLE_THRESHOLD_MS","frictionClicks","frictionMutationObserver","finalIngestionUrl","api","MAX_QUEUE_SIZE","redactionManager","propertyManager","endUserIdKey","existingEndUserId","getCookie","setCookie","persistenceName","_window_id_storage_key","_primary_window_exists_storage_key","getOrCreateSessionId","getOrCreateWindowId","setupWindowUnloadListener","setupPageUnloadHandler","setupNavigationTracking","ensureInitialized","history","pushState","replaceState","trackNavigationEvent","takeFullSnapshot","popstateListener","hashchangeListener","fromUrl","toUrl","pageViewProperties","$navigation_type","navigationType","customEvent","pageViewData","enhancedProperties","eventType","checkAndRefreshSession","label","elementText","addBreadcrumb","page","shouldSkipDueToMinimumDuration","flushPendingCustomEvents","queueCustomEvent","visibilityState","flushCustomEventBatchBeacon","flushCustomEventBatch","batch","sent","drainPendingCustomEventsForTeardown","ev","perEventError","fallbackError","trackButtons","trackLinks","trackForms","includeText","includeClasses","setupAutocapture","setupAutomaticFormTracking","setupFrictionClickDetection","async","closest","clientX","clientY","path","buildDomPath","elementId","className","class","elementClass","depth","segment","unshift","detector","fireFrictionEvent","MutationObserver","characterData","childList","subtree","passive","originalTrackNavigationEvent","setupAutomaticLinkTracking","form","formData","FormData","formId","formAction","action","formMethod","formClass","cleanupNavigationTracking","cleanup","none","trackConsoleEvent","toUpperCase","LONG_LOADING_THRESHOLD_MS","longLoadingTimeoutId","longLoadingTracked","elapsedTime","flushPendingNetworkErrors","lastRequestContext","buildRequestContext","responseBodyText","requestBody","requestHeaders","headersToObject","responseBody","XMLHttpRequest","xhrProto","prototype","originalOpen","originalSend","originalSetRequestHeader","setRequestHeader","rest","__hb","meta","handleXhrComplete","enableWebVitalsTracking","pageLoadId","resolve","webVitals","onFCP","onLCP","onCLS","onINP","onTTFB","metric","$web_vitals_metric","$web_vitals_value","$web_vitals_rating","rating","$web_vitals_id","$web_vitals_navigation_type","$web_vitals_pageload_id","xhr","isNetworkError","redactHeaders","setupErrorCapture","captureException","eventsToFlush","flushPendingLogs","logsToFlush","errorsToFlush","enablePageLoadTracking","trackPageLoad","perfEntry","loadDuration","domContentLoaded","disableConsoleTracking","isSDKStackFrame","consoleData","arg","isHydrationError","sdkPatterns","stackLines","foundNonSDKFrame","flag","__humanBehaviorActiveTracker","emitVisibilityMarker","flushEvents","setupWindowFocusTracking","unloadEvent","sessionEndSent","sendSessionEnd","minimumDuration","sessionDuration","getSessionDuration","eventsToSend","__hb_pending_snapshots","pendingSnapshots","updateActivity","viewLogs","userPropertiesOrArg","wrapped","originalEndUserId","userResponse","actualUserId","wasExistingUser","canonicalEndUserId","cookieName","getUserAttributes","armFlushTimer","startRecording","addRecordingEvent","maskTextSelector","maskTextFn","maskAllInputs","maskInputOptions","password","textarea","number","tel","date","time","month","week","maskInputFn","masked","repeat","HTMLElement","slimDOMOptions","collectFonts","inlineStylesheet","recordCrossOriginIframes","sampling","canvas","dataURLOptions","quality","hooks","querySelector","addCustomEvent","mirror","ASSET_TAGS","checkDomReady","emitFocusMarker","hasFocus","onWindowBlur","onWindowFocus","requestAnimationFrame","hasData","hasNode","sessionStart","eventsWithTimestamps","mostRecentEvent","reduce","latest","current","intervalMs","noteLiveViewerSignal","active","nextTier","hasFullSnapshotQueued","eventsToProcess","fullSnapshotsInFlush","fullSnapshots","sendResults","liveViewerActive","msg","isInteractiveEvent","updateIdleState","isUserInteraction","currentTime","wasIdle","timeSinceLastActivity","_canUseSessionStorage","_canUseLocalStorage","_getWindowIdFromStorage","_setWindowIdInStorage","_removeWindowIdFromStorage","_getPrimaryWindowExists","_setPrimaryWindowExists","lastWindowId","primaryWindowExists","newWindowId","daysToExpire","setTime","getTime","expires","toUTCString","cookie","localStorageError","nameEQ","ca","charAt","cookieValue","localStorageValue","deleteCookie","logout","userIdCookieName","sessionKey","_clearStoredSession","createNewSession","redact","setRedactedFields","restartWithNewRedaction","getStoredSession","updateSessionActivity","sessionStartTimestamp","newSessionId","timeSinceActivity","lastActivityTimestamp","sessionAge","oldSessionId","_adoptOrCreateSession","_readStoredSessionRaw","_writeStoredSession","currentSessionId","idleTimeoutMs","maxLengthMs","isAdoptableSharedSession","shared","fromTab","getSessionId","getCurrentUrl","getSnapshotFrequencyInfo","currentInterval","currentThreshold","testConnection","getConnectionStatus","recommendations","isPreexistingUser","getUserInfo","identifyUserGlobally","globalTracker","sendEventGlobally","track","isGlobalTrackerAvailable","o","persisted","timeStamp","activationStart","prerendering","wasDiscarded","delta","supportedEntryTypes","d","l","T","E","firstHiddenTime","C","S","reportAllChanges","L","A","I","P","M","interactionId","interactionCount","F","durationThreshold","D","R","H","q","entryType","latency","sort","O","requestIdleCallback","N","J","K","hadRecentInput","takeRecords","PerformanceEventTiming","B"],"mappings":"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","x_google_ignoreList":[25]}