flowgrid-sdk 1.7.0 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"flowgrid.min.js","mappings":"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","sources":["webpack://flowgrid/webpack/universalModuleDefinition","webpack://flowgrid/webpack/runtime/load script","webpack://flowgrid/./src/lib/client/transport.ts","webpack://flowgrid/./src/lib/utils/browser.ts","webpack://flowgrid/./src/lib/utils/storage.ts","webpack://flowgrid/webpack/bootstrap","webpack://flowgrid/webpack/runtime/define property getters","webpack://flowgrid/webpack/runtime/ensure chunk","webpack://flowgrid/webpack/runtime/get javascript chunk filename","webpack://flowgrid/webpack/runtime/global","webpack://flowgrid/webpack/runtime/hasOwnProperty shorthand","webpack://flowgrid/webpack/runtime/make namespace object","webpack://flowgrid/webpack/runtime/publicPath","webpack://flowgrid/webpack/runtime/jsonp chunk loading","webpack://flowgrid/./src/lib/utils/identity.ts","webpack://flowgrid/./src/lib/features/core/activation.ts","webpack://flowgrid/./src/lib/features/core/track-feature.ts","webpack://flowgrid/./src/lib/features/core/track-prompt.ts","webpack://flowgrid/./src/lib/features/core/experiment.ts","webpack://flowgrid/./src/lib/features/analytics/page-views.ts","webpack://flowgrid/./src/lib/features/analytics/sessions.ts","webpack://flowgrid/./src/lib/features/analytics/events.ts","webpack://flowgrid/./src/lib/features/analytics/identify.ts","webpack://flowgrid/./src/lib/features/analytics/funnels.ts","webpack://flowgrid/./src/lib/features/analytics/retention.ts","webpack://flowgrid/./src/lib/features/analytics/attribution.ts","webpack://flowgrid/./src/lib/features/analytics/heatmaps.ts","webpack://flowgrid/./src/lib/features/analytics/performance.ts","webpack://flowgrid/./src/lib/features/analytics/replay.ts","webpack://flowgrid/./src/lib/features/ecommerce/products.ts","webpack://flowgrid/./src/lib/features/ecommerce/cart.ts","webpack://flowgrid/./src/lib/features/ecommerce/checkout.ts","webpack://flowgrid/./src/lib/features/ecommerce/purchases.ts","webpack://flowgrid/./src/lib/features/ecommerce/refunds.ts","webpack://flowgrid/./src/lib/features/ecommerce/promotions.ts","webpack://flowgrid/./src/lib/features/ecommerce/wishlist.ts","webpack://flowgrid/./src/lib/features/ecommerce/ltv.ts","webpack://flowgrid/./src/lib/features/ecommerce/search.ts","webpack://flowgrid/./src/lib/features/ecommerce/subscriptions.ts","webpack://flowgrid/./src/lib/tracking/core/activation.ts","webpack://flowgrid/./src/lib/tracking/core/feature.ts","webpack://flowgrid/./src/lib/tracking/core/prompt.ts","webpack://flowgrid/./src/lib/tracking/core/experiment.ts","webpack://flowgrid/./src/lib/tracking/analytics/page-views.ts","webpack://flowgrid/./src/lib/tracking/analytics/sessions.ts","webpack://flowgrid/./src/lib/tracking/analytics/events.ts","webpack://flowgrid/./src/lib/tracking/analytics/identify.ts","webpack://flowgrid/./src/lib/tracking/analytics/funnels.ts","webpack://flowgrid/./src/lib/tracking/analytics/retention.ts","webpack://flowgrid/./src/lib/tracking/analytics/attribution.ts","webpack://flowgrid/./src/lib/tracking/analytics/heatmaps.ts","webpack://flowgrid/./src/lib/tracking/analytics/performance.ts","webpack://flowgrid/./src/lib/tracking/analytics/replay.ts","webpack://flowgrid/./src/lib/tracking/ecommerce/products.ts","webpack://flowgrid/./src/lib/tracking/ecommerce/cart.ts","webpack://flowgrid/./src/lib/tracking/ecommerce/checkout.ts","webpack://flowgrid/./src/lib/tracking/ecommerce/purchases.ts","webpack://flowgrid/./src/lib/tracking/ecommerce/refunds.ts","webpack://flowgrid/./src/lib/tracking/ecommerce/promotions.ts","webpack://flowgrid/./src/lib/tracking/ecommerce/wishlist.ts","webpack://flowgrid/./src/lib/tracking/ecommerce/ltv.ts","webpack://flowgrid/./src/lib/tracking/ecommerce/search.ts","webpack://flowgrid/./src/lib/tracking/ecommerce/subscriptions.ts","webpack://flowgrid/./src/lib/flowgrid.ts","webpack://flowgrid/./src/lib/features/core/declarativeExperiments.ts","webpack://flowgrid/./src/browser.ts"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"flowgrid\"] = factory();\n\telse\n\t\troot[\"flowgrid\"] = factory();\n})(this, () => {\nreturn ","var inProgress = {};\nvar dataWebpackPrefix = \"flowgrid:\";\n// loadScript function to load a script via script tag\n__webpack_require__.l = (url, done, key, chunkId) => {\n\tif(inProgress[url]) { inProgress[url].push(done); return; }\n\tvar script, needAttach;\n\tif(key !== undefined) {\n\t\tvar scripts = document.getElementsByTagName(\"script\");\n\t\tfor(var i = 0; i < scripts.length; i++) {\n\t\t\tvar s = scripts[i];\n\t\t\tif(s.getAttribute(\"src\") == url || s.getAttribute(\"data-webpack\") == dataWebpackPrefix + key) { script = s; break; }\n\t\t}\n\t}\n\tif(!script) {\n\t\tneedAttach = true;\n\t\tscript = document.createElement('script');\n\n\t\tscript.charset = 'utf-8';\n\t\tif (__webpack_require__.nc) {\n\t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n\t\t}\n\t\tscript.setAttribute(\"data-webpack\", dataWebpackPrefix + key);\n\n\t\tscript.src = url;\n\t}\n\tinProgress[url] = [done];\n\tvar onScriptComplete = (prev, event) => {\n\t\t// avoid mem leaks in IE.\n\t\tscript.onerror = script.onload = null;\n\t\tclearTimeout(timeout);\n\t\tvar doneFns = inProgress[url];\n\t\tdelete inProgress[url];\n\t\tscript.parentNode && script.parentNode.removeChild(script);\n\t\tdoneFns && doneFns.forEach((fn) => (fn(event)));\n\t\tif(prev) return prev(event);\n\t}\n\tvar timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000);\n\tscript.onerror = onScriptComplete.bind(null, script.onerror);\n\tscript.onload = onScriptComplete.bind(null, script.onload);\n\tneedAttach && document.head.appendChild(script);\n};","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { IdentityManager } from '../utils/identity';\nimport { getCookie } from '../utils/storage';\n\n// ============================================================================\n// CONSENT\n// ============================================================================\n\nexport type ConsentCategory = 'analytics' | 'marketing';\nexport type BotDetectionMode = 'block' | 'tag' | 'disabled';\n\nexport interface FlowGridTransportConfig {\n /** Bot detection mode: 'block' skips events, 'tag' adds _bot flag, 'disabled' sends all */\n botDetection?: BotDetectionMode;\n /** Respect navigator.doNotTrack / globalPrivacyControl (default true) */\n respectDNT?: boolean;\n /** Global sampling rate 0.0–1.0 (default 1.0 = send everything) */\n sampleRate?: number;\n}\n\n/**\n * SDK Feature Types\n */\nexport type FeatureType = \n | \"engagement\"\n | \"cohorts\"\n | \"churn\"\n | \"monetization\"\n | \"multi_path_funnels\"\n | \"support\"\n | \"acquisition\"\n | \"paths\"\n | \"alerts\"\n | \"security\"\n | \"forecasting\";\n\n/**\n * FlowGridTransport — Unified transport layer for Flowgrid SDK.\n *\n * @description\n * Provides a secure, validated RPC-style transport layer used by all SDK modules.\n * Handles:\n * - Input validation\n * - Payload size enforcement\n * - Timeout handling\n * - Retry logic (network failures only)\n * - Authorization headers\n * - Feature-scoped endpoint routing\n *\n * This class is designed to be extended by higher-level modules\n * (e.g., Analytics, Experiments, Engagement, Cohorts).\n *\n * @example\n * ```ts\n * const client = new FlowGridTransport(\"web_123\", \"https://core.flow-grid.xyz\", \"sk_live_xxx\");\n * ```\n */\nexport class FlowGridTransport {\n\n /**\n * Unique Web ID identifying the site or application.\n * Used by the backend to associate incoming events.\n */\n protected webId: string;\n\n /**\n * Base API endpoint.\n * Example values:\n * - \"core.flow-grid.xyz\"\n * - \"https://core.flow-grid.xyz\"\n *\n * Protocol is normalized automatically.\n */\n protected endpoint: string;\n\n /**\n * Private API key used for authenticating requests.\n * Sent as a Bearer token in the Authorization header.\n */\n protected apiKey: string;\n\n /**\n * Visitor ID for cross-session visitor tracking.\n * Persisted per-visitor (cookie/localStorage) and sent with every event.\n */\n protected visitorId: string;\n\n /**\n * Optional identity manager that owns the visitor/session lifecycle.\n * When provided, visitor_id is resolved at call time (not construction time).\n */\n protected identityManager?: IdentityManager;\n\n /**\n * Maximum allowed payload size (in bytes).\n * Prevents oversized event submissions.\n */\n private static readonly MAX_PAYLOAD_SIZE = 10_000; // 10KB\n\n /**\n * Maximum time (ms) before request is aborted.\n */\n private static readonly TIMEOUT_MS = 10_000;\n\n /**\n * Maximum retry attempts for network-level failures and 5xx server errors.\n */\n private static readonly MAX_RETRIES = 3;\n\n /** Max events to persist in localStorage buffer */\n private static readonly MAX_BUFFER_EVENTS = 100;\n\n /** Max buffer size in bytes */\n private static readonly MAX_BUFFER_SIZE = 50_000; // 50KB\n\n /** localStorage key for failed event buffer */\n private static readonly EVENT_BUFFER_KEY = 'fg_event_buffer';\n\n /** Consent state */\n private static consent: Record<ConsentCategory, boolean> = { analytics: true, marketing: true };\n\n /** Transport config */\n protected transportConfig: FlowGridTransportConfig;\n\n /**\n * Creates a new FlowGridTransport instance.\n *\n * @param webId - Unique identifier for the web property.\n * @param endpoint - API base endpoint.\n * @param apiKey - Private API key for authentication.\n * @param visitorId - Optional visitor ID for cross-session tracking.\n *\n * @throws {Error} If required parameters are missing.\n */\n constructor(webId: string, endpoint: string, apiKey: string, visitorId?: string, identityManager?: IdentityManager, config?: FlowGridTransportConfig) {\n if (!webId?.trim() || !endpoint?.trim()) {\n throw new Error(\"SDK init failed: webId and endpoint are required.\");\n }\n\n this.webId = webId.trim();\n this.endpoint = this.normalizeEndpoint(endpoint);\n this.apiKey = apiKey;\n this.identityManager = identityManager;\n this.visitorId = visitorId ?? '';\n this.transportConfig = {\n botDetection: config?.botDetection ?? 'block',\n respectDNT: config?.respectDNT ?? true,\n sampleRate: config?.sampleRate ?? 1.0,\n };\n\n // Drain buffered events from previous failed attempts\n this.drainEventBuffer();\n }\n\n // ---------------------------------------------------------------------------\n // CONSENT API\n // ---------------------------------------------------------------------------\n\n static setConsent(consent: Partial<Record<ConsentCategory, boolean>>): void {\n Object.assign(FlowGridTransport.consent, consent);\n }\n\n static hasConsent(category: ConsentCategory): boolean {\n return FlowGridTransport.consent[category] ?? false;\n }\n\n // ---------------------------------------------------------------------------\n // BOT DETECTION\n // ---------------------------------------------------------------------------\n\n private static isLikelyBot(): boolean {\n if (typeof navigator === 'undefined') return false;\n if ((navigator as any).webdriver) return true;\n if (/bot|crawl|spider|headless|phantom|puppeteer/i.test(navigator.userAgent)) return true;\n return false;\n }\n\n // ---------------------------------------------------------------------------\n // DNT / GPC CHECK\n // ---------------------------------------------------------------------------\n\n private isDNTEnabled(): boolean {\n if (typeof navigator === 'undefined') return false;\n if ((navigator as any).globalPrivacyControl) return true;\n if (navigator.doNotTrack === '1') return true;\n return false;\n }\n\n // ---------------------------------------------------------------------------\n // DEVICE ENRICHMENT\n // ---------------------------------------------------------------------------\n\n private static getDeviceContext(): Record<string, unknown> {\n if (typeof window === 'undefined' || typeof navigator === 'undefined') return {};\n const loc = window.location;\n const doc = typeof document !== 'undefined' ? document : undefined;\n return {\n _screen_width: window.screen?.width,\n _screen_height: window.screen?.height,\n _device_type: /Mobi|Android/i.test(navigator.userAgent) ? 'mobile' : 'desktop',\n _language: navigator.language,\n // Page context — auto-attached so every event (custom or named) records\n // which page it fired on, enabling per-page breakdowns downstream.\n _url: loc?.href,\n _page: loc?.pathname,\n _referrer: doc?.referrer || undefined,\n _title: doc?.title || undefined,\n // Marketing attribution — auto-captured so EVERY event (feature_usage\n // included) carries the campaign that drove it, with zero customer config.\n // Lets the dashboard segment usage/adoption by source/campaign\n // (Mixpanel-parity). Only present keys are attached.\n ...FlowGridTransport.getUtmContext(),\n };\n }\n\n /**\n * The attribution params FlowGrid tracks — kept in lockstep with\n * flowgrid-core's `public/flowgrid.js` (`captureFromQueryOrCookie`) and the\n * `getUTM()` reader in `utils/storage`. Five UTMs plus `ref`/`via`/`source`.\n */\n private static readonly ATTRIBUTION_KEYS = [\n 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'ref', 'via', 'source',\n ] as const;\n\n /**\n * Resolves marketing attribution into `_utm_source`/`_ref`/… properties.\n *\n * Prefers a fresh value from the current URL (the live campaign), falling back\n * to the `__flowgrid_<name>` cookie that `flowgrid.js` persists on the landing\n * page — so events fired on *later* pages (where the URL no longer carries\n * UTMs) still attribute to the campaign that brought the visitor in. Same\n * cookie names + key set as the loader script. Returns `{}` for non-campaign\n * traffic so the payload stays clean.\n */\n private static getUtmContext(): Record<string, string> {\n if (typeof window === 'undefined') return {};\n let params: URLSearchParams | null = null;\n try {\n params = new URLSearchParams(window.location?.search ?? '');\n } catch {\n params = null;\n }\n const out: Record<string, string> = {};\n for (const name of FlowGridTransport.ATTRIBUTION_KEYS) {\n const value = params?.get(name) || getCookie(`__flowgrid_${name}`);\n if (value) out[`_${name}`] = value;\n }\n return out;\n }\n\n // ---------------------------------------------------------------------------\n // SENDBACON FALLBACK\n // ---------------------------------------------------------------------------\n\n /**\n * Fire-and-forget event via sendBeacon. Used for page unload events.\n */\n sendViaBeacon(eventName: string, properties: Record<string, any> = {}): boolean {\n if (typeof navigator === 'undefined' || typeof navigator.sendBeacon !== 'function') return false;\n\n const resolvedVisitorId = this.identityManager\n ? this.identityManager.getVisitorId()\n : (this.visitorId ?? '');\n\n const callType = properties.type || 'event';\n const url = `${this.endpoint}/api/v1/sdk/functions?call=${encodeURIComponent(callType)}`;\n const payload = JSON.stringify({\n web_id: this.webId,\n visitor_id: resolvedVisitorId,\n event_name: eventName,\n properties: { ...properties, ...FlowGridTransport.getDeviceContext() },\n });\n\n return navigator.sendBeacon(url, new Blob([payload], { type: 'application/json' }));\n }\n\n // ---------------------------------------------------------------------------\n // RAW LARGE-PAYLOAD POST (session replay chunks, etc.)\n // ---------------------------------------------------------------------------\n\n /**\n * POST a large opaque payload to a dedicated SDK endpoint, bypassing the\n * 10KB validator. Used by session replay (`call=replay_chunk`) where each\n * batch of rrweb events can be hundreds of kilobytes.\n *\n * Falls back to `navigator.sendBeacon` on `pagehide` / final flush.\n */\n protected async postRaw(\n callType: string,\n body: Record<string, unknown>,\n options: { useBeacon?: boolean } = {},\n ): Promise<void> {\n if (FlowGridTransport.consent.analytics === false) return;\n if (this.transportConfig.respectDNT !== false && this.isDNTEnabled()) return;\n\n const url = `${this.endpoint}/api/v1/sdk/functions?call=${encodeURIComponent(callType)}`;\n const resolvedVisitorId = this.identityManager\n ? this.identityManager.getVisitorId()\n : (this.visitorId ?? '');\n\n const payload = JSON.stringify({\n web_id: this.webId,\n visitor_id: resolvedVisitorId,\n ...body,\n });\n\n // Transport strategy:\n // - Regular flushes use plain fetch (no keepalive). `fetch + keepalive`\n // has a hard 64 KB request-body limit per the spec — anything larger\n // (notably the rrweb FullSnapshot chunk, which is typically 100KB-500KB+)\n // is silently rejected by the browser, dropping sequence 0 and leaving\n // the player with no DOM to render.\n // - Unload flushes (visibilitychange/pagehide/stop with `useBeacon: true`)\n // MUST use keepalive so the request survives navigation. If the payload\n // is also too large for keepalive, fall back to navigator.sendBeacon —\n // it can't set Authorization headers, so the server's body-auth fallback\n // (api_key in the JSON body) picks it up via the proxy middleware.\n const KEEPALIVE_BYTE_LIMIT = 60_000; // a hair under the 64 KiB spec cap\n const isUnload = options.useBeacon === true;\n const payloadSize = payload.length;\n const useKeepalive = isUnload && payloadSize <= KEEPALIVE_BYTE_LIMIT;\n\n if (isUnload && payloadSize > KEEPALIVE_BYTE_LIMIT) {\n // Too large for keepalive; sendBeacon survives unload but strips headers.\n // Inject api_key into the body so the proxy middleware's body-auth fallback\n // can authenticate the request without an Authorization header.\n const beaconBody = JSON.stringify({ ...JSON.parse(payload), api_key: this.apiKey });\n try {\n const ok = typeof navigator !== 'undefined'\n && typeof navigator.sendBeacon === 'function'\n && navigator.sendBeacon(url, new Blob([beaconBody], { type: 'application/json' }));\n if (!ok && typeof console !== 'undefined') {\n console.warn(`[Flowgrid] ${callType} beacon rejected (payload ${payloadSize} bytes)`);\n }\n } catch (err) {\n if (typeof console !== 'undefined') {\n console.warn(`[Flowgrid] ${callType} beacon error:`, err);\n }\n }\n return;\n }\n\n try {\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n },\n body: payload,\n ...(useKeepalive ? { keepalive: true } : {}),\n });\n // Log server rejections so developers can diagnose silently-dropped chunks\n // (e.g. invalid API key → 403, missing fields → 400, chunk too large → 413).\n if (!res.ok && typeof console !== 'undefined') {\n console.warn(`[Flowgrid] ${callType} rejected: ${res.status} ${res.statusText} (${payloadSize} bytes)`);\n }\n } catch (err) {\n // Best-effort, but surface the failure so silently-dropped chunks are\n // visible during development.\n if (typeof console !== 'undefined') {\n console.warn(`[Flowgrid] ${callType} network error (${payloadSize} bytes):`, err);\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // EVENT BUFFER (localStorage persistence)\n // ---------------------------------------------------------------------------\n\n private bufferEvent(url: string, method: string, body?: string): void {\n if (typeof localStorage === 'undefined') return;\n try {\n const raw = localStorage.getItem(FlowGridTransport.EVENT_BUFFER_KEY);\n const buffer: Array<{ url: string; method: string; body?: string }> = raw ? JSON.parse(raw) : [];\n\n if (buffer.length >= FlowGridTransport.MAX_BUFFER_EVENTS) return;\n\n const serialized = JSON.stringify([...buffer, { url, method, body }]);\n if (new TextEncoder().encode(serialized).length > FlowGridTransport.MAX_BUFFER_SIZE) return;\n\n buffer.push({ url, method, body });\n localStorage.setItem(FlowGridTransport.EVENT_BUFFER_KEY, JSON.stringify(buffer));\n } catch { /* storage unavailable */ }\n }\n\n private drainEventBuffer(): void {\n if (typeof localStorage === 'undefined') return;\n try {\n const raw = localStorage.getItem(FlowGridTransport.EVENT_BUFFER_KEY);\n if (!raw) return;\n const buffer: Array<{ url: string; method: string; body?: string }> = JSON.parse(raw);\n localStorage.removeItem(FlowGridTransport.EVENT_BUFFER_KEY);\n\n for (const evt of buffer) {\n // Best-effort replay; don't await\n fetch(evt.url, {\n method: evt.method,\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n },\n body: evt.body,\n }).catch(() => {});\n }\n } catch { /* storage unavailable */ }\n }\n\n /**\n * Core RPC transport method for standard SDK calls.\n *\n * @template T Expected JSON response type.\n *\n * @param eventName - Logical event or function name.\n * @param properties - Arbitrary event payload properties.\n * @param method - HTTP method (\"GET\", \"POST\", \"PATCH\").\n *\n * @returns Promise resolving to typed response.\n */\n protected async init<T>(\n eventName: string,\n properties: Record<string, any> = {},\n method: \"GET\" | \"POST\" | \"PATCH\" = \"POST\"\n ): Promise<T> {\n\n // Consent gate\n if (!FlowGridTransport.hasConsent('analytics')) {\n return {} as T;\n }\n\n // DNT / GPC gate\n if (this.transportConfig.respectDNT && this.isDNTEnabled()) {\n return {} as T;\n }\n\n // Bot detection\n if (this.transportConfig.botDetection === 'block' && FlowGridTransport.isLikelyBot()) {\n return {} as T;\n }\n\n this.validateInput(eventName, properties);\n\n // Bot tagging\n if (this.transportConfig.botDetection === 'tag' && FlowGridTransport.isLikelyBot()) {\n properties = { ...properties, _bot: true };\n }\n\n // Auto device enrichment\n const enriched = { ...FlowGridTransport.getDeviceContext(), ...properties };\n\n const callType = enriched.type || \"event\";\n const url = `${this.endpoint}/api/v1/sdk/functions?call=${encodeURIComponent(callType)}`;\n\n const resolvedVisitorId = this.identityManager\n ? this.identityManager.getVisitorId()\n : (this.visitorId ?? '');\n\n const body = JSON.stringify({\n web_id: this.webId,\n visitor_id: resolvedVisitorId,\n event_name: eventName,\n properties: enriched,\n });\n\n return this.executeRequest<T>(url, method, method === \"GET\" ? undefined : body);\n }\n\n /**\n * Feature-scoped RPC transport method.\n * Routes to the SDK API endpoint with a feature type qualifier.\n *\n * @param eventName - Name of the event to track.\n * @param properties - Additional properties to send (must include type).\n */\n protected async initFeature<T>(eventName: string, properties: Record<string, any> & { type: FeatureType }): Promise<T> {\n\n this.validateInput(eventName, properties);\n\n if (!properties.type) {\n throw new Error('Feature events must include a type property');\n }\n\n const url = `${this.endpoint}/api/v1/sdk/functions?call=${encodeURIComponent(properties.type)}`;\n\n const resolvedVisitorId = this.identityManager\n ? this.identityManager.getVisitorId()\n : (this.visitorId ?? '');\n\n const body = JSON.stringify({\n web_id: this.webId,\n visitor_id: resolvedVisitorId,\n event_name: eventName,\n properties,\n });\n\n return this.executeRequest(url, \"POST\", body);\n }\n\n /**\n * Executes HTTP request with timeout and retry handling.\n *\n * @template T Expected response type.\n *\n * @param url - Fully constructed request URL.\n * @param method - HTTP method.\n * @param body - Optional JSON body (POST/PATCH only).\n * @param extraHeaders - Additional headers to include.\n *\n * @throws {Error} On timeout, network failure, or non-OK HTTP status.\n */\n private async executeRequest<T>(\n url: string,\n method: \"GET\" | \"POST\" | \"PATCH\",\n body?: string,\n extraHeaders?: Record<string, string>\n ): Promise<T> {\n\n let attempt = 0;\n\n while (attempt <= FlowGridTransport.MAX_RETRIES) {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), FlowGridTransport.TIMEOUT_MS);\n\n try {\n const res = await fetch(url, {\n method,\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${this.apiKey}`,\n ...extraHeaders,\n },\n body,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n // Retry on 5xx (server transient errors)\n if (res.status >= 500 && attempt < FlowGridTransport.MAX_RETRIES) {\n attempt++;\n await this.backoff(attempt);\n continue;\n }\n\n if (!res.ok) {\n const text = await this.safeRead(res);\n throw new Error(\n `SDK request failed (${res.status} ${res.statusText}) ${text}`\n );\n }\n\n return await res.json();\n\n } catch (error) {\n clearTimeout(timeoutId);\n\n const isNetworkError =\n error instanceof Error &&\n (error.name === \"AbortError\" || error.message.includes(\"fetch\"));\n\n if (isNetworkError && attempt < FlowGridTransport.MAX_RETRIES) {\n attempt++;\n await this.backoff(attempt);\n continue;\n }\n\n // Final failure — try sendBeacon, then buffer to localStorage\n if (attempt >= FlowGridTransport.MAX_RETRIES) {\n const beaconSent = body && typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function'\n ? navigator.sendBeacon(url, new Blob([body], { type: 'application/json' }))\n : false;\n\n if (!beaconSent) {\n this.bufferEvent(url, method, body);\n }\n }\n\n if (error instanceof Error && error.name === \"AbortError\") {\n throw new Error(\"SDK request timed out\");\n }\n\n throw error;\n }\n }\n\n // Unreachable: the loop body always returns on success or throws on\n // permanent failure. The explicit throw satisfies TS now that the loop\n // condition is bounded (was `while (true)` previously).\n throw new Error(\"SDK request exhausted retries without resolution\");\n }\n\n /**\n * Exponential backoff with full jitter: ~1s, ~2s, ~4s (capped at 30s),\n * each randomised across its window.\n *\n * The jitter is the important part. Without it, every SDK instance that\n * failed during a backend blip retries at the *same* moment (t+1s, t+2s, …),\n * producing synchronized request waves — a self-inflicted thundering herd\n * that knocks the backend over again the instant it recovers. Spreading each\n * retry uniformly across its window de-synchronises the fleet so recovery\n * traffic ramps smoothly instead of spiking.\n */\n private backoff(attempt: number): Promise<void> {\n const ceiling = Math.min(Math.pow(2, attempt - 1) * 1000, 30_000);\n const ms = Math.random() * ceiling;\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n\n /**\n * Validates event name and payload.\n *\n * @param eventName - Event identifier.\n * @param properties - Associated event properties.\n *\n * @throws {Error} If validation fails.\n */\n private validateInput(eventName: string, properties: Record<string, any>) {\n\n if (!eventName || typeof eventName !== \"string\" || !eventName.trim()) {\n throw new Error(\"Event name must be a non-empty string\");\n }\n\n if (eventName.length > 100) {\n throw new Error(\"Event name must be less than 100 characters\");\n }\n\n let serialized: string;\n\n try {\n serialized = JSON.stringify({\n web_id: this.webId,\n visitor_id: this.visitorId ?? '',\n event_name: eventName,\n properties,\n });\n } catch {\n throw new Error(\"Properties must be JSON serializable\");\n }\n\n if (new TextEncoder().encode(serialized).length > FlowGridTransport.MAX_PAYLOAD_SIZE) {\n throw new Error(\"Event payload too large (max 10KB)\");\n }\n\n const sensitiveKeys = [\"password\", \"token\", \"secret\", \"apikey\", \"authorization\"];\n const lower = serialized.toLowerCase();\n\n for (const key of sensitiveKeys) {\n if (lower.includes(key)) {\n console.warn(\n `SDK Warning: Properties may contain sensitive data (${key}).`\n );\n }\n }\n }\n\n /**\n * Normalizes endpoint input.\n * Ensures protocol is present and removes trailing slashes.\n *\n * @param endpoint - Raw endpoint string.\n * @returns Normalized endpoint URL.\n */\n private normalizeEndpoint(endpoint: string): string {\n let clean = endpoint.trim();\n\n if (!clean.startsWith(\"http://\") && !clean.startsWith(\"https://\")) {\n clean = `https://${clean}`;\n }\n\n return clean.replace(/\\/+$/, \"\");\n }\n\n /**\n * Safely reads response text without throwing.\n *\n * @param res - Fetch Response object.\n * @returns Response text or empty string.\n */\n private async safeRead(res: Response): Promise<string> {\n try {\n return await res.text();\n } catch {\n return \"\";\n }\n }\n}\n","/**\n * @fileoverview Browser environment helpers.\n *\n * Tiny utilities to check for browser globals safely so feature classes can\n * expose `autoTrack()` methods that no-op gracefully on the server.\n */\n\n/** Safe `window` accessor; returns `undefined` outside the browser. */\nexport function getWindow(): (Window & typeof globalThis) | undefined {\n return typeof window !== \"undefined\" ? window : undefined;\n}\n\n/** Safe `document` accessor; returns `undefined` outside the browser. */\nexport function getDocument(): Document | undefined {\n return typeof document !== \"undefined\" ? document : undefined;\n}\n\n/** Returns true when running in a real browser-like environment. */\nexport function isBrowser(): boolean {\n return typeof window !== \"undefined\" && typeof document !== \"undefined\";\n}\n\n/** A teardown function returned by every `autoTrack()` API. */\nexport type Teardown = () => void;\n\n/** Compose multiple teardown functions into one. */\nexport function combineTeardowns(...fns: Array<Teardown | undefined | null | false>): Teardown {\n return () => {\n for (const fn of fns) {\n if (typeof fn === \"function\") {\n try { fn(); } catch { /* swallow */ }\n }\n }\n };\n}\n","/**\n * @fileoverview FlowGrid storage utilities.\n * @module utils/storage\n *\n * @description\n * Reads visitor identity and UTM attribution data from the same\n * cookie / localStorage / sessionStorage keys used by the FlowGrid\n * tracking scripts (track.js & flowgrid_oss_client_sdk.js).\n *\n * These functions are **read-only** — the tracking scripts remain the\n * single writer. The SDK only consumes these values.\n */\n\n// ============================================================================\n// COOKIE HELPERS\n// ============================================================================\n\n/**\n * Read a cookie value by name.\n * Returns `null` when the cookie is absent or document.cookie is unavailable.\n */\nexport function getCookie(name: string): string | null {\n if (typeof document === 'undefined') return null;\n const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));\n return match ? match[2] : null;\n}\n\n// ============================================================================\n// VISITOR IDENTITY\n// ============================================================================\n\n/**\n * Resolve the visitor ID written by track.js / flowgrid_oss_client_sdk.js.\n *\n * Storage locations (checked in order):\n * 1. cookie `visitor_id`\n * 2. localStorage `visitor_id`\n *\n * Returns `null` when neither source contains a value (e.g. tracking\n * script hasn't run yet, or running server-side).\n */\nexport function getVisitorId(): string | null {\n try {\n return getCookie('visitor_id') || localStorage.getItem('visitor_id');\n } catch {\n // localStorage unavailable (SSR, privacy mode, etc.)\n return getCookie('visitor_id');\n }\n}\n\n/**\n * Resolve the session ID written by track.js.\n *\n * Storage: `sessionStorage(\"session_id\")`\n */\nexport function getSessionId(): string | null {\n try {\n return sessionStorage.getItem('session_id');\n } catch {\n return null;\n }\n}\n\n/**\n * Resolve the web_id written by track.js.\n *\n * Storage: `localStorage(\"web_id\")`\n */\nexport function getWebId(): string | null {\n try {\n return localStorage.getItem('web_id');\n } catch {\n return null;\n }\n}\n\n// ============================================================================\n// UTM / ATTRIBUTION\n// ============================================================================\n\nexport interface FlowGridUTM {\n utmSource?: string;\n utmMedium?: string;\n utmCampaign?: string;\n utmTerm?: string;\n utmContent?: string;\n ref?: string;\n source?: string;\n via?: string;\n}\n\n/**\n * Read UTM & attribution parameters persisted by\n * flowgrid_oss_client_sdk.js as `__flowgrid_*` cookies.\n */\nexport function getUTM(): FlowGridUTM {\n return {\n utmSource: getCookie('__flowgrid_utm_source') || undefined,\n utmMedium: getCookie('__flowgrid_utm_medium') || undefined,\n utmCampaign: getCookie('__flowgrid_utm_campaign') || undefined,\n utmTerm: getCookie('__flowgrid_utm_term') || undefined,\n utmContent: getCookie('__flowgrid_utm_content') || undefined,\n ref: getCookie('__flowgrid_ref') || undefined,\n source: getCookie('__flowgrid_source') || undefined,\n via: getCookie('__flowgrid_via') || undefined,\n };\n}\n\nexport interface FlowGridUTMHistoryEntry {\n timestamp: string;\n utm_source?: string;\n utm_medium?: string;\n utm_campaign?: string;\n utm_content?: string;\n utm_term?: string;\n}\n\n/**\n * Read the full UTM history array persisted by flowgrid_oss_client_sdk.js.\n *\n * Checks localStorage first, then the `utm_history` cookie.\n */\nexport function getUTMHistory(): FlowGridUTMHistoryEntry[] {\n try {\n const raw =\n localStorage.getItem('utm_history') ||\n getCookie('utm_history');\n return raw ? JSON.parse(raw) : [];\n } catch {\n return [];\n }\n}\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n// expose the modules object (__webpack_modules__)\n__webpack_require__.m = __webpack_modules__;\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.f = {};\n// This file contains only the entry chunk.\n// The chunk loading function for additional chunks\n__webpack_require__.e = (chunkId) => {\n\treturn Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => {\n\t\t__webpack_require__.f[key](chunkId, promises);\n\t\treturn promises;\n\t}, []));\n};","// This function allow to reference async chunks\n__webpack_require__.u = (chunkId) => {\n\t// return url for filenames based on template\n\treturn \"\" + {\"137\":\"flowgrid-enterprise\",\"717\":\"flowgrid-vitals\",\"897\":\"flowgrid-replay\"}[chunkId] + \".flowgrid.chunk.js\";\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","var scriptUrl;\nif (__webpack_require__.g.importScripts) scriptUrl = __webpack_require__.g.location + \"\";\nvar document = __webpack_require__.g.document;\nif (!scriptUrl && document) {\n\tif (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT')\n\t\tscriptUrl = document.currentScript.src;\n\tif (!scriptUrl) {\n\t\tvar scripts = document.getElementsByTagName(\"script\");\n\t\tif(scripts.length) {\n\t\t\tvar i = scripts.length - 1;\n\t\t\twhile (i > -1 && (!scriptUrl || !/^http(s?):/.test(scriptUrl))) scriptUrl = scripts[i--].src;\n\t\t}\n\t}\n}\n// When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration\n// or pass an empty string (\"\") and set the __webpack_public_path__ variable from your code to use your own logic.\nif (!scriptUrl) throw new Error(\"Automatic publicPath is not supported in this browser\");\nscriptUrl = scriptUrl.replace(/^blob:/, \"\").replace(/#.*$/, \"\").replace(/\\?.*$/, \"\").replace(/\\/[^\\/]+$/, \"/\");\n__webpack_require__.p = scriptUrl;","// no baseURI\n\n// object to store loaded and loading chunks\n// undefined = chunk not loaded, null = chunk preloaded/prefetched\n// [resolve, reject, Promise] = chunk loading, 0 = chunk loaded\nvar installedChunks = {\n\t792: 0\n};\n\n__webpack_require__.f.j = (chunkId, promises) => {\n\t\t// JSONP chunk loading for javascript\n\t\tvar installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;\n\t\tif(installedChunkData !== 0) { // 0 means \"already installed\".\n\n\t\t\t// a Promise means \"currently loading\".\n\t\t\tif(installedChunkData) {\n\t\t\t\tpromises.push(installedChunkData[2]);\n\t\t\t} else {\n\t\t\t\tif(true) { // all chunks have JS\n\t\t\t\t\t// setup Promise in chunk cache\n\t\t\t\t\tvar promise = new Promise((resolve, reject) => (installedChunkData = installedChunks[chunkId] = [resolve, reject]));\n\t\t\t\t\tpromises.push(installedChunkData[2] = promise);\n\n\t\t\t\t\t// start chunk loading\n\t\t\t\t\tvar url = __webpack_require__.p + __webpack_require__.u(chunkId);\n\t\t\t\t\t// create error before stack unwound to get useful stacktrace later\n\t\t\t\t\tvar error = new Error();\n\t\t\t\t\tvar loadingEnded = (event) => {\n\t\t\t\t\t\tif(__webpack_require__.o(installedChunks, chunkId)) {\n\t\t\t\t\t\t\tinstalledChunkData = installedChunks[chunkId];\n\t\t\t\t\t\t\tif(installedChunkData !== 0) installedChunks[chunkId] = undefined;\n\t\t\t\t\t\t\tif(installedChunkData) {\n\t\t\t\t\t\t\t\tvar errorType = event && (event.type === 'load' ? 'missing' : event.type);\n\t\t\t\t\t\t\t\tvar realSrc = event && event.target && event.target.src;\n\t\t\t\t\t\t\t\terror.message = 'Loading chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';\n\t\t\t\t\t\t\t\terror.name = 'ChunkLoadError';\n\t\t\t\t\t\t\t\terror.type = errorType;\n\t\t\t\t\t\t\t\terror.request = realSrc;\n\t\t\t\t\t\t\t\tinstalledChunkData[1](error);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t\t__webpack_require__.l(url, loadingEnded, \"chunk-\" + chunkId, chunkId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n};\n\n// no prefetching\n\n// no preloaded\n\n// no HMR\n\n// no HMR manifest\n\n// no on chunks loaded\n\n// install a JSONP callback for chunk loading\nvar webpackJsonpCallback = (parentChunkLoadingFunction, data) => {\n\tvar [chunkIds, moreModules, runtime] = data;\n\t// add \"moreModules\" to the modules object,\n\t// then flag all \"chunkIds\" as loaded and fire callback\n\tvar moduleId, chunkId, i = 0;\n\tif(chunkIds.some((id) => (installedChunks[id] !== 0))) {\n\t\tfor(moduleId in moreModules) {\n\t\t\tif(__webpack_require__.o(moreModules, moduleId)) {\n\t\t\t\t__webpack_require__.m[moduleId] = moreModules[moduleId];\n\t\t\t}\n\t\t}\n\t\tif(runtime) var result = runtime(__webpack_require__);\n\t}\n\tif(parentChunkLoadingFunction) parentChunkLoadingFunction(data);\n\tfor(;i < chunkIds.length; i++) {\n\t\tchunkId = chunkIds[i];\n\t\tif(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) {\n\t\t\tinstalledChunks[chunkId][0]();\n\t\t}\n\t\tinstalledChunks[chunkId] = 0;\n\t}\n\n}\n\nvar chunkLoadingGlobal = this[\"webpackChunkflowgrid\"] = this[\"webpackChunkflowgrid\"] || [];\nchunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));\nchunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));","/**\n * @fileoverview FlowGrid Identity Manager.\n * @module utils/identity\n *\n * @description\n * Owns the visitor/session identity lifecycle.\n * Writes its own cookies/storage so the SDK can self-bootstrap\n * without depending on an external tracking script (track.js).\n *\n * Backward-compatible: checks for existing track.js `visitor_id`\n * before falling back to its own `fg_visitor_id` key.\n */\n\nimport { getCookie, getVisitorId as getTrackJsVisitorId } from './storage';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\nexport interface IdentityConfig {\n /** Explicit visitor ID (overrides auto-resolution) */\n visitorId?: string;\n /** Cookie domain (defaults to current hostname) */\n cookieDomain?: string;\n /** Visitor cookie max-age in days (default: 365) */\n visitorMaxAgeDays?: number;\n /** Session timeout in minutes (default: 30) */\n sessionTimeoutMinutes?: number;\n /** Storage strategy */\n storage?: 'cookie' | 'localStorage' | 'memory';\n /** Server-side cookie reader (e.g. Next.js `cookies()`) */\n serverCookies?: ServerCookieReader;\n /** Write to `visitor_id` (track.js compat) instead of `fg_visitor_id` (default true) */\n compat?: boolean;\n}\n\n/**\n * Minimal interface for reading cookies server-side.\n * Compatible with Next.js `cookies()` and Nuxt `useCookie()`.\n */\nexport interface ServerCookieReader {\n get(name: string): { value: string } | string | undefined | null;\n}\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst LEGACY_VISITOR_COOKIE = 'fg_visitor_id';\nconst COMPAT_VISITOR_COOKIE = 'visitor_id';\nconst SESSION_KEY = 'fg_session_id';\nconst SESSION_TOUCH_KEY = 'fg_session_touch';\n\n// ============================================================================\n// IDENTITY MANAGER\n// ============================================================================\n\nexport class IdentityManager {\n private visitorId: string = '';\n private sessionId: string = '';\n private config: Required<Omit<IdentityConfig, 'serverCookies'>> & { serverCookies?: ServerCookieReader; compat: boolean };\n\n /** In-memory store for SSR / Node environments */\n private memoryStore: Record<string, string> = {};\n\n constructor(config?: IdentityConfig) {\n this.config = {\n visitorId: config?.visitorId ?? '',\n cookieDomain: config?.cookieDomain ?? '',\n visitorMaxAgeDays: config?.visitorMaxAgeDays ?? 365,\n sessionTimeoutMinutes: config?.sessionTimeoutMinutes ?? 30,\n storage: config?.storage ?? (this.detectBrowser() ? 'cookie' : 'memory'),\n serverCookies: config?.serverCookies,\n compat: config?.compat ?? true,\n };\n\n this.visitorId = this.resolveVisitorId();\n this.sessionId = '';\n }\n\n // ---------------------------------------------------------------------------\n // PUBLIC API\n // ---------------------------------------------------------------------------\n\n /**\n * Get or create a persistent visitor ID.\n *\n * Resolution order:\n * 1. Explicit config.visitorId\n * 2. track.js `visitor_id` cookie/localStorage (backward compat)\n * 3. `fg_visitor_id` cookie\n * 4. `fg_visitor_id` localStorage\n * 5. Server cookie reader (if provided)\n * 6. Generate new UUID v4, persist to cookie + localStorage\n */\n getVisitorId(): string {\n if (this.visitorId) return this.visitorId;\n this.visitorId = this.resolveVisitorId();\n return this.visitorId;\n }\n\n /**\n * Get or create a session ID.\n *\n * - Reads from sessionStorage `fg_session_id`\n * - If absent or expired (>sessionTimeoutMinutes since last activity), creates new\n * - Persists to sessionStorage\n * - Touches timestamp on every call\n */\n getSessionId(): string {\n if (this.config.storage === 'memory') {\n return this.getMemorySessionId();\n }\n\n if (!this.detectBrowser()) {\n return this.getMemorySessionId();\n }\n\n try {\n const existing = sessionStorage.getItem(SESSION_KEY);\n const lastTouch = sessionStorage.getItem(SESSION_TOUCH_KEY);\n\n if (existing && lastTouch) {\n const elapsed = Date.now() - parseInt(lastTouch, 10);\n const timeoutMs = this.config.sessionTimeoutMinutes * 60 * 1000;\n if (elapsed < timeoutMs) {\n sessionStorage.setItem(SESSION_TOUCH_KEY, String(Date.now()));\n this.sessionId = existing;\n return existing;\n }\n }\n\n // Create new session\n const newSession = this.generateSessionId();\n sessionStorage.setItem(SESSION_KEY, newSession);\n sessionStorage.setItem(SESSION_TOUCH_KEY, String(Date.now()));\n this.sessionId = newSession;\n return newSession;\n } catch {\n return this.getMemorySessionId();\n }\n }\n\n /**\n * Update the visitor ID (e.g. after identify() links anonymous → known user).\n * Persists to cookie + localStorage.\n */\n setVisitorId(id: string): void {\n this.visitorId = id;\n this.persistVisitorId(id);\n }\n\n /** Clear session (e.g. on logout). */\n clearSession(): void {\n this.sessionId = '';\n delete this.memoryStore[SESSION_KEY];\n delete this.memoryStore[SESSION_TOUCH_KEY];\n\n if (this.detectBrowser()) {\n try {\n sessionStorage.removeItem(SESSION_KEY);\n sessionStorage.removeItem(SESSION_TOUCH_KEY);\n } catch { /* SSR / privacy mode */ }\n }\n }\n\n /** Clear all identity (GDPR). Removes visitor + session + experiment assignments. */\n clearAll(): void {\n this.visitorId = '';\n this.sessionId = '';\n this.memoryStore = {};\n\n if (!this.detectBrowser()) return;\n\n try {\n // Cookies\n this.deleteCookie(this.visitorCookieName);\n this.deleteCookie(LEGACY_VISITOR_COOKIE);\n\n // localStorage\n localStorage.removeItem(this.visitorCookieName);\n localStorage.removeItem(LEGACY_VISITOR_COOKIE);\n localStorage.removeItem('fg_exp_assignments');\n\n // sessionStorage\n sessionStorage.removeItem(SESSION_KEY);\n sessionStorage.removeItem(SESSION_TOUCH_KEY);\n } catch { /* SSR / privacy mode */ }\n }\n\n /** Check if running in a browser environment. */\n isBrowser(): boolean {\n return this.detectBrowser();\n }\n\n // ---------------------------------------------------------------------------\n // PRIVATE — VISITOR RESOLUTION\n // ---------------------------------------------------------------------------\n\n /** Returns the primary cookie name based on compat mode */\n private get visitorCookieName(): string {\n return this.config.compat ? COMPAT_VISITOR_COOKIE : LEGACY_VISITOR_COOKIE;\n }\n\n private resolveVisitorId(): string {\n // 1. Explicit config\n if (this.config.visitorId) return this.config.visitorId;\n\n // 2. track.js backward compat (cookie/localStorage `visitor_id`)\n if (this.detectBrowser()) {\n const trackJsId = getTrackJsVisitorId();\n if (trackJsId) {\n // Migration: if fg_visitor_id exists with a different value, prefer visitor_id (track.js was first)\n const legacyId = this.readCookie(LEGACY_VISITOR_COOKIE) || this.readLocalStorage(LEGACY_VISITOR_COOKIE);\n if (legacyId && legacyId !== trackJsId) {\n // Merge identities by keeping track.js id as canonical\n this.removeLegacyCookie();\n }\n this.persistVisitorId(trackJsId);\n return trackJsId;\n }\n }\n\n // 3. Migration: fg_visitor_id → visitor_id\n if (this.config.compat) {\n const legacyId = this.readCookie(LEGACY_VISITOR_COOKIE) || this.readLocalStorage(LEGACY_VISITOR_COOKIE);\n if (legacyId) {\n this.persistVisitorId(legacyId);\n this.removeLegacyCookie();\n return legacyId;\n }\n }\n\n // 4. Own cookie\n const cookieId = this.readCookie(this.visitorCookieName);\n if (cookieId) return cookieId;\n\n // 5. localStorage\n if (this.detectBrowser()) {\n try {\n const lsId = localStorage.getItem(this.visitorCookieName);\n if (lsId) return lsId;\n } catch { /* unavailable */ }\n }\n\n // 6. Server cookie reader\n if (this.config.serverCookies) {\n const serverVal = this.readServerCookie(this.visitorCookieName);\n if (serverVal) return serverVal;\n }\n\n // 7. Memory store\n if (this.memoryStore[this.visitorCookieName]) {\n return this.memoryStore[this.visitorCookieName];\n }\n\n // 8. Generate new ID\n const newId = this.generateVisitorId();\n this.persistVisitorId(newId);\n return newId;\n }\n\n private readLocalStorage(key: string): string | null {\n if (!this.detectBrowser()) return null;\n try { return localStorage.getItem(key); } catch { return null; }\n }\n\n private removeLegacyCookie(): void {\n this.deleteCookie(LEGACY_VISITOR_COOKIE);\n if (this.detectBrowser()) {\n try { localStorage.removeItem(LEGACY_VISITOR_COOKIE); } catch { /* */ }\n }\n }\n\n private persistVisitorId(id: string): void {\n const cookieName = this.visitorCookieName;\n\n if (this.config.storage === 'memory') {\n this.memoryStore[cookieName] = id;\n return;\n }\n\n if (!this.detectBrowser()) {\n this.memoryStore[cookieName] = id;\n return;\n }\n\n // Cookie\n this.writeCookie(cookieName, id, this.config.visitorMaxAgeDays);\n\n // localStorage as fallback\n try {\n localStorage.setItem(cookieName, id);\n } catch { /* unavailable */ }\n }\n\n // ---------------------------------------------------------------------------\n // PRIVATE — SESSION HELPERS\n // ---------------------------------------------------------------------------\n\n private getMemorySessionId(): string {\n const existing = this.memoryStore[SESSION_KEY];\n const lastTouch = this.memoryStore[SESSION_TOUCH_KEY];\n\n if (existing && lastTouch) {\n const elapsed = Date.now() - parseInt(lastTouch, 10);\n const timeoutMs = this.config.sessionTimeoutMinutes * 60 * 1000;\n if (elapsed < timeoutMs) {\n this.memoryStore[SESSION_TOUCH_KEY] = String(Date.now());\n this.sessionId = existing;\n return existing;\n }\n }\n\n const newSession = this.generateSessionId();\n this.memoryStore[SESSION_KEY] = newSession;\n this.memoryStore[SESSION_TOUCH_KEY] = String(Date.now());\n this.sessionId = newSession;\n return newSession;\n }\n\n // ---------------------------------------------------------------------------\n // PRIVATE — COOKIE OPERATIONS\n // ---------------------------------------------------------------------------\n\n private readCookie(name: string): string | null {\n // Server-side reader first\n if (this.config.serverCookies) {\n return this.readServerCookie(name);\n }\n\n // Browser cookie\n if (this.detectBrowser()) {\n return getCookie(name);\n }\n\n return null;\n }\n\n private readServerCookie(name: string): string | null {\n if (!this.config.serverCookies) return null;\n const result = this.config.serverCookies.get(name);\n if (!result) return null;\n // Next.js cookies().get() returns { value: string }\n if (typeof result === 'object' && 'value' in result) return result.value || null;\n // Nuxt useCookie() returns string directly\n if (typeof result === 'string') return result || null;\n return null;\n }\n\n private writeCookie(name: string, value: string, maxAgeDays: number): void {\n if (!this.detectBrowser()) return;\n\n const maxAge = maxAgeDays * 24 * 60 * 60;\n const domain = this.config.cookieDomain ? `; Domain=${this.config.cookieDomain}` : '';\n const secure = location.protocol === 'https:' ? '; Secure' : '';\n\n document.cookie = `${name}=${encodeURIComponent(value)}; Path=/; Max-Age=${maxAge}; SameSite=Lax${secure}${domain}`;\n }\n\n private deleteCookie(name: string): void {\n if (!this.detectBrowser()) return;\n const domain = this.config.cookieDomain ? `; Domain=${this.config.cookieDomain}` : '';\n document.cookie = `${name}=; Path=/; Max-Age=0${domain}`;\n }\n\n // ---------------------------------------------------------------------------\n // PRIVATE — ID GENERATION\n // ---------------------------------------------------------------------------\n\n private generateVisitorId(): string {\n return `fg_${this.uuidV4()}`;\n }\n\n private generateSessionId(): string {\n return `fgs_${Date.now()}_${this.randomHex(8)}`;\n }\n\n private uuidV4(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for environments without crypto.randomUUID\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n }\n\n private randomHex(length: number): string {\n const bytes = new Uint8Array(length);\n if (typeof crypto !== 'undefined' && crypto.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < length; i++) bytes[i] = Math.floor(Math.random() * 256);\n }\n return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n }\n\n // ---------------------------------------------------------------------------\n // PRIVATE — ENVIRONMENT DETECTION\n // ---------------------------------------------------------------------------\n\n private detectBrowser(): boolean {\n return typeof window !== 'undefined' && typeof document !== 'undefined';\n }\n}\n","/**\n * @fileoverview Core - User Activation Tracking\n * @module features/core/activation\n * \n * @description\n * Track user activation events including onboarding steps,\n * signup completions, and first-time actions.\n * \n * @useCases\n * - Track onboarding step completions\n * - Monitor signup funnel\n * - Measure time to first value\n * - Identify activation bottlenecks\n * - A/B test onboarding flows\n * \n * @metrics\n * - Activation rate\n * - Onboarding completion rate\n * - Time to activate\n * - Drop-off by step\n * - Activation by source\n * \n * @chartData\n * - Funnel chart: Onboarding steps\n * - Line chart: Activation rate over time\n * - Bar chart: Activation by cohort/source\n * - KPI: Time to first value\n * \n * @example\n * ```typescript\n * const activation = new Activation(webId, endpoint, apiKey);\n * \n * // Track activation event\n * await activation.create({\n * eventName: 'onboarding_completed',\n * properties: { userId: 'user_123', step: 'profile_setup' }\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { TimeSeriesData } from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Base configuration for activation events.\n */\nexport interface ActivationConfig {\n /** Event name */\n eventName: string;\n /** Event properties */\n properties: Record<string, unknown>;\n}\n\n/**\n * Onboarding step configuration.\n */\nexport interface OnboardingStepConfig {\n /** User ID */\n userId: string;\n /** Step identifier */\n stepId: string;\n /** Step name */\n stepName: string;\n /** Step order/number */\n stepOrder: number;\n /** Total steps in flow */\n totalSteps: number;\n /** Time spent on step (ms) */\n timeSpent?: number;\n /** Whether step was skipped */\n skipped?: boolean;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * First action configuration.\n */\nexport interface FirstActionConfig {\n /** User ID */\n userId: string;\n /** Action name */\n actionName: string;\n /** Time since signup (ms) */\n timeSinceSignup: number;\n /** Source/referrer */\n source?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Activation response from API.\n */\nexport interface ActivationResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n tracked: boolean;\n activationId?: string;\n isFirstActivation?: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Activation analytics data.\n */\nexport interface ActivationAnalyticsData {\n /** Summary metrics */\n summary: {\n totalSignups: number;\n activatedUsers: number;\n activationRate: number;\n avgTimeToActivate: number;\n onboardingCompletionRate: number;\n };\n /** Activation by step */\n byStep: Array<{\n stepId: string;\n stepName: string;\n started: number;\n completed: number;\n dropOff: number;\n avgTimeSpent: number;\n }>;\n /** Activation by source */\n bySource: Array<{\n source: string;\n signups: number;\n activated: number;\n activationRate: number;\n }>;\n /** Activation trends */\n trends: TimeSeriesData;\n /** Cohort activation */\n byCohort: Array<{\n cohort: string;\n users: number;\n activated: number;\n activationRate: number;\n avgTimeToActivate: number;\n }>;\n}\n\n// ============================================================================\n// ACTIVATION CLASS\n// ============================================================================\n\n/**\n * Activation - Track user activation events.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides activation tracking including:\n * - Generic activation events\n * - Onboarding step tracking\n * - First action tracking\n * - Signup completion\n * - Activation analytics\n * \n * @example\n * ```typescript\n * const activation = new Activation(webId, endpoint, apiKey);\n * \n * // Track onboarding step\n * await activation.trackOnboardingStep({\n * userId: 'user_123',\n * stepId: 'profile_setup',\n * stepName: 'Set Up Profile',\n * stepOrder: 2,\n * totalSteps: 5\n * });\n * \n * // Track first value action\n * await activation.trackFirstAction({\n * userId: 'user_123',\n * actionName: 'created_first_project',\n * timeSinceSignup: 3600000 // 1 hour\n * });\n * ```\n */\nexport class Activation extends FlowGridTransport {\n /**\n * Creates and tracks an activation event.\n * \n * @param config - Activation configuration\n * @returns Promise resolving to activation response\n */\n async create(config: ActivationConfig): Promise<ActivationResponse> {\n return await this.init(config.eventName, {\n ...config.properties,\n type: \"activation\",\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks an onboarding step completion.\n * \n * @param config - Onboarding step configuration\n * @returns Promise resolving to activation response\n */\n async trackOnboardingStep(config: OnboardingStepConfig): Promise<ActivationResponse> {\n return await this.init(\"onboarding_step\", {\n type: \"activation\",\n subtype: \"onboarding_step\",\n userId: config.userId,\n stepId: config.stepId,\n stepName: config.stepName,\n stepOrder: config.stepOrder,\n totalSteps: config.totalSteps,\n progress: Math.round((config.stepOrder / config.totalSteps) * 100),\n timeSpent: config.timeSpent,\n skipped: config.skipped || false,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks onboarding completion.\n * \n * @param userId - User ID\n * @param totalTimeSpent - Total time in onboarding (ms)\n * @param stepsCompleted - Number of steps completed\n * @param stepsSkipped - Number of steps skipped\n * @returns Promise resolving to activation response\n */\n async completeOnboarding(\n userId: string,\n totalTimeSpent: number,\n stepsCompleted: number,\n stepsSkipped: number\n ): Promise<ActivationResponse> {\n return await this.init(\"onboarding_completed\", {\n type: \"activation\",\n subtype: \"onboarding_complete\",\n userId,\n totalTimeSpent,\n stepsCompleted,\n stepsSkipped,\n completionRate: Math.round((stepsCompleted / (stepsCompleted + stepsSkipped)) * 100),\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks user's first meaningful action (activation moment).\n * \n * @param config - First action configuration\n * @returns Promise resolving to activation response\n */\n async trackFirstAction(config: FirstActionConfig): Promise<ActivationResponse> {\n return await this.init(\"first_action\", {\n type: \"activation\",\n subtype: \"first_action\",\n userId: config.userId,\n actionName: config.actionName,\n timeSinceSignup: config.timeSinceSignup,\n source: config.source,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks signup completion.\n * \n * @param userId - User ID\n * @param method - Signup method (email, google, etc.)\n * @param source - Traffic source\n * @param properties - Additional properties\n * @returns Promise resolving to activation response\n */\n async trackSignup(\n userId: string,\n method: 'email' | 'google' | 'github' | 'apple' | 'sso' | string,\n source?: string,\n properties?: Record<string, unknown>\n ): Promise<ActivationResponse> {\n return await this.init(\"signup_completed\", {\n type: \"activation\",\n subtype: \"signup\",\n userId,\n method,\n source,\n ...properties,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default Activation;\n","/**\n * @fileoverview Core - Feature Usage Tracking\n * @module features/core/track-feature\n * \n * @description\n * Track feature usage events to understand how users\n * interact with different parts of your product.\n * \n * @useCases\n * - Track feature adoption rates\n * - Measure feature engagement\n * - Identify power users\n * - Prioritize feature development\n * - Detect underused features\n * \n * @metrics\n * - Feature adoption rate\n * - Feature usage frequency\n * - Users per feature\n * - Feature stickiness\n * - Time spent per feature\n * \n * @chartData\n * - Bar chart: Most used features\n * - Heatmap: Feature usage by user segment\n * - Line chart: Feature adoption over time\n * - Table: Feature engagement metrics\n * \n * @example\n * ```typescript\n * const trackFeature = new TrackFeature(webId, endpoint, apiKey);\n * \n * await trackFeature.create({\n * userId: 'user_123',\n * action: 'export_report',\n * properties: { featureName: 'export_report', featureId: 'export_report_001' }\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { TimeSeriesData } from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n\ntype FeatureConfigProperties = {\n featureId?: string;\n featureName: string;\n metadata?: Record<string, unknown>;\n};\n\n/**\n * Base configuration for feature tracking.\n */\nexport interface FeatureConfig {\n /** Feature tracking properties */\n action: string;\n\n /** Feature category */\n category?: string;\n\n userId: string;\n\n /** Event properties */\n properties: FeatureConfigProperties;\n\n}\n\n/**\n * Detailed feature usage configuration.\n */\nexport interface FeatureUsageConfig {\n /** Feature identifier */\n featureId: string;\n /** Feature name */\n featureName: string;\n /** User ID */\n userId?: string;\n /** Usage action */\n action: 'viewed' | 'used' | 'completed' | 'abandoned';\n /** Time spent (ms) */\n duration?: number;\n /** Success/failure */\n success?: boolean;\n /** Feature category */\n category?: string;\n /**\n * Wire event name to emit. Defaults to `feature_usage`. The `.track()`\n * router passes the lifecycle-specific name here (`feature_viewed`,\n * `feature_completed`, etc.) so consumers can filter on a distinct\n * `event_name`; `action`/`subtype` still carry the lifecycle stage.\n */\n eventName?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Feature discovery configuration.\n */\nexport interface FeatureDiscoveryConfig {\n /** Feature identifier */\n featureId: string;\n /** User ID */\n userId: string;\n /** How feature was discovered */\n discoveryMethod: 'organic' | 'tooltip' | 'announcement' | 'search' | 'onboarding' | 'recommendation' | string;\n /** Whether user engaged with feature */\n engaged: boolean;\n}\n\n/**\n * Feature tracking response.\n */\nexport interface FeatureResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n tracked: boolean;\n featureId?: string;\n isFirstUse?: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Feature analytics data.\n */\nexport interface FeatureAnalyticsData {\n /** Summary metrics */\n summary: {\n totalFeatures: number;\n totalUsage: number;\n uniqueUsers: number;\n avgUsagePerUser: number;\n mostUsedFeature: string;\n leastUsedFeature: string;\n };\n /** Usage by feature */\n byFeature: Array<{\n featureId: string;\n featureName: string;\n category: string;\n usageCount: number;\n uniqueUsers: number;\n adoptionRate: number;\n avgDuration: number;\n successRate: number;\n }>;\n /** Usage by user segment */\n bySegment: Array<{\n segment: string;\n topFeatures: string[];\n avgFeaturesUsed: number;\n }>;\n /** Feature trends */\n trends: TimeSeriesData;\n /** Feature correlations */\n correlations: Array<{\n featureA: string;\n featureB: string;\n correlation: number;\n }>;\n}\n\n// ============================================================================\n// TRACK FEATURE CLASS\n// ============================================================================\n\n/**\n * TrackFeature - Track feature usage events.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides feature tracking including:\n * - Generic feature events\n * - Detailed usage tracking\n * - Feature discovery tracking\n * \n * @example\n * ```typescript\n * const trackFeature = new TrackFeature(webId, endpoint, apiKey);\n * \n * // Simple feature tracking\n * await trackFeature.create({\n * userId: 'user_123',\n * action: 'dark_mode_enabled',\n * properties: { featureName: 'dark_mode' }\n * });\n * \n * // Detailed feature usage\n * await trackFeature.trackUsage({\n * featureId: 'export',\n * featureName: 'Data Export',\n * userId: 'user_123',\n * action: 'completed',\n * duration: 5000,\n * success: true,\n * properties: { format: 'csv' }\n * });\n * ```\n */\n\nexport class TrackFeature extends FlowGridTransport {\n /**\n * Records a feature usage event.\n * \n * @param config - Feature configuration\n * @returns Promise resolving to feature response\n */\n async create(config: FeatureConfig): Promise<FeatureResponse> {\n return await this.init(\"create_tracking\", {\n type: \"feature_usage\",\n subtype: \"create_custom\",\n userId: config.userId,\n category: config.category,\n action: config.action,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks detailed feature usage.\n * \n * @param config - Feature usage configuration\n * @returns Promise resolving to feature response\n */\n async trackUsage(config: FeatureUsageConfig): Promise<FeatureResponse> {\n return await this.init(config.eventName ?? \"feature_usage\", {\n type: \"feature_usage\",\n subtype: config.action,\n featureId: config.featureId,\n featureName: config.featureName,\n userId: config.userId,\n action: config.action,\n duration: config.duration,\n success: config.success,\n category: config.category,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default TrackFeature;\n","/**\n * @fileoverview Core - Prompt & AI Interaction Tracking\n * @module features/core/track-prompt\n * \n * @description\n * Track AI prompt interactions, completions, and user feedback\n * for AI-powered features.\n * \n * @useCases\n * - Track AI prompt submissions\n * - Monitor response quality\n * - Measure user satisfaction\n * - Analyze prompt patterns\n * - Optimize AI features\n * \n * @metrics\n * - Prompts per user\n * - Response acceptance rate\n * - Average response time\n * - User satisfaction score\n * - Token usage\n * \n * @chartData\n * - Line chart: Prompts over time\n * - Bar chart: Prompts by category\n * - Histogram: Response time distribution\n * - Gauge: Satisfaction score\n * \n * @example\n * ```typescript\n * const trackPrompt = new TrackPrompt(webId, endpoint, apiKey);\n * \n * await trackPrompt.create({\n * eventName: 'ai_completion',\n * properties: { \n * promptType: 'code_suggestion',\n * accepted: true \n * }\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { TimeSeriesData } from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Base configuration for prompt events.\n */\nexport interface PromptConfig {\n /** Event name */\n eventName: string;\n /** Event properties */\n properties: Record<string, unknown>;\n}\n\n/**\n * AI prompt submission configuration.\n */\nexport interface PromptSubmissionConfig {\n /** Prompt ID */\n promptId: string;\n /** User ID */\n userId?: string;\n /** Prompt text (may be truncated/hashed for privacy) */\n promptText?: string;\n /** Prompt category/type */\n promptType: string;\n /** Model used */\n model?: string;\n /** Input token count */\n inputTokens?: number;\n /** Session/conversation ID */\n conversationId?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * AI response configuration.\n */\nexport interface PromptResponseConfig {\n /** Prompt ID */\n promptId: string;\n /** Response time (ms) */\n responseTime: number;\n /** Output token count */\n outputTokens?: number;\n /** Whether response completed */\n completed: boolean;\n /** Error if any */\n error?: string;\n /** Response quality (if auto-detected) */\n qualityScore?: number;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * User feedback on AI response.\n */\nexport interface PromptFeedbackConfig {\n /** Prompt ID */\n promptId: string;\n /** User ID */\n userId?: string;\n /** Feedback type */\n feedbackType: 'accepted' | 'rejected' | 'edited' | 'thumbs_up' | 'thumbs_down';\n /** Rating (1-5) */\n rating?: number;\n /** User comment */\n comment?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Prompt tracking response.\n */\nexport interface PromptResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n tracked: boolean;\n promptId?: string;\n };\n timestamp: string;\n}\n\n/**\n * Prompt analytics data.\n */\nexport interface PromptAnalyticsData {\n /** Summary metrics */\n summary: {\n totalPrompts: number;\n uniqueUsers: number;\n avgResponseTime: number;\n acceptanceRate: number;\n avgSatisfactionScore: number;\n totalTokensUsed: number;\n errorRate: number;\n };\n /** Usage by prompt type */\n byType: Array<{\n promptType: string;\n count: number;\n acceptanceRate: number;\n avgResponseTime: number;\n avgTokens: number;\n }>;\n /** Usage by model */\n byModel: Array<{\n model: string;\n count: number;\n avgResponseTime: number;\n errorRate: number;\n }>;\n /** Feedback breakdown */\n feedback: {\n accepted: number;\n rejected: number;\n edited: number;\n thumbsUp: number;\n thumbsDown: number;\n };\n /** Trends */\n trends: TimeSeriesData;\n}\n\n// ============================================================================\n// TRACK PROMPT CLASS\n// ============================================================================\n\n/**\n * TrackPrompt - Track AI prompt interactions.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides prompt tracking including:\n * - Prompt submissions\n * - Response tracking\n * - User feedback\n * - Quality metrics\n * - Usage analytics\n * \n * @example\n * ```typescript\n * const trackPrompt = new TrackPrompt(webId, endpoint, apiKey);\n * \n * // Track prompt submission\n * await trackPrompt.trackSubmission({\n * promptId: 'prompt_123',\n * userId: 'user_456',\n * promptType: 'code_completion',\n * model: 'gpt-4',\n * inputTokens: 150\n * });\n * \n * // Track response\n * await trackPrompt.trackResponse({\n * promptId: 'prompt_123',\n * responseTime: 1200,\n * outputTokens: 300,\n * completed: true\n * });\n * \n * // Track user feedback\n * await trackPrompt.trackFeedback({\n * promptId: 'prompt_123',\n * feedbackType: 'accepted',\n * rating: 5\n * });\n * ```\n */\nexport class TrackPrompt extends FlowGridTransport {\n /**\n * Creates and tracks a prompt event.\n * \n * @param config - Prompt configuration\n * @returns Promise resolving to prompt response\n */\n async create(config: PromptConfig): Promise<PromptResponse> {\n return await this.init(config.eventName, {\n ...config.properties,\n type: \"prompt\",\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a prompt submission.\n * \n * @param config - Prompt submission configuration\n * @returns Promise resolving to prompt response\n */\n async trackSubmission(config: PromptSubmissionConfig): Promise<PromptResponse> {\n return await this.init(\"prompt_submitted\", {\n type: \"prompt\",\n subtype: \"submission\",\n promptId: config.promptId,\n userId: config.userId,\n promptType: config.promptType,\n model: config.model,\n inputTokens: config.inputTokens,\n conversationId: config.conversationId,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a prompt response.\n * \n * @param config - Prompt response configuration\n * @returns Promise resolving to prompt response\n */\n async trackResponse(config: PromptResponseConfig): Promise<PromptResponse> {\n return await this.init(\"prompt_response\", {\n type: \"prompt\",\n subtype: \"response\",\n promptId: config.promptId,\n responseTime: config.responseTime,\n outputTokens: config.outputTokens,\n completed: config.completed,\n error: config.error,\n qualityScore: config.qualityScore,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks user feedback on a prompt response.\n * \n * @param config - Prompt feedback configuration\n * @returns Promise resolving to prompt response\n */\n async trackFeedback(config: PromptFeedbackConfig): Promise<PromptResponse> {\n return await this.init(\"prompt_feedback\", {\n type: \"prompt\",\n subtype: \"feedback\",\n promptId: config.promptId,\n userId: config.userId,\n feedbackType: config.feedbackType,\n rating: config.rating,\n comment: config.comment,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a conversation/multi-turn prompt session.\n * \n * @param conversationId - Conversation ID\n * @param userId - User ID\n * @param turnCount - Number of turns\n * @param totalDuration - Total conversation duration (ms)\n * @returns Promise resolving to prompt response\n */\n async trackConversation(\n conversationId: string,\n userId: string,\n turnCount: number,\n totalDuration: number\n ): Promise<PromptResponse> {\n return await this.init(\"prompt_conversation\", {\n type: \"prompt\",\n subtype: \"conversation\",\n conversationId,\n userId,\n turnCount,\n totalDuration,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default TrackPrompt;\n","/**\n * @fileoverview Core - A/B Testing & Experiments\n * @module features/core/experiment\n * \n * @description\n * Track A/B tests, experiments, and variant assignments\n * to measure impact and optimize product decisions.\n * \n * @useCases\n * - Run A/B tests on features\n * - Track variant assignments\n * - Measure experiment impact\n * - Statistical significance analysis\n * - Multi-variant testing\n * \n * @metrics\n * - Conversion by variant\n * - Statistical significance\n * - Sample size per variant\n * - Experiment duration\n * - Impact on key metrics\n * \n * @chartData\n * - Bar chart: Conversion by variant\n * - Line chart: Metric trends by variant\n * - Table: Experiment results summary\n * - Confidence interval visualization\n * \n * @example\n * ```typescript\n * const experiment = new Experiment(webId, endpoint, apiKey);\n * \n * await experiment.track({\n * eventName: 'checkout_flow_test',\n * variant: 'simplified_v2',\n * properties: { userId: 'user_123' }\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { TimeSeriesData } from \"../../types\";\nimport { ServerCookieReader } from \"../../utils/identity\";\nimport { getCookie } from \"../../utils/storage\";\n\n// ============================================================================\n// HASHING UTILITY (FNV-1a)\n// ============================================================================\n\n/**\n * FNV-1a 32-bit hash — lightweight deterministic hash for variant assignment.\n * Ensures the same user always gets the same variant regardless of device.\n */\nfunction fnv1a(input: string): number {\n let hash = 0x811c9dc5; // FNV offset basis\n for (let i = 0; i < input.length; i++) {\n hash ^= input.charCodeAt(i);\n hash = (hash * 0x01000193) >>> 0; // FNV prime, unsigned 32-bit\n }\n return hash;\n}\n\n/**\n * Global holdout (guide §3) — a fixed, deterministic slice of all visitors\n * who are excluded from every experiment on the site. Lets teams measure\n * the combined lift of \"all active experiments\" against a clean baseline,\n * with zero server round-trips: each visitor's holdout membership is a pure\n * function of their id, so it's stable across page loads and experiments.\n */\nexport const HOLDOUT_PCT = 5;\n\nfunction isInHoldout(userId: string, holdoutPct: number = HOLDOUT_PCT): boolean {\n if (holdoutPct <= 0) return false;\n if (holdoutPct >= 100) return true;\n const hash = fnv1a('holdout::' + userId);\n return (hash % 10000) / 100 < holdoutPct;\n}\n\n/**\n * Mutual exclusion (guide §3) — experiments that could interact (e.g. two\n * tests on the same checkout flow) share an `exclusion_group` id in their\n * properties. Each visitor deterministically \"claims\" exactly one experiment\n * per group, so no one is ever in two experiments from the same group at once.\n */\nfunction filterMutuallyExclusive(\n userId: string,\n experimentId: string,\n exclusionGroup: string,\n groupMemberIds: string[]\n): boolean {\n const members = Array.from(new Set([...groupMemberIds, experimentId])).sort();\n if (members.length <= 1) return true;\n const hash = fnv1a('exclusion::' + exclusionGroup + '::' + userId);\n const claimed = members[hash % members.length];\n return claimed === experimentId;\n}\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for tracking an experiment-related event.\n *\n * Used when sending a generic experiment event with a specific variant\n * and associated properties.\n */\nexport interface ExperimentConfig {\n /** Logical experiment event name */\n eventName: string;\n\n /** Variant identifier associated with this event */\n variant: string;\n\n /** Additional contextual event data */\n properties: Record<string, unknown>;\n}\n\n/**\n * Defines the structure and metadata of an experiment.\n *\n * Used when creating or updating an experiment configuration.\n */\nexport interface ExperimentDefinition {\n /** Unique identifier for the experiment */\n experimentId: string;\n\n /** Human-readable experiment name */\n name: string;\n\n /** Optional description of experiment purpose */\n description?: string;\n\n /**\n * Variants participating in the experiment.\n *\n * Shape matches the verify endpoint and the client-side engine:\n * `{ id, weight }` (with optional human-readable `name`).\n */\n variants: Array<{\n /** Unique identifier for the variant */\n id: string;\n\n /** Traffic allocation weight (relative distribution) */\n weight: number;\n\n /** Optional human-readable variant name */\n name?: string;\n }>;\n\n /** Primary metric used to evaluate experiment success */\n primaryMetric: string;\n\n /** Optional secondary metrics */\n secondaryMetrics?: string[];\n\n /** Optional ISO start date */\n startDate?: string;\n\n /** Optional ISO end date */\n endDate?: string;\n}\n\n/**\n * Configuration for assigning a user to a specific experiment variant.\n *\n * Represents an explicit assignment event.\n */\nexport interface ExperimentAssignmentConfig {\n /** Experiment identifier */\n experimentId: string;\n\n /** Unique user identifier */\n userId: string;\n\n /** Assigned variant identifier */\n variantId: string;\n\n /**\n * Assignment origin:\n * - random: weighted allocation\n * - targeted: rule-based assignment\n * - override: manual or forced assignment\n */\n source?: 'random' | 'targeted' | 'override';\n}\n\n/**\n * Configuration for recording a conversion event within an experiment.\n *\n * Used to attribute metric changes to a specific variant.\n */\nexport interface ExperimentConversionConfig {\n /** Experiment identifier */\n experimentId: string;\n\n /** Unique user identifier */\n userId: string;\n\n /** Variant associated with the user */\n variantId: string;\n\n /** Metric name being recorded */\n metricName: string;\n\n /** Numeric metric value */\n value: number;\n}\n\n/**\n * Standardized response returned from experiment tracking operations.\n *\n * Ensures consistent structure across assignment, exposure,\n * definition, and conversion events.\n */\nexport interface ExperimentResponse {\n /** Event name associated with the request */\n eventName: string;\n\n /** Properties sent with the event */\n properties: Record<string, unknown>;\n\n /** Server acknowledgment metadata */\n response: {\n /** Indicates whether the event was successfully recorded */\n tracked: boolean;\n\n /** Optional experiment identifier */\n experimentId?: string;\n\n /** Optional variant identifier */\n variantId?: string;\n };\n\n /** ISO timestamp of the recorded event */\n timestamp: string;\n}\n\n/**\n * Aggregated results for a single experiment.\n *\n * Represents computed performance metrics per variant.\n */\nexport interface ExperimentResultsData {\n /** Experiment metadata */\n experiment: {\n experimentId: string;\n name: string;\n status: 'running' | 'paused' | 'completed';\n startDate: string;\n /** Duration in days */\n duration: number;\n };\n\n /** Variant-level performance metrics */\n variants: Array<{\n variantId: string;\n name: string;\n\n /** Total participants assigned */\n participants: number;\n\n /** Total recorded conversions */\n conversions: number;\n\n /** conversions / participants */\n conversionRate: number;\n\n /** Relative improvement compared to baseline */\n improvement: number;\n\n /** Statistical confidence level (0–1 or percentage depending on backend) */\n confidence: number;\n\n /** Indicates highest performing variant */\n isWinner: boolean;\n\n /** Indicates whether statistical threshold is met */\n isStatisticallySignificant: boolean;\n }>;\n\n /** Time-series metric trends per metric name */\n trends: Record<string, TimeSeriesData>;\n\n /** System-generated recommendation */\n recommendation: {\n action: 'ship_winner' | 'continue' | 'stop' | 'needs_more_data';\n winningVariant?: string;\n confidence: number;\n reason: string;\n };\n}\n\n/**\n * Aggregated analytics across all experiments.\n *\n * Provides high-level performance and activity overview.\n */\nexport interface ExperimentAnalyticsData {\n /** Platform-wide experiment summary */\n summary: {\n totalExperiments: number;\n activeExperiments: number;\n completedExperiments: number;\n avgExperimentDuration: number;\n winRate: number;\n };\n\n /** Currently active experiments */\n active: Array<{\n experimentId: string;\n name: string;\n participants: number;\n daysSinceStart: number;\n leadingVariant: string;\n confidence: number;\n }>;\n\n /** Recently completed experiments */\n recentResults: Array<{\n experimentId: string;\n name: string;\n winner: string;\n improvement: number;\n completedDate: string;\n }>;\n}\n\nexport interface VariantResponse {\n experimentId: string;\n userId?: string;\n variantId: string;\n assignedAt: string;\n}\n\n/**\n * A single declarative DOM change applied for an assigned variant — the no-code\n * rendering primitive. The engine applies these on `init()` so a marketer can\n * run a test (different headline, hidden banner, redirect to another page)\n * without an engineer hand-writing `if (variant === \"B\")` branching.\n */\nexport type VariantChange =\n | { type: 'redirect'; url: string }\n | { type: 'text'; selector: string; value: string }\n | { type: 'html'; selector: string; value: string }\n | { type: 'hide'; selector: string }\n | { type: 'style'; selector: string; property: string; value: string }\n | { type: 'addClass'; selector: string; value: string };\n\n/**\n * Local experiment variant definition used by the client-side\n * assignment engine (ported from sdk.js).\n */\nexport interface ExperimentVariant {\n /** Variant identifier */\n id: string;\n /** Traffic allocation weight */\n weight: number;\n /** Optional human-readable label (shown in the dashboard). */\n label?: string;\n /**\n * Split-URL / redirect test shortcut — visitors assigned this variant are\n * sent to `redirectUrl`. Equivalent to a single `{ type: 'redirect' }` change.\n */\n redirectUrl?: string;\n /** Declarative DOM changes applied when this variant is assigned. */\n changes?: VariantChange[];\n}\n\n/**\n * Lightweight experiment config passed to `initExperiments()`.\n */\nexport interface ExperimentInitConfig {\n /** Unique experiment identifier */\n id: string;\n /** Weighted variant list */\n variants: ExperimentVariant[];\n /** Optional properties forwarded to the verify endpoint */\n properties?: Record<string, unknown>;\n}\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst ASSIGNMENT_STORAGE_KEY = 'fg_exp_assignments';\nconst ASSIGNMENT_COOKIE_KEY = 'fg_exp_assignments';\nconst ASSIGNMENT_COOKIE_MAX_AGE_DAYS = 365;\n\n// ============================================================================\n// EXPERIMENT CLASS\n// ============================================================================\n\n/**\n * Experiment - A/B Testing & Experimentation SDK\n *\n * @extends FlowGridTransport\n *\n * @description\n * Provides structured experiment lifecycle management including:\n * - Experiment definition\n * - Deterministic or server-based variant assignment\n * - Exposure tracking\n * - Conversion tracking\n * - Statistical results & analysis\n *\n * This class enforces correct HTTP semantics:\n *\n * Exposure and conversion events are strictly POST to preserve:\n * - Statistical validity\n * - Unique participant counting\n * - Accurate conversion attribution\n * - Idempotent replay safety\n *\n * @lifecycle\n * 1. Define experiment\n * 2. Assign user to variant\n * 3. Track exposure (when user actually sees variant)\n * 4. Track conversions (primary/secondary metrics)\n * 5. Query results & statistical analysis\n *\n * @statistical_model\n * - Variant-level sample sizes tracked independently\n * - Conversion rates calculated per variant\n * - Confidence intervals computed server-side\n * - Statistical significance evaluated against control\n *\n * @guarantees\n * - Assignment is explicitly recorded\n * - Exposure tracking is immutable\n * - Conversion events are attributable to assigned variant\n * - Results queries never mutate experiment state\n *\n * @example\n * ```typescript\n * const experiment = new Experiment(webId, endpoint, apiKey);\n *\n * // 1. Define experiment\n * await experiment.define({\n * experimentId: 'checkout_v2',\n * name: 'Simplified Checkout',\n * variants: [\n * { variantId: 'control', name: 'Current', weight: 50 },\n * { variantId: 'treatment', name: 'Simplified', weight: 50 }\n * ],\n * primaryMetric: 'checkout_completed'\n * });\n *\n * // 2. Assign user\n * await experiment.assign({\n * experimentId: 'checkout_v2',\n * userId: 'user_123',\n * variantId: 'treatment'\n * });\n *\n * // 3. Track exposure\n * await experiment.trackExposure('checkout_v2', 'user_123', 'treatment');\n *\n * // 4. Track conversion\n * await experiment.trackConversion({\n * experimentId: 'checkout_v2',\n * userId: 'user_123',\n * variantId: 'treatment',\n * metricName: 'checkout_completed',\n * value: 1\n * });\n *\n * // 5. Fetch results\n * const results = await experiment.getResults('checkout_v2');\n * ```\n */\nexport class Experiment extends FlowGridTransport {\n\n // ---------------------------------------------------------------------------\n // CLIENT-SIDE EXPERIMENT ENGINE STATE\n // ---------------------------------------------------------------------------\n\n /** Verified experiment configs: { [experiment_id]: { variants, properties, verified } } */\n private experiments: Record<string, {\n variants: ExperimentVariant[];\n properties: Record<string, unknown>;\n verified: boolean;\n }> = {};\n\n /** Local variant assignments: { [experiment_id]: variant_id } */\n private assignments: Record<string, string> = {};\n\n // ---------------------------------------------------------------------------\n // CLIENT-SIDE ENGINE — SSR-safe persistence (localStorage + cookie mirror)\n // ---------------------------------------------------------------------------\n\n /**\n * Decode the cookie’s URL-encoded JSON payload into an assignment map.\n * Returns `{}` on any parse failure so a stale cookie never throws.\n */\n private parseAssignmentCookie(raw: string | null): Record<string, string> {\n if (!raw) return {};\n try {\n const decoded = decodeURIComponent(raw);\n const parsed = JSON.parse(decoded);\n return parsed && typeof parsed === 'object' ? parsed : {};\n } catch {\n return {};\n }\n }\n\n private readAssignmentCookie(): Record<string, string> {\n return this.parseAssignmentCookie(getCookie(ASSIGNMENT_COOKIE_KEY));\n }\n\n /**\n * Persist `this.assignments` to a first-party cookie so SSR requests on\n * subsequent navigations see the same variant. Best-effort — no-op in\n * non-browser contexts or when document.cookie is unavailable.\n */\n private writeAssignmentCookie(): void {\n try {\n if (typeof document === 'undefined') return;\n const value = encodeURIComponent(JSON.stringify(this.assignments));\n const maxAge = ASSIGNMENT_COOKIE_MAX_AGE_DAYS * 24 * 60 * 60;\n const sameSite = 'Lax';\n const secure = typeof location !== 'undefined' && location.protocol === 'https:'\n ? '; Secure'\n : '';\n document.cookie = `${ASSIGNMENT_COOKIE_KEY}=${value}; Path=/; Max-Age=${maxAge}; SameSite=${sameSite}${secure}`;\n } catch { /* SSR or restricted env */ }\n }\n\n private loadAssignments(): Record<string, string> {\n // 1. localStorage — authoritative on the client\n try {\n if (typeof localStorage !== 'undefined') {\n const stored = localStorage.getItem(ASSIGNMENT_STORAGE_KEY);\n if (stored) {\n const parsed = JSON.parse(stored);\n if (parsed && typeof parsed === 'object') return parsed;\n }\n }\n } catch { /* SSR or privacy mode */ }\n\n // 2. Cookie fallback — lets SSR + first-paint resolve a variant\n const cookieAssignments = this.readAssignmentCookie();\n if (Object.keys(cookieAssignments).length > 0) return cookieAssignments;\n\n return {};\n }\n\n private saveAssignments(): void {\n try {\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem(ASSIGNMENT_STORAGE_KEY, JSON.stringify(this.assignments));\n }\n } catch { /* SSR or privacy mode */ }\n // Mirror to cookie so the next SSR request can hydrate without flashing.\n this.writeAssignmentCookie();\n }\n\n /**\n * Inject assignments from server context (SSR/Node).\n * Use this when assignments were resolved server-side and need to be\n * available before localStorage is accessible.\n */\n injectAssignments(assignments: Record<string, string>): void {\n this.assignments = { ...this.assignments, ...assignments };\n }\n\n /**\n * Returns all current assignments (for SSR hydration).\n */\n getAssignments(): Record<string, string> {\n return { ...this.assignments };\n }\n\n /**\n * Hydrate assignments from a server-side cookie reader\n * (Next.js `cookies()`, Nuxt `useCookie()`, Hono ctx, etc.).\n *\n * @example\n * ```ts\n * import { cookies } from \"next/headers\";\n * fg.experiments.client.hydrateFromCookies(cookies());\n * const variant = fg.experiments.variant(\"checkout_v2\");\n * ```\n */\n hydrateFromCookies(reader: ServerCookieReader): void {\n const raw = reader.get(ASSIGNMENT_COOKIE_KEY);\n const value = typeof raw === 'string' ? raw : raw?.value ?? null;\n const parsed = this.parseAssignmentCookie(value ?? null);\n if (Object.keys(parsed).length > 0) this.injectAssignments(parsed);\n }\n\n /**\n * Hydrate assignments from a raw `Cookie` request header string.\n * Useful for runtimes without a structured cookie reader (Workers, Bun, etc.).\n */\n hydrateFromCookieHeader(cookieHeader: string | null | undefined): void {\n if (!cookieHeader) return;\n const parts = cookieHeader.split(/;\\s*/);\n const prefix = `${ASSIGNMENT_COOKIE_KEY}=`;\n const match = parts.find(p => p.startsWith(prefix));\n if (!match) return;\n const parsed = this.parseAssignmentCookie(match.slice(prefix.length));\n if (Object.keys(parsed).length > 0) this.injectAssignments(parsed);\n }\n\n /**\n * Serialise current assignments as a `Set-Cookie` header value.\n * Use server-side after a server-only assignment so the response can\n * seed the visitor’s browser with the same variant.\n */\n serializeAssignmentCookie(): string {\n const value = encodeURIComponent(JSON.stringify(this.assignments));\n const maxAge = ASSIGNMENT_COOKIE_MAX_AGE_DAYS * 24 * 60 * 60;\n return `${ASSIGNMENT_COOKIE_KEY}=${value}; Path=/; Max-Age=${maxAge}; SameSite=Lax`;\n }\n\n /**\n * Clear all locally cached assignments (cookie + localStorage + memory).\n * Useful for logout flows or test resets.\n */\n clearAssignments(): void {\n this.assignments = {};\n try {\n if (typeof localStorage !== 'undefined') localStorage.removeItem(ASSIGNMENT_STORAGE_KEY);\n } catch { /* noop */ }\n try {\n if (typeof document !== 'undefined') {\n document.cookie = `${ASSIGNMENT_COOKIE_KEY}=; Path=/; Max-Age=0; SameSite=Lax`;\n }\n } catch { /* noop */ }\n }\n\n // ---------------------------------------------------------------------------\n // CLIENT-SIDE ENGINE — stable anonymous ID\n // ---------------------------------------------------------------------------\n\n private static readonly ANON_ID_KEY = 'fg_anon_id';\n\n /**\n * Returns a stable anonymous ID. Generates a UUID on first call and\n * persists it in localStorage so the same anonymous visitor always\n * receives a consistent variant assignment.\n */\n private getOrCreateAnonId(): string {\n try {\n if (typeof localStorage !== 'undefined') {\n const existing = localStorage.getItem(Experiment.ANON_ID_KEY);\n if (existing) return existing;\n\n const id = typeof crypto !== 'undefined' && crypto.randomUUID\n ? crypto.randomUUID()\n : 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {\n const r = (Math.random() * 16) | 0;\n return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);\n });\n\n localStorage.setItem(Experiment.ANON_ID_KEY, id);\n return id;\n }\n } catch { /* SSR or privacy mode */ }\n // Non-browser fallback — still deterministic within process lifetime\n return 'anon_' + fnv1a(String(Math.random())).toString(36);\n }\n\n // ---------------------------------------------------------------------------\n // CLIENT-SIDE ENGINE — deterministic hash-based assignment\n // ---------------------------------------------------------------------------\n\n /**\n * Deterministic variant assignment using FNV-1a hash.\n * Same userId + experimentId always produces the same variant.\n */\n private deterministicAssign(userId: string, experimentId: string, variants: ExperimentVariant[]): string {\n const hashValue = fnv1a(userId + experimentId);\n let totalWeight = 0;\n for (const v of variants) totalWeight += v.weight;\n\n const bucket = (hashValue % 10000) / 10000 * totalWeight;\n let cumulative = 0;\n for (const v of variants) {\n cumulative += v.weight;\n if (bucket < cumulative) return v.id;\n }\n return variants[variants.length - 1].id;\n }\n\n private assignVariant(\n experimentId: string,\n variants: ExperimentVariant[],\n properties: Record<string, unknown> = {},\n exclusionGroupMembers: string[] = []\n ): void {\n // Reload from localStorage to handle cross-tab race conditions\n this.assignments = this.loadAssignments();\n\n const existing = this.assignments[experimentId];\n\n // Resolve a userId for the hash: prefer identityManager's visitorId, then fall back\n const userId = this.identityManager?.getVisitorId?.()\n || this.visitorId\n || this.getOrCreateAnonId();\n\n // \"Ship B\" short-circuit (guide §6 `initExperiment` pattern): once the\n // dashboard declares a winner, `properties.status`/`winner_id` on the\n // definition are enough to converge every visitor — new or already\n // bucketed — onto it. Stopping a test becomes a data change (one row\n // update, picked up on the next page load), not a redeploy.\n const status = typeof properties.status === 'string' ? properties.status : undefined;\n const winnerId = typeof properties.winner_id === 'string' ? properties.winner_id : undefined;\n if (status === 'complete' && winnerId && variants.some(v => v.id === winnerId)) {\n if (existing === winnerId) return;\n\n this.assignments[experimentId] = winnerId;\n this.saveAssignments();\n\n this.init('experiment_assignment', {\n type: 'experiment',\n subtype: 'assignment',\n experimentId,\n userId,\n variantId: winnerId,\n source: 'shipped_winner',\n assignmentSource: 'sdk_init',\n timestamp: new Date().toISOString(),\n }).catch(() => {});\n return;\n }\n\n if (existing && variants.some(v => v.id === existing)) {\n // Existing assignment still valid — keep it\n return;\n }\n\n // Traffic-splitting primitives (guide §3) — these only gate *new*\n // assignments. A visitor already bucketed into this experiment keeps\n // their assignment even if they'd now land in the holdout or lose an\n // exclusion-group claim; retroactively pulling people out would bias\n // the very results the test is trying to measure.\n if (isInHoldout(userId)) return;\n\n const exclusionGroup = typeof properties.exclusion_group === 'string' ? properties.exclusion_group : undefined;\n if (exclusionGroup && !filterMutuallyExclusive(userId, experimentId, exclusionGroup, exclusionGroupMembers)) {\n return;\n }\n\n // Deterministic assignment via hash\n const assignedVariantId = this.deterministicAssign(userId, experimentId, variants);\n this.assignments[experimentId] = assignedVariantId;\n this.saveAssignments();\n\n // Fire-and-forget assignment event.\n // The backend AssignmentHandlerNode requires `userId` in properties.\n this.init('experiment_assignment', {\n type: 'experiment',\n subtype: 'assignment',\n experimentId,\n userId,\n variantId: assignedVariantId,\n source: 'random',\n assignmentSource: 'sdk_init',\n timestamp: new Date().toISOString(),\n }).catch(() => {});\n }\n\n // ---------------------------------------------------------------------------\n // CLIENT-SIDE ENGINE — server verification\n // ---------------------------------------------------------------------------\n\n /**\n * Groups experiment ids by their `properties.exclusion_group`, so\n * `assignVariant` can deterministically pick exactly one experiment per\n * group for each visitor (guide §3 mutual exclusion).\n */\n private computeExclusionGroupMembers(\n items: { experiment_id: string; properties?: Record<string, unknown> }[]\n ): Map<string, string[]> {\n const groups = new Map<string, string[]>();\n for (const item of items) {\n const group = typeof item.properties?.exclusion_group === 'string' ? item.properties.exclusion_group : undefined;\n if (!group) continue;\n if (!groups.has(group)) groups.set(group, []);\n groups.get(group)!.push(item.experiment_id);\n }\n return groups;\n }\n\n /**\n * Assigns variants straight from the locally supplied configs — the\n * degraded path when the verify endpoint is unreachable OR returns an\n * error (429 rate limit, 5xx). An unverified local assignment that emits\n * events beats silently disabling the experiment for this visitor.\n */\n private assignFromLocalConfigs(configs: ExperimentInitConfig[]): void {\n const groupMembers = this.computeExclusionGroupMembers(\n configs.map(exp => ({ experiment_id: exp.id, properties: exp.properties }))\n );\n for (const exp of configs) {\n this.experiments[exp.id] = {\n variants: exp.variants,\n properties: exp.properties || {},\n verified: false,\n };\n const exclusionGroup = typeof exp.properties?.exclusion_group === 'string' ? exp.properties.exclusion_group : undefined;\n this.assignVariant(\n exp.id,\n exp.variants,\n exp.properties || {},\n exclusionGroup ? groupMembers.get(exclusionGroup) ?? [] : []\n );\n }\n }\n\n private async verifyExperiments(configs: ExperimentInitConfig[]): Promise<void> {\n const payload = {\n web_id: this.webId,\n experiments: configs.map(exp => ({\n experiment_id: exp.id,\n variants: exp.variants,\n properties: exp.properties || {},\n })),\n };\n\n try {\n const res = await fetch(`${this.endpoint}/api/v1/experiments/verify`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n });\n const response = await res.json();\n\n if (response.ok && Array.isArray(response.data)) {\n const groupMembers = this.computeExclusionGroupMembers(response.data);\n for (const item of response.data) {\n if (item.action !== 'error') {\n this.experiments[item.experiment_id] = {\n variants: item.variants,\n properties: item.properties ?? {},\n verified: true,\n };\n const exclusionGroup = typeof item.properties?.exclusion_group === 'string' ? item.properties.exclusion_group : undefined;\n this.assignVariant(\n item.experiment_id,\n item.variants,\n item.properties ?? {},\n exclusionGroup ? groupMembers.get(exclusionGroup) ?? [] : []\n );\n }\n }\n } else {\n // Server replied but refused (rate limit, validation, 5xx) — degrade\n // to local assignment rather than leaving the visitor unassigned.\n this.assignFromLocalConfigs(configs);\n }\n } catch {\n // Network failure — fall back to local config\n this.assignFromLocalConfigs(configs);\n }\n }\n\n // ---------------------------------------------------------------------------\n // CLIENT-SIDE ENGINE — public init\n // ---------------------------------------------------------------------------\n\n /**\n * Initialise the client-side experiment engine.\n *\n * Verifies experiment configs against the API, assigns variants using\n * weighted random allocation, and persists assignments in localStorage\n * under `fg_exp_assignments`.\n *\n * @param experiments - Array of experiment configs to register\n */\n async initExperiments(experiments: ExperimentInitConfig[]): Promise<void> {\n this.assignments = this.loadAssignments();\n\n // Anti-flicker: hide every element a variant might change *before* the\n // (async) verify round-trip, so visitors never see the original flash to\n // the variant. A safety timeout reveals everything even if init stalls.\n const selectors = this.collectChangeSelectors(experiments);\n if (selectors.length > 0) this.injectAntiFlicker(selectors);\n\n try {\n if (experiments.length > 0) {\n await this.verifyExperiments(experiments);\n }\n this.applyAssignedChanges();\n } finally {\n this.removeAntiFlicker();\n }\n }\n\n // ---------------------------------------------------------------------------\n // CLIENT-SIDE ENGINE — declarative variant rendering (no-code)\n // ---------------------------------------------------------------------------\n\n private antiFlickerStyleEl: HTMLStyleElement | null = null;\n private antiFlickerTimer: ReturnType<typeof setTimeout> | null = null;\n /** Experiments whose assigned variant has already had its changes applied + exposure fired. */\n private appliedExperiments = new Set<string>();\n\n /** Normalises a variant's `redirectUrl` shortcut + `changes` into one list. */\n private variantChanges(variant: ExperimentVariant | undefined): VariantChange[] {\n if (!variant) return [];\n const out: VariantChange[] = [];\n if (variant.redirectUrl) out.push({ type: 'redirect', url: variant.redirectUrl });\n if (Array.isArray(variant.changes)) out.push(...variant.changes);\n return out;\n }\n\n /** All non-redirect selectors across the passed configs — used for anti-flicker. */\n private collectChangeSelectors(experiments: ExperimentInitConfig[]): string[] {\n const set = new Set<string>();\n for (const exp of experiments) {\n for (const v of exp.variants) {\n for (const c of this.variantChanges(v)) {\n if (c.type !== 'redirect') set.add(c.selector);\n }\n }\n }\n return Array.from(set);\n }\n\n private injectAntiFlicker(selectors: string[]): void {\n if (typeof document === 'undefined' || this.antiFlickerStyleEl) return;\n try {\n const style = document.createElement('style');\n style.setAttribute('data-flowgrid-antiflicker', '');\n style.textContent = `${selectors.join(',')}{visibility:hidden !important}`;\n (document.head || document.documentElement).appendChild(style);\n this.antiFlickerStyleEl = style;\n // Never leave the page hidden — reveal after 4s no matter what.\n this.antiFlickerTimer = setTimeout(() => this.removeAntiFlicker(), 4000);\n } catch { /* ignore */ }\n }\n\n private removeAntiFlicker(): void {\n if (this.antiFlickerTimer) { clearTimeout(this.antiFlickerTimer); this.antiFlickerTimer = null; }\n try { this.antiFlickerStyleEl?.remove(); } catch { /* ignore */ }\n this.antiFlickerStyleEl = null;\n }\n\n private applyDomChange(change: Exclude<VariantChange, { type: 'redirect' }>): void {\n if (typeof document === 'undefined') return;\n const nodes = document.querySelectorAll<HTMLElement>(change.selector);\n nodes.forEach((el) => {\n switch (change.type) {\n case 'text': el.textContent = change.value; break;\n case 'html': el.innerHTML = change.value; break;\n case 'hide': el.style.display = 'none'; break;\n case 'style': el.style.setProperty(change.property, change.value); break;\n case 'addClass': el.classList.add(...change.value.split(/\\s+/).filter(Boolean)); break;\n }\n });\n }\n\n /**\n * Apply the assigned variant's declarative changes for every registered\n * experiment, then fire one exposure per experiment. Redirects take priority\n * and short-circuit (the new page re-runs init). DOM changes wait for the\n * document to be ready. Idempotent per experiment.\n */\n private applyAssignedChanges(): void {\n if (typeof window === 'undefined') return;\n\n for (const [experimentId, config] of Object.entries(this.experiments)) {\n if (this.appliedExperiments.has(experimentId)) continue;\n const variantId = this.assignments[experimentId];\n if (!variantId) continue;\n const variant = config.variants.find((v) => v.id === variantId);\n const changes = this.variantChanges(variant);\n\n // Redirect first — if we're not already on the target, go there. The\n // destination page's own init() handles exposure for the redirected view.\n const redirect = changes.find((c): c is Extract<VariantChange, { type: 'redirect' }> => c.type === 'redirect');\n if (redirect) {\n const target = new URL(redirect.url, window.location.href);\n if (target.href !== window.location.href) {\n this.appliedExperiments.add(experimentId);\n this.trackLocalExposure(experimentId).catch(() => {});\n window.location.replace(target.href);\n return;\n }\n }\n\n const domChanges = changes.filter(\n (c): c is Exclude<VariantChange, { type: 'redirect' }> => c.type !== 'redirect'\n );\n const run = () => domChanges.forEach((c) => this.applyDomChange(c));\n if (typeof document !== 'undefined' && document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', run, { once: true });\n } else {\n run();\n }\n\n this.appliedExperiments.add(experimentId);\n // Auto-fire exposure so no-code users get results without extra calls.\n this.trackLocalExposure(experimentId).catch(() => {});\n }\n }\n\n /**\n * Manually (re-)apply the assigned variant's changes for one experiment —\n * useful after client-side navigation re-renders the DOM.\n */\n applyVariant(experimentId: string): void {\n this.appliedExperiments.delete(experimentId);\n this.applyAssignedChanges();\n }\n\n /**\n * Zero-config init: fetch this site's running experiments from the server and\n * run them. This is what lets the no-build snippet run experiments with no\n * author JS — the dashboard is the only place experiments are configured.\n *\n * Best-effort: a network/permission failure is a silent no-op (the page just\n * shows its control experience), never a thrown error.\n */\n async initFromServer(): Promise<void> {\n try {\n const res = await fetch(\n `${this.endpoint}/api/v1/experiments/active?web_id=${encodeURIComponent(this.webId)}`\n );\n const json = await res.json();\n if (json?.ok && Array.isArray(json.data) && json.data.length > 0) {\n await this.initExperiments(json.data as ExperimentInitConfig[]);\n }\n } catch {\n /* offline, blocked, or no experiments — leave the control experience */\n }\n }\n\n /**\n * Returns the locally assigned variant for an experiment.\n *\n * Reads from the in-memory assignment map (backed by localStorage).\n * Returns `null` if no assignment exists.\n */\n getLocalVariant(experimentId: string): string | null {\n // Re-read from storage to stay in sync with other tabs\n this.assignments = this.loadAssignments();\n return this.assignments[experimentId] ?? null;\n }\n\n /**\n * Track a conversion against the locally assigned variant.\n *\n * No-op if the visitor has no assignment for this experiment.\n */\n async trackLocalConversion(\n experimentId: string,\n metricName: string = 'conversion',\n metricValue: number = 1\n ): Promise<void> {\n const variantId = this.getLocalVariant(experimentId);\n if (!variantId) return;\n\n // Backend ConversionHandlerNode requires `userId` — resolve from\n // identity manager or the persisted anonymous ID.\n const userId = this.identityManager?.getVisitorId?.()\n || this.visitorId\n || this.getOrCreateAnonId();\n\n await this.init('experiment_conversion', {\n type: 'experiment',\n subtype: 'conversion',\n experimentId,\n userId,\n variantId,\n metricName,\n value: metricValue,\n timestamp: new Date().toISOString(),\n });\n }\n\n /**\n * Track an exposure for the locally assigned variant, resolving the user id\n * automatically (identity manager → visitor id → persisted anonymous id).\n *\n * No-op if the visitor has no assignment for this experiment. Use this in the\n * browser so callers don't have to thread a user id through every component.\n */\n async trackLocalExposure(experimentId: string): Promise<void> {\n const variantId = this.getLocalVariant(experimentId);\n if (!variantId) return;\n\n const userId = this.identityManager?.getVisitorId?.()\n || this.visitorId\n || this.getOrCreateAnonId();\n\n await this.trackExposure(experimentId, userId, variantId);\n }\n\n // ---------------------------------------------------------------------------\n // SERVER-SIDE EXPERIMENT METHODS\n //\n // Backend supports write events only: definition, assignment, conversion,\n // exposure, plus the deprecated generic custom shim below. Read endpoints\n // (results / analytics / variant lookup) are not exposed via the SDK\n // functions router.\n // ---------------------------------------------------------------------------\n\n /**\n * @deprecated Prefer `assign()`, `trackExposure()`, or `trackConversion()`.\n * Retained as a write-only compatibility shim for generic experiment events.\n */\n async track(config: ExperimentConfig): Promise<ExperimentResponse> {\n return await this.init(config.eventName, {\n ...config.properties,\n type: \"experiment\",\n subtype: \"custom\",\n variant: config.variant,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Defines a new experiment.\n */\n async define(definition: ExperimentDefinition): Promise<ExperimentResponse> {\n return await this.init(\"experiment_definition\", {\n type: \"experiment\",\n subtype: \"definition\",\n ...definition,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Assigns a user to a variant.\n */\n async assign(config: ExperimentAssignmentConfig): Promise<ExperimentResponse> {\n return await this.init(\"experiment_assignment\", {\n type: \"experiment\",\n subtype: \"assignment\",\n experimentId: config.experimentId,\n userId: config.userId,\n variantId: config.variantId,\n source: config.source || \"random\",\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Records a conversion event for an experiment.\n */\n async trackConversion(config: ExperimentConversionConfig): Promise<ExperimentResponse> {\n return await this.init(\"experiment_conversion\", {\n type: \"experiment\",\n subtype: \"conversion\",\n experimentId: config.experimentId,\n userId: config.userId,\n variantId: config.variantId,\n metricName: config.metricName,\n value: config.value,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Records that a user was exposed to a variant.\n */\n async trackExposure(\n experimentId: string,\n userId: string,\n variantId: string\n ): Promise<ExperimentResponse> {\n return await this.init(\"experiment_exposure\", {\n type: \"experiment\",\n subtype: \"exposure\",\n experimentId,\n userId,\n variantId,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default Experiment;\n","/**\n * @fileoverview Product Analytics - Page View Tracking\n * @module features/analytics/page-views\n * \n * @description\n * Track page views with rich context including scroll depth, time on page,\n * referrer information, and user engagement signals.\n * \n * @useCases\n * - Track which pages users visit most frequently\n * - Measure time spent on content pages\n * - Identify high bounce rate pages\n * - Analyze user navigation patterns\n * - Monitor landing page performance\n * \n * @metrics\n * - Total page views\n * - Unique page views \n * - Average time on page\n * - Bounce rate by page\n * - Scroll depth distribution\n * - Entry/exit page analysis\n * \n * @chartData\n * - Line chart: Page views over time\n * - Bar chart: Top pages by views\n * - Heatmap: Scroll depth by page\n * - Funnel: Page flow analysis\n * \n * @example\n * ```typescript\n * const pageViews = new PageViews(webId, endpoint, apiKey);\n * \n * // Track a page view\n * await pageViews.track({\n * url: window.location.href,\n * path: window.location.pathname,\n * title: document.title,\n * referrer: document.referrer\n * });\n * \n * // Track with scroll depth\n * await pageViews.trackScrollDepth({\n * path: '/blog/article-1',\n * scrollDepth: 75\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { getWindow, getDocument, isBrowser, combineTeardowns, Teardown } from \"../../utils/browser\";\nimport { \n PageView, \n TrafficMetrics, \n TimeSeriesData, \n ChartDataPoint,\n DateRangeFilter,\n PaginationParams \n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for tracking a page view.\n */\nexport interface PageViewConfig {\n /** Full page URL */\n url: string;\n /** Page path (without domain) */\n path: string;\n /** Page title */\n title?: string;\n /** Referrer URL */\n referrer?: string;\n /** User ID (if authenticated) */\n userId?: string;\n /** Session ID */\n sessionId?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Configuration for tracking scroll depth.\n */\nexport interface ScrollDepthConfig {\n /** Page path */\n path: string;\n /** Scroll depth percentage (0-100) */\n scrollDepth: number;\n /** Time at this depth (seconds) */\n timeAtDepth?: number;\n}\n\n/**\n * Configuration for tracking time on page.\n */\nexport interface TimeOnPageConfig {\n /** Page path */\n path: string;\n /** Time spent in seconds */\n duration: number;\n /** Whether user engaged (scrolled, clicked, etc.) */\n engaged?: boolean;\n}\n\n/**\n * Page view response from API.\n */\nexport interface PageViewResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n pageViewId: string;\n tracked: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Page analytics data for charts.\n */\nexport interface PageAnalyticsData {\n /** Top pages */\n topPages: Array<{\n path: string;\n title: string;\n views: number;\n uniqueViews: number;\n avgTimeOnPage: number;\n bounceRate: number;\n }>;\n /** Page views time series */\n timeSeries: TimeSeriesData;\n /** Summary metrics */\n summary: {\n totalPageViews: number;\n uniquePageViews: number;\n avgTimeOnPage: number;\n avgScrollDepth: number;\n bounceRate: number;\n };\n}\n\n// ============================================================================\n// PAGE VIEWS CLASS\n// ============================================================================\n\n/**\n * PageViews - Track and analyze page view events.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides comprehensive page view tracking with support for:\n * - Basic page view events\n * - Scroll depth tracking\n * - Time on page measurement\n * - Exit intent detection\n * - Page engagement scoring\n * \n * @example\n * ```typescript\n * const pageViews = new PageViews(webId, endpoint, apiKey);\n * \n * // Basic page view\n * await pageViews.track({\n * url: 'https://example.com/products',\n * path: '/products',\n * title: 'Our Products'\n * });\n * ```\n */\nexport class PageViews extends FlowGridTransport {\n /**\n * Tracks a page view event.\n * \n * @param config - Page view configuration\n * @returns Promise resolving to the API response\n * \n * @example\n * ```typescript\n * await pageViews.track({\n * url: window.location.href,\n * path: window.location.pathname,\n * title: document.title\n * });\n * ```\n */\n async track(config: PageViewConfig): Promise<PageViewResponse> {\n return await this.init(\"page_view\", {\n type: \"page_view\",\n url: config.url,\n path: config.path,\n title: config.title || \"\",\n referrer: config.referrer || \"\",\n userId: config.userId,\n sessionId: config.sessionId,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks scroll depth on a page.\n * \n * @param config - Scroll depth configuration\n * @returns Promise resolving to the API response\n * \n * @example\n * ```typescript\n * // Track when user scrolls to 50%\n * await pageViews.trackScrollDepth({\n * path: '/article/how-to-guide',\n * scrollDepth: 50\n * });\n * ```\n */\n async trackScrollDepth(config: ScrollDepthConfig): Promise<PageViewResponse> {\n return await this.init(\"scroll_depth\", {\n type: \"scroll_depth\",\n path: config.path,\n scrollDepth: Math.min(100, Math.max(0, config.scrollDepth)),\n timeAtDepth: config.timeAtDepth,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks time spent on a page.\n * \n * @param config - Time on page configuration\n * @returns Promise resolving to the API response\n * \n * @example\n * ```typescript\n * // Track on page unload\n * window.addEventListener('beforeunload', () => {\n * pageViews.trackTimeOnPage({\n * path: '/pricing',\n * duration: 45,\n * engaged: true\n * });\n * });\n * ```\n */\n async trackTimeOnPage(config: TimeOnPageConfig): Promise<PageViewResponse> {\n return await this.init(\"time_on_page\", {\n type: \"time_on_page\",\n path: config.path,\n duration: config.duration,\n engaged: config.engaged ?? false,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a page exit event.\n * \n * @param path - Page path being exited\n * @param duration - Time spent on page\n * @param scrollDepth - Final scroll depth\n * @returns Promise resolving to the API response\n */\n async trackExit(\n path: string, \n duration: number, \n scrollDepth: number\n ): Promise<PageViewResponse> {\n return await this.init(\"page_exit\", {\n type: \"page_exit\",\n path,\n duration,\n scrollDepth,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Auto-track page views for both classic full-page navigations and SPA\n * route changes (history.pushState / replaceState / popstate). Also wires\n * scroll-depth thresholds (25/50/75/100%) and a final time-on-page event\n * when the page is hidden or unloaded.\n *\n * @returns A teardown function that detaches every listener.\n *\n * @example\n * ```ts\n * const stop = fg.pageViews.client.autoTrack({ userId: \"u_1\" });\n * ```\n */\n autoTrack(options: { userId?: string; sessionId?: string } = {}): Teardown {\n if (!isBrowser()) return () => { /* no-op on the server */ };\n const win = getWindow()!;\n const doc = getDocument()!;\n\n let currentPath = win.location.pathname;\n let pageStart = Date.now();\n let maxDepth = 0;\n const sentDepths = new Set<number>();\n\n const fireView = () => {\n void this.track({\n url: win.location.href,\n path: win.location.pathname,\n title: doc.title,\n referrer: doc.referrer,\n userId: options.userId,\n sessionId: options.sessionId,\n });\n };\n\n const flushTimeOnPage = () => {\n const duration = Math.round((Date.now() - pageStart) / 1000);\n if (duration <= 0) return;\n void this.trackTimeOnPage({ path: currentPath, duration, engaged: maxDepth > 10 });\n };\n\n const onRouteChange = () => {\n if (win.location.pathname === currentPath) return;\n flushTimeOnPage();\n currentPath = win.location.pathname;\n pageStart = Date.now();\n maxDepth = 0;\n sentDepths.clear();\n fireView();\n };\n\n // Patch history methods so we can detect SPA route changes.\n const origPush = win.history.pushState;\n const origReplace = win.history.replaceState;\n win.history.pushState = function (...args) {\n const r = origPush.apply(this, args);\n win.dispatchEvent(new Event(\"flowgrid:locationchange\"));\n return r;\n };\n win.history.replaceState = function (...args) {\n const r = origReplace.apply(this, args);\n win.dispatchEvent(new Event(\"flowgrid:locationchange\"));\n return r;\n };\n const popHandler = () => onRouteChange();\n const locHandler = () => onRouteChange();\n win.addEventListener(\"popstate\", popHandler);\n win.addEventListener(\"flowgrid:locationchange\", locHandler);\n\n // Scroll-depth thresholds.\n const onScroll = () => {\n const docEl = doc.documentElement;\n const scrollTop = win.scrollY || docEl.scrollTop;\n const height = Math.max(1, docEl.scrollHeight - win.innerHeight);\n const depth = Math.min(100, Math.round((scrollTop / height) * 100));\n if (depth > maxDepth) maxDepth = depth;\n for (const t of [25, 50, 75, 100]) {\n if (depth >= t && !sentDepths.has(t)) {\n sentDepths.add(t);\n void this.trackScrollDepth({ path: currentPath, scrollDepth: t });\n }\n }\n };\n win.addEventListener(\"scroll\", onScroll, { passive: true });\n\n // Flush on unload / visibility change.\n const onHide = () => { if (doc.visibilityState === \"hidden\") flushTimeOnPage(); };\n doc.addEventListener(\"visibilitychange\", onHide);\n win.addEventListener(\"pagehide\", flushTimeOnPage);\n\n // Initial view.\n fireView();\n\n return combineTeardowns(\n () => win.removeEventListener(\"popstate\", popHandler),\n () => win.removeEventListener(\"flowgrid:locationchange\", locHandler),\n () => win.removeEventListener(\"scroll\", onScroll),\n () => doc.removeEventListener(\"visibilitychange\", onHide),\n () => win.removeEventListener(\"pagehide\", flushTimeOnPage),\n () => { win.history.pushState = origPush; win.history.replaceState = origReplace; },\n );\n }\n}\n\nexport default PageViews;\n","/**\n * @fileoverview Product Analytics - Session Tracking\n * @module features/analytics/sessions\n * \n * @description\n * Track user sessions with comprehensive context including device info,\n * geographic location, traffic source, and engagement metrics.\n * \n * @useCases\n * - Monitor active user sessions in real-time\n * - Analyze session duration and depth\n * - Track user journey across pages\n * - Identify traffic sources and campaigns\n * - Segment users by device and location\n * \n * @metrics\n * - Total sessions\n * - Unique visitors\n * - Average session duration\n * - Pages per session\n * - Bounce rate\n * - New vs returning visitors\n * \n * @chartData\n * - Line chart: Sessions over time\n * - Pie chart: Traffic source breakdown\n * - Bar chart: Sessions by device type\n * - Geo map: Sessions by country\n * \n * @example\n * ```typescript\n * const sessions = new Sessions(webId, endpoint, apiKey);\n * \n * // Start a new session\n * const session = await sessions.start({\n * userId: 'user_123',\n * device: { type: 'desktop', browser: 'Chrome' },\n * source: 'google',\n * medium: 'organic'\n * });\n * \n * // End session\n * await sessions.end(session.sessionId);\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { getWindow, getDocument, isBrowser, combineTeardowns, Teardown } from \"../../utils/browser\";\nimport { \n Session,\n DeviceInfo, \n GeoLocation, \n UTMParams,\n TrafficMetrics,\n TimeSeriesData,\n DateRangeFilter,\n PaginationParams,\n UserIdentity\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for starting a new session.\n */\nexport interface SessionStartConfig {\n /** User identity */\n user?: UserIdentity;\n /** Device information */\n device?: DeviceInfo;\n /** Geographic location */\n location?: GeoLocation;\n /** Traffic source */\n source?: string;\n /** Traffic medium */\n medium?: string;\n /** Campaign name */\n campaign?: string;\n /** UTM parameters */\n utm?: UTMParams;\n /** Landing page URL */\n landingPage?: string;\n /** Referrer URL */\n referrer?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Session response from API.\n */\nexport interface SessionResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n sessionId: string;\n started: boolean;\n isNewVisitor: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Session end response.\n */\nexport interface SessionEndResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n sessionId: string;\n duration: number;\n pageViews: number;\n eventCount: number;\n };\n timestamp: string;\n}\n\n/**\n * Session analytics data for charts.\n */\nexport interface SessionAnalyticsData {\n /** Summary metrics */\n summary: {\n totalSessions: number;\n uniqueVisitors: number;\n newVisitors: number;\n returningVisitors: number;\n avgSessionDuration: number;\n avgPagesPerSession: number;\n bounceRate: number;\n };\n /** Sessions time series */\n timeSeries: TimeSeriesData;\n /** Sessions by source */\n bySource: Array<{\n source: string;\n sessions: number;\n percentage: number;\n }>;\n /** Sessions by device */\n byDevice: Array<{\n device: string;\n sessions: number;\n percentage: number;\n }>;\n /** Sessions by country */\n byCountry: Array<{\n country: string;\n countryCode: string;\n sessions: number;\n }>;\n}\n\n/**\n * Real-time session data.\n */\nexport interface RealTimeSessionData {\n /** Currently active sessions */\n activeSessions: number;\n /** Active users in last 5 minutes */\n activeUsers5min: number;\n /** Active users in last 30 minutes */\n activeUsers30min: number;\n /** Sessions by page (real-time) */\n sessionsByPage: Array<{ path: string; count: number }>;\n /** Sessions by country (real-time) */\n sessionsByCountry: Array<{ country: string; count: number }>;\n}\n\n// ============================================================================\n// SESSIONS CLASS\n// ============================================================================\n\n/**\n * Sessions - Track and analyze user sessions.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides comprehensive session tracking including:\n * - Session lifecycle (start, update, end)\n * - Device and location tracking\n * - Traffic source attribution\n * - Real-time session monitoring\n * - Session replay triggers\n * \n * @example\n * ```typescript\n * const sessions = new Sessions(webId, endpoint, apiKey);\n * \n * // Start session on page load\n * const session = await sessions.start({\n * user: { userId: 'user_123' },\n * device: { type: 'mobile', os: 'iOS' },\n * source: 'email',\n * campaign: 'summer_sale'\n * });\n * \n * // Get real-time data\n * const realTime = await sessions.getRealTime();\n * console.log(`${realTime.activeSessions} users online`);\n * ```\n */\nexport class Sessions extends FlowGridTransport {\n /**\n * Starts a new user session.\n * \n * @param config - Session configuration\n * @returns Promise resolving to session response with sessionId\n * \n * @example\n * ```typescript\n * const session = await sessions.start({\n * user: { userId: 'user_123' },\n * source: 'google',\n * medium: 'cpc',\n * campaign: 'brand_terms'\n * });\n * localStorage.setItem('sessionId', session.response.sessionId);\n * ```\n */\n async start(config: SessionStartConfig = {}): Promise<SessionResponse> {\n return await this.init(\"session_start\", {\n type: \"session_start\",\n user: config.user,\n device: config.device,\n location: config.location,\n source: config.source || config.utm?.utmSource,\n medium: config.medium || config.utm?.utmMedium,\n campaign: config.campaign || config.utm?.utmCampaign,\n utm: config.utm,\n landingPage: config.landingPage || (typeof window !== 'undefined' ? window.location.href : ''),\n referrer: config.referrer || (typeof document !== 'undefined' ? document.referrer : ''),\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Updates an existing session with new data.\n * \n * @param sessionId - Session identifier\n * @param properties - Properties to update\n * @returns Promise resolving to update response\n */\n async update(\n sessionId: string, \n properties: Record<string, unknown>\n ): Promise<SessionResponse> {\n return await this.init(\"session_update\", {\n type: \"session_update\",\n sessionId,\n ...properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Ends a user session.\n * \n * @param sessionId - Session identifier to end\n * @returns Promise resolving to session end response with metrics\n * \n * @example\n * ```typescript\n * window.addEventListener('beforeunload', async () => {\n * const sessionId = localStorage.getItem('sessionId');\n * if (sessionId) {\n * await sessions.end(sessionId);\n * }\n * });\n * ```\n */\n async end(sessionId: string): Promise<SessionEndResponse> {\n return await this.init(\"session_end\", {\n type: \"session_end\",\n sessionId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a heartbeat to keep session alive.\n * \n * @param sessionId - Session identifier\n * @returns Promise resolving to heartbeat response\n */\n async heartbeat(sessionId: string): Promise<SessionResponse> {\n return await this.init(\"session_heartbeat\", {\n type: \"session_heartbeat\",\n sessionId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Auto-manage a browser session: starts on call, sends a heartbeat on a\n * fixed interval, and ends the session on `pagehide`. The resolved\n * `sessionId` is stored on `sessionStorage` so reloads within the same tab\n * resume the same session.\n *\n * @param config Optional initial config forwarded to {@link Sessions.start}.\n * @param options.heartbeatMs Interval between heartbeats. Default 30s.\n * @returns A teardown function that ends the session and detaches listeners.\n */\n autoTrack(config: SessionStartConfig = {}, options: { heartbeatMs?: number } = {}): Teardown {\n if (!isBrowser()) return () => { /* no-op on the server */ };\n const win = getWindow()!;\n const doc = getDocument()!;\n const heartbeatMs = options.heartbeatMs ?? 30_000;\n\n const STORE_KEY = \"flowgrid:sessionId\";\n let sessionId: string | undefined;\n try { sessionId = win.sessionStorage.getItem(STORE_KEY) || undefined; } catch { /* private mode */ }\n\n const startPromise = sessionId\n ? Promise.resolve(sessionId)\n : this.start(config).then((r) => {\n sessionId = r.response.sessionId;\n try { win.sessionStorage.setItem(STORE_KEY, sessionId); } catch { /* ignore */ }\n return sessionId;\n }).catch(() => undefined);\n\n const beat = win.setInterval(() => {\n if (sessionId && doc.visibilityState === \"visible\") void this.heartbeat(sessionId);\n }, heartbeatMs);\n\n const onHide = () => { if (sessionId) void this.end(sessionId); };\n win.addEventListener(\"pagehide\", onHide);\n\n return combineTeardowns(\n () => win.clearInterval(beat),\n () => win.removeEventListener(\"pagehide\", onHide),\n () => { void startPromise.then(() => { if (sessionId) void this.end(sessionId); }); },\n () => { try { win.sessionStorage.removeItem(STORE_KEY); } catch { /* ignore */ } },\n );\n }\n}\n\nexport default Sessions;\n","/**\n * @fileoverview Product Analytics - Custom Event Tracking\n * @module features/analytics/events\n * \n * @description\n * Track custom events with flexible properties for any user action\n * or system event. Supports categorization, value tracking, and batching.\n * \n * @useCases\n * - Track button clicks and CTA interactions\n * - Monitor feature usage and engagement\n * - Capture form submissions and errors\n * - Log search queries and results\n * - Record video/audio playback events\n * \n * @metrics\n * - Event counts by type\n * - Event trends over time\n * - Unique users per event\n * - Event value aggregations\n * - Event-to-conversion correlation\n * \n * @chartData\n * - Bar chart: Top events by count\n * - Line chart: Event trends over time\n * - Heatmap: Events by hour/day\n * - Sankey: Event flow paths\n * \n * @example\n * ```typescript\n * const events = new Events(webId, endpoint, apiKey);\n * \n * // Track a button click\n * await events.track({\n * eventName: 'cta_clicked',\n * category: 'engagement',\n * action: 'click',\n * label: 'signup_hero'\n * });\n * \n * // Track with value\n * await events.track({\n * eventName: 'video_watched',\n * value: 120, // seconds\n * properties: { videoId: 'intro-video' }\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { \n CustomEvent,\n ClickEvent,\n FormEvent,\n SearchEvent,\n ErrorEvent,\n TimeSeriesData,\n DateRangeFilter,\n PaginationParams,\n ChartDataPoint\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for tracking a custom event.\n */\nexport interface EventTrackConfig {\n /** Event name (required) */\n eventName: string;\n /** Event category for grouping */\n category?: string;\n /** Event action */\n action?: string;\n /** Event label for sub-categorization */\n label?: string;\n /** Numeric value associated with event */\n value?: number;\n /** Non-interactive event (won't affect bounce rate) */\n nonInteraction?: boolean;\n /** User ID */\n userId?: string;\n /** Session ID */\n sessionId?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Batch event tracking configuration.\n */\nexport interface BatchEventConfig {\n /** Array of events to track */\n events: EventTrackConfig[];\n}\n\n/**\n * Event response from API.\n */\nexport interface EventResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n eventId: string;\n tracked: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Batch event response.\n */\nexport interface BatchEventResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n tracked: number;\n failed: number;\n eventIds: string[];\n };\n timestamp: string;\n}\n\n/**\n * Event analytics data for charts.\n */\nexport interface EventAnalyticsData {\n /** Top events by count */\n topEvents: Array<{\n eventName: string;\n category: string;\n count: number;\n uniqueUsers: number;\n totalValue: number;\n }>;\n /** Events time series */\n timeSeries: TimeSeriesData;\n /** Events by category */\n byCategory: Array<{\n category: string;\n count: number;\n percentage: number;\n }>;\n /** Summary metrics */\n summary: {\n totalEvents: number;\n uniqueEventTypes: number;\n eventsPerUser: number;\n totalValue: number;\n };\n}\n\n// ============================================================================\n// EVENTS CLASS\n// ============================================================================\n\n/**\n * Events - Track and analyze custom events.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides flexible custom event tracking including:\n * - Standard event tracking with categories\n * - Click and interaction tracking\n * - Form submission tracking\n * - Search event tracking\n * - Error and exception logging\n * - Batch event submission\n * \n * @example\n * ```typescript\n * const events = new Events(webId, endpoint, apiKey);\n * \n * // Track feature usage\n * await events.track({\n * eventName: 'feature_used',\n * category: 'features',\n * action: 'use',\n * label: 'export_pdf',\n * properties: { format: 'A4', pages: 5 }\n * });\n * ```\n */\nexport class Events extends FlowGridTransport {\n /**\n * Tracks a custom event.\n * \n * @param config - Event configuration\n * @returns Promise resolving to event response\n * \n * @example\n * ```typescript\n * await events.track({\n * eventName: 'upgrade_clicked',\n * category: 'monetization',\n * action: 'click',\n * label: 'pricing_page',\n * value: 99.99\n * });\n * ```\n */\n async track(config: EventTrackConfig): Promise<EventResponse> {\n return await this.init(\"custom_event\", {\n type: \"custom_event\",\n eventName: config.eventName,\n category: config.category || \"general\",\n action: config.action,\n label: config.label,\n value: config.value,\n nonInteraction: config.nonInteraction || false,\n userId: config.userId,\n sessionId: config.sessionId,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks multiple events in a single request.\n * \n * @param config - Batch configuration with events array\n * @returns Promise resolving to batch response\n * \n * @example\n * ```typescript\n * await events.trackBatch({\n * events: [\n * { eventName: 'step_1_complete', category: 'onboarding' },\n * { eventName: 'step_2_complete', category: 'onboarding' },\n * { eventName: 'step_3_complete', category: 'onboarding' }\n * ]\n * });\n * ```\n */\n async trackBatch(config: BatchEventConfig): Promise<BatchEventResponse> {\n const events = config.events.map(e => ({\n ...e,\n timestamp: new Date().toISOString()\n }));\n\n return await this.init(\"batch_events\", {\n type: \"batch_events\",\n events,\n count: events.length,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a click/interaction event.\n * \n * @param config - Click event configuration\n * @returns Promise resolving to event response\n * \n * @example\n * ```typescript\n * document.addEventListener('click', (e) => {\n * if (e.target.dataset.track) {\n * events.trackClick({\n * elementId: e.target.id,\n * elementText: e.target.textContent,\n * targetUrl: e.target.href\n * });\n * }\n * });\n * ```\n */\n async trackClick(config: ClickEvent): Promise<EventResponse> {\n return await this.init(\"click_event\", {\n type: \"click_event\",\n ...config,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a form submission event.\n * \n * @param config - Form event configuration\n * @returns Promise resolving to event response\n * \n * @example\n * ```typescript\n * form.addEventListener('submit', async (e) => {\n * await events.trackForm({\n * formId: 'contact-form',\n * formName: 'Contact Us',\n * success: true,\n * fields: ['name', 'email', 'message'],\n * completionTime: 45\n * });\n * });\n * ```\n */\n async trackForm(config: FormEvent): Promise<EventResponse> {\n return await this.init(\"form_event\", {\n type: \"form_event\",\n ...config,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a search event.\n * \n * @param config - Search event configuration\n * @returns Promise resolving to event response\n * \n * @example\n * ```typescript\n * await events.trackSearch({\n * query: 'wireless headphones',\n * category: 'products',\n * resultsCount: 42,\n * filters: { brand: 'Sony', priceRange: '100-200' }\n * });\n * ```\n */\n async trackSearch(config: SearchEvent): Promise<EventResponse> {\n return await this.init(\"search_event\", {\n type: \"search_event\",\n query: config.query,\n category: config.category,\n resultsCount: config.resultsCount,\n resultClicked: config.resultClicked,\n clickedPosition: config.clickedPosition,\n filters: config.filters,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks an error or exception.\n * \n * @param config - Error event configuration\n * @returns Promise resolving to event response\n * \n * @example\n * ```typescript\n * window.onerror = (message, source, line, col, error) => {\n * events.trackError({\n * message: message.toString(),\n * stack: error?.stack,\n * type: error?.name,\n * fatal: false,\n * context: source\n * });\n * };\n * ```\n */\n async trackError(config: ErrorEvent): Promise<EventResponse> {\n return await this.init(\"error_event\", {\n type: \"error_event\",\n message: config.message,\n stack: config.stack,\n errorType: config.type,\n fatal: config.fatal || false,\n context: config.context,\n userAction: config.userAction,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default Events;\n","/**\n * @fileoverview Product Analytics - User Identification & Traits\n * @module features/analytics/identify\n * \n * @description\n * Identify users and track their traits for personalization,\n * segmentation, and cohort analysis.\n * \n * @useCases\n * - Link anonymous sessions to authenticated users\n * - Store user profile data for segmentation\n * - Track user lifecycle changes (signup, upgrade, churn)\n * - Enable personalized experiences\n * - Build user cohorts for analysis\n * \n * @metrics\n * - Total identified users\n * - Anonymous to identified conversion rate\n * - Users by plan/segment\n * - User lifetime value distribution\n * - Trait distribution\n * \n * @chartData\n * - Pie chart: Users by plan\n * - Bar chart: Users by trait value\n * - Line chart: New identifications over time\n * - Table: User segments\n * \n * @example\n * ```typescript\n * const identify = new Identify(webId, endpoint, apiKey);\n * \n * // Identify user on login\n * await identify.user({\n * userId: 'user_123',\n * traits: {\n * email: 'user@example.com',\n * name: 'John Doe',\n * plan: 'premium',\n * createdAt: '2025-01-15'\n * }\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { getWindow, getDocument, isBrowser, type Teardown } from \"../../utils/browser\";\nimport {\n UserTraits,\n MoneyValue,\n TimeSeriesData\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for identifying a user.\n */\nexport interface IdentifyConfig {\n /** Unique user identifier */\n userId: string;\n /** Anonymous ID to link (optional) */\n anonymousId?: string;\n /** User traits/properties */\n traits?: UserTraits;\n}\n\n/**\n * Options for {@link Identify.autoTrackForms} passive identity capture.\n */\nexport interface PassiveIdentityOptions {\n /** Debounce for `input` events before reading a field (ms, default 800). */\n debounceMs?: number;\n /** Extra `path` regex on which NO inputs are captured (replaces the default). */\n excludePath?: RegExp;\n}\n\n/**\n * Configuration for aliasing user IDs.\n */\nexport interface AliasConfig {\n /** Previous user ID */\n previousId: string;\n /** New user ID */\n userId: string;\n}\n\n/**\n * Configuration for grouping users.\n */\nexport interface GroupConfig {\n /** User ID */\n userId: string;\n /** Group/company ID */\n groupId: string;\n /** Group traits */\n traits?: {\n name?: string;\n industry?: string;\n employees?: number;\n plan?: string;\n [key: string]: unknown;\n };\n}\n\n/**\n * Identify response from API.\n */\nexport interface IdentifyResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n userId: string;\n identified: boolean;\n isNewUser: boolean;\n linkedAnonymousId?: string;\n };\n timestamp: string;\n}\n\n/**\n * User analytics data for charts.\n */\nexport interface UserAnalyticsData {\n /** Summary metrics */\n summary: {\n totalUsers: number;\n identifiedUsers: number;\n anonymousUsers: number;\n newUsersThisPeriod: number;\n identificationRate: number;\n };\n /** Users by plan/segment */\n byPlan: Array<{\n plan: string;\n count: number;\n percentage: number;\n mrr?: MoneyValue;\n }>;\n /** New users time series */\n newUsersTimeSeries: TimeSeriesData;\n /** User lifecycle distribution */\n byLifecycleStage: Array<{\n stage: string;\n count: number;\n percentage: number;\n }>;\n}\n\n// ============================================================================\n// IDENTIFY CLASS\n// ============================================================================\n\n/**\n * Identify - User identification and trait management.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides user identification capabilities including:\n * - User identification on login/signup\n * - Anonymous to identified user linking\n * - User trait/property storage\n * - User aliasing for ID changes\n * - Group/company association\n * \n * @example\n * ```typescript\n * const identify = new Identify(webId, endpoint, apiKey);\n * \n * // On user signup\n * await identify.user({\n * userId: 'user_123',\n * anonymousId: getAnonymousId(),\n * traits: {\n * email: 'user@example.com',\n * name: 'Jane Smith',\n * plan: 'starter',\n * createdAt: new Date().toISOString()\n * }\n * });\n * \n * // Associate user with company\n * await identify.group({\n * userId: 'user_123',\n * groupId: 'company_456',\n * traits: { name: 'Acme Inc', employees: 50 }\n * });\n * ```\n */\nexport class Identify extends FlowGridTransport {\n /**\n * Identifies a user with traits.\n * \n * @param config - Identification configuration\n * @returns Promise resolving to identify response\n * \n * @example\n * ```typescript\n * // Basic identification\n * await identify.user({\n * userId: 'user_123',\n * traits: { email: 'user@example.com' }\n * });\n * \n * // With anonymous ID linking\n * await identify.user({\n * userId: 'user_123',\n * anonymousId: localStorage.getItem('anonId'),\n * traits: {\n * name: 'John Doe',\n * plan: 'premium',\n * ltv: { amount: 29900, currency: 'USD' }\n * }\n * });\n * ```\n */\n async user(config: IdentifyConfig): Promise<IdentifyResponse> {\n return await this.init(\"identify_user\", {\n type: \"identify\",\n userId: config.userId,\n anonymousId: config.anonymousId,\n traits: config.traits || {},\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Updates traits for an existing user.\n * \n * @param userId - User identifier\n * @param traits - Traits to update/add\n * @returns Promise resolving to identify response\n * \n * @example\n * ```typescript\n * // Update user plan after upgrade\n * await identify.updateTraits('user_123', {\n * plan: 'enterprise',\n * ltv: { amount: 99900, currency: 'USD' }\n * });\n * ```\n */\n async updateTraits(userId: string, traits: UserTraits): Promise<IdentifyResponse> {\n return await this.init(\"update_traits\", {\n type: \"update_traits\",\n userId,\n traits,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Creates an alias between two user IDs.\n * \n * @param config - Alias configuration\n * @returns Promise resolving to alias response\n * \n * @example\n * ```typescript\n * // When user ID changes (e.g., after migration)\n * await identify.alias({\n * previousId: 'old_user_id',\n * userId: 'new_user_id'\n * });\n * ```\n */\n async alias(config: AliasConfig): Promise<IdentifyResponse> {\n return await this.init(\"alias_user\", {\n type: \"alias\",\n previousId: config.previousId,\n userId: config.userId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Associates a user with a group/company.\n * \n * @param config - Group configuration\n * @returns Promise resolving to group response\n * \n * @example\n * ```typescript\n * await identify.group({\n * userId: 'user_123',\n * groupId: 'company_456',\n * traits: {\n * name: 'Acme Corporation',\n * industry: 'Technology',\n * employees: 500,\n * plan: 'enterprise'\n * }\n * });\n * ```\n */\n async group(config: GroupConfig): Promise<IdentifyResponse> {\n return await this.init(\"group_user\", {\n type: \"group\",\n userId: config.userId,\n groupId: config.groupId,\n traits: config.traits || {},\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Resets user identity (logout).\n * \n * @param userId - User ID to reset\n * @returns Promise resolving to reset response\n */\n async reset(userId: string): Promise<IdentifyResponse> {\n return await this.init(\"reset_identity\", {\n type: \"reset\",\n userId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Passive identity capture — the browser-side form-field tracker (parity with\n * `flowgrid.js`'s `setupPassiveIdentityCapture`). Watches inputs (initial scan\n * + a MutationObserver for fields added by modals/SPAs), classifies each by\n * name/id/placeholder/autocomplete, hashes PII (SHA-256) before it leaves the\n * browser, and posts the captured identity to the flowgrid-core identity store\n * (`POST /api/v1/identity/identify`) so the dashboard can map the anonymous\n * `visitor_id` to a person — and, with a CRM connected, to a known customer.\n *\n * Privacy: requires `analytics` consent; never attaches to password/card/cvv/\n * ssn/pin fields or anything marked `data-analytics=\"ignore\"` / `.no-track`;\n * skips sensitive paths (checkout/account-settings/admin). Off by default in\n * {@link FlowGrid.autoTrack} — enable with `autoTrack({ passiveIdentity: true })`.\n *\n * @returns a teardown that detaches every listener (no-op on the server).\n */\n autoTrackForms(options: PassiveIdentityOptions = {}): Teardown {\n if (!isBrowser()) return () => { /* no-op on the server */ };\n if (!FlowGridTransport.hasConsent(\"analytics\")) return () => { /* consent not granted */ };\n\n const win = getWindow()!;\n const doc = getDocument()!;\n const debounceMs = options.debounceMs ?? 800;\n\n const EXCLUDED_PATH_RE = options.excludePath ?? /\\/(checkout|account\\/settings|admin)(\\/|$)/i;\n const BLOCKED_RE = /(password|cardnumber|creditcard|\\bcard\\b|cvv|cvc|ssn|\\bpin\\b|securitycode)/i;\n const SKIP_RE = /(message|comment|note|feedback|search|query)/i;\n\n type FieldRule = { re: RegExp; trait: string; pii: boolean; identify: boolean };\n const FIELD_MAP: FieldRule[] = [\n { re: /(email|emailaddress)/, trait: \"email\", pii: true, identify: true },\n { re: /(phone|tel|mobile|cell)/, trait: \"phone\", pii: true, identify: false },\n { re: /(firstname|fname|givenname)/, trait: \"firstName\", pii: false, identify: false },\n { re: /(lastname|lname|surname|familyname)/, trait: \"lastName\", pii: false, identify: false },\n { re: /(company|organi[sz]ation|businessname)/, trait: \"company\", pii: false, identify: false },\n { re: /(streetaddress|addressline|^address$|^street$)/, trait: \"address\", pii: false, identify: false },\n { re: /(^city$|town)/, trait: \"city\", pii: false, identify: false },\n { re: /(postcode|postalcode|zipcode|^zip$)/, trait: \"postcode\", pii: false, identify: false },\n // A single full-name / username field maps to firstName; listed last so\n // dedicated first/last-name fields win when both exist.\n { re: /(fullname|^name$|username|yourname|contactname|displayname)/, trait: \"firstName\", pii: false, identify: false },\n ];\n\n const captured: Record<string, string> = {};\n const seen = new WeakSet<Element>();\n const debounceTimers = new WeakMap<Element, ReturnType<typeof setTimeout>>();\n const teardowns: Array<() => void> = [];\n\n const fieldEl = (el: Element) => el as HTMLInputElement;\n\n const normalize = (el: Element): string =>\n [fieldEl(el).name, el.id, el.getAttribute(\"placeholder\"), el.getAttribute(\"autocomplete\")]\n .filter(Boolean).join(\" \").toLowerCase().replace(/[\\s_-]/g, \"\");\n\n const isBlocked = (el: Element, key: string): boolean => {\n const input = fieldEl(el);\n if (input.type === \"password\" || input.type === \"hidden\") return true;\n if (el.getAttribute(\"data-analytics\") === \"ignore\") return true;\n if (el.classList && el.classList.contains(\"no-track\")) return true;\n return BLOCKED_RE.test(key);\n };\n\n const classify = (el: Element): FieldRule | { blocked: true } | null => {\n const key = normalize(el);\n if (!key) return null;\n if (isBlocked(el, key)) return { blocked: true };\n if (SKIP_RE.test(key)) return null;\n return FIELD_MAP.find((f) => f.re.test(key)) ?? null;\n };\n\n const sha256hex = async (value: string): Promise<string | null> => {\n try {\n const enc = new TextEncoder().encode(String(value).trim().toLowerCase());\n const buf = await win.crypto.subtle.digest(\"SHA-256\", enc);\n return Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n } catch {\n return null;\n }\n };\n\n const resolveVisitorId = (): string =>\n (this.identityManager ? this.identityManager.getVisitorId() : this.visitorId) || \"\";\n const resolveSessionId = (): string | null =>\n this.identityManager ? this.identityManager.getSessionId() : null;\n\n // Posts the captured identity to the flowgrid-core identity store. Raw email\n // stays server-side (CRM enrichment + dashboard display); the analytics\n // event store only ever sees hashes (the `field_captured` mirror below).\n const sendIdentity = () => {\n const visitorId = resolveVisitorId();\n if (!this.webId || !visitorId) return;\n if (!captured.email && !captured.emailHash && !captured.firstName && !captured.lastName && !captured.phone) return;\n\n const traits = {\n email: captured.email ?? null,\n email_hash: captured.emailHash ?? null,\n // Raw phone is only populated on an explicit form submit (consent);\n // passive blur/input keeps phone hash-only.\n phone: captured.phone ?? null,\n phone_hash: captured.phoneHash ?? null,\n first_name: captured.firstName ?? null,\n last_name: captured.lastName ?? null,\n company: captured.company ?? null,\n address: captured.address ?? null,\n city: captured.city ?? null,\n postcode: captured.postcode ?? null,\n };\n const url = `${this.endpoint}/api/v1/identity/identify`;\n const payload = JSON.stringify({\n web_id: this.webId,\n visitor_id: visitorId,\n session_id: resolveSessionId(),\n path_name: win.location.pathname,\n traits,\n });\n try {\n if (typeof navigator !== \"undefined\" && navigator.sendBeacon &&\n navigator.sendBeacon(url, new Blob([payload], { type: \"application/json\" }))) {\n // sent via beacon\n } else {\n void fetch(url, { method: \"POST\", headers: { \"Content-Type\": \"application/json\" }, body: payload, keepalive: true }).catch(() => { /* swallow */ });\n }\n } catch { /* swallow */ }\n };\n\n const captureField = (el: Element) => {\n const cls = classify(el);\n if (!cls || \"blocked\" in cls) return;\n const value = (fieldEl(el).value || \"\").trim();\n if (!value) return;\n\n if (cls.pii) {\n const toHash = cls.trait === \"phone\" ? value.replace(/[^\\d+]/g, \"\") : value;\n void sha256hex(toHash).then((hash) => {\n if (cls.trait === \"email\") { captured.email = value; if (hash) captured.emailHash = hash; }\n else if (cls.trait === \"phone\" && hash) { captured.phoneHash = hash; }\n if (cls.identify || captured.email) sendIdentity();\n });\n } else {\n captured[cls.trait] = value;\n if (captured.email) sendIdentity();\n }\n };\n\n const attach = (el: Element) => {\n if (seen.has(el)) return;\n const cls = classify(el);\n if (!cls || \"blocked\" in cls) return; // never listen to blocked / unknown fields\n seen.add(el);\n const onBlur = () => captureField(el);\n const onInput = () => {\n clearTimeout(debounceTimers.get(el));\n debounceTimers.set(el, setTimeout(() => captureField(el), debounceMs));\n };\n el.addEventListener(\"blur\", onBlur, { passive: true } as AddEventListenerOptions);\n el.addEventListener(\"input\", onInput, { passive: true } as AddEventListenerOptions);\n teardowns.push(() => {\n el.removeEventListener(\"blur\", onBlur);\n el.removeEventListener(\"input\", onInput);\n });\n };\n\n const scanPage = () => {\n if (EXCLUDED_PATH_RE.test(win.location.pathname)) return;\n doc.querySelectorAll(\"input, textarea, select\").forEach(attach);\n };\n\n // Explicit submit is treated as consent — capture the RAW phone (the one\n // identity field passive tracking keeps hash-only) so the CRM cascade can\n // match by phone, then flush. All other fields are already captured passively.\n const onSubmit = (e: Event) => {\n const form = e.target as HTMLElement | null;\n if (!form || form.tagName !== \"FORM\") return;\n if (EXCLUDED_PATH_RE.test(win.location.pathname)) return;\n let phoneOnSubmit = false;\n (form as HTMLFormElement).querySelectorAll(\"input, textarea, select\").forEach((el) => {\n const cls = classify(el);\n if (!cls || \"blocked\" in cls || cls.trait !== \"phone\") return;\n const raw = (fieldEl(el).value || \"\").trim();\n if (raw) { captured.phone = raw; phoneOnSubmit = true; }\n });\n if (phoneOnSubmit) sendIdentity();\n };\n doc.addEventListener(\"submit\", onSubmit, true);\n teardowns.push(() => doc.removeEventListener(\"submit\", onSubmit, true));\n\n scanPage();\n let mo: MutationObserver | null = null;\n try {\n mo = new MutationObserver(() => scanPage());\n if (doc.body) mo.observe(doc.body, { childList: true, subtree: true });\n } catch { /* MutationObserver unavailable */ }\n\n return () => {\n if (mo) try { mo.disconnect(); } catch { /* noop */ }\n for (const t of teardowns) { try { t(); } catch { /* noop */ } }\n };\n }\n\n}\n\nexport default Identify;\n","/**\n * @fileoverview Product Analytics - Funnel Analysis\n * @module features/analytics/funnels\n * \n * @description\n * Define and analyze conversion funnels to understand user journeys,\n * identify drop-off points, and optimize conversion rates.\n * \n * @useCases\n * - Track signup/onboarding completion rates\n * - Analyze checkout funnel performance\n * - Identify where users drop off in key flows\n * - Compare funnel performance across segments\n * - A/B test different funnel variations\n * \n * @metrics\n * - Step-by-step conversion rates\n * - Drop-off rates at each step\n * - Time to complete each step\n * - Overall funnel conversion rate\n * - Funnel completion time\n * \n * @chartData\n * - Funnel chart: Step-by-step visualization\n * - Bar chart: Conversion by step\n * - Line chart: Funnel performance over time\n * - Comparison: Funnel variants side-by-side\n * \n * @example\n * ```typescript\n * const funnels = new Funnels(webId, endpoint, apiKey);\n * \n * // Track funnel step\n * await funnels.trackStep({\n * funnelId: 'checkout_funnel',\n * step: 'add_payment',\n * stepNumber: 3,\n * userId: 'user_123'\n * });\n * \n * // Get funnel analysis\n * const analysis = await funnels.analyze('checkout_funnel');\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { \n FunnelStep,\n FunnelAnalysis,\n DateRangeFilter,\n TimeSeriesData\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Funnel definition configuration.\n */\nexport interface FunnelDefinition {\n /** Unique funnel identifier */\n funnelId: string;\n /** Funnel name for display */\n name: string;\n /** Funnel description */\n description?: string;\n /** Ordered list of step definitions */\n steps: Array<{\n /** Step name */\n name: string;\n /** Event(s) that trigger this step */\n events: string[];\n /** Step description */\n description?: string;\n /** Whether step is optional */\n optional?: boolean;\n }>;\n}\n\n/**\n * Configuration for tracking a funnel step.\n */\nexport interface FunnelStepConfig {\n /** Funnel identifier */\n funnelId: string;\n /** Step name or event */\n step: string;\n /** Step number (1-indexed) */\n stepNumber: number;\n /** User ID */\n userId?: string;\n /** Session ID */\n sessionId?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Funnel step response from API.\n */\nexport interface FunnelStepResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n funnelId: string;\n step: string;\n tracked: boolean;\n previousStep?: string;\n timeFromPrevious?: number;\n };\n timestamp: string;\n}\n\n/**\n * Funnel analytics data for charts.\n */\nexport interface FunnelAnalyticsData {\n /** Funnel definition */\n funnel: FunnelDefinition;\n /** Analysis results */\n analysis: FunnelAnalysis;\n /** Step-by-step data for visualization */\n visualization: Array<{\n step: string;\n entered: number;\n completed: number;\n conversionRate: number;\n dropOffRate: number;\n avgTime: number;\n }>;\n /** Performance over time */\n trendData: TimeSeriesData;\n}\n\n/**\n * Funnel comparison data.\n */\nexport interface FunnelComparisonData {\n /** Base funnel data */\n baseline: FunnelAnalyticsData;\n /** Comparison funnel data */\n comparison: FunnelAnalyticsData;\n /** Step-by-step comparison */\n stepComparison: Array<{\n step: string;\n baselineRate: number;\n comparisonRate: number;\n difference: number;\n significant: boolean;\n }>;\n}\n\n// ============================================================================\n// FUNNELS CLASS\n// ============================================================================\n\n/**\n * Funnels - Define and analyze conversion funnels.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides funnel analysis capabilities including:\n * - Funnel definition and management\n * - Step-by-step event tracking\n * - Conversion and drop-off analysis\n * - Time-to-completion tracking\n * - Segment-based funnel comparison\n * \n * @example\n * ```typescript\n * const funnels = new Funnels(webId, endpoint, apiKey);\n * \n * // Define a signup funnel\n * await funnels.define({\n * funnelId: 'signup_funnel',\n * name: 'User Signup',\n * steps: [\n * { name: 'Visit Landing', events: ['page_view_landing'] },\n * { name: 'Click Signup', events: ['signup_clicked'] },\n * { name: 'Submit Form', events: ['signup_submitted'] },\n * { name: 'Verify Email', events: ['email_verified'] }\n * ]\n * });\n * \n * // Analyze funnel performance\n * const analysis = await funnels.analyze('signup_funnel', {\n * range: 'last30days'\n * });\n * ```\n */\nexport class Funnels extends FlowGridTransport {\n /**\n * Defines a new funnel or updates existing.\n * \n * @param config - Funnel definition\n * @returns Promise resolving to definition response\n * \n * @example\n * ```typescript\n * await funnels.define({\n * funnelId: 'checkout_funnel',\n * name: 'Checkout Flow',\n * steps: [\n * { name: 'View Cart', events: ['view_cart'] },\n * { name: 'Begin Checkout', events: ['begin_checkout'] },\n * { name: 'Add Shipping', events: ['add_shipping_info'] },\n * { name: 'Add Payment', events: ['add_payment_info'] },\n * { name: 'Purchase', events: ['purchase'] }\n * ]\n * });\n * ```\n */\n async define(config: FunnelDefinition): Promise<FunnelStepResponse> {\n return await this.init(\"define_funnel\", {\n type: \"define_funnel\",\n funnelId: config.funnelId,\n name: config.name,\n description: config.description,\n steps: config.steps,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a funnel step completion.\n * \n * @param config - Funnel step configuration\n * @returns Promise resolving to step response\n * \n * @example\n * ```typescript\n * // Track user progressing through checkout\n * await funnels.trackStep({\n * funnelId: 'checkout_funnel',\n * step: 'add_shipping_info',\n * stepNumber: 3,\n * userId: 'user_123',\n * properties: { shippingMethod: 'express' }\n * });\n * ```\n */\n async trackStep(config: FunnelStepConfig): Promise<FunnelStepResponse> {\n return await this.init(\"funnel_step\", {\n type: \"funnel_step\",\n funnelId: config.funnelId,\n step: config.step,\n stepNumber: config.stepNumber,\n userId: config.userId,\n sessionId: config.sessionId,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks funnel completion.\n * \n * @param funnelId - Funnel identifier\n * @param userId - User who completed\n * @param properties - Completion properties\n * @returns Promise resolving to completion response\n */\n async trackComplete(\n funnelId: string,\n userId?: string,\n properties?: Record<string, unknown>\n ): Promise<FunnelStepResponse> {\n return await this.init(\"funnel_complete\", {\n type: \"funnel_complete\",\n funnelId,\n userId,\n ...properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks funnel abandonment.\n * \n * @param funnelId - Funnel identifier\n * @param lastStep - Last completed step\n * @param userId - User who abandoned\n * @param reason - Optional abandonment reason\n * @returns Promise resolving to abandonment response\n */\n async trackAbandon(\n funnelId: string,\n lastStep: string,\n userId?: string,\n reason?: string\n ): Promise<FunnelStepResponse> {\n return await this.init(\"funnel_abandon\", {\n type: \"funnel_abandon\",\n funnelId,\n lastStep,\n userId,\n reason,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Analyzes funnel performance.\n * \n * @param funnelId - Funnel identifier\n * @param filter - Date range filter\n * @returns Promise resolving to funnel analytics\n * \n * @example\n * ```typescript\n * const analysis = await funnels.analyze('checkout_funnel', {\n * range: 'last7days'\n * });\n * \n * // Funnel visualization data\n * const funnelData = analysis.visualization.map(step => ({\n * name: step.step,\n * value: step.entered,\n * conversion: step.conversionRate\n * }));\n * \n * // Overall conversion rate\n * console.log(`Conversion: ${analysis.analysis.overallConversionRate}%`);\n * ```\n */\n async analyze(\n funnelId: string,\n filter?: DateRangeFilter\n ): Promise<FunnelAnalyticsData> {\n return await this.init(\"analyze_funnel\", {\n type: \"query\",\n funnelId,\n filter: filter || { range: \"last30days\" },\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Compares two funnel periods or segments.\n * \n * @param funnelId - Funnel identifier\n * @param baselineFilter - Baseline period filter\n * @param comparisonFilter - Comparison period filter\n * @returns Promise resolving to comparison data\n * \n * @example\n * ```typescript\n * const comparison = await funnels.compare(\n * 'checkout_funnel',\n * { range: 'last30days' },\n * { range: 'last60days' }\n * );\n * \n * // Side-by-side comparison chart\n * const comparisonData = comparison.stepComparison.map(s => ({\n * step: s.step,\n * baseline: s.baselineRate,\n * comparison: s.comparisonRate\n * }));\n * ```\n */\n async compare(\n funnelId: string,\n baselineFilter: DateRangeFilter,\n comparisonFilter: DateRangeFilter\n ): Promise<FunnelComparisonData> {\n return await this.init(\"compare_funnel\", {\n type: \"query\",\n funnelId,\n baselineFilter,\n comparisonFilter,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Lists all defined funnels.\n * \n * @returns Promise resolving to funnel list\n */\n async list(): Promise<FunnelDefinition[]> {\n return await this.init(\"list_funnels\", {\n type: \"query\",\n timestamp: new Date().toISOString()\n });\n }\n}\n\nexport default Funnels;\n","/**\n * @fileoverview Product Analytics - Retention Analysis\n * @module features/analytics/retention\n * \n * @description\n * Track and analyze user retention using cohort analysis,\n * identify churn patterns, and measure user stickiness.\n * \n * @useCases\n * - Measure week-over-week retention rates\n * - Compare retention across user cohorts\n * - Identify churn risk factors\n * - Track feature adoption impact on retention\n * - Calculate DAU/MAU ratios (stickiness)\n * \n * @metrics\n * - Day 1, 7, 30 retention rates\n * - Cohort retention curves\n * - Churn rate by segment\n * - User resurrection rate\n * - DAU/WAU/MAU ratios\n * \n * @chartData\n * - Retention curve: Line chart over time\n * - Cohort matrix: Heatmap by cohort/period\n * - Bar chart: Retention by segment\n * - Trend line: Stickiness over time\n * \n * @example\n * ```typescript\n * const retention = new Retention(webId, endpoint, apiKey);\n * \n * // Track user activity\n * await retention.trackActivity({\n * userId: 'user_123',\n * action: 'feature_used'\n * });\n * \n * // Get cohort analysis\n * const cohorts = await retention.getCohortAnalysis({\n * range: 'last90days',\n * granularity: 'week'\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { \n RetentionCohort,\n UserLifecycleStage,\n LifecycleMetrics,\n DateRangeFilter,\n TimeGranularity,\n TimeSeriesData\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for tracking user activity.\n */\nexport interface ActivityConfig {\n /** User identifier */\n userId: string;\n /** Activity type/action */\n action?: string;\n /** Session ID */\n sessionId?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Configuration for cohort analysis.\n */\nexport interface CohortAnalysisConfig {\n /** Date range for analysis */\n filter?: DateRangeFilter;\n /** Cohort granularity */\n granularity?: TimeGranularity;\n /** Segment by trait */\n segmentBy?: string;\n /** Number of periods to show */\n periods?: number;\n}\n\n/**\n * Activity tracking response.\n */\nexport interface ActivityResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n userId: string;\n tracked: boolean;\n daysSinceFirstSeen: number;\n isReturning: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Cohort analysis data for charts.\n */\nexport interface CohortAnalysisData {\n /** Cohort data */\n cohorts: RetentionCohort[];\n /** Retention matrix for heatmap */\n matrix: Array<{\n cohort: string;\n periods: Array<{\n period: number;\n retained: number;\n retentionRate: number;\n }>;\n }>;\n /** Summary metrics */\n summary: {\n avgDay1Retention: number;\n avgDay7Retention: number;\n avgDay30Retention: number;\n overallRetentionTrend: 'improving' | 'declining' | 'stable';\n };\n}\n\n/**\n * User stickiness data.\n */\nexport interface StickinessData {\n /** DAU - Daily Active Users */\n dau: number;\n /** WAU - Weekly Active Users */\n wau: number;\n /** MAU - Monthly Active Users */\n mau: number;\n /** DAU/MAU ratio (stickiness) */\n dauMauRatio: number;\n /** DAU/WAU ratio */\n dauWauRatio: number;\n /** Stickiness trend over time */\n trendData: TimeSeriesData;\n}\n\n/**\n * Churn analysis data.\n */\nexport interface ChurnAnalysisData {\n /** Current churn rate */\n churnRate: number;\n /** Users at risk of churning */\n atRiskUsers: number;\n /** Users who churned this period */\n churnedUsers: number;\n /** Churn by segment */\n bySegment: Array<{\n segment: string;\n churnRate: number;\n count: number;\n }>;\n /** Common churn factors */\n churnFactors: Array<{\n factor: string;\n correlation: number;\n }>;\n /** Churn trend */\n trendData: TimeSeriesData;\n}\n\n// ============================================================================\n// RETENTION CLASS\n// ============================================================================\n\n/**\n * Retention - Track and analyze user retention.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides retention analysis capabilities including:\n * - User activity tracking\n * - Cohort-based retention analysis\n * - Stickiness metrics (DAU/MAU)\n * - Churn prediction and analysis\n * - Lifecycle stage tracking\n * \n * @example\n * ```typescript\n * const retention = new Retention(webId, endpoint, apiKey);\n * \n * // Track daily user activity\n * await retention.trackActivity({\n * userId: 'user_123',\n * action: 'daily_login'\n * });\n * \n * // Analyze cohort retention\n * const analysis = await retention.getCohortAnalysis({\n * granularity: 'week',\n * periods: 12\n * });\n * \n * // Cohort heatmap data\n * const heatmapData = analysis.matrix;\n * ```\n */\nexport class Retention extends FlowGridTransport {\n /**\n * Tracks user activity for retention calculation.\n * \n * @param config - Activity configuration\n * @returns Promise resolving to activity response\n * \n * @example\n * ```typescript\n * // Track on app open\n * await retention.trackActivity({\n * userId: 'user_123',\n * action: 'app_opened'\n * });\n * \n * // Track specific feature usage\n * await retention.trackActivity({\n * userId: 'user_123',\n * action: 'used_export',\n * properties: { format: 'pdf' }\n * });\n * ```\n */\n async trackActivity(config: ActivityConfig): Promise<ActivityResponse> {\n return await this.init(\"user_activity\", {\n type: \"user_activity\",\n userId: config.userId,\n action: config.action || \"active\",\n sessionId: config.sessionId,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks user lifecycle stage change.\n * \n * @param userId - User identifier\n * @param stage - New lifecycle stage\n * @param previousStage - Previous stage (optional)\n * @returns Promise resolving to stage change response\n */\n async trackLifecycleChange(\n userId: string,\n stage: UserLifecycleStage,\n previousStage?: UserLifecycleStage\n ): Promise<ActivityResponse> {\n return await this.init(\"lifecycle_change\", {\n type: \"lifecycle_change\",\n userId,\n stage,\n previousStage,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default Retention;\n","/**\n * @fileoverview Product Analytics - Revenue Attribution\n * @module features/analytics/attribution\n * \n * @description\n * Multi-touch attribution modeling to understand which marketing\n * channels and touchpoints drive conversions and revenue.\n * \n * @useCases\n * - Track marketing touchpoints across customer journey\n * - Calculate attribution for conversions\n * - Compare attribution models (first-touch, last-touch, linear)\n * - Optimize marketing spend allocation\n * - Understand channel effectiveness\n * \n * @metrics\n * - Attributed revenue by channel\n * - Conversion paths\n * - Assisted conversions\n * - Time to conversion\n * - Channel influence score\n * \n * @chartData\n * - Sankey diagram: Customer journey paths\n * - Bar chart: Revenue by channel\n * - Table: Attribution comparison by model\n * - Pie chart: Channel contribution\n * \n * @example\n * ```typescript\n * const attribution = new Attribution(webId, endpoint, apiKey);\n * \n * // Track touchpoint\n * await attribution.trackTouchpoint({\n * userId: 'user_123',\n * channel: 'google_ads',\n * campaign: 'brand_terms',\n * medium: 'cpc'\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { \n UTMParams,\n MoneyValue,\n DateRangeFilter,\n TimeSeriesData\n} from \"../../types\";\nimport { getWindow, getDocument, isBrowser } from \"../../utils/browser\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Attribution model types.\n */\nexport type AttributionModel = \n | 'first_touch'\n | 'last_touch'\n | 'linear'\n | 'time_decay'\n | 'position_based'\n | 'data_driven';\n\n/**\n * Marketing touchpoint configuration.\n */\nexport interface TouchpointConfig {\n /** User ID */\n userId: string;\n /** Marketing channel */\n channel: string;\n /** Campaign name */\n campaign?: string;\n /** Traffic medium */\n medium?: string;\n /** Traffic source */\n source?: string;\n /** Content/creative */\n content?: string;\n /** Search term */\n term?: string;\n /** Referrer URL */\n referrer?: string;\n /** Landing page */\n landingPage?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Conversion event for attribution.\n */\nexport interface ConversionEvent {\n /** User ID */\n userId: string;\n /** Conversion type */\n conversionType: 'purchase' | 'signup' | 'lead' | 'custom';\n /** Conversion value */\n value?: MoneyValue;\n /** Conversion identifier (e.g., order ID) */\n conversionId?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Touchpoint response from API.\n */\nexport interface TouchpointResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n touchpointId: string;\n userId: string;\n position: number;\n tracked: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Attribution report data.\n */\nexport interface AttributionReportData {\n /** Selected attribution model */\n model: AttributionModel;\n /** Attribution by channel */\n byChannel: Array<{\n channel: string;\n conversions: number;\n attributedRevenue: MoneyValue;\n assistedConversions: number;\n avgTouchpoints: number;\n firstTouchConversions: number;\n lastTouchConversions: number;\n }>;\n /** Top conversion paths */\n topPaths: Array<{\n path: string[];\n conversions: number;\n revenue: MoneyValue;\n avgTimeToConvert: number;\n }>;\n /** Summary metrics */\n summary: {\n totalConversions: number;\n totalRevenue: MoneyValue;\n avgTouchpointsPerConversion: number;\n avgTimeToConversion: number;\n topChannel: string;\n topCampaign: string;\n };\n /** Model comparison */\n modelComparison: Array<{\n model: AttributionModel;\n channelAttribution: Record<string, number>;\n }>;\n}\n\n// ============================================================================\n// ATTRIBUTION CLASS\n// ============================================================================\n\n/**\n * Attribution - Multi-touch attribution tracking.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides attribution capabilities including:\n * - Touchpoint tracking\n * - Conversion attribution\n * - Multiple attribution models\n * - Path analysis\n * - Channel effectiveness\n * \n * @example\n * ```typescript\n * const attribution = new Attribution(webId, endpoint, apiKey);\n * \n * // Track touchpoints throughout customer journey\n * await attribution.trackTouchpoint({\n * userId: 'user_123',\n * channel: 'email',\n * campaign: 'welcome_series',\n * medium: 'email'\n * });\n * \n * // Record conversion\n * await attribution.recordConversion({\n * userId: 'user_123',\n * conversionType: 'purchase',\n * value: { amount: 9999, currency: 'USD' },\n * conversionId: 'ORD-12345'\n * });\n * ```\n */\nexport class Attribution extends FlowGridTransport {\n /**\n * Tracks a marketing touchpoint.\n * \n * @param config - Touchpoint configuration\n * @returns Promise resolving to touchpoint response\n * \n * @example\n * ```typescript\n * await attribution.trackTouchpoint({\n * userId: 'user_123',\n * channel: 'facebook_ads',\n * campaign: 'retargeting_cart',\n * medium: 'paid_social',\n * landingPage: '/products/special-offer'\n * });\n * ```\n */\n async trackTouchpoint(config: TouchpointConfig): Promise<TouchpointResponse> {\n return await this.init(\"attribution_touchpoint\", {\n type: \"attribution_touchpoint\",\n userId: config.userId,\n channel: config.channel,\n campaign: config.campaign,\n medium: config.medium,\n source: config.source,\n content: config.content,\n term: config.term,\n referrer: config.referrer,\n landingPage: config.landingPage,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Records a conversion for attribution.\n * \n * @param event - Conversion event\n * @returns Promise resolving to conversion response\n * \n * @example\n * ```typescript\n * await attribution.recordConversion({\n * userId: 'user_123',\n * conversionType: 'purchase',\n * value: { amount: 14999, currency: 'USD' },\n * conversionId: 'ORD-98765'\n * });\n * ```\n */\n async recordConversion(event: ConversionEvent): Promise<TouchpointResponse> {\n return await this.init(\"attribution_conversion\", {\n type: \"attribution_conversion\",\n userId: event.userId,\n conversionType: event.conversionType,\n value: event.value,\n conversionId: event.conversionId,\n ...event.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Auto-capture a touchpoint from `utm_*` query params and `document.referrer`.\n * Browser-only; returns null on the server or when no UTM/referrer data is present.\n */\n captureFromUrl(userId: string): TouchpointConfig | null {\n if (!isBrowser()) return null;\n const win = getWindow();\n const doc = getDocument();\n if (!win || !doc) return null;\n\n const params = new URLSearchParams(win.location.search);\n const utm: UTMParams = {\n utmSource: params.get(\"utm_source\") ?? undefined,\n utmMedium: params.get(\"utm_medium\") ?? undefined,\n utmCampaign: params.get(\"utm_campaign\") ?? undefined,\n utmTerm: params.get(\"utm_term\") ?? undefined,\n utmContent: params.get(\"utm_content\") ?? undefined,\n };\n const referrer = doc.referrer || undefined;\n\n if (!utm.utmSource && !utm.utmMedium && !utm.utmCampaign && !referrer) {\n return null;\n }\n\n const config: TouchpointConfig = {\n userId,\n channel: utm.utmSource || (referrer ? \"referral\" : \"direct\"),\n campaign: utm.utmCampaign,\n medium: utm.utmMedium,\n source: utm.utmSource,\n content: utm.utmContent,\n term: utm.utmTerm,\n referrer,\n landingPage: win.location.pathname,\n };\n void this.trackTouchpoint(config);\n return config;\n }\n}\n\nexport default Attribution;\n","/**\n * @fileoverview Product Analytics - Heatmaps & Click Tracking\n * @module features/analytics/heatmaps\n * \n * @description\n * Track user interactions for heatmap visualization including\n * clicks, mouse movements, and scroll behavior.\n * \n * @useCases\n * - Visualize click patterns on pages\n * - Identify dead zones and ignored elements\n * - Analyze scroll depth and attention\n * - Optimize CTA placement\n * - Improve page layout based on behavior\n * \n * @metrics\n * - Click coordinates and density\n * - Scroll depth distribution\n * - Attention time by viewport\n * - Element interaction rates\n * - Rage clicks detection\n * \n * @chartData\n * - Heatmap: Click density overlay\n * - Scroll map: Attention by page section\n * - Element report: Interaction by element\n * - Rage click detection\n * \n * @example\n * ```typescript\n * const heatmaps = new Heatmaps(webId, endpoint, apiKey);\n * \n * // Track click\n * await heatmaps.trackClick({\n * pageUrl: '/pricing',\n * x: 450,\n * y: 320,\n * viewportWidth: 1920,\n * viewportHeight: 1080\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { DateRangeFilter } from \"../../types\";\nimport { getWindow, getDocument, isBrowser, combineTeardowns, Teardown } from \"../../utils/browser\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Click interaction data.\n */\nexport interface ClickData {\n /** Page URL */\n pageUrl: string;\n /** X coordinate (from left) */\n x: number;\n /** Y coordinate (from top) */\n y: number;\n /** Viewport width */\n viewportWidth: number;\n /** Viewport height */\n viewportHeight: number;\n /** Element selector clicked */\n elementSelector?: string;\n /** Element text (truncated) */\n elementText?: string;\n /** User ID */\n userId?: string;\n /** Session ID */\n sessionId?: string;\n}\n\n/**\n * Movement/hover data.\n */\nexport interface MovementData {\n /** Page URL */\n pageUrl: string;\n /** Movement path (array of x,y coordinates) */\n path: Array<{ x: number; y: number; t: number }>;\n /** Viewport dimensions */\n viewport: { width: number; height: number };\n /** Session ID */\n sessionId?: string;\n}\n\n/**\n * Scroll data.\n */\nexport interface ScrollData {\n /** Page URL */\n pageUrl: string;\n /** Maximum scroll depth reached (percentage) */\n maxScrollDepth: number;\n /** Page height */\n pageHeight: number;\n /** Viewport height */\n viewportHeight: number;\n /** Time at various scroll depths */\n timeAtDepths?: Record<number, number>;\n /** Session ID */\n sessionId?: string;\n}\n\n/**\n * Heatmap response from API.\n */\nexport interface HeatmapResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n recorded: boolean;\n interactionId: string;\n };\n timestamp: string;\n}\n\n/**\n * Heatmap data for visualization.\n */\nexport interface HeatmapData {\n /** Page URL */\n pageUrl: string;\n /** Click points for heatmap */\n clicks: Array<{\n x: number;\n y: number;\n count: number;\n normalizedX: number;\n normalizedY: number;\n }>;\n /** Scroll depth data */\n scrollDepth: Array<{\n depth: number;\n usersReached: number;\n percentageReached: number;\n avgTimeAtDepth: number;\n }>;\n /** Element interaction data */\n elements: Array<{\n selector: string;\n clicks: number;\n hovers: number;\n avgTimeHovered: number;\n }>;\n /** Rage click zones */\n rageClicks: Array<{\n x: number;\n y: number;\n count: number;\n element: string;\n }>;\n /** Summary */\n summary: {\n totalClicks: number;\n uniqueSessions: number;\n avgScrollDepth: number;\n rageClickRate: number;\n };\n}\n\n// ============================================================================\n// HEATMAPS CLASS\n// ============================================================================\n\n/**\n * Heatmaps - Track user interactions for heatmap visualization.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides heatmap data collection including:\n * - Click tracking with coordinates\n * - Mouse movement tracking\n * - Scroll depth tracking\n * - Element interaction tracking\n * - Rage click detection\n * \n * @example\n * ```typescript\n * const heatmaps = new Heatmaps(webId, endpoint, apiKey);\n * \n * // Track clicks\n * document.addEventListener('click', (e) => {\n * heatmaps.trackClick({\n * pageUrl: window.location.pathname,\n * x: e.clientX,\n * y: e.clientY + window.scrollY,\n * viewportWidth: window.innerWidth,\n * viewportHeight: window.innerHeight,\n * elementSelector: getSelector(e.target)\n * });\n * });\n * \n * // Get heatmap data for visualization\n * const data = await heatmaps.getData('/pricing', { range: 'last7days' });\n * ```\n */\nexport class Heatmaps extends FlowGridTransport {\n\n /** Throttle timestamps */\n private lastMovementTime = 0;\n private lastScrollTime = 0;\n\n /** Throttle intervals (ms) */\n private static readonly MOVEMENT_THROTTLE_MS = 100;\n private static readonly SCROLL_THROTTLE_MS = 500;\n\n /**\n * Tracks a click interaction.\n * \n * @param data - Click data\n * @returns Promise resolving to heatmap response\n */\n async trackClick(data: ClickData): Promise<HeatmapResponse> {\n return await this.init(\"heatmap_click\", {\n type: \"heatmap_click\",\n pageUrl: data.pageUrl,\n x: data.x,\n y: data.y,\n normalizedX: data.x / data.viewportWidth,\n normalizedY: data.y / data.viewportHeight,\n viewportWidth: data.viewportWidth,\n viewportHeight: data.viewportHeight,\n elementSelector: data.elementSelector,\n elementText: data.elementText?.substring(0, 50),\n userId: data.userId,\n sessionId: data.sessionId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks mouse movement path.\n * \n * @param data - Movement data\n * @returns Promise resolving to heatmap response\n */\n async trackMovement(data: MovementData): Promise<HeatmapResponse> {\n const now = Date.now();\n if (now - this.lastMovementTime < Heatmaps.MOVEMENT_THROTTLE_MS) {\n return { eventName: 'heatmap_movement', properties: {}, response: { recorded: false, interactionId: '' }, timestamp: new Date().toISOString() };\n }\n this.lastMovementTime = now;\n\n return await this.init(\"heatmap_movement\", {\n type: \"heatmap_movement\",\n pageUrl: data.pageUrl,\n path: data.path,\n viewport: data.viewport,\n sessionId: data.sessionId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks scroll behavior.\n * \n * @param data - Scroll data\n * @returns Promise resolving to heatmap response\n */\n async trackScroll(data: ScrollData): Promise<HeatmapResponse> {\n const now = Date.now();\n if (now - this.lastScrollTime < Heatmaps.SCROLL_THROTTLE_MS) {\n return { eventName: 'heatmap_scroll', properties: {}, response: { recorded: false, interactionId: '' }, timestamp: new Date().toISOString() };\n }\n this.lastScrollTime = now;\n\n return await this.init(\"heatmap_scroll\", {\n type: \"heatmap_scroll\",\n pageUrl: data.pageUrl,\n maxScrollDepth: data.maxScrollDepth,\n pageHeight: data.pageHeight,\n viewportHeight: data.viewportHeight,\n timeAtDepths: data.timeAtDepths,\n sessionId: data.sessionId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks rage clicks (repeated clicks in frustration).\n * \n * @param pageUrl - Page URL\n * @param x - X coordinate\n * @param y - Y coordinate\n * @param clickCount - Number of rapid clicks\n * @returns Promise resolving to heatmap response\n */\n async trackRageClick(\n pageUrl: string,\n x: number,\n y: number,\n clickCount: number\n ): Promise<HeatmapResponse> {\n return await this.init(\"heatmap_rage_click\", {\n type: \"heatmap_rage_click\",\n pageUrl,\n x,\n y,\n clickCount,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Auto-collect heatmap interactions: clicks (with element selector + text)\n * and scroll depth on the current page. Mouse-move tracking is opt-in\n * because the volume can be very high.\n *\n * @param options.movement Subscribe to throttled mousemove sampling.\n * @param options.userId Attached to every event.\n * @returns Teardown function detaching all listeners.\n */\n autoTrack(options: { movement?: boolean; userId?: string; sessionId?: string } = {}): Teardown {\n if (!isBrowser()) return () => { /* no-op on the server */ };\n const win = getWindow()!;\n const doc = getDocument()!;\n\n const selectorOf = (el: Element | null): string => {\n if (!el) return \"\";\n if (el.id) return `#${el.id}`;\n const tag = el.tagName.toLowerCase();\n const cls = (el.className && typeof el.className === \"string\")\n ? \".\" + el.className.trim().split(/\\s+/).slice(0, 2).join(\".\")\n : \"\";\n return `${tag}${cls}`;\n };\n\n const onClick = (e: MouseEvent) => {\n const target = e.target as Element | null;\n void this.trackClick({\n pageUrl: win.location.pathname,\n x: e.clientX,\n y: e.clientY + win.scrollY,\n viewportWidth: win.innerWidth,\n viewportHeight: win.innerHeight,\n elementSelector: selectorOf(target),\n elementText: (target?.textContent || \"\").trim().slice(0, 50),\n userId: options.userId,\n sessionId: options.sessionId,\n });\n };\n doc.addEventListener(\"click\", onClick, { capture: true, passive: true });\n\n let maxDepth = 0;\n let scrollFlushScheduled = false;\n const flushScroll = () => {\n scrollFlushScheduled = false;\n const docEl = doc.documentElement;\n void this.trackScroll({\n pageUrl: win.location.pathname,\n maxScrollDepth: maxDepth,\n pageHeight: docEl.scrollHeight,\n viewportHeight: win.innerHeight,\n sessionId: options.sessionId,\n });\n };\n const onScroll = () => {\n const docEl = doc.documentElement;\n const top = win.scrollY || docEl.scrollTop;\n const height = Math.max(1, docEl.scrollHeight - win.innerHeight);\n const depth = Math.min(100, Math.round((top / height) * 100));\n if (depth > maxDepth) maxDepth = depth;\n if (!scrollFlushScheduled) {\n scrollFlushScheduled = true;\n win.addEventListener(\"pagehide\", flushScroll, { once: true });\n }\n };\n win.addEventListener(\"scroll\", onScroll, { passive: true });\n\n let moveTeardown: (() => void) | undefined;\n if (options.movement) {\n let buffer: Array<{ x: number; y: number; t: number }> = [];\n const start = Date.now();\n const onMove = (e: MouseEvent) => {\n buffer.push({ x: e.clientX, y: e.clientY, t: Date.now() - start });\n if (buffer.length >= 50) {\n const path = buffer; buffer = [];\n void this.trackMovement({\n pageUrl: win.location.pathname,\n path,\n viewport: { width: win.innerWidth, height: win.innerHeight },\n sessionId: options.sessionId,\n });\n }\n };\n doc.addEventListener(\"mousemove\", onMove, { passive: true });\n moveTeardown = () => doc.removeEventListener(\"mousemove\", onMove);\n }\n\n return combineTeardowns(\n () => doc.removeEventListener(\"click\", onClick, { capture: true } as EventListenerOptions),\n () => win.removeEventListener(\"scroll\", onScroll),\n () => win.removeEventListener(\"pagehide\", flushScroll),\n moveTeardown,\n );\n }\n}\n\nexport default Heatmaps;\n","/**\n * @fileoverview Product Analytics - Performance Monitoring\n * @module features/analytics/performance\n * \n * @description\n * Track web performance metrics including Core Web Vitals,\n * page load times, and resource performance.\n * \n * @useCases\n * - Monitor Core Web Vitals (LCP, FID, CLS)\n * - Track page load performance\n * - Identify slow pages and resources\n * - Correlate performance with conversions\n * - Monitor API response times\n * \n * @metrics\n * - Largest Contentful Paint (LCP)\n * - First Input Delay (FID)\n * - Cumulative Layout Shift (CLS)\n * - Time to First Byte (TTFB)\n * - First Contentful Paint (FCP)\n * \n * @chartData\n * - Line chart: Core Web Vitals over time\n * - Bar chart: Slowest pages\n * - Histogram: Load time distribution\n * - Gauge: Performance score\n * \n * @example\n * ```typescript\n * const performance = new Performance(webId, endpoint, apiKey);\n * \n * // Track Core Web Vitals\n * await performance.trackWebVitals({\n * lcp: 2500,\n * fid: 100,\n * cls: 0.1,\n * pageUrl: '/products'\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { DateRangeFilter, TimeSeriesData } from \"../../types\";\nimport { getWindow, isBrowser, combineTeardowns, Teardown } from \"../../utils/browser\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Core Web Vitals data.\n */\nexport interface WebVitalsData {\n /** Largest Contentful Paint (ms) */\n lcp?: number;\n /** First Input Delay (ms) */\n fid?: number;\n /** Cumulative Layout Shift (score) */\n cls?: number;\n /** First Contentful Paint (ms) */\n fcp?: number;\n /** Time to First Byte (ms) */\n ttfb?: number;\n /** Interaction to Next Paint (ms) */\n inp?: number;\n /** Page URL */\n pageUrl: string;\n /** User ID */\n userId?: string;\n /** Connection type */\n connectionType?: 'slow-2g' | '2g' | '3g' | '4g' | 'wifi' | 'unknown';\n /** Device type */\n deviceType?: 'mobile' | 'tablet' | 'desktop';\n}\n\n/**\n * Page load timing data.\n */\nexport interface PageLoadData {\n /** Page URL */\n pageUrl: string;\n /** DNS lookup time (ms) */\n dnsTime?: number;\n /** TCP connection time (ms) */\n tcpTime?: number;\n /** Time to first byte (ms) */\n ttfb?: number;\n /** DOM content loaded time (ms) */\n domContentLoaded?: number;\n /** Full page load time (ms) */\n loadTime?: number;\n /** DOM interactive time (ms) */\n domInteractive?: number;\n /** Number of resources */\n resourceCount?: number;\n /** Total transfer size (bytes) */\n transferSize?: number;\n}\n\n/**\n * Resource timing data.\n */\nexport interface ResourceData {\n /** Resource URL */\n resourceUrl: string;\n /** Resource type */\n resourceType: 'script' | 'stylesheet' | 'image' | 'font' | 'fetch' | 'xhr' | 'other';\n /** Duration (ms) */\n duration: number;\n /** Transfer size (bytes) */\n transferSize?: number;\n /** Page URL where resource was loaded */\n pageUrl: string;\n}\n\n/**\n * Performance response from API.\n */\nexport interface PerformanceResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n recorded: boolean;\n performanceScore?: number;\n };\n timestamp: string;\n}\n\n/**\n * Performance analytics data.\n */\nexport interface PerformanceAnalyticsData {\n /** Core Web Vitals summary */\n webVitals: {\n lcp: { p50: number; p75: number; p95: number; good: number; needsImprovement: number; poor: number };\n fid: { p50: number; p75: number; p95: number; good: number; needsImprovement: number; poor: number };\n cls: { p50: number; p75: number; p95: number; good: number; needsImprovement: number; poor: number };\n inp: { p50: number; p75: number; p95: number; good: number; needsImprovement: number; poor: number };\n };\n /** Performance by page */\n byPage: Array<{\n pageUrl: string;\n avgLoadTime: number;\n avgLCP: number;\n avgFID: number;\n avgCLS: number;\n sampleSize: number;\n }>;\n /** Performance by device */\n byDevice: Array<{\n device: string;\n avgLoadTime: number;\n avgLCP: number;\n sampleSize: number;\n }>;\n /** Slowest resources */\n slowResources: Array<{\n resourceUrl: string;\n resourceType: string;\n avgDuration: number;\n occurrences: number;\n }>;\n /** Performance trends */\n trends: {\n lcp: TimeSeriesData;\n fid: TimeSeriesData;\n cls: TimeSeriesData;\n };\n /** Overall score (0-100) */\n performanceScore: number;\n}\n\n// ============================================================================\n// PERFORMANCE CLASS\n// ============================================================================\n\n/**\n * Performance - Track web performance metrics.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides performance monitoring including:\n * - Core Web Vitals tracking\n * - Page load timing\n * - Resource performance\n * - API response times\n * - Performance scoring\n * \n * @example\n * ```typescript\n * const performance = new Performance(webId, endpoint, apiKey);\n * \n * // Track Web Vitals (use web-vitals library)\n * import { onLCP, onFID, onCLS } from 'web-vitals';\n * \n * onLCP((metric) => {\n * performance.trackWebVitals({\n * lcp: metric.value,\n * pageUrl: window.location.pathname\n * });\n * });\n * ```\n */\nexport class PerformanceTracking extends FlowGridTransport {\n /**\n * Tracks Core Web Vitals.\n * \n * @param data - Web Vitals data\n * @returns Promise resolving to performance response\n */\n async trackWebVitals(data: WebVitalsData): Promise<PerformanceResponse> {\n return await this.init(\"web_vitals\", {\n type: \"web_vitals\",\n ...data,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks page load timing.\n * \n * @param data - Page load data\n * @returns Promise resolving to performance response\n */\n async trackPageLoad(data: PageLoadData): Promise<PerformanceResponse> {\n return await this.init(\"page_load\", {\n type: \"page_load\",\n ...data,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks resource performance.\n * \n * @param data - Resource data\n * @returns Promise resolving to performance response\n */\n async trackResource(data: ResourceData): Promise<PerformanceResponse> {\n return await this.init(\"resource_timing\", {\n type: \"resource_timing\",\n ...data,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks API call performance.\n * \n * @param endpoint - API endpoint\n * @param duration - Response time (ms)\n * @param success - Whether call succeeded\n * @param statusCode - HTTP status code\n * @returns Promise resolving to performance response\n */\n async trackApiCall(\n endpoint: string,\n duration: number,\n success: boolean,\n statusCode?: number\n ): Promise<PerformanceResponse> {\n return await this.init(\"api_timing\", {\n type: \"api_timing\",\n endpoint,\n duration,\n success,\n statusCode,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Auto-collect Core Web Vitals, navigation timing, and resource timing\n * directly from the browser. Safe no-op outside the browser.\n *\n * @param options.userId Optional user identifier attached to every metric.\n * @param options.resources When true, also subscribes to PerformanceObserver\n * for `resource` entries. Default: false (noisy).\n * @param options.minDuration Only forward resources slower than this (ms). Default 200.\n * @returns A teardown function that detaches all observers/listeners.\n *\n * @example\n * ```ts\n * const stop = fg.performance.client.autoTrack({ userId: \"u_1\" });\n * // ...later\n * stop();\n * ```\n */\n autoTrack(options: {\n userId?: string;\n resources?: boolean;\n minDuration?: number;\n } = {}): Teardown {\n if (!isBrowser()) return () => { /* no-op on the server */ };\n\n const win = getWindow()!;\n const pageUrl = win.location.pathname;\n const teardowns: Array<() => void> = [];\n\n // --- Web Vitals via the `web-vitals` library (dynamic import keeps it\n // out of server bundles even if tree-shaking misses it).\n let cancelled = false;\n void import(/* webpackChunkName: \"flowgrid-vitals\" */ \"web-vitals\").then((wv) => {\n if (cancelled) return;\n const send = (metric: { name: string; value: number }) => {\n const key = metric.name.toLowerCase() as keyof Pick<WebVitalsData, \"lcp\" | \"fid\" | \"cls\" | \"fcp\" | \"ttfb\" | \"inp\">;\n const payload: WebVitalsData = { pageUrl, userId: options.userId };\n (payload[key] as number | undefined) = metric.value;\n void this.trackWebVitals(payload);\n };\n wv.onLCP?.(send);\n wv.onCLS?.(send);\n wv.onINP?.(send);\n wv.onFCP?.(send);\n wv.onTTFB?.(send);\n // FID was removed in web-vitals v4+, but we still surface it if present\n (wv as { onFID?: (cb: (m: { name: string; value: number }) => void) => void }).onFID?.(send);\n }).catch(() => { /* library missing — silently skip */ });\n teardowns.push(() => { cancelled = true; });\n\n // --- PerformanceNavigationTiming → trackPageLoad\n const reportNavigation = () => {\n const nav = win.performance?.getEntriesByType?.(\"navigation\")?.[0] as\n PerformanceNavigationTiming | undefined;\n if (!nav) return;\n void this.trackPageLoad({\n pageUrl,\n dnsTime: Math.max(0, nav.domainLookupEnd - nav.domainLookupStart),\n tcpTime: Math.max(0, nav.connectEnd - nav.connectStart),\n ttfb: Math.max(0, nav.responseStart - nav.requestStart),\n domContentLoaded: nav.domContentLoadedEventEnd,\n loadTime: nav.loadEventEnd,\n domInteractive: nav.domInteractive,\n transferSize: nav.transferSize,\n });\n };\n if (win.document.readyState === \"complete\") {\n reportNavigation();\n } else {\n const onLoad = () => reportNavigation();\n win.addEventListener(\"load\", onLoad, { once: true });\n teardowns.push(() => win.removeEventListener(\"load\", onLoad));\n }\n\n // --- Resource timing (opt-in)\n if (options.resources && typeof win.PerformanceObserver === \"function\") {\n const min = options.minDuration ?? 200;\n const obs = new win.PerformanceObserver((list) => {\n for (const entry of list.getEntries() as PerformanceResourceTiming[]) {\n if (entry.duration < min) continue;\n const initiator = entry.initiatorType as ResourceData[\"resourceType\"];\n void this.trackResource({\n resourceUrl: entry.name,\n resourceType: ([\n \"script\", \"stylesheet\", \"image\", \"font\", \"fetch\", \"xhr\",\n ].includes(initiator) ? initiator : \"other\"),\n duration: Math.round(entry.duration),\n transferSize: entry.transferSize,\n pageUrl,\n });\n }\n });\n try {\n obs.observe({ type: \"resource\", buffered: true });\n teardowns.push(() => obs.disconnect());\n } catch { /* unsupported */ }\n }\n\n return combineTeardowns(...teardowns);\n }\n}\n\nexport { PerformanceTracking as Performance };\nexport default PerformanceTracking;\n","/**\n * @fileoverview Session Replay (rrweb) capture.\n * @module features/analytics/replay\n *\n * @description\n * Browser-only DOM session replay powered by [rrweb](https://github.com/rrweb-io/rrweb).\n * Records DOM mutations, mouse, scroll, input, and viewport events, batches them\n * into chunks, and ships them to the FlowGrid backend at `call=replay_chunk`.\n *\n * Loaded dynamically so it is only pulled into the bundle when `start()` is\n * called in a browser environment.\n *\n * @example\n * ```ts\n * const stop = await fg.replay.start({\n * sampleRate: 1.0,\n * maskAllInputs: true,\n * });\n * // ...later\n * stop();\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { isBrowser, getDocument, type Teardown } from \"../../utils/browser\";\n\n// ============================================================================\n// CONSENT\n// ============================================================================\n\nconst REPLAY_CONSENT_KEY = \"flowgrid:replayConsent\";\n\n/**\n * Returns true if the user has previously granted replay consent\n * (persisted in localStorage across sessions).\n */\nexport function hasReplayConsent(): boolean {\n if (!isBrowser()) return false;\n try {\n return localStorage.getItem(REPLAY_CONSENT_KEY) === \"granted\";\n } catch {\n return false;\n }\n}\n\n/**\n * Persist replay consent in localStorage.\n * Call this from your cookie/consent banner \"Accept\" handler.\n * Once granted, the banner will not need to re-prompt on subsequent visits\n * and `SessionReplay.start()` will proceed without being blocked.\n *\n * @example\n * ```ts\n * // In your consent banner \"Accept All\" click handler:\n * grantReplayConsent();\n * ```\n */\nexport function grantReplayConsent(): void {\n if (!isBrowser()) return;\n try {\n localStorage.setItem(REPLAY_CONSENT_KEY, \"granted\");\n } catch { /* storage unavailable */ }\n}\n\n/**\n * Revoke replay consent and remove the persisted value.\n * Recording will be blocked on the next `start()` call when\n * `requireConsent: true` is set.\n */\nexport function revokeReplayConsent(): void {\n if (!isBrowser()) return;\n try {\n localStorage.removeItem(REPLAY_CONSENT_KEY);\n } catch { /* storage unavailable */ }\n}\n\n// ============================================================================\n// CONFIG\n// ============================================================================\n\nexport interface SessionReplayOptions {\n\n /** Explicit enable switch. Pass `false` to keep the recorder disabled. */\n enabled?: boolean;\n\n /**\n * Gate recording on explicit user consent.\n * When `true`, `start()` will only proceed if `hasReplayConsent()` returns\n * `true` (localStorage) or `FlowGridTransport.hasConsent(\"analytics\")` is set.\n * Call `grantReplayConsent()` from your consent banner to unblock recording.\n * Default `false` (backwards-compatible — does not break existing installs).\n */\n requireConsent?: boolean;\n\n /** Sampling rate 0.0-1.0. Default 1.0 (record every session). */\n sampleRate?: number;\n\n /** Existing session ID to attach the recording to. Auto-generated otherwise. */\n sessionId?: string;\n\n /** User ID for the recording, if known. */\n userId?: string;\n\n /** Mask all `<input>`, `<textarea>` and `[contenteditable]` values. Default true. */\n maskAllInputs?: boolean;\n\n /** Mask text content under elements matching this CSS class. Default `\"fg-mask\"`. */\n maskTextClass?: string | RegExp;\n\n /** Skip recording elements matching this CSS class entirely. Default `\"fg-block\"`. */\n blockClass?: string | RegExp;\n\n /** Ignore events on elements matching this CSS class. Default `\"fg-ignore\"`. */\n ignoreClass?: string;\n\n /** Flush buffered events at most every N ms. Default 5000. */\n flushIntervalMs?: number;\n\n /** Force flush when buffer reaches N bytes (approximate). Default 512KB. */\n flushBytes?: number;\n\n /** Maximum recording length per session in ms. Default 30 minutes. */\n maxDurationMs?: number;\n\n /** Hard cap on total bytes captured per session. Default 10MB. */\n maxBytes?: number;\n\n /** Invoked when recorder state changes or lifecycle metadata is updated. */\n onStatus?: (status: SessionReplayStatus) => void;\n\n /** Invoked after a chunk is accepted for transport. */\n onFlush?: (metadata: SessionReplayFlushMetadata) => void;\n\n /** Invoked when events are dropped due to limits or serialization issues. */\n onDrop?: (metadata: SessionReplayDropMetadata) => void;\n\n /** Invoked when recorder setup or teardown fails. */\n onError?: (error: Error, context: SessionReplayErrorContext) => void;\n\n}\n\nexport type SessionReplayState =\n | \"idle\"\n | \"starting\"\n | \"recording\"\n | \"stopped\"\n | \"disabled\"\n | \"sampled_out\"\n | \"error\";\n\nexport type SessionReplayFlushReason =\n | \"interval\"\n | \"size\"\n | \"visibility_hidden\"\n | \"pagehide\"\n | \"manual\"\n | \"stop\"\n | \"max_duration\"\n | \"max_bytes\";\n\nexport interface SessionReplayStatus {\n state: SessionReplayState;\n sessionId?: string;\n userId?: string;\n startedAt?: string;\n sequence: number;\n bufferedEvents: number;\n bufferedBytes: number;\n totalBytes: number;\n droppedEvents: number;\n recorderVersion: string;\n lastFlushReason?: SessionReplayFlushReason;\n lastError?: string;\n}\n\nexport interface SessionReplayFlushMetadata {\n sessionId: string;\n userId?: string;\n sequence: number;\n eventsCount: number;\n byteSize: number;\n reason: SessionReplayFlushReason;\n isFinal: boolean;\n droppedEvents: number;\n capturedAt: string;\n idempotencyKey: string;\n}\n\nexport interface SessionReplayDropMetadata {\n sessionId?: string;\n reason: \"serialization_error\" | \"max_bytes\";\n droppedEvents: number;\n byteSize?: number;\n maxBytes?: number;\n}\n\nexport interface SessionReplayErrorContext {\n phase: \"import\" | \"record\" | \"flush\" | \"stop\";\n sessionId?: string;\n}\n\nexport interface SessionReplaySendChunkOptions {\n userId?: string;\n sequence?: number;\n useBeacon?: boolean;\n startedAt?: string;\n capturedAt?: string;\n pageUrl?: string;\n isFinal?: boolean;\n reason?: SessionReplayFlushReason;\n privacy?: ReplayPrivacyMetadata;\n droppedEvents?: number;\n recorderVersion?: string;\n}\n\nexport interface ReplayPrivacyMetadata {\n masking_mode: \"all_inputs\" | \"selective\" | \"custom\";\n blocked_selectors: string[];\n redacted_fields: string[];\n consent_state: \"granted\" | \"denied\" | \"unknown\";\n sampling_state: \"sampled_in\" | \"sampled_out\";\n}\n\ninterface ActiveRecording {\n sessionId: string;\n userId?: string;\n startedAtMs: number;\n startedAt: string;\n sequence: number;\n buffer: unknown[];\n bufferBytes: number;\n totalBytes: number;\n droppedEvents: number;\n stopped: boolean;\n /** Set to true once the first FullSnapshot (rrweb event type 2) is in the buffer. */\n initialSnapshotCaptured: boolean;\n flushTimer: ReturnType<typeof setInterval>;\n durationGuard: ReturnType<typeof setTimeout>;\n stopRecording?: Teardown;\n onVisibility: () => void;\n onPageHide: () => void;\n options: Required<Pick<SessionReplayOptions, \"flushIntervalMs\" | \"flushBytes\" | \"maxDurationMs\" | \"maxBytes\" | \"maskAllInputs\">> & SessionReplayOptions;\n}\n\nconst DEFAULT_FLUSH_MS = 5_000;\nconst DEFAULT_FLUSH_BYTES = 512 * 1024;\nconst DEFAULT_MAX_DURATION_MS = 30 * 60 * 1000;\nconst DEFAULT_MAX_BYTES = 10 * 1024 * 1024;\nconst STORAGE_KEY = \"flowgrid:replaySessionId\";\nconst RECORDER_VERSION = \"rrweb@2\";\n\n// ============================================================================\n// SESSION REPLAY CLASS\n// ============================================================================\n\nexport class SessionReplay extends FlowGridTransport {\n private disabled = false;\n private active: ActiveRecording | null = null;\n private state: SessionReplayState = \"idle\";\n private boundUserId?: string;\n private lastError?: string;\n\n /**\n * Begin capturing the current page as a replay session. Returns a teardown\n * function that flushes the final buffer (via `sendBeacon` when possible)\n * and detaches the recorder.\n *\n * No-op on the server or when the browser fails sampling.\n */\n async start(options: SessionReplayOptions = {}): Promise<Teardown> {\n\n if (options.enabled === false || this.disabled) {\n this.setState(\"disabled\", options);\n return () => {};\n }\n\n if (this.active && !this.active.stopped) {\n this.emitStatus(this.active.options);\n return () => this.stop(\"manual\");\n }\n\n if (!isBrowser()) return () => {};\n\n // Consent gate: when requireConsent is true, recording only proceeds if the\n // user has explicitly granted consent (localStorage key or transport consent).\n if (options.requireConsent === true) {\n const consented = hasReplayConsent() || FlowGridTransport.hasConsent(\"analytics\");\n if (!consented) {\n this.setState(\"disabled\", options);\n return () => {};\n }\n }\n\n this.setState(\"starting\", options);\n\n const sampleRate = options.sampleRate ?? 1.0;\n if (sampleRate < 1 && Math.random() > sampleRate) {\n this.setState(\"sampled_out\", options);\n return () => {};\n }\n\n let rrweb: typeof import(\"rrweb\");\n try {\n rrweb = await import(/* webpackChunkName: \"flowgrid-replay\" */ \"rrweb\");\n } catch (error) {\n // Surface the failure — silent import errors are the #1 reason replays\n // never start without any indication in the dashboard.\n if (typeof console !== \"undefined\") {\n console.error(\"[FlowGrid] Session replay failed to load rrweb:\", error);\n }\n this.handleError(this.toError(error), { phase: \"import\" }, options);\n return () => {};\n }\n\n const doc = getDocument();\n if (!doc) return () => {};\n\n const sessionId = options.sessionId ?? this.resolveSessionId();\n const normalizedOptions = this.normalizeOptions(options);\n const startedAtMs = Date.now();\n \n const active: ActiveRecording = {\n sessionId,\n userId: options.userId ?? this.boundUserId,\n startedAtMs,\n startedAt: new Date(startedAtMs).toISOString(),\n sequence: 0,\n buffer: [],\n bufferBytes: 0,\n totalBytes: 0,\n droppedEvents: 0,\n stopped: false,\n initialSnapshotCaptured: false,\n flushTimer: setInterval(() => { void this.flush(\"interval\"); }, normalizedOptions.flushIntervalMs),\n durationGuard: setTimeout(() => this.stop(\"max_duration\"), normalizedOptions.maxDurationMs),\n onVisibility: () => {\n if (doc.visibilityState === \"hidden\") void this.flush(\"visibility_hidden\", { useBeacon: true });\n },\n onPageHide: () => { void this.flush(\"pagehide\", { useBeacon: true }); },\n options: normalizedOptions,\n };\n\n this.active = active;\n\n try {\n\n active.stopRecording = rrweb.record({\n emit: (event: unknown) => this.captureEvent(event),\n maskAllInputs: normalizedOptions.maskAllInputs,\n maskTextClass: normalizedOptions.maskTextClass ?? \"fg-mask\",\n blockClass: normalizedOptions.blockClass ?? \"fg-block\",\n ignoreClass: normalizedOptions.ignoreClass ?? \"fg-ignore\",\n // Emit a new FullSnapshot every 200 incremental events OR every 30\n // seconds, whichever comes first. The time-based gate ensures the\n // player always has a recent snapshot to seek from even in low-activity\n // sessions where 200 events accumulate very slowly.\n checkoutEveryNth: 200,\n checkoutEveryNms: 30_000,\n // Reduce snapshot payload size: don't inline images or collect fonts\n // in the snapshot — they can bloat the first chunk significantly and\n // increase the chance of the Tinybird 1 MB limit being hit on chunk 0.\n inlineImages: false,\n collectFonts: false,\n });\n\n } catch (error) {\n\n this.handleError(this.toError(error), { \n phase: \"record\", sessionId \n }, normalizedOptions);\n\n active.stopped = true;\n\n this.cleanupActive(\"stop\");\n\n this.active = null;\n\n return () => {};\n\n }\n\n doc.addEventListener(\"visibilitychange\", active.onVisibility);\n window.addEventListener(\"pagehide\", active.onPageHide);\n\n this.setState(\"recording\", normalizedOptions);\n\n return () => this.stop(\"manual\");\n }\n\n /** Enable replay capture and start recording immediately. */\n enable(options: SessionReplayOptions = {}): Promise<Teardown> {\n this.disabled = false;\n return this.start({ ...options, enabled: true });\n }\n\n /** Disable replay capture and stop any active recording. */\n disable(): void {\n this.disabled = true;\n this.stop(\"manual\");\n this.setState(\"disabled\", this.active?.options);\n }\n\n /** Stop recording and flush the final chunk with lifecycle metadata. */\n stop(reason: SessionReplayFlushReason = \"stop\"): void {\n const active = this.active;\n if (!active || active.stopped) return;\n active.stopped = true;\n this.cleanupActive(reason);\n void this.flush(reason, { useBeacon: true, isFinal: true });\n this.setState(\"stopped\", active.options, reason);\n this.active = null;\n }\n\n /** Flush buffered rrweb events without stopping the recorder. */\n async flush(\n reason: SessionReplayFlushReason = \"manual\",\n options: { useBeacon?: boolean; isFinal?: boolean } = {},\n ): Promise<void> {\n\n const active = this.active;\n if (!active || active.buffer.length === 0) return;\n\n const events = active.buffer;\n const eventsCount = events.length;\n const byteSize = active.bufferBytes;\n const sequence = active.sequence++;\n active.buffer = [];\n active.bufferBytes = 0;\n\n const capturedAt = new Date().toISOString();\n const idempotencyKey = this.createIdempotencyKey(active.sessionId, sequence, byteSize);\n const metadata: SessionReplayFlushMetadata = {\n sessionId: active.sessionId,\n userId: active.userId,\n sequence,\n eventsCount,\n byteSize,\n reason,\n isFinal: options.isFinal === true,\n droppedEvents: active.droppedEvents,\n capturedAt,\n idempotencyKey,\n };\n\n try {\n await this.postReplayChunk(active.sessionId, events, {\n userId: active.userId,\n sequence,\n useBeacon: options.useBeacon,\n startedAt: active.startedAt,\n capturedAt,\n pageUrl: this.getPageUrl(),\n isFinal: metadata.isFinal,\n reason,\n privacy: this.createPrivacyMetadata(active.options, \"sampled_in\"),\n droppedEvents: active.droppedEvents,\n recorderVersion: RECORDER_VERSION,\n }, idempotencyKey);\n active.options.onFlush?.(metadata);\n this.emitStatus(active.options, reason);\n } catch (error) {\n this.handleError(this.toError(error), { phase: \"flush\", sessionId: active.sessionId }, active.options);\n }\n }\n\n /** Bind a known user to subsequent chunks in the active recording. */\n identify(userId: string): void {\n this.boundUserId = userId;\n if (this.active) {\n this.active.userId = userId;\n this.emitStatus(this.active.options);\n }\n }\n\n /** Current recorder health/lifecycle state. */\n getStatus(): SessionReplayStatus {\n return this.createStatus(this.active?.options);\n }\n\n /**\n * Manually persist a single rrweb event chunk. Most callers should use\n * `start()` instead; this is exposed for tests and advanced integrations\n * that drive `rrweb.record()` themselves.\n */\n async sendChunk(\n sessionId: string,\n events: unknown[],\n options: SessionReplaySendChunkOptions = {},\n ): Promise<void> {\n if (events.length === 0) return;\n await this.postReplayChunk(sessionId, events, options);\n }\n\n private async postReplayChunk(\n sessionId: string,\n events: unknown[],\n options: SessionReplaySendChunkOptions,\n idempotencyKey = this.createIdempotencyKey(sessionId, options.sequence ?? 0, this.measureEvents(events)),\n ): Promise<void> {\n await this.postRaw(\n \"replay_chunk\",\n {\n type: \"replay_chunk\",\n session_id: sessionId,\n user_id: options.userId,\n sequence: options.sequence ?? 0,\n started_at: options.startedAt,\n captured_at: options.capturedAt ?? new Date().toISOString(),\n events,\n page_url: options.pageUrl ?? this.getPageUrl(),\n idempotency_key: idempotencyKey,\n compression: \"none\",\n is_final: options.isFinal === true,\n chunk_kind: options.isFinal ? \"final\" : \"partial\",\n flush_reason: options.reason ?? \"manual\",\n dropped_events: options.droppedEvents ?? 0,\n recorder_version: options.recorderVersion ?? RECORDER_VERSION,\n privacy: options.privacy ?? this.createPrivacyMetadata(undefined, \"sampled_in\"),\n },\n { useBeacon: options.useBeacon },\n );\n }\n\n private captureEvent(event: unknown): void {\n const active = this.active;\n if (!active || active.stopped) return;\n\n let size = 0;\n try {\n size = JSON.stringify(event).length;\n } catch {\n active.droppedEvents++;\n active.options.onDrop?.({\n sessionId: active.sessionId,\n reason: \"serialization_error\",\n droppedEvents: active.droppedEvents,\n });\n return;\n }\n\n if (active.totalBytes + size > active.options.maxBytes) {\n active.droppedEvents++;\n active.options.onDrop?.({\n sessionId: active.sessionId,\n reason: \"max_bytes\",\n droppedEvents: active.droppedEvents,\n byteSize: active.totalBytes + size,\n maxBytes: active.options.maxBytes,\n });\n this.stop(\"max_bytes\");\n return;\n }\n\n active.totalBytes += size;\n active.buffer.push(event);\n active.bufferBytes += size;\n\n // FullSnapshot (rrweb type 2) is always the first event emitted by rrweb.record().\n // Flush it immediately in the next microtask rather than waiting for the\n // 5-second interval — this guarantees sequence 0 is transmitted right away\n // and reduces the window in which a Tinybird network failure can lose the\n // only snapshot the player needs to initialize.\n if (!active.initialSnapshotCaptured && (event as { type?: number }).type === 2) {\n active.initialSnapshotCaptured = true;\n Promise.resolve().then(() => { void this.flush(\"manual\"); });\n return;\n }\n\n if (active.bufferBytes >= active.options.flushBytes) {\n void this.flush(\"size\");\n }\n }\n\n private cleanupActive(reason: SessionReplayFlushReason): void {\n const active = this.active;\n if (!active) return;\n const doc = getDocument();\n clearInterval(active.flushTimer);\n clearTimeout(active.durationGuard);\n doc?.removeEventListener(\"visibilitychange\", active.onVisibility);\n window.removeEventListener(\"pagehide\", active.onPageHide);\n try {\n active.stopRecording?.();\n } catch (error) {\n this.handleError(this.toError(error), { phase: \"stop\", sessionId: active.sessionId }, active.options);\n }\n this.emitStatus(active.options, reason);\n }\n\n private normalizeOptions(options: SessionReplayOptions): ActiveRecording[\"options\"] {\n return {\n ...options,\n flushIntervalMs: options.flushIntervalMs ?? DEFAULT_FLUSH_MS,\n flushBytes: options.flushBytes ?? DEFAULT_FLUSH_BYTES,\n maxDurationMs: options.maxDurationMs ?? DEFAULT_MAX_DURATION_MS,\n maxBytes: options.maxBytes ?? DEFAULT_MAX_BYTES,\n maskAllInputs: options.maskAllInputs ?? true,\n };\n }\n\n private createStatus(options?: SessionReplayOptions, reason?: SessionReplayFlushReason): SessionReplayStatus {\n const active = this.active;\n return {\n state: this.state,\n sessionId: active?.sessionId,\n userId: active?.userId ?? this.boundUserId,\n startedAt: active?.startedAt,\n sequence: active?.sequence ?? 0,\n bufferedEvents: active?.buffer.length ?? 0,\n bufferedBytes: active?.bufferBytes ?? 0,\n totalBytes: active?.totalBytes ?? 0,\n droppedEvents: active?.droppedEvents ?? 0,\n recorderVersion: RECORDER_VERSION,\n lastFlushReason: reason,\n lastError: this.lastError,\n };\n }\n\n private setState(state: SessionReplayState, options?: SessionReplayOptions, reason?: SessionReplayFlushReason): void {\n this.state = state;\n this.emitStatus(options, reason);\n }\n\n private emitStatus(options?: SessionReplayOptions, reason?: SessionReplayFlushReason): void {\n options?.onStatus?.(this.createStatus(options, reason));\n }\n\n private handleError(error: Error, context: SessionReplayErrorContext, options?: SessionReplayOptions): void {\n this.lastError = error.message;\n this.state = \"error\";\n options?.onError?.(error, context);\n this.emitStatus(options);\n }\n\n private createPrivacyMetadata(\n options?: SessionReplayOptions,\n samplingState: ReplayPrivacyMetadata[\"sampling_state\"] = \"sampled_in\",\n ): ReplayPrivacyMetadata {\n // Check both the persistent localStorage consent and the in-memory transport\n // consent gate so either path to granting consent is reflected accurately.\n const consentGranted = hasReplayConsent() || FlowGridTransport.hasConsent(\"analytics\");\n return {\n masking_mode: options?.maskAllInputs === false ? \"selective\" : \"all_inputs\",\n blocked_selectors: this.selectorList(options?.blockClass ?? \"fg-block\"),\n redacted_fields: this.selectorList(options?.maskTextClass ?? \"fg-mask\"),\n consent_state: consentGranted ? \"granted\" : \"denied\",\n sampling_state: samplingState,\n };\n }\n\n private selectorList(selector: string | RegExp): string[] {\n return typeof selector === \"string\" ? [selector] : [selector.source];\n }\n\n private getPageUrl(): string {\n try {\n return typeof location !== \"undefined\" ? location.href : \"\";\n } catch {\n return \"\";\n }\n }\n\n private createIdempotencyKey(sessionId: string, sequence: number, byteSize: number): string {\n return `${this.webId}:${sessionId}:${sequence}:${byteSize}`;\n }\n\n private measureEvents(events: unknown[]): number {\n try {\n return JSON.stringify(events).length;\n } catch {\n return 0;\n }\n }\n\n private toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n }\n\n private resolveSessionId(): string {\n try {\n const existing = sessionStorage.getItem(STORAGE_KEY);\n if (existing) return existing;\n const fresh = this.generateId();\n sessionStorage.setItem(STORAGE_KEY, fresh);\n return fresh;\n } catch {\n return this.generateId();\n }\n }\n\n private generateId(): string {\n const cryptoObj = (typeof crypto !== \"undefined\" ? crypto : undefined) as Crypto | undefined;\n if (cryptoObj?.randomUUID) return cryptoObj.randomUUID();\n return `rep_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;\n }\n}\n\nexport default SessionReplay;\n","/**\n * @fileoverview Ecommerce - Product Tracking\n * @module features/ecommerce/products\n * \n * @description\n * Track product interactions including views, impressions, clicks,\n * and product list interactions.\n * \n * @useCases\n * - Track product page views\n * - Measure product list/search impressions\n * - Analyze product click-through rates\n * - Monitor product detail engagement\n * - Compare product performance\n * \n * @metrics\n * - Product views\n * - Product impressions\n * - Click-through rate\n * - View-to-cart rate\n * - Revenue per product\n * \n * @chartData\n * - Bar chart: Top products by views\n * - Line chart: Product views over time\n * - Table: Product performance comparison\n * - Scatter: Price vs conversion rate\n * \n * @example\n * ```typescript\n * const products = new Products(webId, endpoint, apiKey);\n * \n * // Track product view\n * await products.view({\n * productId: 'SKU-123',\n * name: 'Premium Headphones',\n * price: 299.99,\n * currency: 'USD',\n * category: 'Electronics/Audio'\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { \n Product,\n ProductList,\n ProductPerformance,\n TimeSeriesData,\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for tracking a product view.\n */\nexport interface ProductViewConfig extends Product {\n /** User ID (if authenticated) */\n userId?: string;\n /** Session ID */\n sessionId?: string;\n /** How user arrived at product (search, category, recommendation) */\n source?: 'search' | 'category' | 'recommendation' | 'direct' | 'ad';\n /** Search query if from search */\n searchQuery?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Configuration for tracking product impressions.\n */\nexport interface ProductImpressionConfig {\n /** List information */\n list: ProductList;\n /** User ID */\n userId?: string;\n /** Session ID */\n sessionId?: string;\n}\n\n/**\n * Configuration for tracking product clicks.\n */\nexport interface ProductClickConfig {\n /** Product clicked */\n product: Product;\n /** List where product was clicked */\n listId?: string;\n /** Position in list */\n position?: number;\n /** User ID */\n userId?: string;\n}\n\n/**\n * Product event response from API.\n */\nexport interface ProductResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n productId: string;\n tracked: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Product analytics data for charts.\n */\nexport interface ProductAnalyticsData {\n /** Top products by various metrics */\n topProducts: {\n byViews: ProductPerformance[];\n byRevenue: ProductPerformance[];\n byConversion: ProductPerformance[];\n };\n /** Product views time series */\n viewsTimeSeries: TimeSeriesData;\n /** Summary metrics */\n summary: {\n totalProductViews: number;\n uniqueProductsViewed: number;\n avgViewsPerProduct: number;\n topCategory: string;\n topBrand: string;\n };\n /** Performance by category */\n byCategory: Array<{\n category: string;\n views: number;\n revenue: number;\n conversionRate: number;\n }>;\n}\n\n// ============================================================================\n// PRODUCTS CLASS\n// ============================================================================\n\n/**\n * Products - Track product interactions.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides product tracking capabilities including:\n * - Product page views\n * - Product list impressions\n * - Product clicks from lists\n * - Product detail interactions\n * - Quick view tracking\n * \n * @example\n * ```typescript\n * const products = new Products(webId, endpoint, apiKey);\n * \n * // Track product page view\n * await products.view({\n * productId: 'PROD-001',\n * name: 'Wireless Earbuds',\n * price: 79.99,\n * currency: 'USD',\n * category: 'Electronics/Audio',\n * brand: 'AudioTech'\n * });\n * \n * // Track list impressions\n * await products.impression({\n * list: {\n * listId: 'search-results',\n * listName: 'Search Results',\n * products: searchResults\n * }\n * });\n * ```\n */\nexport class Products extends FlowGridTransport {\n /**\n * Tracks a product page view.\n * \n * @param config - Product view configuration\n * @returns Promise resolving to product response\n * \n * @example\n * ```typescript\n * await products.view({\n * productId: 'SKU-12345',\n * name: 'Premium Widget',\n * price: 49.99,\n * currency: 'USD',\n * category: 'Widgets/Premium',\n * brand: 'WidgetCo',\n * source: 'search',\n * searchQuery: 'premium widget'\n * });\n * ```\n */\n async view(config: ProductViewConfig): Promise<ProductResponse> {\n return await this.init(\"product_view\", {\n type: \"ecommerce\",\n subtype: \"product_view\",\n product: {\n productId: config.productId,\n name: config.name,\n brand: config.brand,\n category: config.category,\n variant: config.variant,\n price: config.price,\n currency: config.currency,\n url: config.url,\n imageUrl: config.imageUrl,\n rating: config.rating,\n reviewCount: config.reviewCount,\n stockStatus: config.stockStatus,\n attributes: config.attributes\n },\n source: config.source,\n searchQuery: config.searchQuery,\n userId: config.userId,\n sessionId: config.sessionId,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks product list impressions.\n * \n * @param config - Impression configuration\n * @returns Promise resolving to impression response\n * \n * @example\n * ```typescript\n * // Track products shown in search results\n * await products.impression({\n * list: {\n * listId: 'search-results',\n * listName: 'Search Results',\n * products: products.map((p, i) => ({ ...p, position: i + 1 }))\n * }\n * });\n * ```\n */\n async impression(config: ProductImpressionConfig): Promise<ProductResponse> {\n return await this.init(\"product_impression\", {\n type: \"ecommerce\",\n subtype: \"product_impression\",\n list: config.list,\n productCount: config.list.products.length,\n userId: config.userId,\n sessionId: config.sessionId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a product click from a list.\n * \n * @param config - Click configuration\n * @returns Promise resolving to click response\n * \n * @example\n * ```typescript\n * await products.click({\n * product: selectedProduct,\n * listId: 'homepage-featured',\n * position: 3\n * });\n * ```\n */\n async click(config: ProductClickConfig): Promise<ProductResponse> {\n return await this.init(\"product_click\", {\n type: \"ecommerce\",\n subtype: \"product_click\",\n product: config.product,\n listId: config.listId,\n position: config.position,\n userId: config.userId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a quick view interaction (modal product view).\n * \n * @param product - Product being quick-viewed\n * @param listId - List where quick view was triggered\n * @returns Promise resolving to quick view response\n */\n async quickView(product: Product, listId?: string): Promise<ProductResponse> {\n return await this.init(\"product_quick_view\", {\n type: \"ecommerce\",\n subtype: \"product_quick_view\",\n product,\n listId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks product detail interaction (zoom, read reviews, etc.).\n * \n * @param productId - Product identifier\n * @param action - Interaction type\n * @param properties - Additional properties\n * @returns Promise resolving to interaction response\n */\n async interaction(\n productId: string,\n action: 'zoom' | 'read_reviews' | 'size_guide' | 'compare' | 'share' | 'notify_stock',\n properties?: Record<string, unknown>\n ): Promise<ProductResponse> {\n return await this.init(\"product_interaction\", {\n type: \"ecommerce\",\n subtype: \"product_interaction\",\n productId,\n action,\n ...properties,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default Products;\n","/**\n * @fileoverview Ecommerce - Cart Tracking\n * @module features/ecommerce/cart\n * \n * @description\n * Track shopping cart interactions including add/remove items,\n * cart views, and cart abandonment.\n * \n * @useCases\n * - Track add to cart events\n * - Monitor cart modifications\n * - Detect cart abandonment\n * - Analyze popular cart items\n * - Measure cart value trends\n * \n * @metrics\n * - Add to cart rate\n * - Average cart value\n * - Cart abandonment rate\n * - Items per cart\n * - Cart-to-checkout rate\n * \n * @chartData\n * - Line chart: Cart value over time\n * - Bar chart: Top added products\n * - Pie chart: Abandonment reasons\n * - Funnel: Cart to checkout flow\n * \n * @example\n * ```typescript\n * const cart = new CartTracking(webId, endpoint, apiKey);\n * \n * // Track add to cart\n * await cart.addItem({\n * product: { productId: 'SKU-123', name: 'Widget', price: 29.99, currency: 'USD' },\n * quantity: 2,\n * cartId: 'cart_abc'\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { \n Product,\n Cart,\n CartItem,\n CartAnalytics,\n CurrencyCode,\n DateRangeFilter,\n TimeSeriesData\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for adding item to cart.\n */\nexport interface AddToCartConfig {\n /** Product being added */\n product: Product;\n /** Quantity to add */\n quantity: number;\n /** Cart identifier */\n cartId?: string;\n /** User ID */\n userId?: string;\n /** Source of add (product page, quick add, etc.) */\n source?: 'product_page' | 'quick_add' | 'wishlist' | 'recommendation' | 'reorder';\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Configuration for removing item from cart.\n */\nexport interface RemoveFromCartConfig {\n /** Product being removed */\n product: Product;\n /** Quantity to remove */\n quantity: number;\n /** Cart identifier */\n cartId?: string;\n /** User ID */\n userId?: string;\n /** Reason for removal */\n reason?: 'user_action' | 'out_of_stock' | 'price_change' | 'auto_expired';\n}\n\n/**\n * Configuration for updating cart item.\n */\nexport interface UpdateCartItemConfig {\n /** Product being updated */\n product: Product;\n /** Previous quantity */\n previousQuantity: number;\n /** New quantity */\n newQuantity: number;\n /** Cart identifier */\n cartId?: string;\n}\n\n/**\n * Configuration for cart view.\n */\nexport interface ViewCartConfig {\n /** Current cart state */\n cart: Cart;\n /** User ID */\n userId?: string;\n}\n\n/**\n * Cart event response from API.\n */\nexport interface CartResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n cartId: string;\n tracked: boolean;\n cartValue: number;\n itemCount: number;\n };\n timestamp: string;\n}\n\n/**\n * Cart analytics data for charts.\n */\nexport interface CartAnalyticsData {\n /** Cart metrics summary */\n summary: {\n totalAddToCarts: number;\n totalRemoveFromCarts: number;\n averageCartValue: number;\n averageItemsPerCart: number;\n abandonmentRate: number;\n cartToCheckoutRate: number;\n };\n /** Add to cart trends */\n addToCartTimeSeries: TimeSeriesData;\n /** Top products added to cart */\n topAddedProducts: Array<{\n product: Pick<Product, 'productId' | 'name' | 'category'>;\n addCount: number;\n removeCount: number;\n netAdds: number;\n }>;\n /** Abandonment analysis */\n abandonment: {\n rate: number;\n avgAbandonedValue: number;\n topAbandonedProducts: Array<{\n product: Pick<Product, 'productId' | 'name'>;\n abandonCount: number;\n }>;\n };\n}\n\n// ============================================================================\n// CART TRACKING CLASS\n// ============================================================================\n\n/**\n * CartTracking - Track shopping cart interactions.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides cart tracking capabilities including:\n * - Add to cart events\n * - Remove from cart events\n * - Cart quantity updates\n * - Cart view tracking\n * - Cart abandonment detection\n * \n * @example\n * ```typescript\n * const cart = new CartTracking(webId, endpoint, apiKey);\n * \n * // Track add to cart\n * await cart.addItem({\n * product: {\n * productId: 'PROD-001',\n * name: 'Widget Pro',\n * price: 99.99,\n * currency: 'USD'\n * },\n * quantity: 1,\n * source: 'product_page'\n * });\n * \n * // Track cart view\n * await cart.view({\n * cart: currentCart\n * });\n * ```\n */\nexport class CartTracking extends FlowGridTransport {\n /**\n * Tracks adding an item to cart.\n * \n * @param config - Add to cart configuration\n * @returns Promise resolving to cart response\n * \n * @example\n * ```typescript\n * await cart.addItem({\n * product: {\n * productId: 'SKU-123',\n * name: 'Premium Widget',\n * price: 49.99,\n * currency: 'USD',\n * category: 'Widgets'\n * },\n * quantity: 2,\n * source: 'product_page'\n * });\n * ```\n */\n async addItem(config: AddToCartConfig): Promise<CartResponse> {\n const lineTotal = config.product.price * config.quantity - (config.product.discount || 0);\n \n return await this.init(\"add_to_cart\", {\n type: \"ecommerce\",\n subtype: \"add_to_cart\",\n product: config.product,\n quantity: config.quantity,\n lineTotal,\n cartId: config.cartId,\n userId: config.userId,\n source: config.source,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks removing an item from cart.\n * \n * @param config - Remove from cart configuration\n * @returns Promise resolving to cart response\n * \n * @example\n * ```typescript\n * await cart.removeItem({\n * product: productToRemove,\n * quantity: 1,\n * cartId: 'cart_123',\n * reason: 'user_action'\n * });\n * ```\n */\n async removeItem(config: RemoveFromCartConfig): Promise<CartResponse> {\n return await this.init(\"remove_from_cart\", {\n type: \"ecommerce\",\n subtype: \"remove_from_cart\",\n product: config.product,\n quantity: config.quantity,\n cartId: config.cartId,\n userId: config.userId,\n reason: config.reason || \"user_action\",\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks cart item quantity update.\n * \n * @param config - Update configuration\n * @returns Promise resolving to cart response\n * \n * @example\n * ```typescript\n * await cart.updateItem({\n * product: item.product,\n * previousQuantity: 1,\n * newQuantity: 3,\n * cartId: 'cart_123'\n * });\n * ```\n */\n async updateItem(config: UpdateCartItemConfig): Promise<CartResponse> {\n return await this.init(\"update_cart_item\", {\n type: \"ecommerce\",\n subtype: \"update_cart_item\",\n product: config.product,\n previousQuantity: config.previousQuantity,\n newQuantity: config.newQuantity,\n quantityChange: config.newQuantity - config.previousQuantity,\n cartId: config.cartId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a cart view event.\n * \n * @param config - View cart configuration\n * @returns Promise resolving to cart response\n * \n * @example\n * ```typescript\n * await cart.view({\n * cart: {\n * cartId: 'cart_123',\n * items: cartItems,\n * subtotal: 149.97,\n * total: 164.97,\n * currency: 'USD',\n * itemCount: 3\n * }\n * });\n * ```\n */\n async view(config: ViewCartConfig): Promise<CartResponse> {\n return await this.init(\"view_cart\", {\n type: \"ecommerce\",\n subtype: \"view_cart\",\n cartId: config.cart.cartId,\n cart: config.cart,\n cartValue: config.cart.total,\n itemCount: config.cart.itemCount,\n userId: config.userId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks cart abandonment.\n * \n * @param cart - Abandoned cart state\n * @param reason - Abandonment reason (if known)\n * @returns Promise resolving to cart response\n */\n async abandon(\n cart: Cart,\n reason?: 'exit' | 'timeout' | 'price_too_high' | 'shipping_cost' | 'other'\n ): Promise<CartResponse> {\n return await this.init(\"cart_abandoned\", {\n type: \"ecommerce\",\n subtype: \"cart_abandoned\",\n cart,\n cartValue: cart.total,\n itemCount: cart.itemCount,\n reason,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Applies a coupon to the cart.\n * \n * @param cartId - Cart identifier\n * @param couponCode - Coupon code applied\n * @param success - Whether coupon was valid\n * @param discount - Discount amount (if successful)\n * @returns Promise resolving to cart response\n */\n async applyCoupon(\n cartId: string,\n couponCode: string,\n success: boolean,\n discount?: number\n ): Promise<CartResponse> {\n return await this.init(\"apply_coupon\", {\n type: \"ecommerce\",\n subtype: \"apply_coupon\",\n cartId,\n couponCode,\n success,\n discount,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default CartTracking;\n","/**\n * @fileoverview Ecommerce - Checkout Tracking\n * @module features/ecommerce/checkout\n * \n * @description\n * Track checkout flow progress, payment attempts, and checkout\n * optimization opportunities.\n * \n * @useCases\n * - Track checkout step progression\n * - Identify checkout drop-off points\n * - Monitor payment success rates\n * - Analyze shipping method preferences\n * - Optimize checkout UX\n * \n * @metrics\n * - Checkout initiation rate\n * - Step completion rates\n * - Payment success rate\n * - Checkout abandonment rate\n * - Average time to complete\n * \n * @chartData\n * - Funnel chart: Checkout steps\n * - Bar chart: Abandonment by step\n * - Pie chart: Payment methods\n * - Line chart: Checkout completions over time\n * \n * @example\n * ```typescript\n * const checkout = new Checkout(webId, endpoint, apiKey);\n * \n * // Begin checkout\n * await checkout.begin({\n * cart: shoppingCart,\n * userId: 'user_123'\n * });\n * \n * // Track step progress\n * await checkout.step({\n * step: 'add_payment_info',\n * stepNumber: 3,\n * cart: shoppingCart\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { \n Cart,\n CheckoutStep,\n ShippingInfo,\n PaymentInfo,\n TimeSeriesData\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for beginning checkout.\n */\nexport interface BeginCheckoutConfig {\n /** Cart being checked out */\n cart: Cart;\n /** User ID */\n userId?: string;\n /** Session ID */\n sessionId?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Configuration for tracking checkout step.\n */\nexport interface CheckoutStepConfig {\n /** Checkout step */\n step: CheckoutStep;\n /** Step number */\n stepNumber: number;\n /** Cart state at this step */\n cart: Cart;\n /** Option selected at this step (e.g., shipping method) */\n option?: string;\n /** User ID */\n userId?: string;\n}\n\n/**\n * Configuration for adding shipping info.\n */\nexport interface AddShippingConfig {\n /** Cart being checked out */\n cart: Cart;\n /** Shipping information */\n shipping: ShippingInfo;\n /** User ID */\n userId?: string;\n}\n\n/**\n * Configuration for adding payment info.\n */\nexport interface AddPaymentConfig {\n /** Cart being checked out */\n cart: Cart;\n /** Payment information (non-sensitive) */\n payment: PaymentInfo;\n /** User ID */\n userId?: string;\n}\n\n/**\n * Checkout event response from API.\n */\nexport interface CheckoutResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n checkoutId: string;\n step: string;\n tracked: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Checkout analytics data for charts.\n */\nexport interface CheckoutAnalyticsData {\n /** Checkout funnel data */\n funnel: Array<{\n step: CheckoutStep;\n stepNumber: number;\n started: number;\n completed: number;\n conversionRate: number;\n dropOffRate: number;\n avgTimeToComplete: number;\n }>;\n /** Summary metrics */\n summary: {\n checkoutsStarted: number;\n checkoutsCompleted: number;\n overallConversionRate: number;\n avgCheckoutTime: number;\n avgOrderValue: number;\n abandonmentRate: number;\n };\n /** Checkouts over time */\n timeSeries: TimeSeriesData;\n /** Shipping methods used */\n shippingMethods: Array<{\n method: string;\n count: number;\n percentage: number;\n avgShippingCost: number;\n }>;\n /** Payment methods used */\n paymentMethods: Array<{\n method: string;\n count: number;\n percentage: number;\n successRate: number;\n }>;\n /** Abandonment analysis */\n abandonmentByStep: Array<{\n step: CheckoutStep;\n abandonments: number;\n percentage: number;\n avgCartValue: number;\n }>;\n}\n\n// ============================================================================\n// CHECKOUT CLASS\n// ============================================================================\n\n/**\n * Checkout - Track checkout flow and payments.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides checkout tracking capabilities including:\n * - Checkout initiation\n * - Step-by-step progress\n * - Shipping info capture\n * - Payment info capture\n * - Checkout abandonment\n * \n * @example\n * ```typescript\n * const checkout = new Checkout(webId, endpoint, apiKey);\n * \n * // Start checkout\n * await checkout.begin({ cart: shoppingCart });\n * \n * // Track shipping step\n * await checkout.addShipping({\n * cart: shoppingCart,\n * shipping: {\n * method: 'express',\n * carrier: 'FedEx',\n * cost: 9.99\n * }\n * });\n * \n * // Track payment step\n * await checkout.addPayment({\n * cart: shoppingCart,\n * payment: {\n * method: 'credit_card',\n * cardBrand: 'Visa'\n * }\n * });\n * ```\n */\nexport class Checkout extends FlowGridTransport {\n /**\n * Tracks checkout initiation.\n * \n * @param config - Begin checkout configuration\n * @returns Promise resolving to checkout response\n * \n * @example\n * ```typescript\n * await checkout.begin({\n * cart: {\n * cartId: 'cart_123',\n * items: cartItems,\n * total: 199.99,\n * currency: 'USD',\n * itemCount: 3\n * }\n * });\n * ```\n */\n async begin(config: BeginCheckoutConfig): Promise<CheckoutResponse> {\n return await this.init(\"begin_checkout\", {\n type: \"ecommerce\",\n subtype: \"begin_checkout\",\n cart: config.cart,\n cartValue: config.cart.total,\n itemCount: config.cart.itemCount,\n userId: config.userId,\n sessionId: config.sessionId,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks checkout step progress.\n * \n * @param config - Checkout step configuration\n * @returns Promise resolving to checkout response\n * \n * @example\n * ```typescript\n * await checkout.step({\n * step: 'add_shipping_info',\n * stepNumber: 2,\n * cart: shoppingCart,\n * option: 'express_shipping'\n * });\n * ```\n */\n async step(config: CheckoutStepConfig): Promise<CheckoutResponse> {\n return await this.init(\"checkout_step\", {\n type: \"ecommerce\",\n subtype: \"checkout_step\",\n step: config.step,\n stepNumber: config.stepNumber,\n cart: config.cart,\n option: config.option,\n userId: config.userId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks adding shipping information.\n * \n * @param config - Shipping info configuration\n * @returns Promise resolving to checkout response\n * \n * @example\n * ```typescript\n * await checkout.addShipping({\n * cart: shoppingCart,\n * shipping: {\n * method: 'standard',\n * carrier: 'USPS',\n * cost: 5.99,\n * estimatedDelivery: '2026-02-07'\n * }\n * });\n * ```\n */\n async addShipping(config: AddShippingConfig): Promise<CheckoutResponse> {\n return await this.init(\"add_shipping_info\", {\n type: \"ecommerce\",\n subtype: \"shipping_info\",\n cart: config.cart,\n shipping: config.shipping,\n shippingMethod: config.shipping.method,\n shippingCost: config.shipping.cost,\n userId: config.userId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks adding payment information.\n * \n * @param config - Payment info configuration\n * @returns Promise resolving to checkout response\n * \n * @example\n * ```typescript\n * await checkout.addPayment({\n * cart: shoppingCart,\n * payment: {\n * method: 'credit_card',\n * provider: 'stripe',\n * cardBrand: 'Mastercard',\n * cardLast4: '4242'\n * }\n * });\n * ```\n */\n async addPayment(config: AddPaymentConfig): Promise<CheckoutResponse> {\n return await this.init(\"add_payment_info\", {\n type: \"ecommerce\",\n subtype: \"payment_info\",\n cart: config.cart,\n payment: config.payment,\n paymentMethod: config.payment.method,\n userId: config.userId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks checkout abandonment.\n * \n * @param cart - Cart at abandonment\n * @param step - Last completed step\n * @param reason - Abandonment reason (if known)\n * @returns Promise resolving to checkout response\n */\n async abandon(\n cart: Cart,\n step: CheckoutStep,\n reason?: string\n ): Promise<CheckoutResponse> {\n return await this.init(\"checkout_abandoned\", {\n type: \"ecommerce\",\n subtype: \"checkout_abandoned\",\n cart,\n step,\n cartValue: cart.total,\n reason,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks payment attempt (success or failure).\n * \n * @param success - Whether payment succeeded\n * @param cart - Cart being paid for\n * @param payment - Payment information\n * @param errorCode - Error code (if failed)\n * @returns Promise resolving to checkout response\n */\n async paymentAttempt(\n success: boolean,\n cart: Cart,\n payment: PaymentInfo,\n errorCode?: string\n ): Promise<CheckoutResponse> {\n return await this.init(\"payment_attempt\", {\n type: \"ecommerce\",\n subtype: \"payment_attempt\",\n success,\n cart,\n payment,\n errorCode,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default Checkout;\n","/**\n * @fileoverview Ecommerce - Purchase/Transaction Tracking\n * @module features/ecommerce/purchases\n * \n * @description\n * Track completed purchases, transactions, and revenue with\n * full order details and customer information.\n * \n * @useCases\n * - Track completed purchases\n * - Calculate revenue metrics\n * - Monitor order values\n * - Analyze purchase patterns\n * - Track customer lifetime value\n * \n * @metrics\n * - Total revenue\n * - Number of orders\n * - Average order value\n * - Revenue per customer\n * - Items per order\n * \n * @chartData\n * - Line chart: Revenue over time\n * - Bar chart: Revenue by product/category\n * - KPI cards: Revenue, AOV, Orders\n * - Table: Recent transactions\n * \n * @example\n * ```typescript\n * const purchases = new Purchases(webId, endpoint, apiKey);\n * \n * // Track a purchase\n * await purchases.track({\n * order: {\n * orderId: 'ORD-12345',\n * items: orderItems,\n * total: 299.99,\n * currency: 'USD'\n * }\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { \n Order,\n CartItem,\n ShippingInfo,\n PaymentInfo,\n RevenueMetrics,\n MoneyValue,\n CurrencyCode,\n DateRangeFilter,\n TimeSeriesData,\n PaginationParams\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for tracking a purchase.\n */\nexport interface PurchaseConfig {\n /** Order details */\n order: Order;\n /** User ID */\n userId?: string;\n /** Session ID */\n sessionId?: string;\n /** Attribution data */\n attribution?: {\n source?: string;\n medium?: string;\n campaign?: string;\n affiliate?: string;\n };\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Purchase event response from API.\n */\nexport interface PurchaseResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n orderId: string;\n tracked: boolean;\n revenue: number;\n isFirstPurchase: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Revenue analytics data for charts.\n */\nexport interface RevenueAnalyticsData {\n /** Revenue summary */\n summary: {\n totalRevenue: MoneyValue;\n netRevenue: MoneyValue;\n totalOrders: number;\n averageOrderValue: MoneyValue;\n itemsSold: number;\n uniqueCustomers: number;\n repeatPurchaseRate: number;\n };\n /** Revenue time series */\n revenueTimeSeries: TimeSeriesData;\n /** Orders time series */\n ordersTimeSeries: TimeSeriesData;\n /** Revenue by category */\n byCategory: Array<{\n category: string;\n revenue: number;\n orders: number;\n percentage: number;\n }>;\n /** Revenue by product */\n byProduct: Array<{\n productId: string;\n productName: string;\n revenue: number;\n unitsSold: number;\n }>;\n /** Revenue by geography */\n byCountry: Array<{\n country: string;\n revenue: number;\n orders: number;\n }>;\n /** Customer segments */\n customerSegments: Array<{\n segment: 'new' | 'returning' | 'vip';\n customers: number;\n revenue: number;\n avgOrderValue: number;\n }>;\n}\n\n/**\n * Recent orders data.\n */\nexport interface RecentOrdersData {\n orders: Array<{\n orderId: string;\n total: MoneyValue;\n itemCount: number;\n status: string;\n createdAt: string;\n customer?: {\n userId: string;\n isNew: boolean;\n };\n }>;\n pagination: {\n page: number;\n limit: number;\n total: number;\n hasMore: boolean;\n };\n}\n\n// ============================================================================\n// PURCHASES CLASS\n// ============================================================================\n\n/**\n * Purchases - Track completed purchases and revenue.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides purchase tracking capabilities including:\n * - Purchase completion events\n * - Revenue tracking\n * - Order details capture\n * - Customer LTV tracking\n * - Attribution tracking\n * \n * @example\n * ```typescript\n * const purchases = new Purchases(webId, endpoint, apiKey);\n * \n * // Track completed purchase\n * await purchases.track({\n * order: {\n * orderId: 'ORD-12345',\n * items: cartItems,\n * subtotal: 279.99,\n * discountTotal: 30.00,\n * shippingTotal: 9.99,\n * taxTotal: 26.00,\n * total: 285.98,\n * currency: 'USD',\n * shipping: { method: 'express', cost: 9.99 },\n * payment: { method: 'credit_card' }\n * },\n * userId: 'user_123'\n * });\n * ```\n */\nexport class Purchases extends FlowGridTransport {\n /**\n * Tracks a completed purchase.\n * \n * @param config - Purchase configuration\n * @returns Promise resolving to purchase response\n * \n * @example\n * ```typescript\n * await purchases.track({\n * order: {\n * orderId: 'ORD-98765',\n * items: [\n * { productId: 'PROD-1', name: 'Widget', price: 29.99, quantity: 2, lineTotal: 59.98 }\n * ],\n * subtotal: 59.98,\n * discountTotal: 0,\n * shippingTotal: 5.99,\n * taxTotal: 5.40,\n * total: 71.37,\n * currency: 'USD'\n * },\n * attribution: {\n * source: 'google',\n * medium: 'cpc',\n * campaign: 'spring_sale'\n * }\n * });\n * ```\n */\n async track(config: PurchaseConfig): Promise<PurchaseResponse> {\n const order = config.order;\n \n return await this.init(\"purchase\", {\n type: \"ecommerce\",\n subtype: \"purchase\",\n order: {\n orderId: order.orderId,\n items: order.items,\n subtotal: order.subtotal,\n discountTotal: order.discountTotal,\n shippingTotal: order.shippingTotal,\n taxTotal: order.taxTotal,\n total: order.total,\n currency: order.currency,\n shipping: order.shipping,\n payment: order.payment,\n coupons: order.coupons,\n affiliation: order.affiliation\n },\n revenue: order.total,\n itemCount: order.items.reduce((sum, i) => sum + i.quantity, 0),\n userId: config.userId,\n sessionId: config.sessionId,\n attribution: config.attribution,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks order status change.\n * \n * @param orderId - Order identifier\n * @param newStatus - New order status\n * @param previousStatus - Previous status (optional)\n * @returns Promise resolving to status change response\n */\n async statusChange(\n orderId: string,\n newStatus: Order['status'],\n previousStatus?: Order['status']\n ): Promise<PurchaseResponse> {\n return await this.init(\"order_status_change\", {\n type: \"ecommerce\",\n subtype: \"order_status_change\",\n orderId,\n newStatus,\n previousStatus,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks order shipped event.\n * \n * @param orderId - Order identifier\n * @param shipping - Shipping details with tracking\n * @returns Promise resolving to shipped response\n */\n async shipped(orderId: string, shipping: ShippingInfo): Promise<PurchaseResponse> {\n return await this.init(\"order_shipped\", {\n type: \"ecommerce\",\n subtype: \"order_shipped\",\n orderId,\n shipping,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks order delivered event.\n * \n * @param orderId - Order identifier\n * @param deliveredAt - Delivery timestamp\n * @returns Promise resolving to delivered response\n */\n async delivered(orderId: string, deliveredAt?: string): Promise<PurchaseResponse> {\n return await this.init(\"order_delivered\", {\n type: \"ecommerce\",\n subtype: \"order_delivered\",\n orderId,\n deliveredAt: deliveredAt || new Date().toISOString(),\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default Purchases;\n","/**\n * @fileoverview Ecommerce - Refund Tracking\n * @module features/ecommerce/refunds\n * \n * @description\n * Track refunds, returns, and order cancellations with\n * reason analysis and revenue impact.\n * \n * @useCases\n * - Track full and partial refunds\n * - Analyze refund reasons\n * - Monitor return rates by product\n * - Calculate net revenue after refunds\n * - Identify problematic products\n * \n * @metrics\n * - Total refunds\n * - Refund rate\n * - Average refund value\n * - Refunds by reason\n * - Net revenue impact\n * \n * @chartData\n * - Bar chart: Refunds by reason\n * - Line chart: Refund trends\n * - Table: Products with highest return rates\n * - KPI: Refund rate vs benchmark\n * \n * @example\n * ```typescript\n * const refunds = new Refunds(webId, endpoint, apiKey);\n * \n * // Track a refund\n * await refunds.track({\n * orderId: 'ORD-12345',\n * refundId: 'REF-001',\n * amount: 49.99,\n * currency: 'USD',\n * reason: 'defective',\n * isFullRefund: false\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { \n Refund,\n CartItem,\n MoneyValue,\n CurrencyCode,\n DateRangeFilter,\n TimeSeriesData\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for tracking a refund.\n */\nexport interface RefundConfig extends Refund {\n /** User ID */\n userId?: string;\n /** Initiated by (customer or admin) */\n initiatedBy?: 'customer' | 'admin' | 'system';\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Refund event response from API.\n */\nexport interface RefundResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n refundId: string;\n orderId: string;\n tracked: boolean;\n refundedAmount: number;\n };\n timestamp: string;\n}\n\n/**\n * Refund analytics data for charts.\n */\nexport interface RefundAnalyticsData {\n /** Summary metrics */\n summary: {\n totalRefunds: number;\n totalRefundValue: MoneyValue;\n refundRate: number;\n averageRefundValue: MoneyValue;\n fullRefundCount: number;\n partialRefundCount: number;\n };\n /** Refunds time series */\n timeSeries: TimeSeriesData;\n /** Refunds by reason */\n byReason: Array<{\n reason: string;\n count: number;\n totalValue: number;\n percentage: number;\n }>;\n /** Products with highest refund rates */\n topRefundedProducts: Array<{\n productId: string;\n productName: string;\n refundCount: number;\n refundValue: number;\n refundRate: number;\n }>;\n /** Revenue impact */\n revenueImpact: {\n grossRevenue: MoneyValue;\n refundedAmount: MoneyValue;\n netRevenue: MoneyValue;\n refundImpactPercent: number;\n };\n}\n\n// ============================================================================\n// REFUNDS CLASS\n// ============================================================================\n\n/**\n * Refunds - Track refunds and returns.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides refund tracking capabilities including:\n * - Full refund tracking\n * - Partial refund tracking\n * - Refund reason analysis\n * - Return rate monitoring\n * - Revenue impact calculation\n * \n * @example\n * ```typescript\n * const refunds = new Refunds(webId, endpoint, apiKey);\n * \n * // Track full refund\n * await refunds.track({\n * orderId: 'ORD-12345',\n * refundId: 'REF-001',\n * amount: 149.99,\n * currency: 'USD',\n * reason: 'customer_request',\n * isFullRefund: true\n * });\n * \n * // Track partial refund with items\n * await refunds.track({\n * orderId: 'ORD-12345',\n * refundId: 'REF-002',\n * items: [refundedItem],\n * amount: 49.99,\n * currency: 'USD',\n * reason: 'defective',\n * isFullRefund: false\n * });\n * ```\n */\nexport class Refunds extends FlowGridTransport {\n /**\n * Tracks a refund event.\n * \n * @param config - Refund configuration\n * @returns Promise resolving to refund response\n * \n * @example\n * ```typescript\n * await refunds.track({\n * orderId: 'ORD-98765',\n * refundId: 'REF-123',\n * items: [\n * { productId: 'PROD-1', name: 'Widget', price: 29.99, quantity: 1, lineTotal: 29.99 }\n * ],\n * amount: 29.99,\n * currency: 'USD',\n * reason: 'wrong_item',\n * isFullRefund: false,\n * initiatedBy: 'customer'\n * });\n * ```\n */\n async track(config: RefundConfig): Promise<RefundResponse> {\n return await this.init(\"refund\", {\n type: \"ecommerce\",\n subtype: \"refund\",\n orderId: config.orderId,\n refundId: config.refundId,\n items: config.items,\n amount: config.amount,\n currency: config.currency,\n reason: config.reason,\n isFullRefund: config.isFullRefund,\n userId: config.userId,\n initiatedBy: config.initiatedBy,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a refund request (before processing).\n * \n * @param orderId - Original order ID\n * @param items - Items requested for refund\n * @param reason - Refund reason\n * @returns Promise resolving to request response\n */\n async request(\n orderId: string,\n items?: CartItem[],\n reason?: string\n ): Promise<RefundResponse> {\n return await this.init(\"refund_requested\", {\n type: \"ecommerce\",\n subtype: \"refund_requested\",\n orderId,\n items,\n reason,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks refund approval.\n * \n * @param refundId - Refund identifier\n * @param approvedAmount - Approved refund amount\n * @returns Promise resolving to approval response\n */\n async approve(refundId: string, approvedAmount: number): Promise<RefundResponse> {\n return await this.init(\"refund_approved\", {\n type: \"ecommerce\",\n subtype: \"refund_approved\",\n refundId,\n approvedAmount,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks refund denial.\n * \n * @param refundId - Refund identifier\n * @param denialReason - Reason for denial\n * @returns Promise resolving to denial response\n */\n async deny(refundId: string, denialReason: string): Promise<RefundResponse> {\n return await this.init(\"refund_denied\", {\n type: \"ecommerce\",\n subtype: \"refund_denied\",\n refundId,\n denialReason,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks refund completion (money returned).\n * \n * @param refundId - Refund identifier\n * @param processedAmount - Amount actually refunded\n * @returns Promise resolving to completion response\n */\n async complete(refundId: string, processedAmount: number): Promise<RefundResponse> {\n return await this.init(\"refund_completed\", {\n type: \"ecommerce\",\n subtype: \"refund_completed\",\n refundId,\n processedAmount,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default Refunds;\n","/**\n * @fileoverview Ecommerce - Promotion Tracking\n * @module features/ecommerce/promotions\n * \n * @description\n * Track promotional content, coupon usage, and discount\n * effectiveness across your ecommerce store.\n * \n * @useCases\n * - Track promotional banner views\n * - Measure promotion click-through rates\n * - Analyze coupon usage and effectiveness\n * - Monitor discount impact on revenue\n * - Compare promotion performance\n * \n * @metrics\n * - Promotion impressions\n * - Promotion CTR\n * - Coupon redemption rate\n * - Revenue attributed to promotions\n * - Discount impact on margins\n * \n * @chartData\n * - Bar chart: Top promotions by revenue\n * - Line chart: Coupon usage over time\n * - Table: Promotion performance comparison\n * - Pie chart: Revenue by promotion type\n * \n * @example\n * ```typescript\n * const promotions = new Promotions(webId, endpoint, apiKey);\n * \n * // Track promotion view\n * await promotions.view({\n * promotionId: 'PROMO-SUMMER',\n * name: 'Summer Sale 20% Off',\n * creative: 'homepage-banner'\n * });\n * \n * // Track coupon usage\n * await promotions.couponUsed({\n * code: 'SAVE20',\n * discountValue: 20,\n * orderId: 'ORD-123'\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { \n Promotion,\n MoneyValue,\n DateRangeFilter,\n TimeSeriesData\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for tracking promotion view.\n */\nexport interface PromotionViewConfig extends Promotion {\n /** User ID */\n userId?: string;\n /** Page where promotion was shown */\n page?: string;\n}\n\n/**\n * Configuration for tracking promotion click.\n */\nexport interface PromotionClickConfig extends Promotion {\n /** User ID */\n userId?: string;\n /** Destination URL */\n destinationUrl?: string;\n}\n\n/**\n * Configuration for tracking coupon usage.\n */\nexport interface CouponUsageConfig {\n /** Coupon code */\n code: string;\n /** Order ID where coupon was applied */\n orderId: string;\n /** Discount amount */\n discountValue: number;\n /** Discount type */\n discountType: 'percentage' | 'fixed';\n /** Order value before discount */\n orderValueBefore: number;\n /** Order value after discount */\n orderValueAfter: number;\n /** User ID */\n userId?: string;\n}\n\n/**\n * Promotion event response from API.\n */\nexport interface PromotionResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n promotionId: string;\n tracked: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Promotion analytics data for charts.\n */\nexport interface PromotionAnalyticsData {\n /** Summary metrics */\n summary: {\n totalPromotionViews: number;\n totalPromotionClicks: number;\n overallCTR: number;\n totalCouponsUsed: number;\n totalDiscountGiven: MoneyValue;\n revenueFromPromotions: MoneyValue;\n };\n /** Top performing promotions */\n topPromotions: Array<{\n promotionId: string;\n name: string;\n views: number;\n clicks: number;\n ctr: number;\n conversions: number;\n revenue: number;\n }>;\n /** Coupon performance */\n coupons: Array<{\n code: string;\n usageCount: number;\n totalDiscount: number;\n avgOrderValue: number;\n conversionLift: number;\n }>;\n /** Promotions over time */\n timeSeries: TimeSeriesData;\n /** Revenue impact */\n revenueImpact: {\n promotedRevenue: MoneyValue;\n nonPromotedRevenue: MoneyValue;\n promotionRevenueShare: number;\n avgDiscountPercent: number;\n };\n}\n\n// ============================================================================\n// PROMOTIONS CLASS\n// ============================================================================\n\n/**\n * Promotions - Track promotions and coupons.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides promotion tracking capabilities including:\n * - Promotion/banner impressions\n * - Promotion clicks\n * - Coupon applications\n * - Coupon success/failure\n * - Revenue attribution\n * \n * @example\n * ```typescript\n * const promotions = new Promotions(webId, endpoint, apiKey);\n * \n * // Track banner impression\n * await promotions.view({\n * promotionId: 'FLASH-SALE',\n * name: 'Flash Sale - 30% Off',\n * creative: 'hero-banner',\n * position: 'homepage-top'\n * });\n * \n * // Track banner click\n * await promotions.click({\n * promotionId: 'FLASH-SALE',\n * name: 'Flash Sale - 30% Off',\n * destinationUrl: '/sale'\n * });\n * ```\n */\nexport class Promotions extends FlowGridTransport {\n /**\n * Tracks promotion view/impression.\n * \n * @param config - Promotion view configuration\n * @returns Promise resolving to promotion response\n * \n * @example\n * ```typescript\n * await promotions.view({\n * promotionId: 'SUMMER-2026',\n * name: 'Summer Collection Launch',\n * creative: 'collection-banner',\n * position: 'homepage-hero',\n * page: '/home'\n * });\n * ```\n */\n async view(config: PromotionViewConfig): Promise<PromotionResponse> {\n return await this.init(\"promotion_view\", {\n type: \"ecommerce\",\n subtype: \"promotion_view\",\n promotionId: config.promotionId,\n name: config.name,\n creative: config.creative,\n position: config.position,\n page: config.page,\n userId: config.userId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks promotion click.\n * \n * @param config - Promotion click configuration\n * @returns Promise resolving to promotion response\n * \n * @example\n * ```typescript\n * await promotions.click({\n * promotionId: 'SUMMER-2026',\n * name: 'Summer Collection Launch',\n * destinationUrl: '/collections/summer-2026'\n * });\n * ```\n */\n async click(config: PromotionClickConfig): Promise<PromotionResponse> {\n return await this.init(\"promotion_click\", {\n type: \"ecommerce\",\n subtype: \"promotion_click\",\n promotionId: config.promotionId,\n name: config.name,\n creative: config.creative,\n position: config.position,\n destinationUrl: config.destinationUrl,\n userId: config.userId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks successful coupon usage.\n * \n * @param config - Coupon usage configuration\n * @returns Promise resolving to coupon response\n * \n * @example\n * ```typescript\n * await promotions.couponUsed({\n * code: 'SAVE20',\n * orderId: 'ORD-12345',\n * discountValue: 20,\n * discountType: 'percentage',\n * orderValueBefore: 100,\n * orderValueAfter: 80\n * });\n * ```\n */\n async couponUsed(config: CouponUsageConfig): Promise<PromotionResponse> {\n return await this.init(\"coupon_used\", {\n type: \"ecommerce\",\n subtype: \"coupon_used\",\n code: config.code,\n orderId: config.orderId,\n discountValue: config.discountValue,\n discountType: config.discountType,\n orderValueBefore: config.orderValueBefore,\n orderValueAfter: config.orderValueAfter,\n savings: config.orderValueBefore - config.orderValueAfter,\n userId: config.userId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks coupon application attempt (including failures).\n * \n * @param code - Coupon code attempted\n * @param success - Whether coupon was valid\n * @param failureReason - Reason for failure (if failed)\n * @returns Promise resolving to attempt response\n */\n async couponAttempt(\n code: string,\n success: boolean,\n failureReason?: 'expired' | 'invalid' | 'minimum_not_met' | 'already_used' | 'not_applicable'\n ): Promise<PromotionResponse> {\n return await this.init(\"coupon_attempt\", {\n type: \"ecommerce\",\n subtype: \"coupon_attempt\",\n code,\n success,\n failureReason,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default Promotions;\n","/**\n * @fileoverview Ecommerce - Wishlist Tracking\n * @module features/ecommerce/wishlist\n * \n * @description\n * Track wishlist interactions to understand purchase intent\n * and drive conversions from saved items.\n * \n * @useCases\n * - Track wishlist additions\n * - Monitor wishlist to cart conversions\n * - Identify popular wishlist items\n * - Trigger price drop notifications\n * - Analyze wishlist behavior patterns\n * \n * @metrics\n * - Wishlist additions\n * - Wishlist to cart rate\n * - Wishlist to purchase rate\n * - Average items per wishlist\n * - Top wishlisted products\n * \n * @chartData\n * - Bar chart: Top wishlisted products\n * - Line chart: Wishlist activity over time\n * - Funnel: Wishlist to purchase conversion\n * - Table: Wishlist conversion rates by product\n * \n * @example\n * ```typescript\n * const wishlist = new WishlistTracking(webId, endpoint, apiKey);\n * \n * // Add to wishlist\n * await wishlist.add({\n * product: { productId: 'PROD-123', name: 'Widget', price: 49.99, currency: 'USD' },\n * wishlistId: 'wl_user123'\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { \n Product,\n Wishlist,\n WishlistItem,\n DateRangeFilter,\n TimeSeriesData\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for adding to wishlist.\n */\nexport interface AddToWishlistConfig {\n /** Product being added */\n product: Product;\n /** Wishlist ID */\n wishlistId?: string;\n /** User ID */\n userId?: string;\n /** Source of add */\n source?: 'product_page' | 'quick_add' | 'list_view' | 'search';\n}\n\n/**\n * Configuration for removing from wishlist.\n */\nexport interface RemoveFromWishlistConfig {\n /** Product being removed */\n product: Product;\n /** Wishlist ID */\n wishlistId?: string;\n /** User ID */\n userId?: string;\n /** Reason for removal */\n reason?: 'user_action' | 'purchased' | 'moved_to_cart' | 'out_of_stock';\n}\n\n/**\n * Wishlist event response from API.\n */\nexport interface WishlistResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n wishlistId: string;\n productId: string;\n tracked: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Wishlist analytics data for charts.\n */\nexport interface WishlistAnalyticsData {\n /** Summary metrics */\n summary: {\n totalWishlistAdds: number;\n totalWishlistRemoves: number;\n activeWishlists: number;\n avgItemsPerWishlist: number;\n wishlistToCartRate: number;\n wishlistToPurchaseRate: number;\n };\n /** Top wishlisted products */\n topProducts: Array<{\n product: Pick<Product, 'productId' | 'name' | 'category' | 'price'>;\n wishlistCount: number;\n conversionToCart: number;\n conversionToPurchase: number;\n }>;\n /** Wishlist activity time series */\n timeSeries: TimeSeriesData;\n /** Conversion funnel */\n conversionFunnel: {\n wishlisted: number;\n addedToCart: number;\n purchased: number;\n wishlistToCartRate: number;\n cartToPurchaseRate: number;\n };\n}\n\n// ============================================================================\n// WISHLIST TRACKING CLASS\n// ============================================================================\n\n/**\n * WishlistTracking - Track wishlist interactions.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides wishlist tracking capabilities including:\n * - Add to wishlist events\n * - Remove from wishlist events\n * - Wishlist views\n * - Move to cart tracking\n * - Wishlist sharing\n * \n * @example\n * ```typescript\n * const wishlist = new WishlistTracking(webId, endpoint, apiKey);\n * \n * // Add product to wishlist\n * await wishlist.add({\n * product: {\n * productId: 'PROD-001',\n * name: 'Designer Bag',\n * price: 299.99,\n * currency: 'USD'\n * },\n * source: 'product_page'\n * });\n * \n * // Track wishlist view\n * await wishlist.view({\n * wishlistId: 'wl_user123',\n * items: wishlistItems\n * });\n * ```\n */\nexport class WishlistTracking extends FlowGridTransport {\n /**\n * Tracks adding product to wishlist.\n * \n * @param config - Add to wishlist configuration\n * @returns Promise resolving to wishlist response\n * \n * @example\n * ```typescript\n * await wishlist.add({\n * product: {\n * productId: 'SKU-789',\n * name: 'Premium Sneakers',\n * price: 159.99,\n * currency: 'USD',\n * category: 'Footwear'\n * },\n * source: 'product_page'\n * });\n * ```\n */\n async add(config: AddToWishlistConfig): Promise<WishlistResponse> {\n return await this.init(\"add_to_wishlist\", {\n type: \"ecommerce\",\n subtype: \"add_to_wishlist\",\n product: config.product,\n wishlistId: config.wishlistId,\n userId: config.userId,\n source: config.source,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks removing product from wishlist.\n * \n * @param config - Remove from wishlist configuration\n * @returns Promise resolving to wishlist response\n * \n * @example\n * ```typescript\n * await wishlist.remove({\n * product: productToRemove,\n * wishlistId: 'wl_user123',\n * reason: 'moved_to_cart'\n * });\n * ```\n */\n async remove(config: RemoveFromWishlistConfig): Promise<WishlistResponse> {\n return await this.init(\"remove_from_wishlist\", {\n type: \"ecommerce\",\n subtype: \"remove_from_wishlist\",\n product: config.product,\n wishlistId: config.wishlistId,\n userId: config.userId,\n reason: config.reason || \"user_action\",\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks wishlist view.\n * \n * @param wishlist - Wishlist being viewed\n * @returns Promise resolving to wishlist response\n */\n async view(wishlist: Wishlist): Promise<WishlistResponse> {\n return await this.init(\"view_wishlist\", {\n type: \"ecommerce\",\n subtype: \"view_wishlist\",\n wishlistId: wishlist.wishlistId,\n itemCount: wishlist.items.length,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks moving item from wishlist to cart.\n * \n * @param product - Product being moved\n * @param wishlistId - Source wishlist\n * @returns Promise resolving to wishlist response\n */\n async moveToCart(product: Product, wishlistId?: string): Promise<WishlistResponse> {\n return await this.init(\"wishlist_to_cart\", {\n type: \"ecommerce\",\n subtype: \"wishlist_to_cart\",\n product,\n wishlistId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks wishlist sharing.\n * \n * @param wishlistId - Wishlist being shared\n * @param method - Share method\n * @returns Promise resolving to wishlist response\n */\n async share(\n wishlistId: string,\n method: 'email' | 'link' | 'social'\n ): Promise<WishlistResponse> {\n return await this.init(\"share_wishlist\", {\n type: \"ecommerce\",\n subtype: \"share_wishlist\",\n wishlistId,\n method,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default WishlistTracking;\n","/**\n * @fileoverview Ecommerce - Customer Lifetime Value (LTV)\n * @module features/ecommerce/ltv\n * \n * @description\n * Calculate and track customer lifetime value to understand\n * customer worth and optimize acquisition costs.\n * \n * @useCases\n * - Calculate customer LTV\n * - Segment customers by value\n * - Track LTV trends over time\n * - Identify high-value customers\n * - Optimize acquisition spend (LTV:CAC)\n * \n * @metrics\n * - Average LTV\n * - LTV by cohort\n * - LTV by segment\n * - LTV:CAC ratio\n * - Customer value distribution\n * \n * @chartData\n * - Line chart: LTV by cohort over time\n * - Bar chart: LTV by segment\n * - Histogram: Customer value distribution\n * - KPI: Average LTV, LTV:CAC\n * \n * @example\n * ```typescript\n * const ltv = new CustomerLTV(webId, endpoint, apiKey);\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { \n MoneyValue,\n CurrencyCode,\n DateRangeFilter,\n TimeSeriesData\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Customer value data.\n */\nexport interface CustomerValueData {\n /** Customer/user ID */\n userId: string;\n /** Total revenue from customer */\n totalRevenue: MoneyValue;\n /** Number of orders */\n orderCount: number;\n /** Average order value */\n averageOrderValue: MoneyValue;\n /** Predicted future value */\n predictedLTV: MoneyValue;\n /** Customer segment */\n segment: 'low' | 'medium' | 'high' | 'vip';\n /** Days since first purchase */\n customerAge: number;\n /** Days since last purchase */\n daysSinceLastPurchase: number;\n /** Purchase frequency (orders per month) */\n purchaseFrequency: number;\n}\n\n/**\n * Configuration for recording acquisition cost.\n */\nexport interface AcquisitionCostConfig {\n /** Customer/user ID */\n userId: string;\n /** Acquisition cost */\n cost: MoneyValue;\n /** Acquisition channel */\n channel: string;\n /** Campaign (if applicable) */\n campaign?: string;\n}\n\n/**\n * LTV event response from API.\n */\nexport interface LTVResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n userId: string;\n ltv: number;\n tracked: boolean;\n };\n timestamp: string;\n}\n\n/**\n * LTV analytics data for charts.\n */\nexport interface LTVAnalyticsData {\n /** Summary metrics */\n summary: {\n averageLTV: MoneyValue;\n medianLTV: MoneyValue;\n totalCustomerValue: MoneyValue;\n averageCAC: MoneyValue;\n ltvCacRatio: number;\n averageOrderValue: MoneyValue;\n averageOrdersPerCustomer: number;\n };\n /** LTV by customer segment */\n bySegment: Array<{\n segment: string;\n customerCount: number;\n averageLTV: MoneyValue;\n totalRevenue: MoneyValue;\n percentageOfCustomers: number;\n percentageOfRevenue: number;\n }>;\n /** LTV by cohort */\n byCohort: Array<{\n cohort: string;\n customerCount: number;\n averageLTV: MoneyValue;\n ltvProgression: number[];\n }>;\n /** LTV by acquisition channel */\n byChannel: Array<{\n channel: string;\n customers: number;\n averageLTV: MoneyValue;\n averageCAC: MoneyValue;\n ltvCacRatio: number;\n roi: number;\n }>;\n /** LTV distribution */\n distribution: Array<{\n range: string;\n count: number;\n percentage: number;\n }>;\n /** LTV trend over time */\n trendData: TimeSeriesData;\n}\n\n// ============================================================================\n// CUSTOMER LTV CLASS\n// ============================================================================\n\n/**\n * CustomerLTV - Calculate and track customer lifetime value.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides LTV tracking capabilities including:\n * - Customer value calculation\n * - Acquisition cost tracking\n * - LTV:CAC ratio analysis\n * - Customer segmentation by value\n * - Cohort-based LTV analysis\n * \n * @example\n * ```typescript\n * const ltv = new CustomerLTV(webId, endpoint, apiKey);\n * \n * // Get individual customer value\n * const value = await ltv.getCustomerValue('user_123');\n * console.log(`LTV: $${value.totalRevenue.amount / 100}`);\n * \n * // Record acquisition cost\n * await ltv.recordAcquisitionCost({\n * userId: 'user_123',\n * cost: { amount: 5000, currency: 'USD' },\n * channel: 'facebook_ads',\n * campaign: 'spring_campaign'\n * });\n * ```\n */\nexport class CustomerLTV extends FlowGridTransport {\n /**\n * Records customer acquisition cost.\n * \n * @param config - Acquisition cost configuration\n * @returns Promise resolving to LTV response\n * \n * @example\n * ```typescript\n * await ltv.recordAcquisitionCost({\n * userId: 'user_123',\n * cost: { amount: 2500, currency: 'USD' }, // $25.00\n * channel: 'google_ads',\n * campaign: 'brand_terms'\n * });\n * ```\n */\n async recordAcquisitionCost(config: AcquisitionCostConfig): Promise<LTVResponse> {\n return await this.init(\"record_acquisition_cost\", {\n type: \"ecommerce\",\n subtype: \"acquisition_cost\",\n userId: config.userId,\n cost: config.cost,\n channel: config.channel,\n campaign: config.campaign,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Updates customer segment based on value.\n * \n * @param userId - Customer/user identifier\n * @param segment - New segment\n * @returns Promise resolving to LTV response\n */\n async updateSegment(\n userId: string,\n segment: 'low' | 'medium' | 'high' | 'vip'\n ): Promise<LTVResponse> {\n return await this.init(\"update_customer_segment\", {\n type: \"ecommerce\",\n subtype: \"update_customer_segment\",\n userId,\n segment,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default CustomerLTV;\n","/**\n * @fileoverview Ecommerce - Search Analytics\n * @module features/ecommerce/search\n * \n * @description\n * Track site search behavior to improve product discovery\n * and conversion from search results.\n * \n * @useCases\n * - Track search queries and results\n * - Identify zero-result searches\n * - Measure search-to-purchase conversion\n * - Analyze popular search terms\n * - Optimize search suggestions\n * \n * @metrics\n * - Search volume\n * - Zero-result rate\n * - Search refinement rate\n * - Click-through rate from search\n * - Conversion from search\n * \n * @chartData\n * - Word cloud: Popular search terms\n * - Bar chart: Top queries\n * - Table: Zero-result queries\n * - Funnel: Search to purchase\n * \n * @example\n * ```typescript\n * const search = new SearchAnalytics(webId, endpoint, apiKey);\n * \n * // Track search\n * await search.track({\n * query: 'wireless headphones',\n * resultsCount: 42,\n * filters: { brand: 'Sony' }\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { Product, DateRangeFilter, TimeSeriesData } from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Search query configuration.\n */\nexport interface SearchConfig {\n /** Search query */\n query: string;\n /** Number of results */\n resultsCount: number;\n /** Search category/scope */\n category?: string;\n /** Applied filters */\n filters?: Record<string, string | string[]>;\n /** Sort order */\n sortBy?: string;\n /** Page number */\n page?: number;\n /** User ID */\n userId?: string;\n /** Session ID */\n sessionId?: string;\n}\n\n/**\n * Search result click configuration.\n */\nexport interface SearchClickConfig {\n /** Original search query */\n query: string;\n /** Product clicked */\n product: Product;\n /** Position in results */\n position: number;\n /** Results page number */\n page?: number;\n}\n\n/**\n * Search response from API.\n */\nexport interface SearchResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n searchId: string;\n tracked: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Search analytics data.\n */\nexport interface SearchAnalyticsData {\n /** Summary metrics */\n summary: {\n totalSearches: number;\n uniqueSearchers: number;\n zeroResultSearches: number;\n zeroResultRate: number;\n avgResultsPerSearch: number;\n searchToClickRate: number;\n searchToConversionRate: number;\n };\n /** Top search queries */\n topQueries: Array<{\n query: string;\n searches: number;\n clicks: number;\n conversions: number;\n ctr: number;\n conversionRate: number;\n }>;\n /** Zero-result queries */\n zeroResultQueries: Array<{\n query: string;\n occurrences: number;\n lastSearched: string;\n }>;\n /** Popular filters */\n popularFilters: Array<{\n filterName: string;\n filterValue: string;\n usageCount: number;\n }>;\n /** Search trends */\n trends: TimeSeriesData;\n /** Search to purchase funnel */\n funnel: {\n searches: number;\n resultsViewed: number;\n productClicked: number;\n addedToCart: number;\n purchased: number;\n };\n}\n\n// ============================================================================\n// SEARCH ANALYTICS CLASS\n// ============================================================================\n\n/**\n * SearchAnalytics - Track site search behavior.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides search analytics including:\n * - Search query tracking\n * - Result click tracking\n * - Zero-result detection\n * - Search refinement tracking\n * - Search to conversion analysis\n * \n * @example\n * ```typescript\n * const search = new SearchAnalytics(webId, endpoint, apiKey);\n * \n * // Track search\n * await search.track({\n * query: 'running shoes',\n * resultsCount: 156,\n * filters: { size: '10', color: 'black' },\n * sortBy: 'price_asc'\n * });\n * \n * // Track click from search results\n * await search.trackClick({\n * query: 'running shoes',\n * product: clickedProduct,\n * position: 3\n * });\n * ```\n */\nexport class SearchAnalytics extends FlowGridTransport {\n /**\n * Tracks a search query.\n * \n * @param config - Search configuration\n * @returns Promise resolving to search response\n */\n async track(config: SearchConfig): Promise<SearchResponse> {\n return await this.init(\"site_search\", {\n type: \"ecommerce\",\n subtype: \"site_search\",\n query: config.query,\n resultsCount: config.resultsCount,\n hasResults: config.resultsCount > 0,\n category: config.category,\n filters: config.filters,\n sortBy: config.sortBy,\n page: config.page || 1,\n userId: config.userId,\n sessionId: config.sessionId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a click on search result.\n * \n * @param config - Search click configuration\n * @returns Promise resolving to search response\n */\n async trackClick(config: SearchClickConfig): Promise<SearchResponse> {\n return await this.init(\"search_result_click\", {\n type: \"ecommerce\",\n subtype: \"search_result_click\",\n query: config.query,\n product: config.product,\n position: config.position,\n page: config.page || 1,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks search refinement (modifying search).\n * \n * @param originalQuery - Original search query\n * @param refinedQuery - New/refined query\n * @param refinementType - Type of refinement\n * @returns Promise resolving to search response\n */\n async trackRefinement(\n originalQuery: string,\n refinedQuery: string,\n refinementType: 'query_change' | 'add_filter' | 'remove_filter' | 'sort_change'\n ): Promise<SearchResponse> {\n return await this.init(\"search_refinement\", {\n type: \"ecommerce\",\n subtype: \"search_refinement\",\n originalQuery,\n refinedQuery,\n refinementType,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks autocomplete/suggestion selection.\n * \n * @param partialQuery - Partial query typed\n * @param selectedSuggestion - Suggestion selected\n * @param suggestionPosition - Position of suggestion\n * @returns Promise resolving to search response\n */\n async trackSuggestion(\n partialQuery: string,\n selectedSuggestion: string,\n suggestionPosition: number\n ): Promise<SearchResponse> {\n return await this.init(\"search_suggestion\", {\n type: \"ecommerce\",\n subtype: \"search_suggestion\",\n partialQuery,\n selectedSuggestion,\n suggestionPosition,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default SearchAnalytics;\n","/**\n * @fileoverview Ecommerce - Subscription & Recurring Revenue\n * @module features/ecommerce/subscriptions\n * \n * @description\n * Track subscription lifecycle events for SaaS and subscription\n * ecommerce businesses.\n * \n * @useCases\n * - Track subscription signups\n * - Monitor subscription changes (upgrades/downgrades)\n * - Track cancellations and churn\n * - Calculate MRR/ARR metrics\n * - Analyze subscription cohorts\n * \n * @metrics\n * - Monthly Recurring Revenue (MRR)\n * - Annual Recurring Revenue (ARR)\n * - Churn rate\n * - Expansion revenue\n * - Net revenue retention\n * \n * @chartData\n * - Line chart: MRR over time\n * - Waterfall: MRR movement\n * - Bar chart: Revenue by plan\n * - Cohort: Subscription retention\n * \n * @example\n * ```typescript\n * const subscriptions = new Subscriptions(webId, endpoint, apiKey);\n * \n * // Track subscription start\n * await subscriptions.start({\n * subscriptionId: 'sub_123',\n * userId: 'user_456',\n * plan: 'pro',\n * mrr: { amount: 4900, currency: 'USD' },\n * billingCycle: 'monthly'\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { MoneyValue, CurrencyCode, DateRangeFilter, TimeSeriesData } from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Subscription start configuration.\n */\nexport interface SubscriptionStartConfig {\n /** Subscription ID */\n subscriptionId: string;\n /** User ID */\n userId: string;\n /** Plan name */\n plan: string;\n /** Monthly recurring revenue */\n mrr: MoneyValue;\n /** Billing cycle */\n billingCycle: 'monthly' | 'quarterly' | 'yearly';\n /** Trial period (if applicable) */\n trialDays?: number;\n /** Coupon applied */\n coupon?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Subscription change configuration.\n */\nexport interface SubscriptionChangeConfig {\n /** Subscription ID */\n subscriptionId: string;\n /** User ID */\n userId: string;\n /** Previous plan */\n previousPlan: string;\n /** New plan */\n newPlan: string;\n /** Previous MRR */\n previousMrr: MoneyValue;\n /** New MRR */\n newMrr: MoneyValue;\n /** Change type */\n changeType: 'upgrade' | 'downgrade' | 'cross_grade';\n}\n\n/**\n * Subscription cancellation configuration.\n */\nexport interface SubscriptionCancelConfig {\n /** Subscription ID */\n subscriptionId: string;\n /** User ID */\n userId: string;\n /** Plan being cancelled */\n plan: string;\n /** Lost MRR */\n lostMrr: MoneyValue;\n /** Cancellation reason */\n reason?: string;\n /** Feedback provided */\n feedback?: string;\n /** Whether cancelled immediately or at period end */\n immediate: boolean;\n}\n\n/**\n * Subscription response from API.\n */\nexport interface SubscriptionResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n subscriptionId: string;\n tracked: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Subscription analytics data.\n */\nexport interface SubscriptionAnalyticsData {\n /** Summary metrics */\n summary: {\n totalMrr: MoneyValue;\n totalArr: MoneyValue;\n activeSubscriptions: number;\n newSubscriptions: number;\n churnedSubscriptions: number;\n churnRate: number;\n expansionMrr: MoneyValue;\n contractionMrr: MoneyValue;\n netMrrMovement: MoneyValue;\n netRevenueRetention: number;\n avgRevenuePerUser: MoneyValue;\n };\n /** MRR by plan */\n byPlan: Array<{\n plan: string;\n subscriptions: number;\n mrr: MoneyValue;\n percentage: number;\n }>;\n /** MRR waterfall */\n mrrMovement: {\n startingMrr: MoneyValue;\n newMrr: MoneyValue;\n expansionMrr: MoneyValue;\n contractionMrr: MoneyValue;\n churnedMrr: MoneyValue;\n endingMrr: MoneyValue;\n };\n /** MRR trend */\n mrrTrend: TimeSeriesData;\n /** Churn by reason */\n churnByReason: Array<{\n reason: string;\n count: number;\n lostMrr: MoneyValue;\n }>;\n /** Trial conversion rate */\n trialMetrics: {\n trialsStarted: number;\n trialsConverted: number;\n conversionRate: number;\n };\n}\n\n// ============================================================================\n// SUBSCRIPTIONS CLASS\n// ============================================================================\n\n/**\n * Subscriptions - Track subscription lifecycle events.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides subscription tracking including:\n * - Subscription starts (new MRR)\n * - Plan changes (expansion/contraction)\n * - Cancellations (churned MRR)\n * - Renewals\n * - Trial conversions\n * \n * @example\n * ```typescript\n * const subscriptions = new Subscriptions(webId, endpoint, apiKey);\n * \n * // New subscription\n * await subscriptions.start({\n * subscriptionId: 'sub_abc123',\n * userId: 'user_456',\n * plan: 'professional',\n * mrr: { amount: 9900, currency: 'USD' },\n * billingCycle: 'monthly',\n * trialDays: 14\n * });\n * \n * // Upgrade\n * await subscriptions.change({\n * subscriptionId: 'sub_abc123',\n * userId: 'user_456',\n * previousPlan: 'professional',\n * newPlan: 'enterprise',\n * previousMrr: { amount: 9900, currency: 'USD' },\n * newMrr: { amount: 29900, currency: 'USD' },\n * changeType: 'upgrade'\n * });\n * ```\n */\nexport class Subscriptions extends FlowGridTransport {\n /**\n * Tracks subscription start.\n * \n * @param config - Subscription start configuration\n * @returns Promise resolving to subscription response\n */\n async start(config: SubscriptionStartConfig): Promise<SubscriptionResponse> {\n return await this.init(\"subscription_start\", {\n type: \"ecommerce\",\n subtype: \"subscription_start\",\n subscriptionId: config.subscriptionId,\n userId: config.userId,\n plan: config.plan,\n mrr: config.mrr,\n billingCycle: config.billingCycle,\n trialDays: config.trialDays,\n coupon: config.coupon,\n isTrialing: (config.trialDays || 0) > 0,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks subscription plan change.\n * \n * @param config - Subscription change configuration\n * @returns Promise resolving to subscription response\n */\n async change(config: SubscriptionChangeConfig): Promise<SubscriptionResponse> {\n const mrrDiff = config.newMrr.amount - config.previousMrr.amount;\n \n return await this.init(\"subscription_change\", {\n type: \"ecommerce\",\n subtype: \"subscription_change\",\n subscriptionId: config.subscriptionId,\n userId: config.userId,\n previousPlan: config.previousPlan,\n newPlan: config.newPlan,\n previousMrr: config.previousMrr,\n newMrr: config.newMrr,\n mrrChange: mrrDiff,\n changeType: config.changeType,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks subscription cancellation.\n * \n * @param config - Cancellation configuration\n * @returns Promise resolving to subscription response\n */\n async cancel(config: SubscriptionCancelConfig): Promise<SubscriptionResponse> {\n return await this.init(\"subscription_cancel\", {\n type: \"ecommerce\",\n subtype: \"subscription_cancel\",\n subscriptionId: config.subscriptionId,\n userId: config.userId,\n plan: config.plan,\n lostMrr: config.lostMrr,\n reason: config.reason,\n feedback: config.feedback,\n immediate: config.immediate,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks subscription renewal.\n * \n * @param subscriptionId - Subscription ID\n * @param userId - User ID\n * @param mrr - Renewed MRR\n * @returns Promise resolving to subscription response\n */\n async renew(\n subscriptionId: string,\n userId: string,\n mrr: MoneyValue\n ): Promise<SubscriptionResponse> {\n return await this.init(\"subscription_renew\", {\n type: \"ecommerce\",\n subtype: \"subscription_renew\",\n subscriptionId,\n userId,\n mrr,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks trial conversion to paid.\n * \n * @param subscriptionId - Subscription ID\n * @param userId - User ID\n * @param plan - Converted to plan\n * @param mrr - New MRR\n * @returns Promise resolving to subscription response\n */\n async trialConverted(\n subscriptionId: string,\n userId: string,\n plan: string,\n mrr: MoneyValue\n ): Promise<SubscriptionResponse> {\n return await this.init(\"trial_converted\", {\n type: \"ecommerce\",\n subtype: \"trial_converted\",\n subscriptionId,\n userId,\n plan,\n mrr,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks trial expiration without conversion.\n * \n * @param subscriptionId - Subscription ID\n * @param userId - User ID\n * @returns Promise resolving to subscription response\n */\n async trialExpired(\n subscriptionId: string,\n userId: string\n ): Promise<SubscriptionResponse> {\n return await this.init(\"trial_expired\", {\n type: \"ecommerce\",\n subtype: \"trial_expired\",\n subscriptionId,\n userId,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default Subscriptions;\n","/**\n * Activation shortcuts — one helper per event, positional args, sensible defaults.\n * Underlying class accessible via `.client`.\n */\nimport { Activation } from \"../../features/core/activation\";\n\nexport class ActivationTracker {\n constructor(public readonly client: Activation) {}\n\n /** New user signup. */\n signup(userId: string, method: string, source?: string, props?: Record<string, unknown>) {\n return this.client.trackSignup(userId, method, source, props);\n }\n\n /** Step in an onboarding flow (e.g. `step 2 of 5`). */\n onboardingStep(\n userId: string,\n stepId: string,\n stepName: string,\n stepOrder: number,\n totalSteps: number,\n extra?: { skipped?: boolean; timeSpent?: number; properties?: Record<string, unknown> }\n ) {\n return this.client.trackOnboardingStep({\n userId,\n stepId,\n stepName,\n stepOrder,\n totalSteps,\n ...extra,\n });\n }\n\n /** All onboarding steps finished. */\n onboardingComplete(userId: string, totalTimeSpent: number, stepsCompleted: number, stepsSkipped = 0) {\n return this.client.completeOnboarding(userId, totalTimeSpent, stepsCompleted, stepsSkipped);\n }\n\n /** First meaningful action after signup (\"aha\" moment). */\n firstAction(userId: string, actionName: string, timeSinceSignup: number, properties?: Record<string, unknown>) {\n return this.client.trackFirstAction({ userId, actionName, timeSinceSignup, properties });\n }\n\n /** Generic activation event. */\n event(eventName: string, properties: Record<string, unknown> = {}) {\n return this.client.create({ eventName, properties });\n }\n\n}\n","/**\n * Feature usage shortcuts.\n *\n * Semantic helpers over `track(\"feature_usage\", { featureId, featureName, action })`.\n * Pick the verb that describes what happened — the dashboard funnel maps each\n * action onto a lifecycle stage (a *viewed* feature is Discovered, a *used* one\n * is Adopted). No hidden state, no guessing: you send exactly what you call.\n */\nimport { TrackFeature } from \"../../features/core/track-feature\";\n\nexport type FeatureAction = \"viewed\" | \"used\" | \"completed\" | \"abandoned\";\n\nexport class FeatureTracker {\n constructor(public readonly client: TrackFeature) {}\n\n /** Emit a `feature_usage` event with an explicit action (defaults to \"used\"). */\n record(featureId: string, featureName: string, action: FeatureAction = \"used\", properties?: Record<string, unknown>) {\n return this.client.trackUsage({ featureId, featureName, action, properties });\n }\n\n /** Visitor opened / looked at the feature. Funnel → Discovered + Viewed. */\n viewed(featureId: string, featureName: string, properties?: Record<string, unknown>) {\n return this.record(featureId, featureName, \"viewed\", properties);\n }\n\n /** Visitor used the feature. Funnel → Adopted. */\n used(featureId: string, featureName: string, properties?: Record<string, unknown>) {\n return this.record(featureId, featureName, \"used\", properties);\n }\n\n /** Visitor finished the feature's job successfully. Funnel → Completed. */\n completed(featureId: string, featureName: string, properties?: Record<string, unknown>) {\n return this.record(featureId, featureName, \"completed\", properties);\n }\n\n /** Visitor started but gave up. Funnel → Abandonment. */\n abandoned(featureId: string, featureName: string, properties?: Record<string, unknown>) {\n return this.record(featureId, featureName, \"abandoned\", properties);\n }\n\n /** Create / register a feature event (advanced — see `client.create`). */\n create(userId: string, action: string, featureName: string, properties?: Record<string, unknown>) {\n return this.client.create({ userId, action, properties: { featureName, ...properties } });\n }\n}\n","/**\n * AI prompt tracking shortcuts.\n */\nimport { TrackPrompt, PromptFeedbackConfig } from \"../../features/core/track-prompt\";\n\nexport class PromptTracker {\n constructor(public readonly client: TrackPrompt) {}\n\n /** A user submitted a prompt. */\n submitted(promptId: string, promptType: string, properties?: Record<string, unknown>) {\n return this.client.trackSubmission({ promptId, promptType, properties });\n }\n\n /** The model returned a response. */\n responded(promptId: string, responseTime: number, completed: boolean, properties?: Record<string, unknown>) {\n return this.client.trackResponse({ promptId, responseTime, completed, properties });\n }\n\n /** User feedback on a prompt response (thumbs up/down etc.). */\n feedback(promptId: string, feedbackType: PromptFeedbackConfig[\"feedbackType\"], properties?: Record<string, unknown>) {\n return this.client.trackFeedback({ promptId, feedbackType, properties });\n }\n\n /** Multi-turn conversation summary. */\n conversation(conversationId: string, userId: string, turnCount: number, totalDuration: number) {\n return this.client.trackConversation(conversationId, userId, turnCount, totalDuration);\n }\n\n /** Generic prompt event. */\n event(eventName: string, properties: Record<string, unknown> = {}) {\n return this.client.create({ eventName, properties });\n }\n}\n","/**\n * Experiment tracker — A/B testing API.\n *\n * Covers the full A/B test lifecycle:\n * 1. `define()` — register an experiment (one-time admin setup).\n * 2. `init()` — initialise the client-side engine (browser only).\n * 3. `assign()` / `variant()` — get a user's variant.\n * 4. `exposure()` — log that the user saw the assigned variant.\n * 5. `conversion()` — log a success metric.\n *\n * The browser flow is the simplest:\n * ```ts\n * await fg.experiments.init([{ experimentId, variants: [...] }]);\n * const variant = fg.experiments.variant(experimentId);\n * if (variant === \"B\") renderNewCheckout();\n * await fg.experiments.exposure(experimentId, userId, variant);\n * // …user converts…\n * await fg.experiments.conversion(experimentId, userId, variant, \"purchase\", 1);\n * ```\n */\nimport {\n Experiment,\n ExperimentDefinition,\n ExperimentInitConfig,\n} from \"../../features/core/experiment\";\nimport type { ServerCookieReader } from \"../../utils/identity\";\n\nexport class ExperimentTracker {\n constructor(public readonly client: Experiment) {}\n\n // ---------------------------------------------------------------------------\n // SETUP\n // ---------------------------------------------------------------------------\n\n /**\n * Define an experiment. Run this once during admin setup.\n *\n * @example\n * ```ts\n * await fg.experiments.define({\n * experimentId: \"checkout_v2\",\n * name: \"New checkout flow\",\n * variants: [\n * { id: \"control\", weight: 50 },\n * { id: \"B\", weight: 50 },\n * ],\n * });\n * ```\n */\n define(definition: ExperimentDefinition) {\n return this.client.define(definition);\n }\n\n /**\n * Initialise the client-side engine (browser only).\n *\n * Verifies experiments, performs weighted-random assignment, and\n * persists assignments in `localStorage` so the same visitor always\n * sees the same variant.\n */\n init(experiments: ExperimentInitConfig[]) {\n return this.client.initExperiments(experiments);\n }\n\n /**\n * Zero-config init — fetch this site's running experiments from the server\n * and run them automatically (variant assignment + redirect/DOM changes +\n * exposure). No experiment config needed in code: the dashboard is the source\n * of truth. This is what powers the no-build snippet path.\n *\n * @example\n * ```ts\n * await fg.experiments.initFromServer();\n * ```\n */\n initFromServer() {\n return this.client.initFromServer();\n }\n\n // ---------------------------------------------------------------------------\n // ASSIGNMENT\n // ---------------------------------------------------------------------------\n\n /**\n * Synchronously read the locally-cached variant assignment.\n * Returns `null` if no assignment exists. Browser-only.\n */\n variant(experimentId: string): string | null {\n return this.client.getLocalVariant(experimentId);\n }\n\n /**\n * Explicitly assign (or override) a user's variant. Use this for\n * targeted assignments or admin overrides.\n */\n assign(\n experimentId: string,\n userId: string,\n variantId: string,\n source: \"random\" | \"targeted\" | \"override\" = \"targeted\"\n ) {\n return this.client.assign({ experimentId, userId, variantId, source });\n }\n\n // ---------------------------------------------------------------------------\n // EXPOSURE & CONVERSION\n // ---------------------------------------------------------------------------\n\n /**\n * Record that a user was exposed to (saw / used) their variant.\n * Call this when the variant is rendered.\n */\n exposure(experimentId: string, userId: string, variantId: string) {\n return this.client.trackExposure(experimentId, userId, variantId);\n }\n\n /**\n * Browser-side exposure for the locally-assigned variant — resolves both the\n * variant and the user id for you. Call this right after rendering the\n * variant; no-op if the visitor has no assignment yet.\n *\n * @example\n * ```ts\n * await fg.experiments.init([{ id: \"checkout_v2\", variants: [...] }]);\n * await fg.experiments.exposureAuto(\"checkout_v2\");\n * ```\n */\n exposureAuto(experimentId: string) {\n return this.client.trackLocalExposure(experimentId);\n }\n\n /**\n * Record a conversion against a specific user's variant (server-side).\n *\n * @example\n * ```ts\n * await fg.experiments.conversion(\"checkout_v2\", \"u_1\", \"B\", \"purchase\", 49.99);\n * ```\n */\n conversion(\n experimentId: string,\n userId: string,\n variantId: string,\n metricName: string,\n value: number = 1\n ) {\n return this.client.trackConversion({\n experimentId,\n userId,\n variantId,\n metricName,\n value,\n });\n }\n\n /**\n * Browser-side conversion using the locally-cached variant assignment.\n * No-op if the visitor has no assignment for this experiment.\n *\n * @example\n * ```ts\n * await fg.experiments.localConversion(\"checkout_v2\", \"purchase\", 49.99);\n * ```\n */\n localConversion(experimentId: string, metricName: string = \"conversion\", value: number = 1) {\n return this.client.trackLocalConversion(experimentId, metricName, value);\n }\n\n // ---------------------------------------------------------------------------\n // SSR / COOKIE HYDRATION\n //\n // The underlying engine mirrors assignments to a `fg_exp_assignments`\n // cookie so that server-rendered HTML can pick the right variant before\n // any JS executes. These helpers wire the cookie into common runtimes.\n // ---------------------------------------------------------------------------\n\n /**\n * Hydrate assignments from a server cookie reader (Next.js `cookies()`,\n * Nuxt `useCookie()`, etc.). Call this once per request before reading\n * `variant()` server-side.\n *\n * @example\n * ```ts\n * import { cookies } from \"next/headers\";\n * fg.experiments.hydrateFromCookies(cookies());\n * const variant = fg.experiments.variant(\"checkout_v2\");\n * ```\n */\n hydrateFromCookies(reader: ServerCookieReader) {\n this.client.hydrateFromCookies(reader);\n }\n\n /**\n * Hydrate assignments from a raw `Cookie` header (Workers, Bun, Hono, etc.).\n */\n hydrateFromCookieHeader(cookieHeader: string | null | undefined) {\n this.client.hydrateFromCookieHeader(cookieHeader);\n }\n\n /**\n * Inject assignments resolved server-side. Use when the assignment was\n * computed without a cookie (e.g. logged-in user fetched from DB).\n */\n injectAssignments(assignments: Record<string, string>) {\n this.client.injectAssignments(assignments);\n }\n\n /**\n * Snapshot of all known assignments. Useful for SSR payload hand-off.\n */\n getAssignments(): Record<string, string> {\n return this.client.getAssignments();\n }\n\n /**\n * Build a `Set-Cookie` header value seeding the visitor's browser with\n * the current assignment map.\n *\n * @example\n * ```ts\n * res.headers.set(\"Set-Cookie\", fg.experiments.serializeAssignmentCookie());\n * ```\n */\n serializeAssignmentCookie(): string {\n return this.client.serializeAssignmentCookie();\n }\n\n /** Wipe all locally-cached assignments (cookie + localStorage). */\n clearAssignments() {\n this.client.clearAssignments();\n }\n}\n","/**\n * Page view shortcuts. Browser fields auto-detected when available.\n */\nimport { PageViews } from \"../../features/analytics/page-views\";\n\nfunction browserDefaults() {\n const isBrowser = typeof window !== \"undefined\" && typeof document !== \"undefined\";\n return {\n url: isBrowser ? window.location.href : \"\",\n path: isBrowser ? window.location.pathname : \"\",\n title: isBrowser ? document.title : undefined,\n referrer: isBrowser ? document.referrer : undefined,\n };\n}\n\nexport class PageViewTracker {\n constructor(public readonly client: PageViews) {}\n\n /**\n * Track a page view. With no args, auto-detects from `window`/`document`.\n * Pass a path or full URL to override.\n */\n view(\n pathOrUrl?: string,\n properties?: Record<string, unknown>,\n opts?: { title?: string; referrer?: string; userId?: string; sessionId?: string }\n ) {\n const d = browserDefaults();\n const isFullUrl = pathOrUrl && /^https?:\\/\\//i.test(pathOrUrl);\n const url = isFullUrl ? pathOrUrl! : d.url;\n const path = isFullUrl ? new URL(pathOrUrl!).pathname : (pathOrUrl ?? d.path);\n return this.client.track({\n url,\n path,\n title: opts?.title ?? d.title,\n referrer: opts?.referrer ?? d.referrer,\n userId: opts?.userId,\n sessionId: opts?.sessionId,\n properties,\n });\n }\n\n /** Record scroll depth (0-100). */\n scroll(path: string, scrollDepth: number, timeAtDepth?: number) {\n return this.client.trackScrollDepth({ path, scrollDepth, timeAtDepth });\n }\n\n /** Record time spent on a page (seconds). */\n time(path: string, duration: number) {\n return this.client.trackTimeOnPage({ path, duration });\n }\n\n /** Record page exit. */\n exit(path: string, duration: number, scrollDepth: number) {\n return this.client.trackExit(path, duration, scrollDepth);\n }\n\n /** Auto-track SPA route changes, scroll-depth thresholds, and time-on-page. */\n autoTrack(options?: { userId?: string; sessionId?: string }) {\n return this.client.autoTrack(options);\n }\n}\n","/**\n * Session lifecycle shortcuts.\n */\nimport { Sessions } from \"../../features/analytics/sessions\";\n\nexport class SessionTracker {\n constructor(public readonly client: Sessions) {}\n\n /** Start a new session. */\n start(opts?: Parameters<Sessions[\"start\"]>[0]) {\n return this.client.start(opts);\n }\n\n /** Update an active session with new properties. */\n update(sessionId: string, properties: Record<string, unknown>) {\n return this.client.update(sessionId, properties);\n }\n\n /** End a session. */\n end(sessionId: string) {\n return this.client.end(sessionId);\n }\n\n /** Heartbeat to keep a session alive. */\n heartbeat(sessionId: string) {\n return this.client.heartbeat(sessionId);\n }\n\n /** Auto-manage browser session lifecycle (start, heartbeat, end on pagehide). */\n autoTrack(config?: Parameters<Sessions[\"autoTrack\"]>[0], options?: { heartbeatMs?: number }) {\n return this.client.autoTrack(config, options);\n }\n}\n","/**\n * Custom event shortcuts.\n */\nimport { Events } from \"../../features/analytics/events\";\n\nexport class EventTracker {\n constructor(public readonly client: Events) {}\n\n /** Track a custom event. */\n track(eventName: string, properties?: Record<string, unknown>, opts?: { userId?: string; sessionId?: string }) {\n return this.client.track({ eventName, properties, userId: opts?.userId, sessionId: opts?.sessionId });\n }\n\n /** Track many events in one request. */\n batch(events: Array<{ eventName: string; properties?: Record<string, unknown> }>) {\n return this.client.trackBatch({ events });\n }\n\n /** Track a click on a tagged element. */\n click(elementId: string, elementText: string, properties?: Record<string, unknown>) {\n return this.client.trackClick({ elementId, elementText, properties });\n }\n\n /** Track a form interaction. */\n form(formId: string, formName: string, properties?: Record<string, unknown>) {\n return this.client.trackForm({ formId, formName, properties });\n }\n\n /** Track a search query. */\n search(query: string, category: string, properties?: Record<string, unknown>) {\n return this.client.trackSearch({ query, category, properties });\n }\n\n /** Track a runtime error. */\n error(message: string, type: string, properties?: Record<string, unknown>) {\n return this.client.trackError({ message, type, properties });\n }\n}\n","/**\n * User identification shortcuts.\n */\nimport { Identify } from \"../../features/analytics/identify\";\n\nexport class IdentifyTracker {\n constructor(public readonly client: Identify) {}\n\n /** Identify a user with traits. */\n user(userId: string, traits?: Record<string, unknown>, anonymousId?: string) {\n return this.client.user({ userId, anonymousId, traits: traits });\n }\n\n /** Update traits on an existing user. */\n updateTraits(userId: string, traits: Record<string, unknown>) {\n return this.client.updateTraits(userId, traits);\n }\n\n /** Alias an anonymous ID to a known user ID. */\n alias(previousId: string, userId: string) {\n return this.client.alias({ previousId, userId });\n }\n\n /** Associate a user with a group/company. */\n group(userId: string, groupId: string, traits?: Record<string, unknown>) {\n return this.client.group({ userId, groupId, traits: traits });\n }\n}\n","/**\n * Funnel shortcuts.\n */\nimport { Funnels } from \"../../features/analytics/funnels\";\n\nexport class FunnelTracker {\n constructor(public readonly client: Funnels) {}\n\n /** Define a new funnel (admin / setup). */\n define(definition: Parameters<Funnels[\"define\"]>[0]) {\n return this.client.define(definition);\n }\n\n /** Record a step completion in a funnel. */\n step(funnelId: string, step: string, stepNumber: number, userId?: string, properties?: Record<string, unknown>) {\n return this.client.trackStep({ funnelId, step, stepNumber, userId, properties });\n }\n\n /** Mark a funnel as completed. */\n complete(funnelId: string, userId?: string, properties?: Record<string, unknown>) {\n return this.client.trackComplete(funnelId, userId, properties);\n }\n\n /** Mark a funnel as abandoned. */\n abandon(funnelId: string, lastStep: string, userId?: string, reason?: string) {\n return this.client.trackAbandon(funnelId, lastStep, userId, reason);\n }\n}\n","/**\n * Retention shortcuts.\n */\nimport { Retention } from \"../../features/analytics/retention\";\n\nexport class RetentionTracker {\n constructor(public readonly client: Retention) {}\n\n /** Record that a user was active. */\n active(userId: string, properties?: Record<string, unknown>) {\n return this.client.trackActivity({ userId, properties });\n }\n}\n","/**\n * Multi-touch attribution shortcuts.\n */\nimport { Attribution, ConversionEvent } from \"../../features/analytics/attribution\";\n\nexport class AttributionTracker {\n constructor(public readonly client: Attribution) {}\n\n /** Record a marketing touchpoint. */\n touchpoint(userId: string, channel: string, properties?: Record<string, unknown>) {\n return this.client.trackTouchpoint({ userId, channel, properties });\n }\n\n /** Record a conversion that should be attributed. */\n conversion(userId: string, conversionType: ConversionEvent[\"conversionType\"], properties?: Record<string, unknown>) {\n return this.client.recordConversion({ userId, conversionType, properties });\n }\n\n /** Auto-capture a touchpoint from `utm_*` params + `document.referrer`. */\n captureFromUrl(userId: string) {\n return this.client.captureFromUrl(userId);\n }\n}\n","/**\n * Heatmap shortcuts.\n */\nimport { Heatmaps } from \"../../features/analytics/heatmaps\";\n\nexport class HeatmapTracker {\n constructor(public readonly client: Heatmaps) {}\n\n /** Record a click point. */\n click(pageUrl: string, x: number, y: number, viewportWidth: number, viewportHeight: number) {\n return this.client.trackClick({ pageUrl, x, y, viewportWidth, viewportHeight });\n }\n\n /** Record a mouse-movement path. */\n movement(pageUrl: string, path: Array<{ x: number; y: number; t: number }>, viewport: { width: number; height: number }) {\n return this.client.trackMovement({ pageUrl, path, viewport });\n }\n\n /** Record scroll behaviour. */\n scroll(pageUrl: string, maxScrollDepth: number, pageHeight: number, viewportHeight: number) {\n return this.client.trackScroll({ pageUrl, maxScrollDepth, pageHeight, viewportHeight });\n }\n\n /** Auto-attach click + scroll listeners (and optionally mousemove). */\n autoTrack(options?: { movement?: boolean; userId?: string; sessionId?: string }) {\n return this.client.autoTrack(options);\n }\n}\n","/**\n * Performance shortcuts.\n */\nimport { PerformanceTracking } from \"../../features/analytics/performance\";\n\ntype ResourceType = \"script\" | \"stylesheet\" | \"image\" | \"font\" | \"xhr\" | \"fetch\" | \"other\";\n\nexport class PerformanceTracker {\n constructor(public readonly client: PerformanceTracking) {}\n\n /** Report Core Web Vitals (LCP/FID/CLS/INP/TTFB). */\n webVitals(pageUrl: string, vitals: Record<string, number>) {\n return this.client.trackWebVitals({ pageUrl, ...vitals });\n }\n\n /** Report page-load timing. */\n pageLoad(pageUrl: string, timings: Record<string, number>) {\n return this.client.trackPageLoad({ pageUrl, ...timings });\n }\n\n /** Report a single resource load. */\n resource(pageUrl: string, resourceUrl: string, resourceType: ResourceType, duration: number) {\n return this.client.trackResource({ pageUrl, resourceUrl, resourceType, duration });\n }\n\n /** Auto-collect Core Web Vitals + navigation timing from the browser. */\n autoTrack(options?: { userId?: string; resources?: boolean; minDuration?: number }) {\n return this.client.autoTrack(options);\n }\n}\n","/**\n * Session Replay shortcut wrapper.\n */\nimport {\n SessionReplay,\n type SessionReplayFlushMetadata,\n type SessionReplayFlushReason,\n type SessionReplayOptions,\n type SessionReplayStatus,\n} from \"../../features/analytics/replay\";\nimport type { Teardown } from \"../../utils/browser\";\n\nexport class ReplayTracker {\n constructor(public readonly client: SessionReplay) {}\n\n /**\n * Start recording the current page. Returns a teardown that stops capture\n * and flushes the final buffer.\n *\n * @example\n * ```ts\n * const stop = await fg.replay.start({ sampleRate: 1.0, maskAllInputs: true });\n * ```\n */\n start(options?: SessionReplayOptions): Promise<Teardown> {\n return this.client.start(options ?? {});\n }\n\n /** Enable replay and start recording immediately. */\n enable(options?: SessionReplayOptions): Promise<Teardown> {\n return this.client.enable(options ?? {});\n }\n\n /** Disable replay and stop any active recorder. */\n disable(): void {\n this.client.disable();\n }\n\n /** Stop the active recorder and flush a final chunk. */\n stop(reason?: SessionReplayFlushReason): void {\n this.client.stop(reason);\n }\n\n /** Flush buffered events without stopping recording. */\n flush(reason?: SessionReplayFlushReason): Promise<void> {\n return this.client.flush(reason ?? \"manual\");\n }\n\n /** Bind a known user ID to subsequent replay chunks. */\n identify(userId: string): void {\n this.client.identify(userId);\n }\n\n /** Read recorder lifecycle and health state. */\n status(): SessionReplayStatus {\n return this.client.getStatus();\n }\n\n /** Send a pre-recorded rrweb chunk for custom recorder integrations. */\n sendChunk(\n sessionId: string,\n events: unknown[],\n options?: Parameters<SessionReplay[\"sendChunk\"]>[2],\n ): Promise<void> {\n return this.client.sendChunk(sessionId, events, options ?? {});\n }\n}\n\nexport type { SessionReplayFlushMetadata, SessionReplayFlushReason, SessionReplayOptions, SessionReplayStatus };\n","/**\n * Product shortcuts.\n */\nimport { Products } from \"../../features/ecommerce/products\";\nimport type { Product } from \"../../types\";\n\nexport class ProductTracker {\n constructor(public readonly client: Products) {}\n\n /** Track a product detail view. */\n view(product: Pick<Product, \"productId\" | \"name\" | \"price\" | \"currency\"> & Partial<Product>, properties?: Record<string, unknown>) {\n return this.client.view({ ...product, properties });\n }\n\n /** Track a product list impression. */\n impression(listId: string, listName: string, products: Product[]) {\n return this.client.impression({ list: { listId, listName, products } });\n }\n\n /** Track a click on a product in a list. */\n click(product: Product, listId: string, position: number) {\n return this.client.click({ product, listId, position });\n }\n}\n","/**\n * Cart shortcuts.\n */\nimport { CartTracking } from \"../../features/ecommerce/cart\";\nimport type { Product, Cart } from \"../../types\";\n\nexport class CartTracker {\n constructor(public readonly client: CartTracking) {}\n\n /** Add a product to cart. */\n add(product: Product, quantity = 1, cartId?: string) {\n return this.client.addItem({ product, quantity, cartId });\n }\n\n /** Remove a product from cart. */\n remove(product: Product, quantity = 1, cartId?: string) {\n return this.client.removeItem({ product, quantity, cartId });\n }\n\n /** Change the quantity of a cart item. */\n update(product: Product, previousQuantity: number, newQuantity: number, cartId?: string) {\n return this.client.updateItem({ product, previousQuantity, newQuantity, cartId });\n }\n\n /** Track a cart view. */\n view(cart: Cart) {\n return this.client.view({ cart });\n }\n}\n","/**\n * Checkout shortcuts.\n */\nimport { Checkout } from \"../../features/ecommerce/checkout\";\nimport type { Cart, ShippingInfo, PaymentInfo, CheckoutStep } from \"../../types\";\n\nexport class CheckoutTracker {\n constructor(public readonly client: Checkout) {}\n\n /** Begin checkout for the current cart. */\n begin(cart: Cart) {\n return this.client.begin({ cart });\n }\n\n /** Move to a checkout step. */\n step(step: CheckoutStep, stepNumber: number, cart: Cart) {\n return this.client.step({ step, stepNumber, cart });\n }\n\n /** Add shipping info. */\n shipping(cart: Cart, shipping: ShippingInfo) {\n return this.client.addShipping({ cart, shipping });\n }\n\n /** Add payment info. */\n payment(cart: Cart, payment: PaymentInfo) {\n return this.client.addPayment({ cart, payment });\n }\n}\n","/**\n * Purchase shortcuts.\n */\nimport { Purchases } from \"../../features/ecommerce/purchases\";\nimport type { Order } from \"../../types\";\n\nexport class PurchaseTracker {\n constructor(public readonly client: Purchases) {}\n\n /** Track a completed purchase. */\n complete(order: Order) {\n return this.client.track({ order });\n }\n}\n","/**\n * Refund shortcuts.\n */\nimport { Refunds } from \"../../features/ecommerce/refunds\";\nimport type { CartItem } from \"../../types\";\n\nexport class RefundTracker {\n constructor(public readonly client: Refunds) {}\n\n /** Customer requested a refund. */\n request(orderId: string, items?: CartItem[], reason?: string) {\n return this.client.request(orderId, items, reason);\n }\n\n /** Refund approved. */\n approve(refundId: string, approvedAmount: number) {\n return this.client.approve(refundId, approvedAmount);\n }\n\n /** Refund denied. */\n deny(refundId: string, reason: string) {\n return this.client.deny(refundId, reason);\n }\n\n /** Track a completed refund (partial or full). */\n complete(\n orderId: string,\n refundId: string,\n amount: number,\n currency: string,\n reason: string,\n isFullRefund = false\n ) {\n return this.client.track({ orderId, refundId, amount, currency, reason, isFullRefund });\n }\n}\n","/**\n * Promotion shortcuts.\n */\nimport { Promotions } from \"../../features/ecommerce/promotions\";\n\nexport class PromotionTracker {\n constructor(public readonly client: Promotions) {}\n\n /** A user saw a promotion. */\n view(promotionId: string, name: string, creative: string, position: string) {\n return this.client.view({ promotionId, name, creative, position });\n }\n\n /** A user clicked a promotion. */\n click(promotionId: string, name: string, creative: string, position: string) {\n return this.client.click({ promotionId, name, creative, position });\n }\n\n /** A coupon was applied. */\n coupon(\n code: string,\n orderId: string,\n discountValue: number,\n discountType: \"percentage\" | \"fixed\",\n orderValueBefore: number,\n orderValueAfter: number\n ) {\n return this.client.couponUsed({\n code,\n orderId,\n discountValue,\n discountType,\n orderValueBefore,\n orderValueAfter,\n });\n }\n}\n","/**\n * Wishlist shortcuts.\n */\nimport { WishlistTracking } from \"../../features/ecommerce/wishlist\";\nimport type { Product, Wishlist } from \"../../types\";\n\nexport class WishlistTracker {\n constructor(public readonly client: WishlistTracking) {}\n\n /** Add a product to wishlist. */\n add(product: Product, wishlistId?: string) {\n return this.client.add({ product, wishlistId });\n }\n\n /** Remove a product from wishlist. */\n remove(product: Product, wishlistId?: string) {\n return this.client.remove({ product, wishlistId });\n }\n\n /** Track a wishlist view. */\n view(wishlist: Wishlist) {\n return this.client.view(wishlist);\n }\n\n /** Move a wishlist item into the cart. */\n moveToCart(product: Product, wishlistId?: string) {\n return this.client.moveToCart(product, wishlistId);\n }\n\n /** Wishlist was shared. */\n share(wishlistId: string, method: \"email\" | \"link\" | \"social\") {\n return this.client.share(wishlistId, method);\n }\n}\n","/**\n * Customer LTV shortcuts.\n */\nimport { CustomerLTV } from \"../../features/ecommerce/ltv\";\nimport type { MoneyValue } from \"../../types\";\n\ntype LTVSegment = \"low\" | \"medium\" | \"high\" | \"vip\";\n\nexport class LTVTracker {\n constructor(public readonly client: CustomerLTV) {}\n\n /** Record a customer acquisition cost (CAC). */\n acquisitionCost(userId: string, cost: MoneyValue, channel: string) {\n return this.client.recordAcquisitionCost({ userId, cost, channel });\n }\n\n /** Move a user into a different LTV segment. */\n segment(userId: string, newSegment: LTVSegment) {\n return this.client.updateSegment(userId, newSegment);\n }\n}\n","/**\n * Site search shortcuts.\n */\nimport { SearchAnalytics } from \"../../features/ecommerce/search\";\nimport type { Product } from \"../../types\";\n\ntype RefinementType = \"query_change\" | \"add_filter\" | \"remove_filter\" | \"sort_change\";\n\nexport class SearchTracker {\n constructor(public readonly client: SearchAnalytics) {}\n\n /** A user searched. */\n query(query: string, resultsCount: number, extra?: { category?: string; filters?: Record<string, string | string[]>; userId?: string; sessionId?: string }) {\n return this.client.track({ query, resultsCount, ...extra });\n }\n\n /** A user clicked a search result. */\n click(query: string, product: Product, position: number) {\n return this.client.trackClick({ query, product, position });\n }\n\n /** A user refined an existing search. */\n refine(originalQuery: string, refinedQuery: string, refinementType: RefinementType) {\n return this.client.trackRefinement(originalQuery, refinedQuery, refinementType);\n }\n\n /** Autocomplete suggestion accepted. */\n suggestion(partialQuery: string, selectedSuggestion: string, suggestionPosition: number) {\n return this.client.trackSuggestion(partialQuery, selectedSuggestion, suggestionPosition);\n }\n}\n","/**\n * Subscription shortcuts.\n */\nimport { Subscriptions } from \"../../features/ecommerce/subscriptions\";\nimport type { MoneyValue } from \"../../types\";\n\nexport class SubscriptionTracker {\n constructor(public readonly client: Subscriptions) {}\n\n /** New subscription started. */\n start(\n subscriptionId: string,\n userId: string,\n plan: string,\n mrr: MoneyValue,\n billingCycle: \"monthly\" | \"quarterly\" | \"yearly\"\n ) {\n return this.client.start({ subscriptionId, userId, plan, mrr, billingCycle });\n }\n\n /** Plan changed (upgrade / downgrade). */\n change(\n subscriptionId: string,\n userId: string,\n previousPlan: string,\n newPlan: string,\n previousMrr: MoneyValue,\n newMrr: MoneyValue,\n changeType: \"upgrade\" | \"downgrade\" | \"cross_grade\"\n ) {\n return this.client.change({\n subscriptionId,\n userId,\n previousPlan,\n newPlan,\n previousMrr,\n newMrr,\n changeType,\n });\n }\n\n /** Subscription cancelled. */\n cancel(subscriptionId: string, userId: string, plan: string, lostMrr: MoneyValue, immediate = false) {\n return this.client.cancel({ subscriptionId, userId, plan, lostMrr, immediate });\n }\n\n /** Subscription renewed. */\n renew(subscriptionId: string, userId: string, mrr: MoneyValue) {\n return this.client.renew(subscriptionId, userId, mrr);\n }\n\n /** Trial converted to paid. */\n trialConverted(\n subscriptionId: string,\n userId: string,\n plan: string,\n mrr: MoneyValue\n ) {\n return this.client.trialConverted(subscriptionId, userId, plan, mrr);\n }\n\n /** Trial expired without converting. */\n trialExpired(subscriptionId: string, userId: string) {\n return this.client.trialExpired(subscriptionId, userId);\n }\n}\n","/**\n * @fileoverview Unified FlowGrid SDK facade.\n * @module flowgrid\n *\n * @description\n * A single class that aggregates every feature module so consumers can\n * instantiate the SDK once and access all tracking domains from one object.\n *\n * Every namespace returns an ergonomic *shortcut* wrapper exposing\n * positional-arg methods for the 80% case. The raw underlying class is\n * always available on `.client` for advanced configuration.\n *\n * @example\n * ```ts\n * import { FlowGrid } from \"flowgrid-sdk\";\n *\n * const fg = new FlowGrid(\"web_123\", \"https://core.flow-grid.xyz\", \"sk_live_xxx\");\n *\n * // Top-level shortcuts (the 80% case)\n * fg.track(\"signup_completed\", { plan: \"pro\" });\n * fg.page();\n * fg.identifyUser(\"u_1\", { email: \"alice@example.com\" });\n *\n * // Per-feature shortcuts\n * fg.cart.add({ productId: \"sku_1\", name: \"Tee\", price: 29, currency: \"USD\" });\n * fg.experiments.assign(\"checkout_v2\", \"u_1\");\n *\n * // Power-user escape hatch — raw class with full config surface\n * fg.events.client.track({ eventName: \"x\", sessionId: \"...\", category: \"...\" });\n * ```\n */\n\nimport { FlowGridTransport, FlowGridTransportConfig } from \"./client/transport\";\nimport { IdentityManager } from \"./utils/identity\";\n\nimport { Activation, TrackFeature, TrackPrompt, Experiment } from \"./features/core\";\nimport {\n PageViews,\n Sessions,\n Events,\n Identify,\n Funnels,\n Retention,\n Attribution,\n Heatmaps,\n PerformanceTracking,\n SessionReplay,\n} from \"./features/analytics\";\nimport {\n Products,\n CartTracking,\n Checkout,\n Purchases,\n Refunds,\n Promotions,\n WishlistTracking,\n CustomerLTV,\n SearchAnalytics,\n Subscriptions,\n} from \"./features/ecommerce\";\n// Enterprise feature + tracker classes are dynamically imported at runtime so\n// webpack can split them into a separate async chunk. The type-only imports\n// below give TypeScript full type information without adding any runtime bytes\n// to the initial bundle.\nimport type {\n Engagement,\n ChurnAnalytics,\n Monetization,\n MultiPathFunnels,\n SupportAnalytics,\n AcquisitionAnalytics,\n PathAnalytics,\n SecurityAnalytics,\n} from \"./features/enterprise\";\nimport type * as _EnterpriseFeaturesNS from \"./features/enterprise\";\n\nimport type {\n EngagementTracker,\n CohortTracker,\n ChurnTracker,\n MonetizationTracker,\n MultiPathFunnelTracker,\n SupportTracker,\n AcquisitionTracker,\n PathTracker,\n AlertTracker,\n SecurityTracker,\n ForecastingTracker,\n} from \"./tracking/enterprise\";\nimport type * as _EnterpriseTrackingNS from \"./tracking/enterprise\";\n\n// Import directly from sub-barrels (not the top-level ./tracking barrel) so\n// the static module graph never reaches ./tracking/enterprise and its\n// dependencies can be tree-shaken / placed only in the async chunk.\nimport { ActivationTracker, ExperimentTracker, FeatureTracker, PromptTracker } from \"./tracking/core\";\nimport {\n EventTracker, PageViewTracker, SessionTracker, IdentifyTracker,\n FunnelTracker, RetentionTracker, AttributionTracker,\n HeatmapTracker, PerformanceTracker, ReplayTracker,\n} from \"./tracking/analytics\";\nimport {\n ProductTracker, CartTracker, CheckoutTracker, PurchaseTracker,\n RefundTracker, PromotionTracker, WishlistTracker,\n SubscriptionTracker, LTVTracker, SearchTracker,\n} from \"./tracking/ecommerce\";\n\n// ---- Enterprise lazy-load state (module-level singleton) ----\nlet _enterpriseFeatures: typeof _EnterpriseFeaturesNS | null = null;\nlet _enterpriseTracking: typeof _EnterpriseTrackingNS | null = null;\n\nfunction loadEnterpriseModules(): Promise<void> {\n if (_enterpriseFeatures && _enterpriseTracking) return Promise.resolve();\n return Promise.all([\n import(/* webpackChunkName: \"flowgrid-enterprise\" */ \"./features/enterprise\"),\n import(/* webpackChunkName: \"flowgrid-enterprise\" */ \"./tracking/enterprise\"),\n ]).then(([features, tracking]) => {\n _enterpriseFeatures = features as typeof _EnterpriseFeaturesNS;\n _enterpriseTracking = tracking as typeof _EnterpriseTrackingNS;\n });\n}\n\ntype Ctor<T> = new (\n webId: string,\n endpoint: string,\n apiKey: string,\n visitorId?: string,\n identityManager?: IdentityManager,\n config?: FlowGridTransportConfig\n) => T;\n\nexport type FlowGridTrackDomain =\n | \"event\"\n | \"activation\"\n | \"feature\"\n | \"prompt\"\n | \"experiment\"\n | \"ecommerce\"\n | \"analytics\"\n | \"retention\"\n | \"attribution\"\n | \"funnel\"\n | \"engagement\"\n | \"churn\"\n | \"monetization\"\n | \"support\"\n | \"acquisition\"\n | \"paths\"\n | \"security\"\n | \"multiPathFunnels\";\n\nexport interface FlowGridTrackOptions {\n userId?: string;\n sessionId?: string;\n /** Force a specific module when an event name is intentionally generic. */\n domain?: FlowGridTrackDomain;\n}\n\n/**\n * Property shapes for every event name the `.track()` router understands.\n * Required fields are enforced at the call site; extra fields are always\n * permitted via the `Record<string, unknown>` intersection.\n */\nexport interface FlowGridEventMap {\n // ---- Activation ----\n signup_completed: { userId: string; method: string; source?: string } & Record<string, unknown>;\n onboarding_step: { userId: string; stepId: string; stepName: string; stepOrder: number; totalSteps: number } & Record<string, unknown>;\n onboarding_completed: { userId: string; totalTimeSpent: number; stepsCompleted: number; stepsSkipped?: number } & Record<string, unknown>;\n first_action: { userId: string; actionName: string; timeSinceSignup: number } & Record<string, unknown>;\n // ---- Feature ----\n feature_usage: { featureId: string; featureName: string; action?: string } & Record<string, unknown>;\n feature_viewed: { featureId: string; featureName: string } & Record<string, unknown>;\n feature_used: { featureId: string; featureName: string } & Record<string, unknown>;\n feature_completed: { featureId: string; featureName: string } & Record<string, unknown>;\n feature_abandoned: { featureId: string; featureName: string } & Record<string, unknown>;\n // ---- Prompt ----\n prompt_submitted: { promptId: string; promptType: string } & Record<string, unknown>;\n prompt_response: { promptId: string; responseTime: number; completed: boolean } & Record<string, unknown>;\n prompt_feedback: { promptId: string; feedbackType: string } & Record<string, unknown>;\n prompt_conversation: { conversationId: string; userId: string; turnCount: number; totalDuration: number } & Record<string, unknown>;\n // ---- Experiment ----\n experiment_assignment: { experimentId: string; userId: string; variantId: string } & Record<string, unknown>;\n experiment_exposure: { experimentId: string; userId: string; variantId: string } & Record<string, unknown>;\n experiment_conversion: { experimentId: string; userId: string; variantId: string; metricName: string } & Record<string, unknown>;\n // ---- Analytics ----\n click_event: { elementId: string; elementText: string } & Record<string, unknown>;\n form_event: { formId: string; formName: string } & Record<string, unknown>;\n search_event: { query: string; category: string } & Record<string, unknown>;\n error_event: { message: string; type: string } & Record<string, unknown>;\n funnel_step: { funnelId: string; step: string; stepNumber: number } & Record<string, unknown>;\n funnel_complete: { funnelId: string } & Record<string, unknown>;\n funnel_abandon: { funnelId: string; lastStep: string } & Record<string, unknown>;\n user_activity: { userId: string } & Record<string, unknown>;\n attribution_touchpoint: { userId: string; channel: string } & Record<string, unknown>;\n attribution_conversion: { userId: string; conversionType: string } & Record<string, unknown>;\n // ---- Ecommerce ----\n product_view: { productId: string; name: string; price: number; currency: string } & Record<string, unknown>;\n product_impression: { list: Record<string, unknown> } & Record<string, unknown>;\n product_click: { productId: string; name: string; price: number; currency: string; listId: string; position: number } & Record<string, unknown>;\n add_to_cart: { productId: string; name: string; price: number; currency: string; quantity: number } & Record<string, unknown>;\n remove_from_cart: { productId: string; name: string; price: number; currency: string } & Record<string, unknown>;\n update_cart_item: { product: Record<string, unknown>; previousQuantity: number; newQuantity: number } & Record<string, unknown>;\n view_cart: { cart: Record<string, unknown> } & Record<string, unknown>;\n begin_checkout: { cart: Record<string, unknown> } & Record<string, unknown>;\n checkout_step: { step: string; stepNumber: number; cart: Record<string, unknown> } & Record<string, unknown>;\n add_shipping_info: { cart: Record<string, unknown>; shipping: Record<string, unknown> } & Record<string, unknown>;\n add_payment_info: { cart: Record<string, unknown>; payment: Record<string, unknown> } & Record<string, unknown>;\n purchase: { order: Record<string, unknown> } & Record<string, unknown>;\n refund: { orderId: string; refundId: string; amount: number; currency: string; isFullRefund: boolean } & Record<string, unknown>;\n refund_requested: { orderId: string } & Record<string, unknown>;\n refund_approved: { refundId: string; approvedAmount: number } & Record<string, unknown>;\n refund_denied: { refundId: string; reason: string } & Record<string, unknown>;\n refund_completed: { refundId: string; processedAmount: number } & Record<string, unknown>;\n promotion_view: { promotionId: string; name: string; creative: string; position: number } & Record<string, unknown>;\n promotion_click: { promotionId: string; name: string; creative: string; position: number } & Record<string, unknown>;\n coupon_used: { code: string; orderId: string; discountValue: number; discountType: string; orderValueBefore: number; orderValueAfter: number } & Record<string, unknown>;\n add_to_wishlist: { productId: string; name: string; price: number; currency: string } & Record<string, unknown>;\n remove_from_wishlist: { productId: string; name: string; price: number; currency: string } & Record<string, unknown>;\n view_wishlist: { wishlist: Record<string, unknown> } & Record<string, unknown>;\n wishlist_to_cart: { productId: string; name: string; price: number; currency: string } & Record<string, unknown>;\n share_wishlist: { wishlistId: string; method: string } & Record<string, unknown>;\n subscription_start: { subscriptionId: string; userId: string; plan: string; mrr: Record<string, unknown>; billingCycle: string } & Record<string, unknown>;\n subscription_change: { subscriptionId: string; userId: string; previousPlan: string; newPlan: string; previousMrr: Record<string, unknown>; newMrr: Record<string, unknown>; changeType: string } & Record<string, unknown>;\n subscription_cancel: { subscriptionId: string; userId: string; plan: string; lostMrr: Record<string, unknown>; immediate: boolean } & Record<string, unknown>;\n subscription_renew: { subscriptionId: string; userId: string; mrr: Record<string, unknown> } & Record<string, unknown>;\n trial_converted: { subscriptionId: string; userId: string; plan: string; mrr: Record<string, unknown> } & Record<string, unknown>;\n trial_expired: { subscriptionId: string; userId: string } & Record<string, unknown>;\n record_acquisition_cost: { userId: string; cost: Record<string, unknown>; channel: string } & Record<string, unknown>;\n update_customer_segment: { userId: string; newSegment: string } & Record<string, unknown>;\n site_search: { query: string; resultsCount: number } & Record<string, unknown>;\n search_result_click: { query: string; position: number; productId: string; name: string; price: number; currency: string } & Record<string, unknown>;\n search_refinement: { originalQuery: string; refinedQuery: string; refinementType: string } & Record<string, unknown>;\n search_suggestion: { partialQuery: string; selectedSuggestion: string; suggestionPosition: number } & Record<string, unknown>;\n // ---- Enterprise: Engagement ----\n \"engagement.dwell_time\": { featureId: string; dwellTimeMs: number } & Record<string, unknown>;\n \"engagement_dwell_time\": { featureId: string; dwellTimeMs: number } & Record<string, unknown>;\n \"engagement.session_depth\": { sessionId: string; pagesVisited: number; featuresUsed: number; actionsTaken: number; durationMs: number } & Record<string, unknown>;\n \"engagement_session_depth\": { sessionId: string; pagesVisited: number; featuresUsed: number; actionsTaken: number; durationMs: number } & Record<string, unknown>;\n \"engagement.content_consumption\": { contentId: string; contentType: string; percentageConsumed: number; timeSpentMs: number } & Record<string, unknown>;\n \"engagement_content_consumption\": { contentId: string; contentType: string; percentageConsumed: number; timeSpentMs: number } & Record<string, unknown>;\n \"engagement.user_activity\": { userId: string; activityType: string; durationMs: number } & Record<string, unknown>;\n \"engagement_user_activity\": { userId: string; activityType: string; durationMs: number } & Record<string, unknown>;\n // ---- Enterprise: Churn ----\n \"churn.usage_decay\": { userId: string; metric: string; periods: unknown } & Record<string, unknown>;\n \"churn_usage_decay\": { userId: string; metric: string; periods: unknown } & Record<string, unknown>;\n \"churn.nps\": { userId: string; score: number } & Record<string, unknown>;\n \"churn_nps\": { userId: string; score: number } & Record<string, unknown>;\n \"churn.churn_event\": { userId: string; churnType: string } & Record<string, unknown>;\n \"churn_churn_event\": { userId: string; churnType: string } & Record<string, unknown>;\n \"churn.prevention_action\": { userId: string; actionType: string } & Record<string, unknown>;\n \"churn_prevention_action\": { userId: string; actionType: string } & Record<string, unknown>;\n // ---- Enterprise: Monetization ----\n \"monetization.revenue\": { userId: string; amount: number; currency: string; type: string } & Record<string, unknown>;\n \"monetization_revenue\": { userId: string; amount: number; currency: string; type: string } & Record<string, unknown>;\n // ---- Enterprise: Support ----\n \"support.ticket_created\": { ticketId: string; userId: string; subject: string; category: string; priority: string; channel: string } & Record<string, unknown>;\n \"support_ticket_created\": { ticketId: string; userId: string; subject: string; category: string; priority: string; channel: string } & Record<string, unknown>;\n \"support.ticket_updated\": { ticketId: string; status: string } & Record<string, unknown>;\n \"support_ticket_updated\": { ticketId: string; status: string } & Record<string, unknown>;\n \"support.csat\": { ticketId: string; userId: string; score: number } & Record<string, unknown>;\n \"support_csat\": { ticketId: string; userId: string; score: number } & Record<string, unknown>;\n \"support.feature_complaint\": { userId: string; featureId: string; complaintType: string; severity: string } & Record<string, unknown>;\n \"support_feature_complaint\": { userId: string; featureId: string; complaintType: string; severity: string } & Record<string, unknown>;\n // ---- Enterprise: Acquisition ----\n \"acquisition.acquired\": { userId: string; source: string } & Record<string, unknown>;\n \"acquisition_acquired\": { userId: string; source: string } & Record<string, unknown>;\n \"acquisition.lifecycle_transition\": { userId: string; fromStage: string; toStage: string } & Record<string, unknown>;\n \"acquisition_lifecycle_transition\": { userId: string; fromStage: string; toStage: string } & Record<string, unknown>;\n \"acquisition.campaign_spend\": { campaignId: string; amount: Record<string, unknown>; date: string } & Record<string, unknown>;\n \"acquisition_campaign_spend\": { campaignId: string; amount: Record<string, unknown>; date: string } & Record<string, unknown>;\n // ---- Enterprise: Paths ----\n \"paths.transition\": { userId: string; fromScreen: string; toScreen: string } & Record<string, unknown>;\n \"paths_transition\": { userId: string; fromScreen: string; toScreen: string } & Record<string, unknown>;\n \"paths.feature_sequence\": { userId: string; feature: string; order: number } & Record<string, unknown>;\n \"paths_feature_sequence\": { userId: string; feature: string; order: number } & Record<string, unknown>;\n // ---- Enterprise: Security ----\n \"security.login_attempt\": { userId: string; success: boolean } & Record<string, unknown>;\n \"security_login_attempt\": { userId: string; success: boolean } & Record<string, unknown>;\n \"security.role_change\": { userId: string; changedBy: string; previousRole: string; newRole: string } & Record<string, unknown>;\n \"security_role_change\": { userId: string; changedBy: string; previousRole: string; newRole: string } & Record<string, unknown>;\n \"security.permission_change\": { userId: string; changedBy: string; permission: string; action: string } & Record<string, unknown>;\n \"security_permission_change\": { userId: string; changedBy: string; permission: string; action: string } & Record<string, unknown>;\n \"security.data_access\": { userId: string; resourceType: string; accessType: string } & Record<string, unknown>;\n \"security_data_access\": { userId: string; resourceType: string; accessType: string } & Record<string, unknown>;\n \"security.compliance_event\": { userId: string; eventType: string } & Record<string, unknown>;\n \"security_compliance_event\": { userId: string; eventType: string } & Record<string, unknown>;\n // ---- Enterprise: Multi-path Funnels ----\n \"multi_path_funnels.track_step\": { funnelId: string; step: string; userId: string } & Record<string, unknown>;\n \"multi_path_funnels_track_step\": { funnelId: string; step: string; userId: string } & Record<string, unknown>;\n \"multi_path_funnels.track_micro_conversion\": { conversionId: string; userId: string } & Record<string, unknown>;\n \"multi_path_funnels_track_micro_conversion\": { conversionId: string; userId: string } & Record<string, unknown>;\n}\n\n/** Union of every event name the `.track()` router recognises. */\nexport type FlowGridEventName = keyof FlowGridEventMap;\n\ntype FlowGridKnownTrackFields = Partial<{\n userId: string;\n sessionId: string;\n method: string;\n source: string;\n featureId: string;\n featureName: string;\n action: string;\n promptId: string;\n promptType: string;\n responseTime: number;\n completed: boolean;\n feedbackType: string;\n conversationId: string;\n turnCount: number;\n totalDuration: number;\n experimentId: string;\n variantId: string;\n variant: string;\n metricName: string;\n value: number;\n elementId: string;\n elementText: string;\n formId: string;\n formName: string;\n query: string;\n category: string;\n message: string;\n type: string;\n funnelId: string;\n step: string;\n stepNumber: number;\n lastStep: string;\n channel: string;\n conversionType: string;\n list: Record<string, unknown>;\n product: Record<string, unknown>;\n productId: string;\n name: string;\n price: number;\n currency: string;\n position: number;\n quantity: number;\n previousQuantity: number;\n newQuantity: number;\n cart: Record<string, unknown>;\n shipping: Record<string, unknown>;\n payment: Record<string, unknown>;\n order: Record<string, unknown>;\n orderId: string;\n refundId: string;\n amount: unknown;\n approvedAmount: number;\n processedAmount: number;\n reason: string;\n promotionId: string;\n creative: string;\n code: string;\n discountValue: number;\n discountType: string;\n orderValueBefore: number;\n orderValueAfter: number;\n wishlist: Record<string, unknown>;\n wishlistId: string;\n subscriptionId: string;\n plan: string;\n mrr: Record<string, unknown>;\n billingCycle: string;\n previousPlan: string;\n newPlan: string;\n previousMrr: Record<string, unknown>;\n newMrr: Record<string, unknown>;\n changeType: string;\n lostMrr: Record<string, unknown>;\n cost: Record<string, unknown>;\n newSegment: string;\n resultsCount: number;\n originalQuery: string;\n refinedQuery: string;\n refinementType: string;\n partialQuery: string;\n selectedSuggestion: string;\n suggestionPosition: number;\n dwellTimeMs: number;\n pagesVisited: number;\n featuresUsed: number;\n actionsTaken: number;\n durationMs: number;\n contentId: string;\n contentType: string;\n percentageConsumed: number;\n timeSpentMs: number;\n metric: string;\n periods: unknown;\n score: number;\n churnType: string;\n actionType: string;\n activityType: string;\n ticketId: string;\n subject: string;\n priority: string;\n status: string;\n complaintType: string;\n severity: string;\n fromStage: string;\n toStage: string;\n campaignId: string;\n date: string;\n fromScreen: string;\n toScreen: string;\n feature: string;\n success: boolean;\n isFullRefund: boolean;\n immediate: boolean;\n changedBy: string;\n previousRole: string;\n newRole: string;\n permission: string;\n resourceType: string;\n accessType: string;\n eventType: string;\n conversionId: string;\n}>;\n\nexport type FlowGridTrackProperties = Record<string, unknown> & FlowGridKnownTrackFields;\n\ntype TrackDispatchResult = { handled: true; value: unknown } | { handled: false };\ntype UnifiedFeatureAction = \"viewed\" | \"used\" | \"completed\" | \"abandoned\";\n\n/** The lifecycle action a {@link FeatureHandle} emits. */\nexport type FeatureHandleAction = UnifiedFeatureAction;\n\n/**\n * A feature bound to its id/name (+ optional category) via\n * {@link FlowGrid.defineFeature}. Call the verb that describes what happened;\n * each emits a `feature_usage` event through the unified `.track()` router.\n */\nexport interface FeatureHandle {\n readonly featureId: string;\n readonly featureName: string;\n /** Visitor opened / looked at the feature → Discovered + Viewed. */\n viewed(properties?: Record<string, unknown>): unknown;\n /** Visitor used the feature → Adopted. */\n used(properties?: Record<string, unknown>): unknown;\n /** Visitor finished the feature's job → Completed. */\n completed(properties?: Record<string, unknown>): unknown;\n /** Visitor started but gave up → Abandonment. */\n abandoned(properties?: Record<string, unknown>): unknown;\n /** Escape hatch — emit any action explicitly. */\n track(action: FeatureHandleAction, properties?: Record<string, unknown>): unknown;\n}\n\nconst UNHANDLED_TRACK: TrackDispatchResult = { handled: false };\nconst PRODUCT_KEYS = [\n \"productId\",\n \"name\",\n \"brand\",\n \"category\",\n \"variant\",\n \"price\",\n \"currency\",\n \"quantity\",\n \"discount\",\n \"coupon\",\n \"position\",\n \"url\",\n \"imageUrl\",\n \"rating\",\n \"reviewCount\",\n \"stockStatus\",\n \"attributes\",\n] as const;\n\nfunction handled(value: unknown): TrackDispatchResult {\n return { handled: true, value };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction stringValue(props: FlowGridTrackProperties, key: string): string | undefined {\n const value = props[key];\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction numberValue(props: FlowGridTrackProperties, key: string): number | undefined {\n const value = props[key];\n return typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n}\n\nfunction booleanValue(props: FlowGridTrackProperties, key: string): boolean | undefined {\n const value = props[key];\n return typeof value === \"boolean\" ? value : undefined;\n}\n\nfunction recordValue(props: FlowGridTrackProperties, key: string): FlowGridTrackProperties | undefined {\n const value = props[key];\n return isRecord(value) ? value as FlowGridTrackProperties : undefined;\n}\n\nfunction hasValue(props: FlowGridTrackProperties, key: string): boolean {\n const value = props[key];\n return value !== undefined && value !== null && !(typeof value === \"string\" && value.length === 0);\n}\n\nfunction omitProperties(props: FlowGridTrackProperties, keys: readonly string[]): FlowGridTrackProperties {\n const next: FlowGridTrackProperties = {};\n const blocked = new Set([...keys, \"domain\"]);\n for (const [key, value] of Object.entries(props)) {\n if (!blocked.has(key)) next[key] = value;\n }\n return next;\n}\n\nfunction productFrom(props: FlowGridTrackProperties): FlowGridTrackProperties | undefined {\n const nested = recordValue(props, \"product\");\n if (nested) return { ...nested, currency: nested.currency ?? \"USD\" };\n\n if (!hasValue(props, \"productId\") || !hasValue(props, \"name\") || !hasValue(props, \"price\")) {\n return undefined;\n }\n\n const product: FlowGridTrackProperties = { currency: \"USD\" };\n for (const key of PRODUCT_KEYS) {\n if (props[key] !== undefined) (product as Record<string, unknown>)[key] = props[key];\n }\n return product;\n}\n\n// ============================================================================\n// PUBLIC INIT TYPES\n// ============================================================================\n\n/** Granular auto-instrumentation toggles. */\nexport interface AutoTrackOptions {\n sessions?: boolean;\n pageViews?: boolean;\n performance?: boolean | { resources?: boolean; minDuration?: number };\n heatmaps?: boolean | { movement?: boolean };\n attribution?: boolean;\n replay?: boolean | import(\"./features/analytics/replay\").SessionReplayOptions;\n /** Auto-track session depth + user activity transitions. Default on. */\n engagement?: boolean | { idleAfterMs?: number; pollMs?: number };\n}\n\n/** Top-level config for `FlowGrid.init()`. */\nexport interface FlowGridInitConfig {\n\n /** Public web/site identifier (e.g. `\"wx_abc123\"`). Required. */\n webId: string;\n\n /**\n * Public API key. Optional — when omitted, the SDK runs in `data-site`-only\n * mode (web_id body-auth), matching the core `flowgrid.js` snippet. This is\n * what lets a no-build / script-tag install run experiments. Provide a key\n * for full server-authenticated ingestion.\n */\n apiKey?: string;\n\n /**\n * API base URL. Defaults to `\"https://core.flow-grid.xyz\"` — most users never\n * need to set this.\n */\n endpoint?: string;\n\n /**\n * Identify the user up-front so every subsequent event is attributed.\n * Equivalent to calling `fg.identifyUser(userId, traits)` after init.\n */\n user?: { userId: string; traits?: Record<string, unknown> };\n\n /**\n * Browser auto-instrumentation. `true` (default) turns on engagement and\n * session replay. Pageviews, sessions, heatmaps, performance, and attribution\n * are handled by the flowgrid.js core script and are off by default here.\n * Pass an object to control individual features, or `false` to opt out completely.\n */\n autoTrack?: boolean | AutoTrackOptions;\n\n /**\n * Session replay (rrweb). Off by default — opt in by passing `true` or an\n * options object. Requires the `rrweb` package (already a dependency).\n */\n replay?: boolean | import(\"./features/analytics/replay\").SessionReplayOptions;\n\n /**\n * Initial consent state. Equivalent to calling\n * `FlowGrid.setConsent({ analytics, marketing })` before any tracking.\n */\n consent?: { analytics?: boolean; marketing?: boolean };\n\n /** Low-level transport tunables (sampling, DNT, bot detection). */\n transport?: FlowGridTransportConfig;\n\n /** Optional pre-resolved visitor ID (server-side hydration). */\n visitorId?: string;\n\n /** Optional identity manager (advanced — visitor/session lifecycle). */\n identityManager?: IdentityManager;\n \n}\n\nconst DEFAULT_ENDPOINT = \"https://core.flow-grid.xyz\";\n\n/**\n * Unified FlowGrid SDK client. Instantiate once, use everywhere.\n *\n * Each feature is exposed as a lazily-initialised shortcut wrapper, so the\n * SDK has zero overhead for unused features.\n */\nexport class FlowGrid {\n /** Singleton created by {@link FlowGrid.init}. */\n private static _instance: FlowGrid | null = null;\n /** Teardown for the auto-instrumentation started during `init()`. */\n private _autoTrackStop: (() => void) | null = null;\n\n private readonly webId: string;\n private readonly endpoint: string;\n private readonly apiKey: string;\n private readonly visitorId?: string;\n private readonly identityManager?: IdentityManager;\n private readonly config?: FlowGridTransportConfig;\n\n private readonly _raw = new Map<string, unknown>();\n private readonly _wrapped = new Map<string, unknown>();\n\n /**\n * One-line init. Idempotent — calling twice returns the same instance and\n * logs a warning instead of starting auto-instrumentation again.\n *\n * @example\n * ```ts\n * // Painless: one line, sane defaults, everything on.\n * const fg = FlowGrid.init({ webId: \"wx_xxx\", apiKey: \"pk_xxx\" });\n *\n * // Or be explicit:\n * const fg = FlowGrid.init({\n * webId: \"wx_xxx\",\n * apiKey: \"pk_xxx\",\n * user: { userId: \"u_1\", traits: { plan: \"pro\" } },\n * replay: { sampleRate: 0.25, maskAllInputs: true },\n * consent: { analytics: true },\n * });\n * ```\n */\n static init(config: FlowGridInitConfig): FlowGrid {\n if (FlowGrid._instance) {\n if (typeof console !== \"undefined\") {\n console.warn(\"[FlowGrid] init() called more than once — returning existing instance.\");\n }\n return FlowGrid._instance;\n }\n\n if (!config?.webId) {\n throw new Error(\"FlowGrid.init: `webId` is required.\");\n }\n\n if (config.consent) {\n FlowGrid.setConsent(config.consent);\n }\n\n const fg = new FlowGrid(\n config.webId,\n config.endpoint ?? DEFAULT_ENDPOINT,\n config.apiKey ?? \"\",\n config.visitorId,\n config.identityManager,\n config.transport,\n );\n FlowGrid._instance = fg;\n\n // Pre-fetch the enterprise chunk immediately so getters are ready by the\n // time user code first accesses them (typically well after init returns).\n void loadEnterpriseModules();\n\n // Identify the user before auto-instrumentation so the very first events\n // (session_start, page_view) carry the userId.\n if (config.user?.userId) {\n void fg.identifyUser(config.user.userId, config.user.traits);\n }\n\n const autoTrack = config.autoTrack;\n const replay = config.replay;\n const autoOpts: AutoTrackOptions =\n autoTrack === false ? { sessions: false, pageViews: false, performance: false, heatmaps: false, attribution: false, engagement: false, replay: false }\n : autoTrack === true || autoTrack === undefined ? { sessions: false, pageViews: false, performance: false, heatmaps: false, attribution: false }\n : autoTrack;\n\n // Top-level `replay` always wins over autoTrack defaults. Previously this\n // only applied when autoOpts.replay was undefined, which meant\n // `init({ autoTrack: false, replay: true })` silently disabled replay.\n if (replay !== undefined) {\n autoOpts.replay = replay;\n }\n\n fg._autoTrackStop = fg.autoTrack({\n ...autoOpts,\n userId: config.user?.userId ?? (autoOpts as { userId?: string }).userId,\n });\n\n return fg;\n }\n\n /** Returns the singleton created by {@link FlowGrid.init}, if any. */\n static instance(): FlowGrid | null {\n return FlowGrid._instance;\n }\n\n /**\n * Await the enterprise feature chunk. Call this before accessing any\n * enterprise namespace (`fg.engagement`, `fg.churn`, etc.) if you need\n * guaranteed availability rather than relying on the background pre-fetch\n * started by `init()`.\n */\n static loadEnterprise(): Promise<void> {\n return loadEnterpriseModules();\n }\n\n /**\n * Reset the singleton — primarily for tests / hot-reload. Tears down any\n * active auto-instrumentation.\n */\n static reset(): void {\n if (FlowGrid._instance?._autoTrackStop) {\n try { FlowGrid._instance._autoTrackStop(); } catch { /* noop */ }\n }\n FlowGrid._instance = null;\n }\n\n /**\n * Explicit constructor for advanced cases (multi-tenant SSR, tests). Most\n * apps should use the static {@link FlowGrid.init} instead.\n */\n constructor(\n webId: string,\n endpoint: string,\n apiKey: string,\n visitorId?: string,\n identityManager?: IdentityManager,\n config?: FlowGridTransportConfig\n ) {\n this.webId = webId;\n this.endpoint = endpoint;\n this.apiKey = apiKey;\n this.visitorId = visitorId;\n this.identityManager = identityManager ?? new IdentityManager(visitorId ? { visitorId } : undefined);\n this.config = config;\n }\n\n private raw<T>(key: string, Klass: Ctor<T>): T {\n let instance = this._raw.get(key) as T | undefined;\n if (!instance) {\n instance = new Klass(\n this.webId,\n this.endpoint,\n this.apiKey,\n this.visitorId,\n this.identityManager,\n this.config\n );\n this._raw.set(key, instance);\n }\n return instance;\n }\n\n private wrap<W, T>(key: string, Klass: Ctor<T>, Wrapper: new (client: T) => W): W {\n let instance = this._wrapped.get(key) as W | undefined;\n if (!instance) {\n instance = new Wrapper(this.raw(key, Klass));\n this._wrapped.set(key, instance);\n }\n return instance;\n }\n\n // ---- Core ----\n get activation(): ActivationTracker { return this.wrap(\"activation\", Activation, ActivationTracker); }\n get features(): FeatureTracker { return this.wrap(\"features\", TrackFeature, FeatureTracker); }\n get prompts(): PromptTracker { return this.wrap(\"prompts\", TrackPrompt, PromptTracker); }\n get experiments(): ExperimentTracker { return this.wrap(\"experiments\", Experiment, ExperimentTracker); }\n\n // ---- Analytics ----\n get pageViews(): PageViewTracker { return this.wrap(\"pageViews\", PageViews, PageViewTracker); }\n get sessions(): SessionTracker { return this.wrap(\"sessions\", Sessions, SessionTracker); }\n get events(): EventTracker { return this.wrap(\"events\", Events, EventTracker); }\n get identify(): IdentifyTracker { return this.wrap(\"identify\", Identify, IdentifyTracker); }\n get funnels(): FunnelTracker { return this.wrap(\"funnels\", Funnels, FunnelTracker); }\n get retention(): RetentionTracker { return this.wrap(\"retention\", Retention, RetentionTracker); }\n get attribution(): AttributionTracker { return this.wrap(\"attribution\", Attribution, AttributionTracker); }\n get heatmaps(): HeatmapTracker { return this.wrap(\"heatmaps\", Heatmaps, HeatmapTracker); }\n get performance(): PerformanceTracker { return this.wrap(\"performance\", PerformanceTracking, PerformanceTracker); }\n get replay(): ReplayTracker { return this.wrap(\"replay\", SessionReplay, ReplayTracker); }\n\n // ---- Ecommerce ----\n get products(): ProductTracker { return this.wrap(\"products\", Products, ProductTracker); }\n get cart(): CartTracker { return this.wrap(\"cart\", CartTracking, CartTracker); }\n get checkout(): CheckoutTracker { return this.wrap(\"checkout\", Checkout, CheckoutTracker); }\n get purchases(): PurchaseTracker { return this.wrap(\"purchases\", Purchases, PurchaseTracker); }\n get refunds(): RefundTracker { return this.wrap(\"refunds\", Refunds, RefundTracker); }\n get promotions(): PromotionTracker { return this.wrap(\"promotions\", Promotions, PromotionTracker); }\n get wishlist(): WishlistTracker { return this.wrap(\"wishlist\", WishlistTracking, WishlistTracker); }\n get ltv(): LTVTracker { return this.wrap(\"ltv\", CustomerLTV, LTVTracker); }\n get search(): SearchTracker { return this.wrap(\"search\", SearchAnalytics, SearchTracker); }\n get subscriptions(): SubscriptionTracker { return this.wrap(\"subscriptions\", Subscriptions, SubscriptionTracker); }\n\n // ---- Enterprise ----\n // These getters pull classes from the lazily-loaded enterprise chunk.\n // The chunk is pre-fetched by init() so it's almost always ready by the\n // time user code accesses these properties. loadEnterprise() is available\n // for the rare case where callers need to await readiness explicitly.\n private _requireEnterprise() {\n const ent = _enterpriseFeatures;\n const entT = _enterpriseTracking;\n if (!ent || !entT) {\n throw new Error(\n \"[FlowGrid] Enterprise features are still loading. \" +\n \"await FlowGrid.loadEnterprise() before accessing enterprise namespaces directly.\"\n );\n }\n return { ent, entT };\n }\n\n get engagement(): EngagementTracker { const { ent, entT } = this._requireEnterprise(); return this.wrap(\"engagement\", ent.Engagement, entT.EngagementTracker); }\n get cohorts(): CohortTracker { const { ent, entT } = this._requireEnterprise(); return this.wrap(\"cohorts\", ent.Cohorts, entT.CohortTracker); }\n get churn(): ChurnTracker { const { ent, entT } = this._requireEnterprise(); return this.wrap(\"churn\", ent.ChurnAnalytics, entT.ChurnTracker); }\n get monetization(): MonetizationTracker { const { ent, entT } = this._requireEnterprise(); return this.wrap(\"monetization\", ent.Monetization, entT.MonetizationTracker); }\n get multiPathFunnels(): MultiPathFunnelTracker { const { ent, entT } = this._requireEnterprise(); return this.wrap(\"multiPathFunnels\", ent.MultiPathFunnels, entT.MultiPathFunnelTracker); }\n get support(): SupportTracker { const { ent, entT } = this._requireEnterprise(); return this.wrap(\"support\", ent.SupportAnalytics, entT.SupportTracker); }\n get acquisition(): AcquisitionTracker { const { ent, entT } = this._requireEnterprise(); return this.wrap(\"acquisition\", ent.AcquisitionAnalytics, entT.AcquisitionTracker); }\n get paths(): PathTracker { const { ent, entT } = this._requireEnterprise(); return this.wrap(\"paths\", ent.PathAnalytics, entT.PathTracker); }\n get alerts(): AlertTracker { const { ent, entT } = this._requireEnterprise(); return this.wrap(\"alerts\", ent.AlertsAnalytics, entT.AlertTracker); }\n get security(): SecurityTracker { const { ent, entT } = this._requireEnterprise(); return this.wrap(\"security\", ent.SecurityAnalytics, entT.SecurityTracker); }\n get forecasting(): ForecastingTracker { const { ent, entT } = this._requireEnterprise(); return this.wrap(\"forecasting\", ent.Forecasting, entT.ForecastingTracker); }\n\n // ---- Consent passthrough (static on transport) ----\n static setConsent = FlowGridTransport.setConsent.bind(FlowGridTransport);\n static hasConsent = FlowGridTransport.hasConsent.bind(FlowGridTransport);\n\n // ==========================================================================\n // TOP-LEVEL SHORTCUTS — the 80% case\n // ==========================================================================\n\n /**\n * Track a semantic event. The `eventName` must be one of the recognised\n * {@link FlowGridEventName} literals — TypeScript will error on unknown\n * names and narrow `properties` to the required shape for that event.\n *\n * @example\n * ```ts\n * fg.track(\"signup_completed\", { userId: \"u_1\", method: \"google\" });\n * fg.track(\"add_to_cart\", { productId: \"sku_1\", name: \"Tee\", price: 29, currency: \"USD\", quantity: 1 });\n * ```\n */\n track<E extends FlowGridEventName>(\n eventName: E,\n properties: FlowGridEventMap[E],\n options?: FlowGridTrackOptions\n ): unknown;\n track(\n eventName: string,\n properties: FlowGridTrackProperties = {},\n options: FlowGridTrackOptions = {}\n ) {\n const contextualProperties = this.withTrackContext(properties, options);\n const routed = this.dispatchTrack(eventName, contextualProperties, Boolean(options.domain), options.domain);\n if (routed.handled) return routed.value;\n return this.events.track(eventName, properties, options);\n }\n\n private withTrackContext(\n properties: FlowGridTrackProperties,\n options: FlowGridTrackOptions\n ): FlowGridTrackProperties {\n return {\n ...properties,\n ...(options.userId !== undefined && properties.userId === undefined ? { userId: options.userId } : {}),\n ...(options.sessionId !== undefined && properties.sessionId === undefined ? { sessionId: options.sessionId } : {}),\n };\n }\n\n private requireTrackProperties(\n eventName: string,\n properties: FlowGridTrackProperties,\n keys: readonly string[],\n strict: boolean\n ): boolean {\n const missing = keys.filter(key => !hasValue(properties, key));\n if (missing.length === 0) return true;\n if (strict) {\n throw new Error(`FlowGrid.track(\"${eventName}\") missing required properties: ${missing.join(\", \")}`);\n }\n return false;\n }\n\n private dispatchTrack(\n eventName: string,\n properties: FlowGridTrackProperties,\n strict: boolean,\n domain?: FlowGridTrackDomain\n ): TrackDispatchResult {\n const dispatchers: Array<() => TrackDispatchResult> = [];\n\n if (!domain || domain === \"activation\") dispatchers.push(() => this.dispatchActivationTrack(eventName, properties, strict));\n\n if (!domain || domain === \"feature\") dispatchers.push(() => this.dispatchFeatureTrack(eventName, properties, strict));\n\n if (!domain || domain === \"prompt\") dispatchers.push(() => this.dispatchPromptTrack(eventName, properties, strict));\n\n if (!domain || domain === \"experiment\") dispatchers.push(() => this.dispatchExperimentTrack(eventName, properties, strict));\n\n if (!domain || domain === \"analytics\" || domain === \"funnel\" || domain === \"retention\" || domain === \"attribution\") {\n dispatchers.push(() => this.dispatchAnalyticsTrack(eventName, properties, strict, domain));\n }\n\n if (!domain || domain === \"ecommerce\") dispatchers.push(() => this.dispatchEcommerceTrack(eventName, properties, strict));\n\n if (\n !domain ||\n domain === \"engagement\" ||\n domain === \"churn\" ||\n domain === \"monetization\" ||\n domain === \"support\" ||\n domain === \"acquisition\" ||\n domain === \"paths\" ||\n domain === \"security\" ||\n domain === \"multiPathFunnels\"\n ) {\n dispatchers.push(() => this.dispatchEnterpriseTrack(eventName, properties, strict, domain));\n }\n\n for (const dispatch of dispatchers) {\n const result = dispatch();\n if (result.handled) return result;\n }\n\n return UNHANDLED_TRACK;\n }\n\n private dispatchActivationTrack(\n eventName: string,\n properties: FlowGridTrackProperties,\n strict: boolean\n ): TrackDispatchResult {\n switch (eventName) {\n case \"signup_completed\": {\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"method\"], strict)) return UNHANDLED_TRACK;\n return handled(this.activation.signup(\n stringValue(properties, \"userId\")!,\n stringValue(properties, \"method\")!,\n stringValue(properties, \"source\"),\n omitProperties(properties, [\"userId\", \"method\", \"source\"])\n ));\n }\n case \"onboarding_step\": {\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"stepId\", \"stepName\", \"stepOrder\", \"totalSteps\"], strict)) return UNHANDLED_TRACK;\n return handled(this.activation.onboardingStep(\n stringValue(properties, \"userId\")!,\n stringValue(properties, \"stepId\")!,\n stringValue(properties, \"stepName\")!,\n numberValue(properties, \"stepOrder\")!,\n numberValue(properties, \"totalSteps\")!,\n {\n skipped: booleanValue(properties, \"skipped\"),\n timeSpent: numberValue(properties, \"timeSpent\"),\n properties: omitProperties(properties, [\"userId\", \"stepId\", \"stepName\", \"stepOrder\", \"totalSteps\", \"skipped\", \"timeSpent\"]),\n }\n ));\n }\n case \"onboarding_completed\": {\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"totalTimeSpent\", \"stepsCompleted\"], strict)) return UNHANDLED_TRACK;\n return handled(this.activation.onboardingComplete(\n stringValue(properties, \"userId\")!,\n numberValue(properties, \"totalTimeSpent\")!,\n numberValue(properties, \"stepsCompleted\")!,\n numberValue(properties, \"stepsSkipped\") ?? 0\n ));\n }\n case \"first_action\": {\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"actionName\", \"timeSinceSignup\"], strict)) return UNHANDLED_TRACK;\n return handled(this.activation.firstAction(\n stringValue(properties, \"userId\")!,\n stringValue(properties, \"actionName\")!,\n numberValue(properties, \"timeSinceSignup\")!,\n omitProperties(properties, [\"userId\", \"actionName\", \"timeSinceSignup\"])\n ));\n }\n default:\n if (strict) return handled(this.activation.event(eventName, properties));\n return UNHANDLED_TRACK;\n }\n }\n\n private dispatchFeatureTrack(\n eventName: string,\n properties: FlowGridTrackProperties,\n strict: boolean\n ): TrackDispatchResult {\n\n const actions: Record<string, UnifiedFeatureAction> = {\n feature_viewed: \"viewed\",\n feature_used: \"used\",\n feature_completed: \"completed\",\n feature_abandoned: \"abandoned\",\n };\n\n const action = actions[eventName] ?? (eventName === \"feature_usage\" \n ? properties.action : undefined);\n\n if (eventName === \"feature_usage\" || action) {\n if (!this.requireTrackProperties(eventName, properties, [\"featureId\", \"featureName\"], strict)) return UNHANDLED_TRACK;\n return handled(this.features.client.trackUsage({\n ...properties,\n action: typeof action === \"string\" ? action : \"used\",\n // Emit the lifecycle-specific event name on the wire (feature_viewed,\n // feature_completed, …). For the generic `feature_usage` entry point\n // this is just \"feature_usage\", preserving the prior behaviour.\n eventName,\n } as Parameters<TrackFeature[\"trackUsage\"]>[0]));\n }\n\n if (strict) {\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"featureName\"], true)) return UNHANDLED_TRACK;\n return handled(this.features.create(\n stringValue(properties, \"userId\")!,\n eventName,\n stringValue(properties, \"featureName\")!,\n omitProperties(properties, [\"userId\", \"featureName\"])\n ));\n }\n\n return UNHANDLED_TRACK;\n }\n\n private dispatchPromptTrack(\n eventName: string,\n properties: FlowGridTrackProperties,\n strict: boolean\n ): TrackDispatchResult {\n switch (eventName) {\n case \"prompt_submitted\":\n if (!this.requireTrackProperties(eventName, properties, [\"promptId\", \"promptType\"], strict)) return UNHANDLED_TRACK;\n return handled(this.prompts.client.trackSubmission(properties as Parameters<TrackPrompt[\"trackSubmission\"]>[0]));\n case \"prompt_response\":\n if (!this.requireTrackProperties(eventName, properties, [\"promptId\", \"responseTime\", \"completed\"], strict)) return UNHANDLED_TRACK;\n return handled(this.prompts.client.trackResponse(properties as Parameters<TrackPrompt[\"trackResponse\"]>[0]));\n case \"prompt_feedback\":\n if (!this.requireTrackProperties(eventName, properties, [\"promptId\", \"feedbackType\"], strict)) return UNHANDLED_TRACK;\n return handled(this.prompts.client.trackFeedback(properties as Parameters<TrackPrompt[\"trackFeedback\"]>[0]));\n case \"prompt_conversation\":\n if (!this.requireTrackProperties(eventName, properties, [\"conversationId\", \"userId\", \"turnCount\", \"totalDuration\"], strict)) return UNHANDLED_TRACK;\n return handled(this.prompts.conversation(\n stringValue(properties, \"conversationId\")!,\n stringValue(properties, \"userId\")!,\n numberValue(properties, \"turnCount\")!,\n numberValue(properties, \"totalDuration\")!\n ));\n default:\n if (strict || eventName.startsWith(\"prompt_\")) return handled(this.prompts.event(eventName, properties));\n return UNHANDLED_TRACK;\n }\n }\n\n private dispatchExperimentTrack(\n eventName: string,\n properties: FlowGridTrackProperties,\n strict: boolean\n ): TrackDispatchResult {\n switch (eventName) {\n case \"experiment_assignment\":\n if (!this.requireTrackProperties(eventName, properties, [\"experimentId\", \"userId\", \"variantId\"], strict)) return UNHANDLED_TRACK;\n return handled(this.experiments.client.assign(properties as Parameters<Experiment[\"assign\"]>[0]));\n case \"experiment_exposure\":\n if (!this.requireTrackProperties(eventName, properties, [\"experimentId\", \"userId\", \"variantId\"], strict)) return UNHANDLED_TRACK;\n return handled(this.experiments.exposure(\n stringValue(properties, \"experimentId\")!,\n stringValue(properties, \"userId\")!,\n stringValue(properties, \"variantId\")!\n ));\n case \"experiment_conversion\":\n if (!this.requireTrackProperties(eventName, properties, [\"experimentId\", \"userId\", \"variantId\", \"metricName\"], strict)) return UNHANDLED_TRACK;\n return handled(this.experiments.client.trackConversion({\n ...properties,\n value: numberValue(properties, \"value\") ?? 1,\n } as Parameters<Experiment[\"trackConversion\"]>[0]));\n default:\n if (strict || eventName.startsWith(\"experiment_\")) {\n if (!this.requireTrackProperties(eventName, properties, [\"variant\"], strict)) return UNHANDLED_TRACK;\n return handled(this.experiments.client.track({\n eventName,\n variant: stringValue(properties, \"variant\")!,\n properties: omitProperties(properties, [\"variant\"]),\n }));\n }\n return UNHANDLED_TRACK;\n }\n }\n\n private dispatchAnalyticsTrack(\n eventName: string,\n properties: FlowGridTrackProperties,\n strict: boolean,\n domain?: FlowGridTrackDomain\n ): TrackDispatchResult {\n switch (eventName) {\n case \"click_event\":\n if (!this.requireTrackProperties(eventName, properties, [\"elementId\", \"elementText\"], strict)) return UNHANDLED_TRACK;\n return handled(this.events.client.trackClick(properties as Parameters<Events[\"trackClick\"]>[0]));\n case \"form_event\":\n if (!this.requireTrackProperties(eventName, properties, [\"formId\", \"formName\"], strict)) return UNHANDLED_TRACK;\n return handled(this.events.client.trackForm(properties as Parameters<Events[\"trackForm\"]>[0]));\n case \"search_event\":\n if (!this.requireTrackProperties(eventName, properties, [\"query\", \"category\"], strict)) return UNHANDLED_TRACK;\n return handled(this.events.client.trackSearch(properties as Parameters<Events[\"trackSearch\"]>[0]));\n case \"error_event\":\n if (!this.requireTrackProperties(eventName, properties, [\"message\", \"type\"], strict)) return UNHANDLED_TRACK;\n return handled(this.events.client.trackError(properties as Parameters<Events[\"trackError\"]>[0]));\n case \"funnel_step\":\n if (!this.requireTrackProperties(eventName, properties, [\"funnelId\", \"step\", \"stepNumber\"], strict)) return UNHANDLED_TRACK;\n return handled(this.funnels.client.trackStep(properties as Parameters<Funnels[\"trackStep\"]>[0]));\n case \"funnel_complete\":\n if (!this.requireTrackProperties(eventName, properties, [\"funnelId\"], strict)) return UNHANDLED_TRACK;\n return handled(this.funnels.complete(stringValue(properties, \"funnelId\")!, stringValue(properties, \"userId\"), omitProperties(properties, [\"funnelId\", \"userId\"])));\n case \"funnel_abandon\":\n if (!this.requireTrackProperties(eventName, properties, [\"funnelId\", \"lastStep\"], strict)) return UNHANDLED_TRACK;\n return handled(this.funnels.abandon(stringValue(properties, \"funnelId\")!, stringValue(properties, \"lastStep\")!, stringValue(properties, \"userId\"), stringValue(properties, \"reason\")));\n case \"user_activity\":\n if (domain && domain !== \"retention\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\"], strict)) return UNHANDLED_TRACK;\n return handled(this.retention.client.trackActivity(properties as Parameters<Retention[\"trackActivity\"]>[0]));\n case \"attribution_touchpoint\":\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"channel\"], strict)) return UNHANDLED_TRACK;\n return handled(this.attribution.client.trackTouchpoint(properties as Parameters<Attribution[\"trackTouchpoint\"]>[0]));\n case \"attribution_conversion\":\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"conversionType\"], strict)) return UNHANDLED_TRACK;\n return handled(this.attribution.client.recordConversion(properties as Parameters<Attribution[\"recordConversion\"]>[0]));\n default:\n return UNHANDLED_TRACK;\n }\n }\n\n private dispatchEcommerceTrack(\n eventName: string,\n properties: FlowGridTrackProperties,\n strict: boolean\n ): TrackDispatchResult {\n switch (eventName) {\n case \"product_view\": {\n const product = productFrom(properties);\n if (!product) {\n if (strict) throw new Error(`FlowGrid.track(\"${eventName}\") missing required properties: product or productId, name, price`);\n return UNHANDLED_TRACK;\n }\n return handled(this.products.client.view({\n ...product,\n ...omitProperties(properties, [\"product\", ...PRODUCT_KEYS]),\n } as Parameters<Products[\"view\"]>[0]));\n }\n case \"product_impression\":\n if (!this.requireTrackProperties(eventName, properties, [\"list\"], strict)) return UNHANDLED_TRACK;\n return handled(this.products.client.impression(properties as unknown as Parameters<Products[\"impression\"]>[0]));\n case \"product_click\": {\n const product = productFrom(properties);\n if (!product || !this.requireTrackProperties(eventName, properties, [\"listId\", \"position\"], strict)) return UNHANDLED_TRACK;\n return handled(this.products.client.click({\n ...properties,\n product,\n } as Parameters<Products[\"click\"]>[0]));\n }\n case \"add_to_cart\":\n case \"remove_from_cart\": {\n const product = productFrom(properties);\n if (!product) {\n if (strict) throw new Error(`FlowGrid.track(\"${eventName}\") missing required properties: product or productId, name, price`);\n return UNHANDLED_TRACK;\n }\n const config = {\n ...omitProperties(properties, [\"product\", ...PRODUCT_KEYS]),\n product,\n quantity: numberValue(properties, \"quantity\") ?? 1,\n };\n return handled(eventName === \"add_to_cart\"\n ? this.cart.client.addItem(config as Parameters<CartTracking[\"addItem\"]>[0])\n : this.cart.client.removeItem(config as Parameters<CartTracking[\"removeItem\"]>[0]));\n }\n case \"update_cart_item\": {\n const product = productFrom(properties);\n if (!product || !this.requireTrackProperties(eventName, properties, [\"previousQuantity\", \"newQuantity\"], strict)) return UNHANDLED_TRACK;\n return handled(this.cart.client.updateItem({\n ...omitProperties(properties, [\"product\", ...PRODUCT_KEYS]),\n product,\n } as Parameters<CartTracking[\"updateItem\"]>[0]));\n }\n case \"view_cart\":\n if (!this.requireTrackProperties(eventName, properties, [\"cart\"], strict)) return UNHANDLED_TRACK;\n return handled(this.cart.client.view(properties as unknown as Parameters<CartTracking[\"view\"]>[0]));\n case \"begin_checkout\":\n if (!this.requireTrackProperties(eventName, properties, [\"cart\"], strict)) return UNHANDLED_TRACK;\n return handled(this.checkout.client.begin(properties as unknown as Parameters<Checkout[\"begin\"]>[0]));\n case \"checkout_step\":\n if (!this.requireTrackProperties(eventName, properties, [\"step\", \"stepNumber\", \"cart\"], strict)) return UNHANDLED_TRACK;\n return handled(this.checkout.client.step(properties as unknown as Parameters<Checkout[\"step\"]>[0]));\n case \"add_shipping_info\":\n if (!this.requireTrackProperties(eventName, properties, [\"cart\", \"shipping\"], strict)) return UNHANDLED_TRACK;\n return handled(this.checkout.client.addShipping(properties as unknown as Parameters<Checkout[\"addShipping\"]>[0]));\n case \"add_payment_info\":\n if (!this.requireTrackProperties(eventName, properties, [\"cart\", \"payment\"], strict)) return UNHANDLED_TRACK;\n return handled(this.checkout.client.addPayment(properties as unknown as Parameters<Checkout[\"addPayment\"]>[0]));\n case \"purchase\":\n if (!this.requireTrackProperties(eventName, properties, [\"order\"], strict)) return UNHANDLED_TRACK;\n return handled(this.purchases.client.track(properties as unknown as Parameters<Purchases[\"track\"]>[0]));\n case \"refund\":\n if (!this.requireTrackProperties(eventName, properties, [\"orderId\", \"refundId\", \"amount\", \"currency\"], strict)) return UNHANDLED_TRACK;\n return handled(this.refunds.client.track(properties as unknown as Parameters<Refunds[\"track\"]>[0]));\n case \"refund_requested\":\n if (!this.requireTrackProperties(eventName, properties, [\"orderId\"], strict)) return UNHANDLED_TRACK;\n return handled(this.refunds.client.request(stringValue(properties, \"orderId\")!, properties.items as Parameters<Refunds[\"request\"]>[1], stringValue(properties, \"reason\")));\n case \"refund_approved\":\n if (!this.requireTrackProperties(eventName, properties, [\"refundId\", \"approvedAmount\"], strict)) return UNHANDLED_TRACK;\n return handled(this.refunds.client.approve(stringValue(properties, \"refundId\")!, numberValue(properties, \"approvedAmount\")!));\n case \"refund_denied\":\n if (!this.requireTrackProperties(eventName, properties, [\"refundId\", \"reason\"], strict)) return UNHANDLED_TRACK;\n return handled(this.refunds.client.deny(stringValue(properties, \"refundId\")!, stringValue(properties, \"reason\")!));\n case \"refund_completed\":\n if (!this.requireTrackProperties(eventName, properties, [\"refundId\", \"processedAmount\"], strict)) return UNHANDLED_TRACK;\n return handled(this.refunds.client.complete(stringValue(properties, \"refundId\")!, numberValue(properties, \"processedAmount\")!));\n case \"promotion_view\":\n if (!this.requireTrackProperties(eventName, properties, [\"promotionId\", \"name\", \"creative\", \"position\"], strict)) return UNHANDLED_TRACK;\n return handled(this.promotions.client.view(properties as Parameters<Promotions[\"view\"]>[0]));\n case \"promotion_click\":\n if (!this.requireTrackProperties(eventName, properties, [\"promotionId\", \"name\", \"creative\", \"position\"], strict)) return UNHANDLED_TRACK;\n return handled(this.promotions.client.click(properties as Parameters<Promotions[\"click\"]>[0]));\n case \"coupon_used\":\n if (!this.requireTrackProperties(eventName, properties, [\"code\", \"orderId\", \"discountValue\", \"discountType\", \"orderValueBefore\", \"orderValueAfter\"], strict)) return UNHANDLED_TRACK;\n return handled(this.promotions.client.couponUsed(properties as Parameters<Promotions[\"couponUsed\"]>[0]));\n case \"add_to_wishlist\":\n case \"remove_from_wishlist\": {\n const product = productFrom(properties);\n if (!product) {\n if (strict) throw new Error(`FlowGrid.track(\"${eventName}\") missing required properties: product or productId, name, price`);\n return UNHANDLED_TRACK;\n }\n const config = { ...omitProperties(properties, [\"product\", ...PRODUCT_KEYS]), product };\n return handled(eventName === \"add_to_wishlist\"\n ? this.wishlist.client.add(config as Parameters<WishlistTracking[\"add\"]>[0])\n : this.wishlist.client.remove(config as Parameters<WishlistTracking[\"remove\"]>[0]));\n }\n case \"view_wishlist\":\n if (!this.requireTrackProperties(eventName, properties, [\"wishlist\"], strict)) return UNHANDLED_TRACK;\n return handled(this.wishlist.client.view(recordValue(properties, \"wishlist\") as unknown as Parameters<WishlistTracking[\"view\"]>[0]));\n case \"wishlist_to_cart\": {\n const product = productFrom(properties);\n if (!product) {\n if (strict) throw new Error(`FlowGrid.track(\"${eventName}\") missing required properties: product or productId, name, price`);\n return UNHANDLED_TRACK;\n }\n return handled(this.wishlist.client.moveToCart(product as Parameters<WishlistTracking[\"moveToCart\"]>[0], stringValue(properties, \"wishlistId\")));\n }\n case \"share_wishlist\":\n if (!this.requireTrackProperties(eventName, properties, [\"wishlistId\", \"method\"], strict)) return UNHANDLED_TRACK;\n return handled(this.wishlist.client.share(stringValue(properties, \"wishlistId\")!, stringValue(properties, \"method\") as Parameters<WishlistTracking[\"share\"]>[1]));\n case \"subscription_start\":\n if (!this.requireTrackProperties(eventName, properties, [\"subscriptionId\", \"userId\", \"plan\", \"mrr\", \"billingCycle\"], strict)) return UNHANDLED_TRACK;\n return handled(this.subscriptions.client.start(properties as unknown as Parameters<Subscriptions[\"start\"]>[0]));\n case \"subscription_change\":\n if (!this.requireTrackProperties(eventName, properties, [\"subscriptionId\", \"userId\", \"previousPlan\", \"newPlan\", \"previousMrr\", \"newMrr\", \"changeType\"], strict)) return UNHANDLED_TRACK;\n return handled(this.subscriptions.client.change(properties as unknown as Parameters<Subscriptions[\"change\"]>[0]));\n case \"subscription_cancel\":\n if (!this.requireTrackProperties(eventName, properties, [\"subscriptionId\", \"userId\", \"plan\", \"lostMrr\"], strict)) return UNHANDLED_TRACK;\n return handled(this.subscriptions.client.cancel(properties as unknown as Parameters<Subscriptions[\"cancel\"]>[0]));\n case \"subscription_renew\":\n if (!this.requireTrackProperties(eventName, properties, [\"subscriptionId\", \"userId\", \"mrr\"], strict)) return UNHANDLED_TRACK;\n return handled(this.subscriptions.client.renew(stringValue(properties, \"subscriptionId\")!, stringValue(properties, \"userId\")!, recordValue(properties, \"mrr\") as unknown as Parameters<Subscriptions[\"renew\"]>[2]));\n case \"trial_converted\":\n if (!this.requireTrackProperties(eventName, properties, [\"subscriptionId\", \"userId\", \"plan\", \"mrr\"], strict)) return UNHANDLED_TRACK;\n return handled(this.subscriptions.client.trialConverted(stringValue(properties, \"subscriptionId\")!, stringValue(properties, \"userId\")!, stringValue(properties, \"plan\")!, recordValue(properties, \"mrr\") as unknown as Parameters<Subscriptions[\"trialConverted\"]>[3]));\n case \"trial_expired\":\n if (!this.requireTrackProperties(eventName, properties, [\"subscriptionId\", \"userId\"], strict)) return UNHANDLED_TRACK;\n return handled(this.subscriptions.client.trialExpired(stringValue(properties, \"subscriptionId\")!, stringValue(properties, \"userId\")!));\n case \"record_acquisition_cost\":\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"cost\", \"channel\"], strict)) return UNHANDLED_TRACK;\n return handled(this.ltv.client.recordAcquisitionCost(properties as unknown as Parameters<CustomerLTV[\"recordAcquisitionCost\"]>[0]));\n case \"update_customer_segment\":\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"newSegment\"], strict)) return UNHANDLED_TRACK;\n return handled(this.ltv.client.updateSegment(stringValue(properties, \"userId\")!, stringValue(properties, \"newSegment\") as Parameters<CustomerLTV[\"updateSegment\"]>[1]));\n case \"site_search\":\n if (!this.requireTrackProperties(eventName, properties, [\"query\", \"resultsCount\"], strict)) return UNHANDLED_TRACK;\n return handled(this.search.client.track(properties as Parameters<SearchAnalytics[\"track\"]>[0]));\n case \"search_result_click\": {\n const product = productFrom(properties);\n if (!product || !this.requireTrackProperties(eventName, properties, [\"query\", \"position\"], strict)) return UNHANDLED_TRACK;\n return handled(this.search.client.trackClick({ ...properties, product } as Parameters<SearchAnalytics[\"trackClick\"]>[0]));\n }\n case \"search_refinement\":\n if (!this.requireTrackProperties(eventName, properties, [\"originalQuery\", \"refinedQuery\", \"refinementType\"], strict)) return UNHANDLED_TRACK;\n return handled(this.search.client.trackRefinement(stringValue(properties, \"originalQuery\")!, stringValue(properties, \"refinedQuery\")!, stringValue(properties, \"refinementType\") as Parameters<SearchAnalytics[\"trackRefinement\"]>[2]));\n case \"search_suggestion\":\n if (!this.requireTrackProperties(eventName, properties, [\"partialQuery\", \"selectedSuggestion\", \"suggestionPosition\"], strict)) return UNHANDLED_TRACK;\n return handled(this.search.client.trackSuggestion(stringValue(properties, \"partialQuery\")!, stringValue(properties, \"selectedSuggestion\")!, numberValue(properties, \"suggestionPosition\")!));\n default:\n return UNHANDLED_TRACK;\n }\n }\n\n private dispatchEnterpriseTrack(\n eventName: string,\n properties: FlowGridTrackProperties,\n strict: boolean,\n domain?: FlowGridTrackDomain\n ): TrackDispatchResult {\n // Enterprise chunk not yet loaded — let the event fall through to events.track().\n if (!_enterpriseFeatures || !_enterpriseTracking) return UNHANDLED_TRACK;\n\n switch (eventName) {\n\n case \"engagement.dwell_time\":\n case \"engagement_dwell_time\":\n if (domain && domain !== \"engagement\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"featureId\", \"dwellTimeMs\"], strict)) return UNHANDLED_TRACK;\n return handled(this.engagement.client.trackDwellTime(properties as Parameters<Engagement[\"trackDwellTime\"]>[0]));\n\n case \"engagement.session_depth\":\n case \"engagement_session_depth\":\n if (domain && domain !== \"engagement\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"sessionId\", \"pagesVisited\", \"featuresUsed\", \"actionsTaken\", \"durationMs\"], strict)) return UNHANDLED_TRACK;\n return handled(this.engagement.client.trackSessionDepth(properties as Parameters<Engagement[\"trackSessionDepth\"]>[0]));\n\n case \"engagement.content_consumption\":\n case \"engagement_content_consumption\":\n if (domain && domain !== \"engagement\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"contentId\", \"contentType\", \"percentageConsumed\", \"timeSpentMs\"], strict)) return UNHANDLED_TRACK;\n return handled(this.engagement.client.trackContentConsumption(properties as Parameters<Engagement[\"trackContentConsumption\"]>[0]));\n\n case \"engagement.user_activity\":\n case \"engagement_user_activity\":\n if (domain && domain !== \"engagement\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"activityType\", \"durationMs\"], strict)) return UNHANDLED_TRACK;\n return handled(this.engagement.client.trackUserActivity(properties as unknown as Parameters<Engagement[\"trackUserActivity\"]>[0]));\n\n case \"churn.usage_decay\":\n case \"churn_usage_decay\":\n if (domain && domain !== \"churn\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"metric\", \"periods\"], strict)) return UNHANDLED_TRACK;\n return handled(this.churn.client.trackUsageDecay(properties as Parameters<ChurnAnalytics[\"trackUsageDecay\"]>[0]));\n\n case \"churn.nps\":\n case \"churn_nps\":\n if (domain && domain !== \"churn\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"score\"], strict)) return UNHANDLED_TRACK;\n return handled(this.churn.client.trackNPS(properties as Parameters<ChurnAnalytics[\"trackNPS\"]>[0]));\n\n case \"churn.churn_event\":\n case \"churn_churn_event\":\n if (domain && domain !== \"churn\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"churnType\"], strict)) return UNHANDLED_TRACK;\n return handled(this.churn.client.trackChurn(properties as Parameters<ChurnAnalytics[\"trackChurn\"]>[0]));\n\n case \"churn.prevention_action\":\n case \"churn_prevention_action\":\n if (domain && domain !== \"churn\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"actionType\"], strict)) return UNHANDLED_TRACK;\n return handled(this.churn.client.trackPreventionAction(properties as Parameters<ChurnAnalytics[\"trackPreventionAction\"]>[0]));\n\n case \"monetization.revenue\":\n case \"monetization_revenue\":\n if (domain && domain !== \"monetization\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"amount\", \"currency\", \"type\"], strict)) return UNHANDLED_TRACK;\n return handled(this.monetization.client.trackRevenue(properties as Parameters<Monetization[\"trackRevenue\"]>[0]));\n\n case \"support.ticket_created\":\n case \"support_ticket_created\":\n if (domain && domain !== \"support\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"ticketId\", \"userId\", \"subject\", \"category\", \"priority\", \"channel\"], strict)) return UNHANDLED_TRACK;\n return handled(this.support.client.trackTicket(properties as Parameters<SupportAnalytics[\"trackTicket\"]>[0]));\n\n case \"support.ticket_updated\":\n case \"support_ticket_updated\":\n if (domain && domain !== \"support\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"ticketId\", \"status\"], strict)) return UNHANDLED_TRACK;\n return handled(this.support.client.updateTicket(properties as Parameters<SupportAnalytics[\"updateTicket\"]>[0]));\n\n case \"support.csat\":\n case \"support_csat\":\n if (domain && domain !== \"support\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"ticketId\", \"userId\", \"score\"], strict)) return UNHANDLED_TRACK;\n return handled(this.support.client.trackCSAT(properties as Parameters<SupportAnalytics[\"trackCSAT\"]>[0]));\n\n case \"support.feature_complaint\":\n case \"support_feature_complaint\":\n if (domain && domain !== \"support\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"featureId\", \"complaintType\", \"severity\"], strict)) return UNHANDLED_TRACK;\n return handled(this.support.client.trackFeatureComplaint(properties as Parameters<SupportAnalytics[\"trackFeatureComplaint\"]>[0]));\n\n case \"acquisition.acquired\":\n case \"acquisition_acquired\":\n if (domain && domain !== \"acquisition\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"source\"], strict)) return UNHANDLED_TRACK;\n return handled(this.acquisition.client.trackAcquisition(properties as Parameters<AcquisitionAnalytics[\"trackAcquisition\"]>[0]));\n\n case \"acquisition.lifecycle_transition\":\n case \"acquisition_lifecycle_transition\":\n if (domain && domain !== \"acquisition\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"fromStage\", \"toStage\"], strict)) return UNHANDLED_TRACK;\n return handled(this.acquisition.client.trackLifecycleTransition(properties as Parameters<AcquisitionAnalytics[\"trackLifecycleTransition\"]>[0]));\n\n case \"acquisition.campaign_spend\":\n case \"acquisition_campaign_spend\":\n if (domain && domain !== \"acquisition\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"campaignId\", \"amount\", \"date\"], strict)) return UNHANDLED_TRACK;\n return handled(this.acquisition.client.trackCampaignSpend(properties as unknown as Parameters<AcquisitionAnalytics[\"trackCampaignSpend\"]>[0]));\n\n case \"paths.transition\":\n case \"paths_transition\":\n if (domain && domain !== \"paths\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"fromScreen\", \"toScreen\"], strict)) return UNHANDLED_TRACK;\n return handled(this.paths.client.trackTransition(properties as Parameters<PathAnalytics[\"trackTransition\"]>[0]));\n\n case \"paths.feature_sequence\":\n case \"paths_feature_sequence\":\n if (domain && domain !== \"paths\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"feature\", \"order\"], strict)) return UNHANDLED_TRACK;\n return handled(this.paths.client.trackFeatureSequence(properties as unknown as Parameters<PathAnalytics[\"trackFeatureSequence\"]>[0]));\n\n case \"security.login_attempt\":\n case \"security_login_attempt\":\n if (domain && domain !== \"security\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"success\"], strict)) return UNHANDLED_TRACK;\n return handled(this.security.client.trackLoginAttempt(properties as Parameters<SecurityAnalytics[\"trackLoginAttempt\"]>[0]));\n\n case \"security.role_change\":\n case \"security_role_change\":\n if (domain && domain !== \"security\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"changedBy\", \"previousRole\", \"newRole\"], strict)) return UNHANDLED_TRACK;\n return handled(this.security.client.trackRoleChange(properties as Parameters<SecurityAnalytics[\"trackRoleChange\"]>[0]));\n\n case \"security.permission_change\":\n case \"security_permission_change\":\n if (domain && domain !== \"security\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"changedBy\", \"permission\", \"action\"], strict)) return UNHANDLED_TRACK;\n return handled(this.security.client.trackPermissionChange(properties as Parameters<SecurityAnalytics[\"trackPermissionChange\"]>[0]));\n\n case \"security.data_access\":\n case \"security_data_access\":\n if (domain && domain !== \"security\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"resourceType\", \"accessType\"], strict)) return UNHANDLED_TRACK;\n return handled(this.security.client.trackDataAccess(properties as Parameters<SecurityAnalytics[\"trackDataAccess\"]>[0]));\n \n case \"security.compliance_event\":\n case \"security_compliance_event\":\n if (domain && domain !== \"security\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"eventType\"], strict)) return UNHANDLED_TRACK;\n return handled(this.security.client.trackComplianceEvent(properties as Parameters<SecurityAnalytics[\"trackComplianceEvent\"]>[0]));\n\n case \"multi_path_funnels.track_step\":\n case \"multi_path_funnels_track_step\":\n if (domain && domain !== \"multiPathFunnels\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"funnelId\", \"step\", \"userId\"], strict)) return UNHANDLED_TRACK;\n return handled(this.multiPathFunnels.client.trackStep(properties as Parameters<MultiPathFunnels[\"trackStep\"]>[0]));\n\n case \"multi_path_funnels.track_micro_conversion\":\n case \"multi_path_funnels_track_micro_conversion\":\n if (domain && domain !== \"multiPathFunnels\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"conversionId\", \"userId\"], strict)) return UNHANDLED_TRACK;\n return handled(this.multiPathFunnels.client.trackMicroConversion(properties as Parameters<MultiPathFunnels[\"trackMicroConversion\"]>[0]));\n\n default:\n return UNHANDLED_TRACK;\n \n }\n }\n\n /**\n * Record an experiment exposure for a user.\n *\n * For the browser-cached flow, prefer:\n * ```ts\n * await fg.experiments.init([{ experimentId, variants: [...] }]);\n * const variant = fg.experiments.variant(experimentId);\n * fg.experiment(\"checkout_v2\", { userId, variant });\n * ```\n */\n experiment(\n experimentId: string,\n options: { variant: string; userId: string }\n ) {\n return this.experiments.exposure(experimentId, options.userId, options.variant);\n }\n\n /**\n * Identify a user.\n *\n * @example\n * ```ts\n * fg.identifyUser(\"u_1\", { plan: \"pro\", email: \"alice@example.com\" });\n * ```\n */\n identifyUser(userId: string, traits?: Record<string, unknown>, anonymousId?: string) {\n return this.identify.user(userId, traits, anonymousId);\n }\n\n /**\n * Bind a feature's `id` + `name` (and optional `category`) once, and get back\n * a small set of semantic helpers — `viewed` / `used` / `completed` /\n * `abandoned` — so you don't repeat the id/name on every call. Each helper is\n * a thin, explicit wrapper over `track(\"feature_usage\", …)` (full\n * routing/consent/transport). No hidden state, no lifecycle guessing: every\n * call sends exactly the action you name. The dashboard does the interpreting\n * (a *viewed* feature is Discovered, a *used* one is Adopted).\n *\n * @example\n * ```ts\n * const agent = fg.defineFeature(\"ai-agent-builder\", \"AI Agent Builder\", { category: \"automation\" });\n * agent.viewed(); // → action: \"viewed\" (Discovered + Viewed)\n * agent.used({ model: \"opus\" }); // → action: \"used\" (Adopted)\n * agent.completed(); // → action: \"completed\"\n * agent.abandoned(); // → action: \"abandoned\"\n * ```\n */\n defineFeature(\n featureId: string,\n featureName: string,\n opts: { category?: string } = {},\n ): FeatureHandle {\n const emit = (action: FeatureHandleAction, properties?: Record<string, unknown>) =>\n this.track(\n \"feature_usage\",\n {\n featureId,\n featureName,\n action,\n ...(opts.category ? { category: opts.category } : {}),\n ...(properties ?? {}),\n },\n { domain: \"feature\" },\n );\n return {\n featureId,\n featureName,\n viewed: (properties) => emit(\"viewed\", properties),\n used: (properties) => emit(\"used\", properties),\n completed: (properties) => emit(\"completed\", properties),\n abandoned: (properties) => emit(\"abandoned\", properties),\n track: (action, properties) => emit(action, properties),\n };\n }\n\n /**\n * Track a page view. Auto-detects URL/path/title/referrer in the browser.\n *\n * @example\n * ```ts\n * fg.page(); // auto-detect everything\n * fg.page(\"/pricing\", { plan: \"pro\" }); // override path + properties\n * ```\n */\n page(\n pathOrUrl?: string,\n properties?: Record<string, unknown>,\n options?: { title?: string; referrer?: string; userId?: string; sessionId?: string }\n ) {\n return this.pageViews.view(pathOrUrl, properties, options);\n }\n\n /**\n * Enable browser-side auto-instrumentation in a single call.\n *\n * On by default (opt-out with `false`):\n * - **Engagement** — session depth, action count, and active/passive/idle transitions\n * - **Replay** — DOM session recording via rrweb\n *\n * Off by default (these are handled by the flowgrid.js core script):\n * - **Sessions**, **Page views**, **Performance**, **Heatmaps**, **Attribution**\n *\n * Pass `false` for any feature to disable it. Returns a single teardown\n * function that detaches every listener; safe no-op on the server.\n *\n * @example\n * ```ts\n * const stop = fg.autoTrack({ userId: \"u_1\" });\n * // ...later\n * stop();\n * ```\n */\n autoTrack(options: {\n userId?: string;\n sessions?: boolean;\n pageViews?: boolean;\n performance?: boolean | { resources?: boolean; minDuration?: number };\n heatmaps?: boolean | { movement?: boolean };\n attribution?: boolean;\n replay?: boolean | import(\"./features/analytics/replay\").SessionReplayOptions;\n engagement?: boolean | { idleAfterMs?: number; pollMs?: number };\n passiveIdentity?: boolean | import(\"./features/analytics/identify\").PassiveIdentityOptions;\n } = {}): () => void {\n const teardowns: Array<() => void> = [];\n const ctx = { userId: options.userId };\n\n if (options.sessions === true) {\n teardowns.push(this.sessions.autoTrack(ctx.userId ? { user: { userId: ctx.userId } } : {}));\n }\n if (options.pageViews === true) {\n teardowns.push(this.pageViews.autoTrack(ctx));\n }\n if (options.performance === true || (options.performance !== false && typeof options.performance === \"object\")) {\n const perfOpts = typeof options.performance === \"object\" ? options.performance : {};\n teardowns.push(this.performance.autoTrack({ ...perfOpts, userId: ctx.userId }));\n }\n if (options.heatmaps === true || (options.heatmaps !== false && typeof options.heatmaps === \"object\")) {\n const hmOpts = typeof options.heatmaps === \"object\" ? options.heatmaps : {};\n teardowns.push(this.heatmaps.autoTrack({ ...hmOpts, userId: ctx.userId }));\n }\n if (options.engagement !== false) {\n const engOpts = typeof options.engagement === \"object\" ? options.engagement : {};\n // Engagement lives in the enterprise chunk — start async (mirrors replay pattern).\n let resolvedEngStop: (() => void) | null = null;\n let engStopRequested = false;\n void loadEnterpriseModules().then(() => {\n if (engStopRequested) return;\n resolvedEngStop = this.engagement.client.autoTrack({ ...engOpts, userId: ctx.userId });\n });\n teardowns.push(() => {\n engStopRequested = true;\n if (resolvedEngStop) try { resolvedEngStop(); } catch { /* noop */ }\n });\n }\n if (options.attribution === true && ctx.userId) {\n this.attribution.captureFromUrl(ctx.userId);\n }\n // Passive identity capture is off by default — the flowgrid.js core script\n // already runs it for snippet installs; SDK-only sites opt in here.\n if (options.passiveIdentity === true || (options.passiveIdentity !== false && typeof options.passiveIdentity === \"object\")) {\n const piOpts = typeof options.passiveIdentity === \"object\" ? options.passiveIdentity : {};\n teardowns.push(this.identify.client.autoTrackForms(piOpts));\n }\n if (options.replay !== false) {\n const replayOpts = typeof options.replay === \"object\" ? options.replay : {};\n // start() is async; track its resolved teardown so the outer stop()\n // tears down recording even if the user calls it before rrweb resolved.\n let resolvedStop: (() => void) | null = null;\n let stopRequested = false;\n void this.replay.start({ userId: ctx.userId, ...replayOpts }).then(stop => {\n if (stopRequested) { try { stop(); } catch { /* noop */ } }\n else { resolvedStop = stop; }\n });\n teardowns.push(() => {\n stopRequested = true;\n if (resolvedStop) { try { resolvedStop(); } catch { /* noop */ } }\n });\n }\n\n return () => { for (const t of teardowns) { try { t(); } catch { /* swallow */ } } };\n }\n}\n\nexport default FlowGrid;\n","/**\n * @fileoverview Declarative, attribute-driven experiment tracking (no-code).\n * @module features/core/declarativeExperiments\n *\n * @description\n * Lets a non-technical user (WordPress / Wix / Squarespace marketer) wire\n * experiment conversions, page-level goals, and exposures using ONLY HTML\n * attributes — no JavaScript. It does not contain any experiment logic itself:\n * it reads the visitor's already-assigned variant from the real engine\n * (`Experiment` in `experiment.ts`) and calls its public tracking methods, so\n * the deterministic assignment, anti-flicker, holdout and exposure semantics\n * (\"dynamic integrity\") are untouched. This is purely the wiring layer.\n *\n * @attributes\n * data-fg-goal=\"<experimentId>\" Mark an element as a conversion trigger for\n * that experiment. Use \"*\" for every experiment\n * the visitor is currently bucketed into.\n * data-fg-metric=\"signup\" Metric name recorded (default \"conversion\").\n * data-fg-value=\"49\" Numeric value, e.g. order total (default 1).\n * data-fg-on=\"click\" Trigger: click (default) | load | submit | view.\n * click — on element click\n * load — once on page load (e.g. thank-you page)\n * submit — on the element's (or nearest) form submit\n * view — once when scrolled into view\n * data-fg-event=\"<name>\" Fire a generic `track(name)` event on the same\n * trigger (page-level/interaction tracking). Can be\n * combined with or used instead of data-fg-goal.\n *\n * @example\n * ```html\n * <!-- count a click on the signup button as a conversion for the hero test -->\n * <button data-fg-goal=\"homepage-hero-headline\" data-fg-metric=\"signup\">Start free</button>\n *\n * <!-- a thank-you page: count the visit as a conversion for every active test -->\n * <body data-fg-goal=\"*\" data-fg-on=\"load\" data-fg-metric=\"purchase\">\n * ```\n *\n * Robust to dynamically-injected DOM: a MutationObserver re-scans on changes, so\n * page builders and SPA route changes keep working with zero extra calls.\n */\n\n/** Minimal surface of the FlowGrid instance this layer depends on. */\nexport interface DeclarativeExperimentHost {\n experiments: {\n trackLocalConversion(experimentId: string, metricName?: string, value?: number): Promise<void> | void;\n getAssignments(): Record<string, string>;\n };\n track(eventName: string, properties?: Record<string, unknown>): unknown;\n}\n\nconst WIRED_ATTR = \"data-fg-wired\";\nconst GOAL_ATTR = \"data-fg-goal\";\nconst EVENT_ATTR = \"data-fg-event\";\n\ntype Trigger = \"click\" | \"load\" | \"submit\" | \"view\";\n\nfunction parseTrigger(raw: string | null): Trigger {\n switch ((raw || \"\").toLowerCase()) {\n case \"load\": return \"load\";\n case \"submit\": return \"submit\";\n case \"view\": return \"view\";\n default: return \"click\";\n }\n}\n\n/** Resolve the experiment ids a `data-fg-goal` value targets. \"*\" = all active. */\nfunction resolveExperimentIds(host: DeclarativeExperimentHost, goal: string): string[] {\n const trimmed = goal.trim();\n if (trimmed === \"*\") {\n try { return Object.keys(host.experiments.getAssignments()); } catch { return []; }\n }\n // Allow a comma-separated list of ids for multi-experiment conversions.\n return trimmed.split(\",\").map((s) => s.trim()).filter(Boolean);\n}\n\n/**\n * Run the element's goal + event side effects once. `trackLocalConversion` is a\n * safe no-op when the visitor isn't bucketed into a given experiment (control,\n * holdout, or no running test), so firing for every resolved id is harmless.\n */\nfunction fire(host: DeclarativeExperimentHost, el: Element): void {\n const goal = el.getAttribute(GOAL_ATTR);\n if (goal) {\n const metric = el.getAttribute(\"data-fg-metric\") || \"conversion\";\n const rawValue = el.getAttribute(\"data-fg-value\");\n const value = rawValue != null && rawValue.trim() !== \"\" && Number.isFinite(Number(rawValue))\n ? Number(rawValue)\n : 1;\n for (const id of resolveExperimentIds(host, goal)) {\n try { void host.experiments.trackLocalConversion(id, metric, value); } catch { /* ignore */ }\n }\n }\n\n const eventName = el.getAttribute(EVENT_ATTR);\n if (eventName) {\n try { \n host.track(eventName, { source: \"declarative\", trigger: el.getAttribute(\"data-fg-on\") || \"click\" }); \n } catch { /* ignore */ }\n }\n}\n\n/**\n * Wire every `[data-fg-goal]` / `[data-fg-event]` element under `root` that\n * isn't already wired, according to its trigger. Idempotent per element.\n */\nexport function wireDeclarativeExperiments(host: DeclarativeExperimentHost): {\n rescan: () => void;\n flushLoad: () => void;\n disconnect: () => void;\n} {\n if (typeof document === \"undefined\") {\n return { rescan: () => {}, flushLoad: () => {}, disconnect: () => {} };\n }\n\n // Whether the engine has had a chance to resolve assignments. `load`/`view`\n // goals discovered before this is true are deferred so a conversion is never\n // recorded before the visitor is bucketed.\n let assignmentsReady = false;\n const pendingImmediate: Element[] = [];\n let viewObserver: IntersectionObserver | null = null;\n\n const fireOrDefer = (el: Element) => {\n if (assignmentsReady) fire(host, el);\n else pendingImmediate.push(el);\n };\n\n const wireElement = (el: Element) => {\n if (el.hasAttribute(WIRED_ATTR)) return;\n el.setAttribute(WIRED_ATTR, \"\");\n const trigger = parseTrigger(el.getAttribute(\"data-fg-on\"));\n\n switch (trigger) {\n case \"click\":\n el.addEventListener(\"click\", () => fire(host, el));\n break;\n case \"submit\": {\n const form = el.tagName === \"FORM\" ? (el as HTMLFormElement) : el.closest(\"form\");\n (form || el).addEventListener(\"submit\", () => fire(host, el));\n break;\n }\n case \"view\":\n if (typeof IntersectionObserver !== \"undefined\") {\n if (!viewObserver) {\n viewObserver = new IntersectionObserver((entries, obs) => {\n for (const entry of entries) {\n if (entry.isIntersecting) {\n obs.unobserve(entry.target);\n fireOrDefer(entry.target);\n }\n }\n }, { threshold: 0.5 });\n }\n viewObserver.observe(el);\n } else {\n fireOrDefer(el); // no IO support — count it as seen\n }\n break;\n case \"load\":\n fireOrDefer(el);\n break;\n }\n };\n\n const scan = (root: ParentNode = document) => {\n const sel = `[${GOAL_ATTR}]:not([${WIRED_ATTR}]),[${EVENT_ATTR}]:not([${WIRED_ATTR}])`;\n root.querySelectorAll(sel).forEach(wireElement);\n };\n\n scan();\n\n // Re-scan when the DOM changes (page builders, SPA navigation, lazy content).\n const observer = typeof MutationObserver !== \"undefined\"\n ? new MutationObserver((mutations) => {\n for (const m of mutations) {\n m.addedNodes.forEach((node) => {\n if (node.nodeType === 1) scan(node as Element);\n });\n }\n })\n : null;\n observer?.observe(document.documentElement, { childList: true, subtree: true });\n\n return {\n rescan: () => scan(),\n /**\n * Mark assignments as resolved and fire any `load`/`view` goals that were\n * discovered before the engine had bucketed the visitor. Call once\n * `initFromServer()` settles. Idempotent.\n */\n flushLoad: () => {\n if (assignmentsReady) return;\n assignmentsReady = true;\n const queued = pendingImmediate.splice(0, pendingImmediate.length);\n for (const el of queued) fire(host, el);\n },\n disconnect: () => {\n try { observer?.disconnect(); } catch { /* ignore */ }\n try { viewObserver?.disconnect(); } catch { /* ignore */ }\n },\n };\n}\n","/**\n * @fileoverview FlowGrid SDK - Browser Bundle\n * @module browser\n *\n * @description\n * Minimal script-tag facade over the unified FlowGrid SDK. Browser users get a\n * tiny top-level API for the common path, while advanced users can call\n * `FlowGrid.instance()` and use the full SDK namespaces.\n *\n * @example\n * ```html\n * <script src=\"https://cdn.flow-grid.xyz/flowgrid.min.js\"></script>\n * <script>\n * FlowGrid.init({ webId: 'your-web-id', apiKey: 'your-api-key' });\n * FlowGrid.track('button_click', { button: 'signup' });\n * FlowGrid.page();\n * FlowGrid.identify('user_123', { plan: 'pro' });\n * </script>\n * ```\n */\n\nimport { FlowGrid, FlowGridInitConfig } from \"./lib/flowgrid\";\nimport type { ConsentCategory } from \"./lib/client/transport\";\nimport type { Order, Product } from \"./lib/types\";\nimport { wireDeclarativeExperiments, type DeclarativeExperimentHost } from \"./lib/features/core/declarativeExperiments\";\n\ntype BrowserProduct = Pick<Product, \"productId\" | \"name\" | \"price\"> & Partial<Product>;\n\ntype BrowserOrder = {\n orderId: string;\n total: number;\n currency?: Product[\"currency\"];\n items: Array<Pick<Product, \"productId\" | \"name\" | \"price\"> & Partial<Product> & { quantity: number }>;\n subtotal?: number;\n discountTotal?: number;\n shippingTotal?: number;\n taxTotal?: number;\n};\n\ntype BrowserFacade = {\n init(config: FlowGridInitConfig): FlowGrid;\n instance(): FlowGrid | null;\n reset(): void;\n setConsent(consent: Partial<Record<ConsentCategory, boolean>>): void;\n hasConsent(category: ConsentCategory): boolean;\n autoTrack(options?: Parameters<FlowGrid[\"autoTrack\"]>[0]): () => void;\n track: FlowGrid[\"track\"];\n page: FlowGrid[\"page\"];\n identify: FlowGrid[\"identifyUser\"];\n defineFeature: FlowGrid[\"defineFeature\"];\n experiment: FlowGrid[\"experiment\"];\n wireExperiments(): void;\n productView(product: BrowserProduct, properties?: Record<string, unknown>): ReturnType<FlowGrid[\"products\"][\"view\"]>;\n addToCart(product: BrowserProduct, quantity?: number, cartId?: string): ReturnType<FlowGrid[\"cart\"][\"add\"]>;\n purchase(order: BrowserOrder): ReturnType<FlowGrid[\"purchases\"][\"complete\"]>;\n};\n\nfunction getInstance(): FlowGrid {\n const instance = FlowGrid.instance();\n if (!instance) {\n throw new Error(\"FlowGrid not initialized. Call FlowGrid.init() first.\");\n }\n return instance;\n}\n\nfunction normalizeProduct(product: BrowserProduct): Product {\n return {\n ...product,\n currency: product.currency ?? \"USD\",\n };\n}\n\nfunction normalizeOrder(order: BrowserOrder): Order {\n const currency = order.currency ?? \"USD\";\n const items = order.items.map((item) => ({\n ...item,\n currency: item.currency ?? currency,\n quantity: item.quantity,\n lineTotal: item.price * item.quantity,\n }));\n const subtotal = order.subtotal ?? items.reduce((sum, item) => sum + item.lineTotal, 0);\n\n return {\n orderId: order.orderId,\n items,\n subtotal,\n discountTotal: order.discountTotal ?? 0,\n shippingTotal: order.shippingTotal ?? 0,\n taxTotal: order.taxTotal ?? 0,\n total: order.total,\n currency,\n };\n}\n\nconst browserFlowGrid: BrowserFacade = {\n init(config) {\n return FlowGrid.init(config);\n },\n\n instance() {\n return FlowGrid.instance();\n },\n\n reset() {\n FlowGrid.reset();\n },\n\n setConsent(consent) {\n FlowGrid.setConsent(consent);\n },\n\n hasConsent(category) {\n return FlowGrid.hasConsent(category);\n },\n\n autoTrack(options) {\n return getInstance().autoTrack(options);\n },\n\n track(eventName, properties, options) {\n return getInstance().track(eventName, properties, options);\n },\n\n page(pathOrUrl, properties, options) {\n return getInstance().page(pathOrUrl, properties, options);\n },\n\n identify(userId, traits, anonymousId) {\n return getInstance().identifyUser(userId, traits, anonymousId);\n },\n\n defineFeature(featureId, featureName, opts) {\n return getInstance().defineFeature(featureId, featureName, opts);\n },\n\n experiment(experimentId, options) {\n return getInstance().experiment(experimentId, options);\n },\n\n // Manually (re-)scan the DOM for `data-fg-*` experiment attributes. The\n // no-build path wires this automatically; SPA/programmatic users call it\n // after a client-side navigation re-renders the page.\n wireExperiments() {\n const wiring = wireDeclarativeExperiments(getInstance() as unknown as DeclarativeExperimentHost);\n wiring.flushLoad();\n },\n\n productView(product, properties) {\n return getInstance().products.view(normalizeProduct(product), properties);\n },\n\n addToCart(product, quantity = 1, cartId) {\n return getInstance().cart.add(normalizeProduct(product), quantity, cartId);\n },\n\n purchase(order) {\n return getInstance().purchases.complete(normalizeOrder(order));\n },\n};\n\ndeclare global {\n interface Window {\n FlowGrid: BrowserFacade;\n flowgrid: BrowserFacade;\n /**\n * Optional full config for the no-build auto-init path. Set this *before*\n * the `<script>` tag to enable replay, autoTrack tuning, user identify,\n * consent, custom endpoint/transport, etc. without writing an `init()` call.\n * `webId` may be omitted here and supplied via `data-site` instead.\n */\n FlowGridConfig?: Partial<FlowGridInitConfig>;\n }\n}\n\n/**\n * Read init config from the loading `<script>` tag's data attributes, so a\n * no-build install can self-initialize:\n *\n * ```html\n * <script src=\"https://cdn.flow-grid.xyz/flowgrid.min.js\" data-site=\"YOUR_WEBSITE_ID\"></script>\n * ```\n *\n * Config sources, lowest → highest precedence:\n * 1. `window.FlowGridConfig` — a full {@link FlowGridInitConfig} object set\n * *before* the tag loads. This is how the no-build path enables replay,\n * autoTrack tuning, user identify, consent, custom transport, etc. — every\n * option, declaratively, with no imperative `init()` call.\n * 2. `data-*` attributes on the tag (`data-site`/`data-website-id`/`data-web-id`,\n * `data-api-key`, `data-endpoint`, `data-replay`).\n *\n * `data-manual` suppresses auto-init entirely (call `FlowGrid.init()` yourself).\n * `apiKey` is optional (web_id-only mode) but recommended. Returns `null` when\n * there's no resolvable web id or auto-init is suppressed, leaving the manual\n * `FlowGrid.init({...})` path fully in control.\n */\nfunction readAutoInitConfig(): { config: FlowGridInitConfig; runExperiments: boolean } | null {\n const el =\n typeof document !== \"undefined\"\n ? (document.currentScript as HTMLScriptElement | null)\n : null;\n const attr = (name: string): string | null =>\n el && typeof el.getAttribute === \"function\" ? el.getAttribute(name) : null;\n\n // Programmatic users opt out of auto-init and configure everything in code.\n if (attr(\"data-manual\") != null) return null;\n\n const fromGlobal =\n typeof window !== \"undefined\" ? window.FlowGridConfig : undefined;\n\n const webId =\n attr(\"data-site\") ||\n attr(\"data-website-id\") ||\n attr(\"data-web-id\") ||\n fromGlobal?.webId;\n if (!webId) return null;\n\n // Spread the global config first so its replay/autoTrack/user/consent/transport\n // survive; data-* attributes (and the resolved webId) win where present.\n const config: FlowGridInitConfig = {\n ...fromGlobal,\n webId,\n apiKey: attr(\"data-api-key\") ?? fromGlobal?.apiKey,\n endpoint: attr(\"data-endpoint\") ?? fromGlobal?.endpoint,\n };\n\n // Simple on/off replay via attribute (the options-object form goes through\n // FlowGridConfig). Don't override an explicit FlowGridConfig.replay.\n const replayAttr = attr(\"data-replay\");\n if (replayAttr != null && config.replay === undefined) {\n config.replay = replayAttr !== \"false\";\n }\n\n return { config, runExperiments: attr(\"data-no-experiments\") == null };\n}\n\nif (typeof window !== \"undefined\") {\n // `FlowGrid` is always the SDK facade. `flowgrid` (lowercase) may already be\n // claimed by the core flowgrid.js snippet (a different API) — don't clobber it.\n window.FlowGrid = browserFlowGrid;\n if (typeof window.flowgrid === \"undefined\") {\n window.flowgrid = browserFlowGrid;\n }\n\n // Auto-init from FlowGridConfig / data-* (no-build path). Skipped entirely if\n // an instance already exists (a synchronous manual init won) or `data-manual`\n // is set — so full programmatic configuration is never locked out.\n const auto = readAutoInitConfig();\n if (auto && !FlowGrid.instance()) {\n try {\n const fg = FlowGrid.init(auto.config);\n // Zero-config experiments: assign + apply the site's dashboard-configured\n // tests with no author JS. Opt out with `data-no-experiments` on the tag.\n if (auto.runExperiments) {\n // Wire the declarative `data-fg-*` attribute layer now (click/submit/view\n // listeners + a MutationObserver for dynamically-injected DOM), then flush\n // any page-level `load` goals once assignment settles — so a marketer needs\n // only HTML attributes to record conversions, never JavaScript.\n const wiring = wireDeclarativeExperiments(fg as unknown as DeclarativeExperimentHost);\n fg.experiments\n .initFromServer()\n .catch(() => {})\n .then(() => wiring.flushLoad());\n }\n } catch {\n /* init surfaces its own error; never throw from the bundle's top level */\n }\n }\n}\n\nexport { FlowGrid };\nexport type { BrowserFacade, BrowserOrder, BrowserProduct };\nexport default browserFlowGrid;\n"],"names":["root","factory","exports","module","define","amd","this","inProgress","dataWebpackPrefix","FlowGridTransport","constructor","webId","endpoint","apiKey","visitorId","identityManager","config","trim","Error","normalizeEndpoint","transportConfig","botDetection","respectDNT","sampleRate","drainEventBuffer","setConsent","consent","Object","assign","hasConsent","category","isLikelyBot","navigator","webdriver","test","userAgent","isDNTEnabled","globalPrivacyControl","doNotTrack","getDeviceContext","window","loc","location","doc","document","undefined","_screen_width","screen","width","_screen_height","height","_device_type","_language","language","_url","href","_page","pathname","_referrer","referrer","_title","title","getUtmContext","params","URLSearchParams","search","out","name","ATTRIBUTION_KEYS","value","get","sendViaBeacon","eventName","properties","sendBeacon","resolvedVisitorId","getVisitorId","callType","type","url","encodeURIComponent","payload","JSON","stringify","web_id","visitor_id","event_name","Blob","postRaw","body","options","analytics","isUnload","useBeacon","payloadSize","length","useKeepalive","beaconBody","parse","api_key","console","warn","err","res","fetch","method","headers","keepalive","ok","status","statusText","bufferEvent","localStorage","raw","getItem","EVENT_BUFFER_KEY","buffer","MAX_BUFFER_EVENTS","serialized","TextEncoder","encode","MAX_BUFFER_SIZE","push","setItem","removeItem","evt","catch","init","validateInput","_bot","enriched","executeRequest","initFeature","extraHeaders","attempt","MAX_RETRIES","controller","AbortController","timeoutId","setTimeout","abort","TIMEOUT_MS","signal","clearTimeout","backoff","text","safeRead","json","error","message","includes","ceiling","Math","min","pow","ms","random","Promise","resolve","MAX_PAYLOAD_SIZE","sensitiveKeys","lower","toLowerCase","key","clean","startsWith","replace","marketing","getWindow","getDocument","isBrowser","combineTeardowns","fns","fn","getCookie","match","cookie","RegExp","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__","m","d","definition","o","defineProperty","enumerable","f","e","chunkId","all","keys","reduce","promises","u","g","globalThis","Function","obj","prop","prototype","hasOwnProperty","call","l","done","script","needAttach","scripts","getElementsByTagName","i","s","getAttribute","createElement","charset","nc","setAttribute","src","onScriptComplete","prev","event","onerror","onload","timeout","doneFns","parentNode","removeChild","forEach","bind","target","head","appendChild","r","Symbol","toStringTag","scriptUrl","importScripts","currentScript","tagName","toUpperCase","p","installedChunks","j","installedChunkData","promise","reject","errorType","realSrc","request","webpackJsonpCallback","parentChunkLoadingFunction","data","chunkIds","moreModules","runtime","some","id","chunkLoadingGlobal","LEGACY_VISITOR_COOKIE","SESSION_KEY","SESSION_TOUCH_KEY","IdentityManager","sessionId","memoryStore","cookieDomain","visitorMaxAgeDays","sessionTimeoutMinutes","storage","detectBrowser","serverCookies","compat","resolveVisitorId","getSessionId","getMemorySessionId","existing","sessionStorage","lastTouch","Date","now","parseInt","String","newSession","generateSessionId","setVisitorId","persistVisitorId","clearSession","clearAll","deleteCookie","visitorCookieName","trackJsId","legacyId","readCookie","readLocalStorage","removeLegacyCookie","cookieId","lsId","serverVal","readServerCookie","newId","generateVisitorId","cookieName","writeCookie","result","maxAgeDays","maxAge","domain","secure","protocol","uuidV4","randomHex","crypto","randomUUID","c","toString","bytes","Uint8Array","getRandomValues","floor","Array","from","b","padStart","join","Activation","create","timestamp","toISOString","trackOnboardingStep","subtype","userId","stepId","stepName","stepOrder","totalSteps","progress","round","timeSpent","skipped","completeOnboarding","totalTimeSpent","stepsCompleted","stepsSkipped","completionRate","trackFirstAction","actionName","timeSinceSignup","source","trackSignup","TrackFeature","action","trackUsage","featureId","featureName","duration","success","TrackPrompt","trackSubmission","promptId","promptType","model","inputTokens","conversationId","trackResponse","responseTime","outputTokens","completed","qualityScore","trackFeedback","feedbackType","rating","comment","trackConversation","turnCount","totalDuration","fnv1a","input","hash","charCodeAt","ASSIGNMENT_STORAGE_KEY","ASSIGNMENT_COOKIE_KEY","Experiment","experiments","assignments","antiFlickerStyleEl","antiFlickerTimer","appliedExperiments","Set","parseAssignmentCookie","decoded","decodeURIComponent","parsed","readAssignmentCookie","writeAssignmentCookie","ASSIGNMENT_COOKIE_MAX_AGE_DAYS","sameSite","loadAssignments","stored","cookieAssignments","saveAssignments","injectAssignments","getAssignments","hydrateFromCookies","reader","hydrateFromCookieHeader","cookieHeader","parts","split","prefix","find","slice","serializeAssignmentCookie","clearAssignments","getOrCreateAnonId","ANON_ID_KEY","deterministicAssign","experimentId","variants","hashValue","totalWeight","v","weight","bucket","cumulative","assignVariant","exclusionGroupMembers","winnerId","winner_id","variantId","assignmentSource","holdoutPct","isInHoldout","exclusionGroup","exclusion_group","groupMemberIds","members","sort","filterMutuallyExclusive","assignedVariantId","computeExclusionGroupMembers","items","groups","Map","item","group","has","set","experiment_id","assignFromLocalConfigs","configs","groupMembers","map","exp","verified","verifyExperiments","response","isArray","initExperiments","selectors","collectChangeSelectors","injectAntiFlicker","applyAssignedChanges","removeAntiFlicker","variantChanges","variant","redirectUrl","changes","add","selector","style","textContent","documentElement","remove","applyDomChange","change","querySelectorAll","el","innerHTML","display","setProperty","property","classList","filter","Boolean","entries","redirect","URL","trackLocalExposure","domChanges","run","readyState","addEventListener","once","applyVariant","delete","initFromServer","getLocalVariant","trackLocalConversion","metricName","metricValue","trackExposure","track","trackConversion","PageViews","path","trackScrollDepth","scrollDepth","max","timeAtDepth","trackTimeOnPage","engaged","trackExit","autoTrack","win","currentPath","pageStart","maxDepth","sentDepths","fireView","flushTimeOnPage","onRouteChange","clear","origPush","history","pushState","origReplace","replaceState","args","apply","dispatchEvent","Event","popHandler","locHandler","onScroll","docEl","scrollTop","scrollY","scrollHeight","innerHeight","depth","t","passive","onHide","visibilityState","removeEventListener","Sessions","start","user","device","utm","utmSource","medium","utmMedium","campaign","utmCampaign","landingPage","update","end","heartbeat","heartbeatMs","STORE_KEY","startPromise","then","beat","setInterval","clearInterval","Events","label","nonInteraction","trackBatch","events","count","trackClick","trackForm","trackSearch","query","resultsCount","resultClicked","clickedPosition","filters","trackError","stack","fatal","context","userAction","Identify","anonymousId","traits","updateTraits","alias","previousId","groupId","reset","autoTrackForms","debounceMs","EXCLUDED_PATH_RE","excludePath","BLOCKED_RE","SKIP_RE","FIELD_MAP","re","trait","pii","identify","captured","seen","WeakSet","debounceTimers","WeakMap","teardowns","classify","normalize","contains","isBlocked","blocked","resolveSessionId","sendIdentity","email","emailHash","firstName","lastName","phone","email_hash","phone_hash","phoneHash","first_name","last_name","company","address","city","postcode","session_id","path_name","captureField","cls","async","enc","buf","subtle","digest","sha256hex","attach","onBlur","onInput","scanPage","onSubmit","form","phoneOnSubmit","mo","MutationObserver","observe","childList","subtree","disconnect","Funnels","funnelId","description","steps","trackStep","step","stepNumber","trackComplete","trackAbandon","lastStep","reason","analyze","range","compare","baselineFilter","comparisonFilter","list","Retention","trackActivity","trackLifecycleChange","stage","previousStage","Attribution","trackTouchpoint","channel","content","term","recordConversion","conversionType","conversionId","captureFromUrl","utmTerm","utmContent","Heatmaps","lastMovementTime","lastScrollTime","pageUrl","x","y","normalizedX","viewportWidth","normalizedY","viewportHeight","elementSelector","elementText","substring","trackMovement","MOVEMENT_THROTTLE_MS","recorded","interactionId","viewport","trackScroll","SCROLL_THROTTLE_MS","maxScrollDepth","pageHeight","timeAtDepths","trackRageClick","clickCount","onClick","clientX","clientY","innerWidth","className","capture","scrollFlushScheduled","flushScroll","top","moveTeardown","movement","onMove","PerformanceTracking","trackWebVitals","trackPageLoad","trackResource","trackApiCall","statusCode","cancelled","wv","send","metric","onLCP","onCLS","onINP","onFCP","onTTFB","onFID","reportNavigation","nav","performance","getEntriesByType","dnsTime","domainLookupEnd","domainLookupStart","tcpTime","connectEnd","connectStart","ttfb","responseStart","requestStart","domContentLoaded","domContentLoadedEventEnd","loadTime","loadEventEnd","domInteractive","transferSize","onLoad","resources","PerformanceObserver","minDuration","obs","entry","getEntries","initiator","initiatorType","resourceUrl","resourceType","buffered","hasReplayConsent","STORAGE_KEY","RECORDER_VERSION","SessionReplay","disabled","active","state","enabled","setState","stopped","emitStatus","stop","requireConsent","rrweb","handleError","toError","phase","normalizedOptions","normalizeOptions","startedAtMs","boundUserId","startedAt","sequence","bufferBytes","totalBytes","droppedEvents","initialSnapshotCaptured","flushTimer","flush","flushIntervalMs","durationGuard","maxDurationMs","onVisibility","onPageHide","stopRecording","record","emit","captureEvent","maskAllInputs","maskTextClass","blockClass","ignoreClass","checkoutEveryNth","checkoutEveryNms","inlineImages","collectFonts","cleanupActive","enable","disable","isFinal","eventsCount","byteSize","capturedAt","idempotencyKey","createIdempotencyKey","metadata","postReplayChunk","getPageUrl","privacy","createPrivacyMetadata","recorderVersion","onFlush","getStatus","createStatus","sendChunk","measureEvents","user_id","started_at","captured_at","page_url","idempotency_key","compression","is_final","chunk_kind","flush_reason","dropped_events","recorder_version","size","onDrop","maxBytes","flushBytes","bufferedEvents","bufferedBytes","lastFlushReason","lastError","onStatus","onError","samplingState","consentGranted","masking_mode","blocked_selectors","selectorList","redacted_fields","consent_state","sampling_state","fresh","generateId","cryptoObj","Products","view","product","productId","brand","price","currency","imageUrl","reviewCount","stockStatus","attributes","searchQuery","impression","productCount","products","click","listId","position","quickView","interaction","CartTracking","addItem","lineTotal","quantity","discount","cartId","updateItem","previousQuantity","newQuantity","quantityChange","cart","cartValue","total","itemCount","abandon","applyCoupon","couponCode","Checkout","begin","option","addShipping","shipping","shippingMethod","shippingCost","cost","addPayment","payment","paymentMethod","paymentAttempt","errorCode","Purchases","order","orderId","subtotal","discountTotal","shippingTotal","taxTotal","coupons","affiliation","revenue","sum","attribution","statusChange","newStatus","previousStatus","shipped","delivered","deliveredAt","Refunds","refundId","amount","isFullRefund","initiatedBy","approve","approvedAmount","deny","denialReason","complete","processedAmount","Promotions","promotionId","creative","page","destinationUrl","couponUsed","code","discountValue","discountType","orderValueBefore","orderValueAfter","savings","couponAttempt","failureReason","WishlistTracking","wishlistId","wishlist","moveToCart","share","CustomerLTV","recordAcquisitionCost","updateSegment","segment","SearchAnalytics","hasResults","sortBy","trackRefinement","originalQuery","refinedQuery","refinementType","trackSuggestion","partialQuery","selectedSuggestion","suggestionPosition","Subscriptions","subscriptionId","plan","mrr","billingCycle","trialDays","coupon","isTrialing","mrrDiff","newMrr","previousMrr","previousPlan","newPlan","mrrChange","changeType","cancel","lostMrr","feedback","immediate","renew","trialConverted","trialExpired","ActivationTracker","client","signup","props","onboardingStep","extra","onboardingComplete","firstAction","FeatureTracker","viewed","used","abandoned","PromptTracker","submitted","responded","conversation","ExperimentTracker","exposure","exposureAuto","conversion","localConversion","PageViewTracker","pathOrUrl","opts","browserDefaults","isFullUrl","scroll","time","exit","SessionTracker","EventTracker","batch","elementId","formId","formName","IdentifyTracker","FunnelTracker","RetentionTracker","AttributionTracker","touchpoint","HeatmapTracker","PerformanceTracker","webVitals","vitals","pageLoad","timings","resource","ReplayTracker","ProductTracker","listName","CartTracker","CheckoutTracker","PurchaseTracker","RefundTracker","PromotionTracker","WishlistTracker","LTVTracker","acquisitionCost","newSegment","SearchTracker","refine","suggestion","SubscriptionTracker","_enterpriseFeatures","_enterpriseTracking","loadEnterpriseModules","features","tracking","UNHANDLED_TRACK","handled","PRODUCT_KEYS","stringValue","numberValue","Number","isFinite","booleanValue","recordValue","isRecord","hasValue","omitProperties","next","productFrom","nested","FlowGrid","_instance","fg","transport","identifyUser","replay","autoOpts","sessions","pageViews","heatmaps","engagement","_autoTrackStop","instance","loadEnterprise","_raw","_wrapped","Klass","wrap","Wrapper","activation","prompts","funnels","retention","checkout","purchases","refunds","promotions","ltv","subscriptions","_requireEnterprise","ent","entT","Engagement","EngagementTracker","cohorts","Cohorts","CohortTracker","churn","ChurnAnalytics","ChurnTracker","monetization","Monetization","MonetizationTracker","multiPathFunnels","MultiPathFunnels","MultiPathFunnelTracker","support","SupportAnalytics","SupportTracker","acquisition","AcquisitionAnalytics","AcquisitionTracker","paths","PathAnalytics","PathTracker","alerts","AlertsAnalytics","AlertTracker","security","SecurityAnalytics","SecurityTracker","forecasting","Forecasting","ForecastingTracker","contextualProperties","withTrackContext","routed","dispatchTrack","requireTrackProperties","strict","missing","dispatchers","dispatchActivationTrack","dispatchFeatureTrack","dispatchPromptTrack","dispatchExperimentTrack","dispatchAnalyticsTrack","dispatchEcommerceTrack","dispatchEnterpriseTrack","dispatch","feature_viewed","feature_used","feature_completed","feature_abandoned","trackDwellTime","trackSessionDepth","trackContentConsumption","trackUserActivity","trackUsageDecay","trackNPS","trackChurn","trackPreventionAction","trackRevenue","trackTicket","updateTicket","trackCSAT","trackFeatureComplaint","trackAcquisition","trackLifecycleTransition","trackCampaignSpend","trackTransition","trackFeatureSequence","trackLoginAttempt","trackRoleChange","trackPermissionChange","trackDataAccess","trackComplianceEvent","trackMicroConversion","experiment","defineFeature","ctx","perfOpts","hmOpts","engOpts","resolvedEngStop","engStopRequested","passiveIdentity","piOpts","replayOpts","resolvedStop","stopRequested","WIRED_ATTR","GOAL_ATTR","EVENT_ATTR","fire","host","goal","rawValue","trimmed","resolveExperimentIds","trigger","wireDeclarativeExperiments","rescan","flushLoad","assignmentsReady","pendingImmediate","viewObserver","fireOrDefer","wireElement","hasAttribute","parseTrigger","closest","IntersectionObserver","isIntersecting","unobserve","threshold","scan","sel","observer","mutations","addedNodes","node","nodeType","queued","splice","getInstance","normalizeProduct","browserFlowGrid","wireExperiments","productView","addToCart","purchase","normalizeOrder","flowgrid","auto","attr","fromGlobal","FlowGridConfig","replayAttr","runExperiments","readAutoInitConfig","wiring"],"sourceRoot":""}
1
+ {"version":3,"file":"flowgrid.min.js","mappings":"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","sources":["webpack://flowgrid/webpack/universalModuleDefinition","webpack://flowgrid/webpack/runtime/load script","webpack://flowgrid/./src/lib/client/transport.ts","webpack://flowgrid/./src/lib/utils/browser.ts","webpack://flowgrid/./src/lib/utils/storage.ts","webpack://flowgrid/webpack/bootstrap","webpack://flowgrid/webpack/runtime/define property getters","webpack://flowgrid/webpack/runtime/ensure chunk","webpack://flowgrid/webpack/runtime/get javascript chunk filename","webpack://flowgrid/webpack/runtime/global","webpack://flowgrid/webpack/runtime/hasOwnProperty shorthand","webpack://flowgrid/webpack/runtime/make namespace object","webpack://flowgrid/webpack/runtime/publicPath","webpack://flowgrid/webpack/runtime/jsonp chunk loading","webpack://flowgrid/./src/lib/utils/identity.ts","webpack://flowgrid/./src/lib/features/core/activation.ts","webpack://flowgrid/./src/lib/features/core/track-feature.ts","webpack://flowgrid/./src/lib/features/core/track-prompt.ts","webpack://flowgrid/./src/lib/features/core/experiment.ts","webpack://flowgrid/./src/lib/features/analytics/page-views.ts","webpack://flowgrid/./src/lib/features/analytics/sessions.ts","webpack://flowgrid/./src/lib/features/analytics/events.ts","webpack://flowgrid/./src/lib/features/analytics/identify.ts","webpack://flowgrid/./src/lib/features/analytics/funnels.ts","webpack://flowgrid/./src/lib/features/analytics/retention.ts","webpack://flowgrid/./src/lib/features/analytics/attribution.ts","webpack://flowgrid/./src/lib/features/analytics/heatmaps.ts","webpack://flowgrid/./src/lib/features/analytics/performance.ts","webpack://flowgrid/./src/lib/features/analytics/replay.ts","webpack://flowgrid/./src/lib/features/ecommerce/products.ts","webpack://flowgrid/./src/lib/features/ecommerce/cart.ts","webpack://flowgrid/./src/lib/features/ecommerce/checkout.ts","webpack://flowgrid/./src/lib/features/ecommerce/purchases.ts","webpack://flowgrid/./src/lib/features/ecommerce/refunds.ts","webpack://flowgrid/./src/lib/features/ecommerce/promotions.ts","webpack://flowgrid/./src/lib/features/ecommerce/wishlist.ts","webpack://flowgrid/./src/lib/features/ecommerce/ltv.ts","webpack://flowgrid/./src/lib/features/ecommerce/search.ts","webpack://flowgrid/./src/lib/features/ecommerce/subscriptions.ts","webpack://flowgrid/./src/lib/tracking/core/activation.ts","webpack://flowgrid/./src/lib/tracking/core/feature.ts","webpack://flowgrid/./src/lib/tracking/core/prompt.ts","webpack://flowgrid/./src/lib/tracking/core/experiment.ts","webpack://flowgrid/./src/lib/tracking/analytics/page-views.ts","webpack://flowgrid/./src/lib/tracking/analytics/sessions.ts","webpack://flowgrid/./src/lib/tracking/analytics/events.ts","webpack://flowgrid/./src/lib/tracking/analytics/identify.ts","webpack://flowgrid/./src/lib/tracking/analytics/funnels.ts","webpack://flowgrid/./src/lib/tracking/analytics/retention.ts","webpack://flowgrid/./src/lib/tracking/analytics/attribution.ts","webpack://flowgrid/./src/lib/tracking/analytics/heatmaps.ts","webpack://flowgrid/./src/lib/tracking/analytics/performance.ts","webpack://flowgrid/./src/lib/tracking/analytics/replay.ts","webpack://flowgrid/./src/lib/tracking/ecommerce/products.ts","webpack://flowgrid/./src/lib/tracking/ecommerce/cart.ts","webpack://flowgrid/./src/lib/tracking/ecommerce/checkout.ts","webpack://flowgrid/./src/lib/tracking/ecommerce/purchases.ts","webpack://flowgrid/./src/lib/tracking/ecommerce/refunds.ts","webpack://flowgrid/./src/lib/tracking/ecommerce/promotions.ts","webpack://flowgrid/./src/lib/tracking/ecommerce/wishlist.ts","webpack://flowgrid/./src/lib/tracking/ecommerce/ltv.ts","webpack://flowgrid/./src/lib/tracking/ecommerce/search.ts","webpack://flowgrid/./src/lib/tracking/ecommerce/subscriptions.ts","webpack://flowgrid/./src/lib/flowgrid.ts","webpack://flowgrid/./src/lib/features/core/declarativeExperiments.ts","webpack://flowgrid/./src/browser.ts"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"flowgrid\"] = factory();\n\telse\n\t\troot[\"flowgrid\"] = factory();\n})(this, () => {\nreturn ","var inProgress = {};\nvar dataWebpackPrefix = \"flowgrid:\";\n// loadScript function to load a script via script tag\n__webpack_require__.l = (url, done, key, chunkId) => {\n\tif(inProgress[url]) { inProgress[url].push(done); return; }\n\tvar script, needAttach;\n\tif(key !== undefined) {\n\t\tvar scripts = document.getElementsByTagName(\"script\");\n\t\tfor(var i = 0; i < scripts.length; i++) {\n\t\t\tvar s = scripts[i];\n\t\t\tif(s.getAttribute(\"src\") == url || s.getAttribute(\"data-webpack\") == dataWebpackPrefix + key) { script = s; break; }\n\t\t}\n\t}\n\tif(!script) {\n\t\tneedAttach = true;\n\t\tscript = document.createElement('script');\n\n\t\tscript.charset = 'utf-8';\n\t\tif (__webpack_require__.nc) {\n\t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n\t\t}\n\t\tscript.setAttribute(\"data-webpack\", dataWebpackPrefix + key);\n\n\t\tscript.src = url;\n\t}\n\tinProgress[url] = [done];\n\tvar onScriptComplete = (prev, event) => {\n\t\t// avoid mem leaks in IE.\n\t\tscript.onerror = script.onload = null;\n\t\tclearTimeout(timeout);\n\t\tvar doneFns = inProgress[url];\n\t\tdelete inProgress[url];\n\t\tscript.parentNode && script.parentNode.removeChild(script);\n\t\tdoneFns && doneFns.forEach((fn) => (fn(event)));\n\t\tif(prev) return prev(event);\n\t}\n\tvar timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000);\n\tscript.onerror = onScriptComplete.bind(null, script.onerror);\n\tscript.onload = onScriptComplete.bind(null, script.onload);\n\tneedAttach && document.head.appendChild(script);\n};","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { IdentityManager } from '../utils/identity';\nimport { getCookie } from '../utils/storage';\n\n// ============================================================================\n// CONSENT\n// ============================================================================\n\nexport type ConsentCategory = 'analytics' | 'marketing';\nexport type BotDetectionMode = 'block' | 'tag' | 'disabled';\n\nexport interface FlowGridTransportConfig {\n /** Bot detection mode: 'block' skips events, 'tag' adds _bot flag, 'disabled' sends all */\n botDetection?: BotDetectionMode;\n /** Respect navigator.doNotTrack / globalPrivacyControl (default true) */\n respectDNT?: boolean;\n /** Global sampling rate 0.0–1.0 (default 1.0 = send everything) */\n sampleRate?: number;\n}\n\n/**\n * SDK Feature Types\n */\nexport type FeatureType = \n | \"engagement\"\n | \"cohorts\"\n | \"churn\"\n | \"monetization\"\n | \"multi_path_funnels\"\n | \"support\"\n | \"acquisition\"\n | \"paths\"\n | \"alerts\"\n | \"security\"\n | \"forecasting\";\n\n/**\n * FlowGridTransport — Unified transport layer for Flowgrid SDK.\n *\n * @description\n * Provides a secure, validated RPC-style transport layer used by all SDK modules.\n * Handles:\n * - Input validation\n * - Payload size enforcement\n * - Timeout handling\n * - Retry logic (network failures only)\n * - Authorization headers\n * - Feature-scoped endpoint routing\n *\n * This class is designed to be extended by higher-level modules\n * (e.g., Analytics, Experiments, Engagement, Cohorts).\n *\n * @example\n * ```ts\n * const client = new FlowGridTransport(\"web_123\", \"https://core.flow-grid.xyz\", \"sk_live_xxx\");\n * ```\n */\nexport class FlowGridTransport {\n\n /**\n * Unique Web ID identifying the site or application.\n * Used by the backend to associate incoming events.\n */\n protected webId: string;\n\n /**\n * Base API endpoint.\n * Example values:\n * - \"core.flow-grid.xyz\"\n * - \"https://core.flow-grid.xyz\"\n *\n * Protocol is normalized automatically.\n */\n protected endpoint: string;\n\n /**\n * Private API key used for authenticating requests.\n * Sent as a Bearer token in the Authorization header.\n */\n protected apiKey: string;\n\n /**\n * Visitor ID for cross-session visitor tracking.\n * Persisted per-visitor (cookie/localStorage) and sent with every event.\n */\n protected visitorId: string;\n\n /**\n * Optional identity manager that owns the visitor/session lifecycle.\n * When provided, visitor_id is resolved at call time (not construction time).\n */\n protected identityManager?: IdentityManager;\n\n /**\n * Maximum allowed payload size (in bytes).\n * Prevents oversized event submissions.\n */\n private static readonly MAX_PAYLOAD_SIZE = 10_000; // 10KB\n\n /**\n * Maximum time (ms) before request is aborted.\n */\n private static readonly TIMEOUT_MS = 10_000;\n\n /**\n * Maximum retry attempts for network-level failures and 5xx server errors.\n */\n private static readonly MAX_RETRIES = 3;\n\n /** Max events to persist in localStorage buffer */\n private static readonly MAX_BUFFER_EVENTS = 100;\n\n /** Max buffer size in bytes */\n private static readonly MAX_BUFFER_SIZE = 50_000; // 50KB\n\n /** localStorage key for failed event buffer */\n private static readonly EVENT_BUFFER_KEY = 'fg_event_buffer';\n\n /** Consent state */\n private static consent: Record<ConsentCategory, boolean> = { analytics: true, marketing: true };\n\n /** Transport config */\n protected transportConfig: FlowGridTransportConfig;\n\n /**\n * Creates a new FlowGridTransport instance.\n *\n * @param webId - Unique identifier for the web property.\n * @param endpoint - API base endpoint.\n * @param apiKey - Private API key for authentication.\n * @param visitorId - Optional visitor ID for cross-session tracking.\n *\n * @throws {Error} If required parameters are missing.\n */\n constructor(webId: string, endpoint: string, apiKey: string, visitorId?: string, identityManager?: IdentityManager, config?: FlowGridTransportConfig) {\n if (!webId?.trim() || !endpoint?.trim()) {\n throw new Error(\"SDK init failed: webId and endpoint are required.\");\n }\n\n this.webId = webId.trim();\n this.endpoint = this.normalizeEndpoint(endpoint);\n this.apiKey = apiKey;\n this.identityManager = identityManager;\n this.visitorId = visitorId ?? '';\n this.transportConfig = {\n botDetection: config?.botDetection ?? 'block',\n respectDNT: config?.respectDNT ?? true,\n sampleRate: config?.sampleRate ?? 1.0,\n };\n\n // Drain buffered events from previous failed attempts\n this.drainEventBuffer();\n }\n\n // ---------------------------------------------------------------------------\n // CONSENT API\n // ---------------------------------------------------------------------------\n\n static setConsent(consent: Partial<Record<ConsentCategory, boolean>>): void {\n Object.assign(FlowGridTransport.consent, consent);\n }\n\n static hasConsent(category: ConsentCategory): boolean {\n return FlowGridTransport.consent[category] ?? false;\n }\n\n // ---------------------------------------------------------------------------\n // BOT DETECTION\n // ---------------------------------------------------------------------------\n\n private static isLikelyBot(): boolean {\n if (typeof navigator === 'undefined') return false;\n if ((navigator as any).webdriver) return true;\n if (/bot|crawl|spider|headless|phantom|puppeteer/i.test(navigator.userAgent)) return true;\n return false;\n }\n\n // ---------------------------------------------------------------------------\n // DNT / GPC CHECK\n // ---------------------------------------------------------------------------\n\n private isDNTEnabled(): boolean {\n if (typeof navigator === 'undefined') return false;\n if ((navigator as any).globalPrivacyControl) return true;\n if (navigator.doNotTrack === '1') return true;\n return false;\n }\n\n // ---------------------------------------------------------------------------\n // DEVICE ENRICHMENT\n // ---------------------------------------------------------------------------\n\n private static getDeviceContext(): Record<string, unknown> {\n if (typeof window === 'undefined' || typeof navigator === 'undefined') return {};\n const loc = window.location;\n const doc = typeof document !== 'undefined' ? document : undefined;\n return {\n _screen_width: window.screen?.width,\n _screen_height: window.screen?.height,\n _device_type: /Mobi|Android/i.test(navigator.userAgent) ? 'mobile' : 'desktop',\n _language: navigator.language,\n // Page context — auto-attached so every event (custom or named) records\n // which page it fired on, enabling per-page breakdowns downstream.\n _url: loc?.href,\n _page: loc?.pathname,\n _referrer: doc?.referrer || undefined,\n _title: doc?.title || undefined,\n // Marketing attribution — auto-captured so EVERY event (feature_usage\n // included) carries the campaign that drove it, with zero customer config.\n // Lets the dashboard segment usage/adoption by source/campaign\n // (Mixpanel-parity). Only present keys are attached.\n ...FlowGridTransport.getUtmContext(),\n };\n }\n\n /**\n * The attribution params FlowGrid tracks — kept in lockstep with\n * flowgrid-core's `public/flowgrid.js` (`captureFromQueryOrCookie`) and the\n * `getUTM()` reader in `utils/storage`. Five UTMs plus `ref`/`via`/`source`.\n */\n private static readonly ATTRIBUTION_KEYS = [\n 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'ref', 'via', 'source',\n ] as const;\n\n /**\n * Resolves marketing attribution into `_utm_source`/`_ref`/… properties.\n *\n * Prefers a fresh value from the current URL (the live campaign), falling back\n * to the `__flowgrid_<name>` cookie that `flowgrid.js` persists on the landing\n * page — so events fired on *later* pages (where the URL no longer carries\n * UTMs) still attribute to the campaign that brought the visitor in. Same\n * cookie names + key set as the loader script. Returns `{}` for non-campaign\n * traffic so the payload stays clean.\n */\n private static getUtmContext(): Record<string, string> {\n if (typeof window === 'undefined') return {};\n let params: URLSearchParams | null = null;\n try {\n params = new URLSearchParams(window.location?.search ?? '');\n } catch {\n params = null;\n }\n const out: Record<string, string> = {};\n for (const name of FlowGridTransport.ATTRIBUTION_KEYS) {\n const value = params?.get(name) || getCookie(`__flowgrid_${name}`);\n if (value) out[`_${name}`] = value;\n }\n return out;\n }\n\n // ---------------------------------------------------------------------------\n // SENDBACON FALLBACK\n // ---------------------------------------------------------------------------\n\n /**\n * Fire-and-forget event via sendBeacon. Used for page unload events.\n */\n sendViaBeacon(eventName: string, properties: Record<string, any> = {}): boolean {\n if (typeof navigator === 'undefined' || typeof navigator.sendBeacon !== 'function') return false;\n\n const resolvedVisitorId = this.identityManager\n ? this.identityManager.getVisitorId()\n : (this.visitorId ?? '');\n\n const callType = properties.type || 'event';\n const url = `${this.endpoint}/api/v1/sdk/functions?call=${encodeURIComponent(callType)}`;\n const payload = JSON.stringify({\n web_id: this.webId,\n visitor_id: resolvedVisitorId,\n event_name: eventName,\n properties: { ...properties, ...FlowGridTransport.getDeviceContext() },\n });\n\n return navigator.sendBeacon(url, new Blob([payload], { type: 'application/json' }));\n }\n\n // ---------------------------------------------------------------------------\n // RAW LARGE-PAYLOAD POST (session replay chunks, etc.)\n // ---------------------------------------------------------------------------\n\n /**\n * POST a large opaque payload to a dedicated SDK endpoint, bypassing the\n * 10KB validator. Used by session replay (`call=replay_chunk`) where each\n * batch of rrweb events can be hundreds of kilobytes.\n *\n * Falls back to `navigator.sendBeacon` on `pagehide` / final flush.\n */\n protected async postRaw(\n callType: string,\n body: Record<string, unknown>,\n options: { useBeacon?: boolean } = {},\n ): Promise<void> {\n if (FlowGridTransport.consent.analytics === false) return;\n if (this.transportConfig.respectDNT !== false && this.isDNTEnabled()) return;\n\n const url = `${this.endpoint}/api/v1/sdk/functions?call=${encodeURIComponent(callType)}`;\n const resolvedVisitorId = this.identityManager\n ? this.identityManager.getVisitorId()\n : (this.visitorId ?? '');\n\n const payload = JSON.stringify({\n web_id: this.webId,\n visitor_id: resolvedVisitorId,\n ...body,\n });\n\n // Transport strategy:\n // - Regular flushes use plain fetch (no keepalive). `fetch + keepalive`\n // has a hard 64 KB request-body limit per the spec — anything larger\n // (notably the rrweb FullSnapshot chunk, which is typically 100KB-500KB+)\n // is silently rejected by the browser, dropping sequence 0 and leaving\n // the player with no DOM to render.\n // - Unload flushes (visibilitychange/pagehide/stop with `useBeacon: true`)\n // MUST use keepalive so the request survives navigation. If the payload\n // is also too large for keepalive, fall back to navigator.sendBeacon —\n // it can't set Authorization headers, so the server's body-auth fallback\n // (api_key in the JSON body) picks it up via the proxy middleware.\n const KEEPALIVE_BYTE_LIMIT = 60_000; // a hair under the 64 KiB spec cap\n const isUnload = options.useBeacon === true;\n const payloadSize = payload.length;\n const useKeepalive = isUnload && payloadSize <= KEEPALIVE_BYTE_LIMIT;\n\n if (isUnload && payloadSize > KEEPALIVE_BYTE_LIMIT) {\n // Too large for keepalive; sendBeacon survives unload but strips headers.\n // Inject api_key into the body so the proxy middleware's body-auth fallback\n // can authenticate the request without an Authorization header.\n const beaconBody = JSON.stringify({ ...JSON.parse(payload), api_key: this.apiKey });\n try {\n const ok = typeof navigator !== 'undefined'\n && typeof navigator.sendBeacon === 'function'\n && navigator.sendBeacon(url, new Blob([beaconBody], { type: 'application/json' }));\n if (!ok && typeof console !== 'undefined') {\n console.warn(`[Flowgrid] ${callType} beacon rejected (payload ${payloadSize} bytes)`);\n }\n } catch (err) {\n if (typeof console !== 'undefined') {\n console.warn(`[Flowgrid] ${callType} beacon error:`, err);\n }\n }\n return;\n }\n\n try {\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n },\n body: payload,\n ...(useKeepalive ? { keepalive: true } : {}),\n });\n // Log server rejections so developers can diagnose silently-dropped chunks\n // (e.g. invalid API key → 403, missing fields → 400, chunk too large → 413).\n if (!res.ok && typeof console !== 'undefined') {\n console.warn(`[Flowgrid] ${callType} rejected: ${res.status} ${res.statusText} (${payloadSize} bytes)`);\n }\n } catch (err) {\n // Best-effort, but surface the failure so silently-dropped chunks are\n // visible during development.\n if (typeof console !== 'undefined') {\n console.warn(`[Flowgrid] ${callType} network error (${payloadSize} bytes):`, err);\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // EVENT BUFFER (localStorage persistence)\n // ---------------------------------------------------------------------------\n\n private bufferEvent(url: string, method: string, body?: string): void {\n if (typeof localStorage === 'undefined') return;\n try {\n const raw = localStorage.getItem(FlowGridTransport.EVENT_BUFFER_KEY);\n const buffer: Array<{ url: string; method: string; body?: string }> = raw ? JSON.parse(raw) : [];\n\n if (buffer.length >= FlowGridTransport.MAX_BUFFER_EVENTS) return;\n\n const serialized = JSON.stringify([...buffer, { url, method, body }]);\n if (new TextEncoder().encode(serialized).length > FlowGridTransport.MAX_BUFFER_SIZE) return;\n\n buffer.push({ url, method, body });\n localStorage.setItem(FlowGridTransport.EVENT_BUFFER_KEY, JSON.stringify(buffer));\n } catch { /* storage unavailable */ }\n }\n\n private drainEventBuffer(): void {\n if (typeof localStorage === 'undefined') return;\n try {\n const raw = localStorage.getItem(FlowGridTransport.EVENT_BUFFER_KEY);\n if (!raw) return;\n const buffer: Array<{ url: string; method: string; body?: string }> = JSON.parse(raw);\n localStorage.removeItem(FlowGridTransport.EVENT_BUFFER_KEY);\n\n for (const evt of buffer) {\n // Best-effort replay; don't await\n fetch(evt.url, {\n method: evt.method,\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n },\n body: evt.body,\n }).catch(() => {});\n }\n } catch { /* storage unavailable */ }\n }\n\n /**\n * Core RPC transport method for standard SDK calls.\n *\n * @template T Expected JSON response type.\n *\n * @param eventName - Logical event or function name.\n * @param properties - Arbitrary event payload properties.\n * @param method - HTTP method (\"GET\", \"POST\", \"PATCH\").\n *\n * @returns Promise resolving to typed response.\n */\n protected async init<T>(\n eventName: string,\n properties: Record<string, any> = {},\n method: \"GET\" | \"POST\" | \"PATCH\" = \"POST\"\n ): Promise<T> {\n\n // Consent gate\n if (!FlowGridTransport.hasConsent('analytics')) {\n return {} as T;\n }\n\n // DNT / GPC gate\n if (this.transportConfig.respectDNT && this.isDNTEnabled()) {\n return {} as T;\n }\n\n // Bot detection\n if (this.transportConfig.botDetection === 'block' && FlowGridTransport.isLikelyBot()) {\n return {} as T;\n }\n\n this.validateInput(eventName, properties);\n\n // Bot tagging\n if (this.transportConfig.botDetection === 'tag' && FlowGridTransport.isLikelyBot()) {\n properties = { ...properties, _bot: true };\n }\n\n // Auto device enrichment\n const enriched = { ...FlowGridTransport.getDeviceContext(), ...properties };\n\n const callType = enriched.type || \"event\";\n const url = `${this.endpoint}/api/v1/sdk/functions?call=${encodeURIComponent(callType)}`;\n\n const resolvedVisitorId = this.identityManager\n ? this.identityManager.getVisitorId()\n : (this.visitorId ?? '');\n\n const body = JSON.stringify({\n web_id: this.webId,\n visitor_id: resolvedVisitorId,\n event_name: eventName,\n properties: enriched,\n });\n\n return this.executeRequest<T>(url, method, method === \"GET\" ? undefined : body);\n }\n\n /**\n * Feature-scoped RPC transport method.\n * Routes to the SDK API endpoint with a feature type qualifier.\n *\n * @param eventName - Name of the event to track.\n * @param properties - Additional properties to send (must include type).\n */\n protected async initFeature<T>(eventName: string, properties: Record<string, any> & { type: FeatureType }): Promise<T> {\n\n this.validateInput(eventName, properties);\n\n if (!properties.type) {\n throw new Error('Feature events must include a type property');\n }\n\n const url = `${this.endpoint}/api/v1/sdk/functions?call=${encodeURIComponent(properties.type)}`;\n\n const resolvedVisitorId = this.identityManager\n ? this.identityManager.getVisitorId()\n : (this.visitorId ?? '');\n\n const body = JSON.stringify({\n web_id: this.webId,\n visitor_id: resolvedVisitorId,\n event_name: eventName,\n properties,\n });\n\n return this.executeRequest(url, \"POST\", body);\n }\n\n /**\n * Executes HTTP request with timeout and retry handling.\n *\n * @template T Expected response type.\n *\n * @param url - Fully constructed request URL.\n * @param method - HTTP method.\n * @param body - Optional JSON body (POST/PATCH only).\n * @param extraHeaders - Additional headers to include.\n *\n * @throws {Error} On timeout, network failure, or non-OK HTTP status.\n */\n private async executeRequest<T>(\n url: string,\n method: \"GET\" | \"POST\" | \"PATCH\",\n body?: string,\n extraHeaders?: Record<string, string>\n ): Promise<T> {\n\n let attempt = 0;\n\n while (attempt <= FlowGridTransport.MAX_RETRIES) {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), FlowGridTransport.TIMEOUT_MS);\n\n try {\n const res = await fetch(url, {\n method,\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${this.apiKey}`,\n ...extraHeaders,\n },\n body,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n // Retry on 5xx (server transient errors)\n if (res.status >= 500 && attempt < FlowGridTransport.MAX_RETRIES) {\n attempt++;\n await this.backoff(attempt);\n continue;\n }\n\n if (!res.ok) {\n const text = await this.safeRead(res);\n throw new Error(\n `SDK request failed (${res.status} ${res.statusText}) ${text}`\n );\n }\n\n return await res.json();\n\n } catch (error) {\n clearTimeout(timeoutId);\n\n const isNetworkError =\n error instanceof Error &&\n (error.name === \"AbortError\" || error.message.includes(\"fetch\"));\n\n if (isNetworkError && attempt < FlowGridTransport.MAX_RETRIES) {\n attempt++;\n await this.backoff(attempt);\n continue;\n }\n\n // Final failure — try sendBeacon, then buffer to localStorage\n if (attempt >= FlowGridTransport.MAX_RETRIES) {\n const beaconSent = body && typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function'\n ? navigator.sendBeacon(url, new Blob([body], { type: 'application/json' }))\n : false;\n\n if (!beaconSent) {\n this.bufferEvent(url, method, body);\n }\n }\n\n if (error instanceof Error && error.name === \"AbortError\") {\n throw new Error(\"SDK request timed out\");\n }\n\n throw error;\n }\n }\n\n // Unreachable: the loop body always returns on success or throws on\n // permanent failure. The explicit throw satisfies TS now that the loop\n // condition is bounded (was `while (true)` previously).\n throw new Error(\"SDK request exhausted retries without resolution\");\n }\n\n /**\n * Exponential backoff with full jitter: ~1s, ~2s, ~4s (capped at 30s),\n * each randomised across its window.\n *\n * The jitter is the important part. Without it, every SDK instance that\n * failed during a backend blip retries at the *same* moment (t+1s, t+2s, …),\n * producing synchronized request waves — a self-inflicted thundering herd\n * that knocks the backend over again the instant it recovers. Spreading each\n * retry uniformly across its window de-synchronises the fleet so recovery\n * traffic ramps smoothly instead of spiking.\n */\n private backoff(attempt: number): Promise<void> {\n const ceiling = Math.min(Math.pow(2, attempt - 1) * 1000, 30_000);\n const ms = Math.random() * ceiling;\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n\n /**\n * Validates event name and payload.\n *\n * @param eventName - Event identifier.\n * @param properties - Associated event properties.\n *\n * @throws {Error} If validation fails.\n */\n private validateInput(eventName: string, properties: Record<string, any>) {\n\n if (!eventName || typeof eventName !== \"string\" || !eventName.trim()) {\n throw new Error(\"Event name must be a non-empty string\");\n }\n\n if (eventName.length > 100) {\n throw new Error(\"Event name must be less than 100 characters\");\n }\n\n let serialized: string;\n\n try {\n serialized = JSON.stringify({\n web_id: this.webId,\n visitor_id: this.visitorId ?? '',\n event_name: eventName,\n properties,\n });\n } catch {\n throw new Error(\"Properties must be JSON serializable\");\n }\n\n if (new TextEncoder().encode(serialized).length > FlowGridTransport.MAX_PAYLOAD_SIZE) {\n throw new Error(\"Event payload too large (max 10KB)\");\n }\n\n const sensitiveKeys = [\"password\", \"token\", \"secret\", \"apikey\", \"authorization\"];\n const lower = serialized.toLowerCase();\n\n for (const key of sensitiveKeys) {\n if (lower.includes(key)) {\n console.warn(\n `SDK Warning: Properties may contain sensitive data (${key}).`\n );\n }\n }\n }\n\n /**\n * Normalizes endpoint input.\n * Ensures protocol is present and removes trailing slashes.\n *\n * @param endpoint - Raw endpoint string.\n * @returns Normalized endpoint URL.\n */\n private normalizeEndpoint(endpoint: string): string {\n let clean = endpoint.trim();\n\n if (!clean.startsWith(\"http://\") && !clean.startsWith(\"https://\")) {\n clean = `https://${clean}`;\n }\n\n return clean.replace(/\\/+$/, \"\");\n }\n\n /**\n * Safely reads response text without throwing.\n *\n * @param res - Fetch Response object.\n * @returns Response text or empty string.\n */\n private async safeRead(res: Response): Promise<string> {\n try {\n return await res.text();\n } catch {\n return \"\";\n }\n }\n}\n","/**\n * @fileoverview Browser environment helpers.\n *\n * Tiny utilities to check for browser globals safely so feature classes can\n * expose `autoTrack()` methods that no-op gracefully on the server.\n */\n\n/** Safe `window` accessor; returns `undefined` outside the browser. */\nexport function getWindow(): (Window & typeof globalThis) | undefined {\n return typeof window !== \"undefined\" ? window : undefined;\n}\n\n/** Safe `document` accessor; returns `undefined` outside the browser. */\nexport function getDocument(): Document | undefined {\n return typeof document !== \"undefined\" ? document : undefined;\n}\n\n/** Returns true when running in a real browser-like environment. */\nexport function isBrowser(): boolean {\n return typeof window !== \"undefined\" && typeof document !== \"undefined\";\n}\n\n/** A teardown function returned by every `autoTrack()` API. */\nexport type Teardown = () => void;\n\n/** Compose multiple teardown functions into one. */\nexport function combineTeardowns(...fns: Array<Teardown | undefined | null | false>): Teardown {\n return () => {\n for (const fn of fns) {\n if (typeof fn === \"function\") {\n try { fn(); } catch { /* swallow */ }\n }\n }\n };\n}\n","/**\n * @fileoverview FlowGrid storage utilities.\n * @module utils/storage\n *\n * @description\n * Reads visitor identity and UTM attribution data from the same\n * cookie / localStorage / sessionStorage keys used by the FlowGrid\n * tracking scripts (track.js & flowgrid_oss_client_sdk.js).\n *\n * These functions are **read-only** — the tracking scripts remain the\n * single writer. The SDK only consumes these values.\n */\n\n// ============================================================================\n// COOKIE HELPERS\n// ============================================================================\n\n/**\n * Read a cookie value by name.\n * Returns `null` when the cookie is absent or document.cookie is unavailable.\n */\nexport function getCookie(name: string): string | null {\n if (typeof document === 'undefined') return null;\n const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));\n return match ? match[2] : null;\n}\n\n// ============================================================================\n// VISITOR IDENTITY\n// ============================================================================\n\n/**\n * Resolve the visitor ID written by track.js / flowgrid_oss_client_sdk.js.\n *\n * Storage locations (checked in order):\n * 1. cookie `visitor_id`\n * 2. localStorage `visitor_id`\n *\n * Returns `null` when neither source contains a value (e.g. tracking\n * script hasn't run yet, or running server-side).\n */\nexport function getVisitorId(): string | null {\n try {\n return getCookie('visitor_id') || localStorage.getItem('visitor_id');\n } catch {\n // localStorage unavailable (SSR, privacy mode, etc.)\n return getCookie('visitor_id');\n }\n}\n\n/**\n * Resolve the session ID written by track.js.\n *\n * Storage: `sessionStorage(\"session_id\")`\n */\nexport function getSessionId(): string | null {\n try {\n return sessionStorage.getItem('session_id');\n } catch {\n return null;\n }\n}\n\n/**\n * Resolve the web_id written by track.js.\n *\n * Storage: `localStorage(\"web_id\")`\n */\nexport function getWebId(): string | null {\n try {\n return localStorage.getItem('web_id');\n } catch {\n return null;\n }\n}\n\n// ============================================================================\n// UTM / ATTRIBUTION\n// ============================================================================\n\nexport interface FlowGridUTM {\n utmSource?: string;\n utmMedium?: string;\n utmCampaign?: string;\n utmTerm?: string;\n utmContent?: string;\n ref?: string;\n source?: string;\n via?: string;\n}\n\n/**\n * Read UTM & attribution parameters persisted by\n * flowgrid_oss_client_sdk.js as `__flowgrid_*` cookies.\n */\nexport function getUTM(): FlowGridUTM {\n return {\n utmSource: getCookie('__flowgrid_utm_source') || undefined,\n utmMedium: getCookie('__flowgrid_utm_medium') || undefined,\n utmCampaign: getCookie('__flowgrid_utm_campaign') || undefined,\n utmTerm: getCookie('__flowgrid_utm_term') || undefined,\n utmContent: getCookie('__flowgrid_utm_content') || undefined,\n ref: getCookie('__flowgrid_ref') || undefined,\n source: getCookie('__flowgrid_source') || undefined,\n via: getCookie('__flowgrid_via') || undefined,\n };\n}\n\nexport interface FlowGridUTMHistoryEntry {\n timestamp: string;\n utm_source?: string;\n utm_medium?: string;\n utm_campaign?: string;\n utm_content?: string;\n utm_term?: string;\n}\n\n/**\n * Read the full UTM history array persisted by flowgrid_oss_client_sdk.js.\n *\n * Checks localStorage first, then the `utm_history` cookie.\n */\nexport function getUTMHistory(): FlowGridUTMHistoryEntry[] {\n try {\n const raw =\n localStorage.getItem('utm_history') ||\n getCookie('utm_history');\n return raw ? JSON.parse(raw) : [];\n } catch {\n return [];\n }\n}\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n// expose the modules object (__webpack_modules__)\n__webpack_require__.m = __webpack_modules__;\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.f = {};\n// This file contains only the entry chunk.\n// The chunk loading function for additional chunks\n__webpack_require__.e = (chunkId) => {\n\treturn Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => {\n\t\t__webpack_require__.f[key](chunkId, promises);\n\t\treturn promises;\n\t}, []));\n};","// This function allow to reference async chunks\n__webpack_require__.u = (chunkId) => {\n\t// return url for filenames based on template\n\treturn \"\" + {\"137\":\"flowgrid-enterprise\",\"717\":\"flowgrid-vitals\",\"897\":\"flowgrid-replay\"}[chunkId] + \".flowgrid.chunk.js\";\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","var scriptUrl;\nif (__webpack_require__.g.importScripts) scriptUrl = __webpack_require__.g.location + \"\";\nvar document = __webpack_require__.g.document;\nif (!scriptUrl && document) {\n\tif (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT')\n\t\tscriptUrl = document.currentScript.src;\n\tif (!scriptUrl) {\n\t\tvar scripts = document.getElementsByTagName(\"script\");\n\t\tif(scripts.length) {\n\t\t\tvar i = scripts.length - 1;\n\t\t\twhile (i > -1 && (!scriptUrl || !/^http(s?):/.test(scriptUrl))) scriptUrl = scripts[i--].src;\n\t\t}\n\t}\n}\n// When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration\n// or pass an empty string (\"\") and set the __webpack_public_path__ variable from your code to use your own logic.\nif (!scriptUrl) throw new Error(\"Automatic publicPath is not supported in this browser\");\nscriptUrl = scriptUrl.replace(/^blob:/, \"\").replace(/#.*$/, \"\").replace(/\\?.*$/, \"\").replace(/\\/[^\\/]+$/, \"/\");\n__webpack_require__.p = scriptUrl;","// no baseURI\n\n// object to store loaded and loading chunks\n// undefined = chunk not loaded, null = chunk preloaded/prefetched\n// [resolve, reject, Promise] = chunk loading, 0 = chunk loaded\nvar installedChunks = {\n\t792: 0\n};\n\n__webpack_require__.f.j = (chunkId, promises) => {\n\t\t// JSONP chunk loading for javascript\n\t\tvar installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;\n\t\tif(installedChunkData !== 0) { // 0 means \"already installed\".\n\n\t\t\t// a Promise means \"currently loading\".\n\t\t\tif(installedChunkData) {\n\t\t\t\tpromises.push(installedChunkData[2]);\n\t\t\t} else {\n\t\t\t\tif(true) { // all chunks have JS\n\t\t\t\t\t// setup Promise in chunk cache\n\t\t\t\t\tvar promise = new Promise((resolve, reject) => (installedChunkData = installedChunks[chunkId] = [resolve, reject]));\n\t\t\t\t\tpromises.push(installedChunkData[2] = promise);\n\n\t\t\t\t\t// start chunk loading\n\t\t\t\t\tvar url = __webpack_require__.p + __webpack_require__.u(chunkId);\n\t\t\t\t\t// create error before stack unwound to get useful stacktrace later\n\t\t\t\t\tvar error = new Error();\n\t\t\t\t\tvar loadingEnded = (event) => {\n\t\t\t\t\t\tif(__webpack_require__.o(installedChunks, chunkId)) {\n\t\t\t\t\t\t\tinstalledChunkData = installedChunks[chunkId];\n\t\t\t\t\t\t\tif(installedChunkData !== 0) installedChunks[chunkId] = undefined;\n\t\t\t\t\t\t\tif(installedChunkData) {\n\t\t\t\t\t\t\t\tvar errorType = event && (event.type === 'load' ? 'missing' : event.type);\n\t\t\t\t\t\t\t\tvar realSrc = event && event.target && event.target.src;\n\t\t\t\t\t\t\t\terror.message = 'Loading chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';\n\t\t\t\t\t\t\t\terror.name = 'ChunkLoadError';\n\t\t\t\t\t\t\t\terror.type = errorType;\n\t\t\t\t\t\t\t\terror.request = realSrc;\n\t\t\t\t\t\t\t\tinstalledChunkData[1](error);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t\t__webpack_require__.l(url, loadingEnded, \"chunk-\" + chunkId, chunkId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n};\n\n// no prefetching\n\n// no preloaded\n\n// no HMR\n\n// no HMR manifest\n\n// no on chunks loaded\n\n// install a JSONP callback for chunk loading\nvar webpackJsonpCallback = (parentChunkLoadingFunction, data) => {\n\tvar [chunkIds, moreModules, runtime] = data;\n\t// add \"moreModules\" to the modules object,\n\t// then flag all \"chunkIds\" as loaded and fire callback\n\tvar moduleId, chunkId, i = 0;\n\tif(chunkIds.some((id) => (installedChunks[id] !== 0))) {\n\t\tfor(moduleId in moreModules) {\n\t\t\tif(__webpack_require__.o(moreModules, moduleId)) {\n\t\t\t\t__webpack_require__.m[moduleId] = moreModules[moduleId];\n\t\t\t}\n\t\t}\n\t\tif(runtime) var result = runtime(__webpack_require__);\n\t}\n\tif(parentChunkLoadingFunction) parentChunkLoadingFunction(data);\n\tfor(;i < chunkIds.length; i++) {\n\t\tchunkId = chunkIds[i];\n\t\tif(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) {\n\t\t\tinstalledChunks[chunkId][0]();\n\t\t}\n\t\tinstalledChunks[chunkId] = 0;\n\t}\n\n}\n\nvar chunkLoadingGlobal = this[\"webpackChunkflowgrid\"] = this[\"webpackChunkflowgrid\"] || [];\nchunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));\nchunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));","/**\n * @fileoverview FlowGrid Identity Manager.\n * @module utils/identity\n *\n * @description\n * Owns the visitor/session identity lifecycle.\n * Writes its own cookies/storage so the SDK can self-bootstrap\n * without depending on an external tracking script (track.js).\n *\n * Backward-compatible: checks for existing track.js `visitor_id`\n * before falling back to its own `fg_visitor_id` key.\n */\n\nimport { getCookie, getVisitorId as getTrackJsVisitorId } from './storage';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\nexport interface IdentityConfig {\n /** Explicit visitor ID (overrides auto-resolution) */\n visitorId?: string;\n /** Cookie domain (defaults to current hostname) */\n cookieDomain?: string;\n /** Visitor cookie max-age in days (default: 365) */\n visitorMaxAgeDays?: number;\n /** Session timeout in minutes (default: 30) */\n sessionTimeoutMinutes?: number;\n /** Storage strategy */\n storage?: 'cookie' | 'localStorage' | 'memory';\n /** Server-side cookie reader (e.g. Next.js `cookies()`) */\n serverCookies?: ServerCookieReader;\n /** Write to `visitor_id` (track.js compat) instead of `fg_visitor_id` (default true) */\n compat?: boolean;\n}\n\n/**\n * Minimal interface for reading cookies server-side.\n * Compatible with Next.js `cookies()` and Nuxt `useCookie()`.\n */\nexport interface ServerCookieReader {\n get(name: string): { value: string } | string | undefined | null;\n}\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst LEGACY_VISITOR_COOKIE = 'fg_visitor_id';\nconst COMPAT_VISITOR_COOKIE = 'visitor_id';\nconst SESSION_KEY = 'fg_session_id';\nconst SESSION_TOUCH_KEY = 'fg_session_touch';\n\n// ============================================================================\n// IDENTITY MANAGER\n// ============================================================================\n\nexport class IdentityManager {\n private visitorId: string = '';\n private sessionId: string = '';\n private config: Required<Omit<IdentityConfig, 'serverCookies'>> & { serverCookies?: ServerCookieReader; compat: boolean };\n\n /** In-memory store for SSR / Node environments */\n private memoryStore: Record<string, string> = {};\n\n constructor(config?: IdentityConfig) {\n this.config = {\n visitorId: config?.visitorId ?? '',\n cookieDomain: config?.cookieDomain ?? '',\n visitorMaxAgeDays: config?.visitorMaxAgeDays ?? 365,\n sessionTimeoutMinutes: config?.sessionTimeoutMinutes ?? 30,\n storage: config?.storage ?? (this.detectBrowser() ? 'cookie' : 'memory'),\n serverCookies: config?.serverCookies,\n compat: config?.compat ?? true,\n };\n\n this.visitorId = this.resolveVisitorId();\n this.sessionId = '';\n }\n\n // ---------------------------------------------------------------------------\n // PUBLIC API\n // ---------------------------------------------------------------------------\n\n /**\n * Get or create a persistent visitor ID.\n *\n * Resolution order:\n * 1. Explicit config.visitorId\n * 2. track.js `visitor_id` cookie/localStorage (backward compat)\n * 3. `fg_visitor_id` cookie\n * 4. `fg_visitor_id` localStorage\n * 5. Server cookie reader (if provided)\n * 6. Generate new UUID v4, persist to cookie + localStorage\n */\n getVisitorId(): string {\n if (this.visitorId) return this.visitorId;\n this.visitorId = this.resolveVisitorId();\n return this.visitorId;\n }\n\n /**\n * Get or create a session ID.\n *\n * - Reads from sessionStorage `fg_session_id`\n * - If absent or expired (>sessionTimeoutMinutes since last activity), creates new\n * - Persists to sessionStorage\n * - Touches timestamp on every call\n */\n getSessionId(): string {\n if (this.config.storage === 'memory') {\n return this.getMemorySessionId();\n }\n\n if (!this.detectBrowser()) {\n return this.getMemorySessionId();\n }\n\n try {\n const existing = sessionStorage.getItem(SESSION_KEY);\n const lastTouch = sessionStorage.getItem(SESSION_TOUCH_KEY);\n\n if (existing && lastTouch) {\n const elapsed = Date.now() - parseInt(lastTouch, 10);\n const timeoutMs = this.config.sessionTimeoutMinutes * 60 * 1000;\n if (elapsed < timeoutMs) {\n sessionStorage.setItem(SESSION_TOUCH_KEY, String(Date.now()));\n this.sessionId = existing;\n return existing;\n }\n }\n\n // Create new session\n const newSession = this.generateSessionId();\n sessionStorage.setItem(SESSION_KEY, newSession);\n sessionStorage.setItem(SESSION_TOUCH_KEY, String(Date.now()));\n this.sessionId = newSession;\n return newSession;\n } catch {\n return this.getMemorySessionId();\n }\n }\n\n /**\n * Update the visitor ID (e.g. after identify() links anonymous → known user).\n * Persists to cookie + localStorage.\n */\n setVisitorId(id: string): void {\n this.visitorId = id;\n this.persistVisitorId(id);\n }\n\n /** Clear session (e.g. on logout). */\n clearSession(): void {\n this.sessionId = '';\n delete this.memoryStore[SESSION_KEY];\n delete this.memoryStore[SESSION_TOUCH_KEY];\n\n if (this.detectBrowser()) {\n try {\n sessionStorage.removeItem(SESSION_KEY);\n sessionStorage.removeItem(SESSION_TOUCH_KEY);\n } catch { /* SSR / privacy mode */ }\n }\n }\n\n /** Clear all identity (GDPR). Removes visitor + session + experiment assignments. */\n clearAll(): void {\n this.visitorId = '';\n this.sessionId = '';\n this.memoryStore = {};\n\n if (!this.detectBrowser()) return;\n\n try {\n // Cookies\n this.deleteCookie(this.visitorCookieName);\n this.deleteCookie(LEGACY_VISITOR_COOKIE);\n\n // localStorage\n localStorage.removeItem(this.visitorCookieName);\n localStorage.removeItem(LEGACY_VISITOR_COOKIE);\n localStorage.removeItem('fg_exp_assignments');\n\n // sessionStorage\n sessionStorage.removeItem(SESSION_KEY);\n sessionStorage.removeItem(SESSION_TOUCH_KEY);\n } catch { /* SSR / privacy mode */ }\n }\n\n /** Check if running in a browser environment. */\n isBrowser(): boolean {\n return this.detectBrowser();\n }\n\n // ---------------------------------------------------------------------------\n // PRIVATE — VISITOR RESOLUTION\n // ---------------------------------------------------------------------------\n\n /** Returns the primary cookie name based on compat mode */\n private get visitorCookieName(): string {\n return this.config.compat ? COMPAT_VISITOR_COOKIE : LEGACY_VISITOR_COOKIE;\n }\n\n private resolveVisitorId(): string {\n // 1. Explicit config\n if (this.config.visitorId) return this.config.visitorId;\n\n // 2. track.js backward compat (cookie/localStorage `visitor_id`)\n if (this.detectBrowser()) {\n const trackJsId = getTrackJsVisitorId();\n if (trackJsId) {\n // Migration: if fg_visitor_id exists with a different value, prefer visitor_id (track.js was first)\n const legacyId = this.readCookie(LEGACY_VISITOR_COOKIE) || this.readLocalStorage(LEGACY_VISITOR_COOKIE);\n if (legacyId && legacyId !== trackJsId) {\n // Merge identities by keeping track.js id as canonical\n this.removeLegacyCookie();\n }\n this.persistVisitorId(trackJsId);\n return trackJsId;\n }\n }\n\n // 3. Migration: fg_visitor_id → visitor_id\n if (this.config.compat) {\n const legacyId = this.readCookie(LEGACY_VISITOR_COOKIE) || this.readLocalStorage(LEGACY_VISITOR_COOKIE);\n if (legacyId) {\n this.persistVisitorId(legacyId);\n this.removeLegacyCookie();\n return legacyId;\n }\n }\n\n // 4. Own cookie\n const cookieId = this.readCookie(this.visitorCookieName);\n if (cookieId) return cookieId;\n\n // 5. localStorage\n if (this.detectBrowser()) {\n try {\n const lsId = localStorage.getItem(this.visitorCookieName);\n if (lsId) return lsId;\n } catch { /* unavailable */ }\n }\n\n // 6. Server cookie reader\n if (this.config.serverCookies) {\n const serverVal = this.readServerCookie(this.visitorCookieName);\n if (serverVal) return serverVal;\n }\n\n // 7. Memory store\n if (this.memoryStore[this.visitorCookieName]) {\n return this.memoryStore[this.visitorCookieName];\n }\n\n // 8. Generate new ID\n const newId = this.generateVisitorId();\n this.persistVisitorId(newId);\n return newId;\n }\n\n private readLocalStorage(key: string): string | null {\n if (!this.detectBrowser()) return null;\n try { return localStorage.getItem(key); } catch { return null; }\n }\n\n private removeLegacyCookie(): void {\n this.deleteCookie(LEGACY_VISITOR_COOKIE);\n if (this.detectBrowser()) {\n try { localStorage.removeItem(LEGACY_VISITOR_COOKIE); } catch { /* */ }\n }\n }\n\n private persistVisitorId(id: string): void {\n const cookieName = this.visitorCookieName;\n\n if (this.config.storage === 'memory') {\n this.memoryStore[cookieName] = id;\n return;\n }\n\n if (!this.detectBrowser()) {\n this.memoryStore[cookieName] = id;\n return;\n }\n\n // Cookie\n this.writeCookie(cookieName, id, this.config.visitorMaxAgeDays);\n\n // localStorage as fallback\n try {\n localStorage.setItem(cookieName, id);\n } catch { /* unavailable */ }\n }\n\n // ---------------------------------------------------------------------------\n // PRIVATE — SESSION HELPERS\n // ---------------------------------------------------------------------------\n\n private getMemorySessionId(): string {\n const existing = this.memoryStore[SESSION_KEY];\n const lastTouch = this.memoryStore[SESSION_TOUCH_KEY];\n\n if (existing && lastTouch) {\n const elapsed = Date.now() - parseInt(lastTouch, 10);\n const timeoutMs = this.config.sessionTimeoutMinutes * 60 * 1000;\n if (elapsed < timeoutMs) {\n this.memoryStore[SESSION_TOUCH_KEY] = String(Date.now());\n this.sessionId = existing;\n return existing;\n }\n }\n\n const newSession = this.generateSessionId();\n this.memoryStore[SESSION_KEY] = newSession;\n this.memoryStore[SESSION_TOUCH_KEY] = String(Date.now());\n this.sessionId = newSession;\n return newSession;\n }\n\n // ---------------------------------------------------------------------------\n // PRIVATE — COOKIE OPERATIONS\n // ---------------------------------------------------------------------------\n\n private readCookie(name: string): string | null {\n // Server-side reader first\n if (this.config.serverCookies) {\n return this.readServerCookie(name);\n }\n\n // Browser cookie\n if (this.detectBrowser()) {\n return getCookie(name);\n }\n\n return null;\n }\n\n private readServerCookie(name: string): string | null {\n if (!this.config.serverCookies) return null;\n const result = this.config.serverCookies.get(name);\n if (!result) return null;\n // Next.js cookies().get() returns { value: string }\n if (typeof result === 'object' && 'value' in result) return result.value || null;\n // Nuxt useCookie() returns string directly\n if (typeof result === 'string') return result || null;\n return null;\n }\n\n private writeCookie(name: string, value: string, maxAgeDays: number): void {\n if (!this.detectBrowser()) return;\n\n const maxAge = maxAgeDays * 24 * 60 * 60;\n const domain = this.config.cookieDomain ? `; Domain=${this.config.cookieDomain}` : '';\n const secure = location.protocol === 'https:' ? '; Secure' : '';\n\n document.cookie = `${name}=${encodeURIComponent(value)}; Path=/; Max-Age=${maxAge}; SameSite=Lax${secure}${domain}`;\n }\n\n private deleteCookie(name: string): void {\n if (!this.detectBrowser()) return;\n const domain = this.config.cookieDomain ? `; Domain=${this.config.cookieDomain}` : '';\n document.cookie = `${name}=; Path=/; Max-Age=0${domain}`;\n }\n\n // ---------------------------------------------------------------------------\n // PRIVATE — ID GENERATION\n // ---------------------------------------------------------------------------\n\n private generateVisitorId(): string {\n return `fg_${this.uuidV4()}`;\n }\n\n private generateSessionId(): string {\n return `fgs_${Date.now()}_${this.randomHex(8)}`;\n }\n\n private uuidV4(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for environments without crypto.randomUUID\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n }\n\n private randomHex(length: number): string {\n const bytes = new Uint8Array(length);\n if (typeof crypto !== 'undefined' && crypto.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < length; i++) bytes[i] = Math.floor(Math.random() * 256);\n }\n return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n }\n\n // ---------------------------------------------------------------------------\n // PRIVATE — ENVIRONMENT DETECTION\n // ---------------------------------------------------------------------------\n\n private detectBrowser(): boolean {\n return typeof window !== 'undefined' && typeof document !== 'undefined';\n }\n}\n","/**\n * @fileoverview Core - User Activation Tracking\n * @module features/core/activation\n * \n * @description\n * Track user activation events including onboarding steps,\n * signup completions, and first-time actions.\n * \n * @useCases\n * - Track onboarding step completions\n * - Monitor signup funnel\n * - Measure time to first value\n * - Identify activation bottlenecks\n * - A/B test onboarding flows\n * \n * @metrics\n * - Activation rate\n * - Onboarding completion rate\n * - Time to activate\n * - Drop-off by step\n * - Activation by source\n * \n * @chartData\n * - Funnel chart: Onboarding steps\n * - Line chart: Activation rate over time\n * - Bar chart: Activation by cohort/source\n * - KPI: Time to first value\n * \n * @example\n * ```typescript\n * const activation = new Activation(webId, endpoint, apiKey);\n * \n * // Track activation event\n * await activation.create({\n * eventName: 'onboarding_completed',\n * properties: { userId: 'user_123', step: 'profile_setup' }\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { TimeSeriesData } from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Base configuration for activation events.\n */\nexport interface ActivationConfig {\n /** Event name */\n eventName: string;\n /** Event properties */\n properties: Record<string, unknown>;\n}\n\n/**\n * Onboarding step configuration.\n */\nexport interface OnboardingStepConfig {\n /** User ID */\n userId: string;\n /** Step identifier */\n stepId: string;\n /** Step name */\n stepName: string;\n /** Step order/number */\n stepOrder: number;\n /** Total steps in flow */\n totalSteps: number;\n /** Time spent on step (ms) */\n timeSpent?: number;\n /** Whether step was skipped */\n skipped?: boolean;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * First action configuration.\n */\nexport interface FirstActionConfig {\n /** User ID */\n userId: string;\n /** Action name */\n actionName: string;\n /** Time since signup (ms) */\n timeSinceSignup: number;\n /** Source/referrer */\n source?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Activation response from API.\n */\nexport interface ActivationResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n tracked: boolean;\n activationId?: string;\n isFirstActivation?: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Activation analytics data.\n */\nexport interface ActivationAnalyticsData {\n /** Summary metrics */\n summary: {\n totalSignups: number;\n activatedUsers: number;\n activationRate: number;\n avgTimeToActivate: number;\n onboardingCompletionRate: number;\n };\n /** Activation by step */\n byStep: Array<{\n stepId: string;\n stepName: string;\n started: number;\n completed: number;\n dropOff: number;\n avgTimeSpent: number;\n }>;\n /** Activation by source */\n bySource: Array<{\n source: string;\n signups: number;\n activated: number;\n activationRate: number;\n }>;\n /** Activation trends */\n trends: TimeSeriesData;\n /** Cohort activation */\n byCohort: Array<{\n cohort: string;\n users: number;\n activated: number;\n activationRate: number;\n avgTimeToActivate: number;\n }>;\n}\n\n// ============================================================================\n// ACTIVATION CLASS\n// ============================================================================\n\n/**\n * Activation - Track user activation events.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides activation tracking including:\n * - Generic activation events\n * - Onboarding step tracking\n * - First action tracking\n * - Signup completion\n * - Activation analytics\n * \n * @example\n * ```typescript\n * const activation = new Activation(webId, endpoint, apiKey);\n * \n * // Track onboarding step\n * await activation.trackOnboardingStep({\n * userId: 'user_123',\n * stepId: 'profile_setup',\n * stepName: 'Set Up Profile',\n * stepOrder: 2,\n * totalSteps: 5\n * });\n * \n * // Track first value action\n * await activation.trackFirstAction({\n * userId: 'user_123',\n * actionName: 'created_first_project',\n * timeSinceSignup: 3600000 // 1 hour\n * });\n * ```\n */\nexport class Activation extends FlowGridTransport {\n /**\n * Creates and tracks an activation event.\n * \n * @param config - Activation configuration\n * @returns Promise resolving to activation response\n */\n async create(config: ActivationConfig): Promise<ActivationResponse> {\n return await this.init(config.eventName, {\n ...config.properties,\n type: \"activation\",\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks an onboarding step completion.\n * \n * @param config - Onboarding step configuration\n * @returns Promise resolving to activation response\n */\n async trackOnboardingStep(config: OnboardingStepConfig): Promise<ActivationResponse> {\n return await this.init(\"onboarding_step\", {\n type: \"activation\",\n subtype: \"onboarding_step\",\n userId: config.userId,\n stepId: config.stepId,\n stepName: config.stepName,\n stepOrder: config.stepOrder,\n totalSteps: config.totalSteps,\n progress: Math.round((config.stepOrder / config.totalSteps) * 100),\n timeSpent: config.timeSpent,\n skipped: config.skipped || false,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks onboarding completion.\n * \n * @param userId - User ID\n * @param totalTimeSpent - Total time in onboarding (ms)\n * @param stepsCompleted - Number of steps completed\n * @param stepsSkipped - Number of steps skipped\n * @returns Promise resolving to activation response\n */\n async completeOnboarding(\n userId: string,\n totalTimeSpent: number,\n stepsCompleted: number,\n stepsSkipped: number\n ): Promise<ActivationResponse> {\n return await this.init(\"onboarding_completed\", {\n type: \"activation\",\n subtype: \"onboarding_complete\",\n userId,\n totalTimeSpent,\n stepsCompleted,\n stepsSkipped,\n completionRate: Math.round((stepsCompleted / (stepsCompleted + stepsSkipped)) * 100),\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks user's first meaningful action (activation moment).\n * \n * @param config - First action configuration\n * @returns Promise resolving to activation response\n */\n async trackFirstAction(config: FirstActionConfig): Promise<ActivationResponse> {\n return await this.init(\"first_action\", {\n type: \"activation\",\n subtype: \"first_action\",\n userId: config.userId,\n actionName: config.actionName,\n timeSinceSignup: config.timeSinceSignup,\n source: config.source,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks signup completion.\n * \n * @param userId - User ID\n * @param method - Signup method (email, google, etc.)\n * @param source - Traffic source\n * @param properties - Additional properties\n * @returns Promise resolving to activation response\n */\n async trackSignup(\n userId: string,\n method: 'email' | 'google' | 'github' | 'apple' | 'sso' | string,\n source?: string,\n properties?: Record<string, unknown>\n ): Promise<ActivationResponse> {\n return await this.init(\"signup_completed\", {\n type: \"activation\",\n subtype: \"signup\",\n userId,\n method,\n source,\n ...properties,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default Activation;\n","/**\n * @fileoverview Core - Feature Usage Tracking\n * @module features/core/track-feature\n * \n * @description\n * Track feature usage events to understand how users\n * interact with different parts of your product.\n * \n * @useCases\n * - Track feature adoption rates\n * - Measure feature engagement\n * - Identify power users\n * - Prioritize feature development\n * - Detect underused features\n * \n * @metrics\n * - Feature adoption rate\n * - Feature usage frequency\n * - Users per feature\n * - Feature stickiness\n * - Time spent per feature\n * \n * @chartData\n * - Bar chart: Most used features\n * - Heatmap: Feature usage by user segment\n * - Line chart: Feature adoption over time\n * - Table: Feature engagement metrics\n * \n * @example\n * ```typescript\n * const trackFeature = new TrackFeature(webId, endpoint, apiKey);\n * \n * await trackFeature.create({\n * userId: 'user_123',\n * action: 'export_report',\n * properties: { featureName: 'export_report', featureId: 'export_report_001' }\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { TimeSeriesData } from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n\ntype FeatureConfigProperties = {\n featureId?: string;\n featureName: string;\n metadata?: Record<string, unknown>;\n};\n\n/**\n * Base configuration for feature tracking.\n */\nexport interface FeatureConfig {\n /** Feature tracking properties */\n action: string;\n\n /** Feature category */\n category?: string;\n\n userId: string;\n\n /** Event properties */\n properties: FeatureConfigProperties;\n\n}\n\n/**\n * Detailed feature usage configuration.\n */\nexport interface FeatureUsageConfig {\n /** Feature identifier */\n featureId: string;\n /** Feature name */\n featureName: string;\n /** User ID */\n userId?: string;\n /** Usage action */\n action: 'viewed' | 'used' | 'completed' | 'abandoned';\n /** Time spent (ms) */\n duration?: number;\n /** Success/failure */\n success?: boolean;\n /** Feature category */\n category?: string;\n /**\n * Wire event name to emit. Defaults to `feature_usage`. The `.track()`\n * router passes the lifecycle-specific name here (`feature_viewed`,\n * `feature_completed`, etc.) so consumers can filter on a distinct\n * `event_name`; `action`/`subtype` still carry the lifecycle stage.\n */\n eventName?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Feature discovery configuration.\n */\nexport interface FeatureDiscoveryConfig {\n /** Feature identifier */\n featureId: string;\n /** User ID */\n userId: string;\n /** How feature was discovered */\n discoveryMethod: 'organic' | 'tooltip' | 'announcement' | 'search' | 'onboarding' | 'recommendation' | string;\n /** Whether user engaged with feature */\n engaged: boolean;\n}\n\n/**\n * Feature tracking response.\n */\nexport interface FeatureResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n tracked: boolean;\n featureId?: string;\n isFirstUse?: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Feature analytics data.\n */\nexport interface FeatureAnalyticsData {\n /** Summary metrics */\n summary: {\n totalFeatures: number;\n totalUsage: number;\n uniqueUsers: number;\n avgUsagePerUser: number;\n mostUsedFeature: string;\n leastUsedFeature: string;\n };\n /** Usage by feature */\n byFeature: Array<{\n featureId: string;\n featureName: string;\n category: string;\n usageCount: number;\n uniqueUsers: number;\n adoptionRate: number;\n avgDuration: number;\n successRate: number;\n }>;\n /** Usage by user segment */\n bySegment: Array<{\n segment: string;\n topFeatures: string[];\n avgFeaturesUsed: number;\n }>;\n /** Feature trends */\n trends: TimeSeriesData;\n /** Feature correlations */\n correlations: Array<{\n featureA: string;\n featureB: string;\n correlation: number;\n }>;\n}\n\n// ============================================================================\n// TRACK FEATURE CLASS\n// ============================================================================\n\n/**\n * TrackFeature - Track feature usage events.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides feature tracking including:\n * - Generic feature events\n * - Detailed usage tracking\n * - Feature discovery tracking\n * \n * @example\n * ```typescript\n * const trackFeature = new TrackFeature(webId, endpoint, apiKey);\n * \n * // Simple feature tracking\n * await trackFeature.create({\n * userId: 'user_123',\n * action: 'dark_mode_enabled',\n * properties: { featureName: 'dark_mode' }\n * });\n * \n * // Detailed feature usage\n * await trackFeature.trackUsage({\n * featureId: 'export',\n * featureName: 'Data Export',\n * userId: 'user_123',\n * action: 'completed',\n * duration: 5000,\n * success: true,\n * properties: { format: 'csv' }\n * });\n * ```\n */\n\nexport class TrackFeature extends FlowGridTransport {\n /**\n * Records a feature usage event.\n * \n * @param config - Feature configuration\n * @returns Promise resolving to feature response\n */\n async create(config: FeatureConfig): Promise<FeatureResponse> {\n return await this.init(\"create_tracking\", {\n type: \"feature_usage\",\n subtype: \"create_custom\",\n userId: config.userId,\n category: config.category,\n action: config.action,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks detailed feature usage.\n * \n * @param config - Feature usage configuration\n * @returns Promise resolving to feature response\n */\n async trackUsage(config: FeatureUsageConfig): Promise<FeatureResponse> {\n return await this.init(config.eventName ?? \"feature_usage\", {\n type: \"feature_usage\",\n subtype: config.action,\n featureId: config.featureId,\n featureName: config.featureName,\n userId: config.userId,\n action: config.action,\n duration: config.duration,\n success: config.success,\n category: config.category,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default TrackFeature;\n","/**\n * @fileoverview Core - Prompt & AI Interaction Tracking\n * @module features/core/track-prompt\n * \n * @description\n * Track AI prompt interactions, completions, and user feedback\n * for AI-powered features.\n * \n * @useCases\n * - Track AI prompt submissions\n * - Monitor response quality\n * - Measure user satisfaction\n * - Analyze prompt patterns\n * - Optimize AI features\n * \n * @metrics\n * - Prompts per user\n * - Response acceptance rate\n * - Average response time\n * - User satisfaction score\n * - Token usage\n * \n * @chartData\n * - Line chart: Prompts over time\n * - Bar chart: Prompts by category\n * - Histogram: Response time distribution\n * - Gauge: Satisfaction score\n * \n * @example\n * ```typescript\n * const trackPrompt = new TrackPrompt(webId, endpoint, apiKey);\n * \n * await trackPrompt.create({\n * eventName: 'ai_completion',\n * properties: { \n * promptType: 'code_suggestion',\n * accepted: true \n * }\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { TimeSeriesData } from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Base configuration for prompt events.\n */\nexport interface PromptConfig {\n /** Event name */\n eventName: string;\n /** Event properties */\n properties: Record<string, unknown>;\n}\n\n/**\n * AI prompt submission configuration.\n */\nexport interface PromptSubmissionConfig {\n /** Prompt ID */\n promptId: string;\n /** User ID */\n userId?: string;\n /** Prompt text (may be truncated/hashed for privacy) */\n promptText?: string;\n /** Prompt category/type */\n promptType: string;\n /** Model used */\n model?: string;\n /** Input token count */\n inputTokens?: number;\n /** Session/conversation ID */\n conversationId?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * AI response configuration.\n */\nexport interface PromptResponseConfig {\n /** Prompt ID */\n promptId: string;\n /** Response time (ms) */\n responseTime: number;\n /** Output token count */\n outputTokens?: number;\n /** Whether response completed */\n completed: boolean;\n /** Error if any */\n error?: string;\n /** Response quality (if auto-detected) */\n qualityScore?: number;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * User feedback on AI response.\n */\nexport interface PromptFeedbackConfig {\n /** Prompt ID */\n promptId: string;\n /** User ID */\n userId?: string;\n /** Feedback type */\n feedbackType: 'accepted' | 'rejected' | 'edited' | 'thumbs_up' | 'thumbs_down';\n /** Rating (1-5) */\n rating?: number;\n /** User comment */\n comment?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Prompt tracking response.\n */\nexport interface PromptResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n tracked: boolean;\n promptId?: string;\n };\n timestamp: string;\n}\n\n/**\n * Prompt analytics data.\n */\nexport interface PromptAnalyticsData {\n /** Summary metrics */\n summary: {\n totalPrompts: number;\n uniqueUsers: number;\n avgResponseTime: number;\n acceptanceRate: number;\n avgSatisfactionScore: number;\n totalTokensUsed: number;\n errorRate: number;\n };\n /** Usage by prompt type */\n byType: Array<{\n promptType: string;\n count: number;\n acceptanceRate: number;\n avgResponseTime: number;\n avgTokens: number;\n }>;\n /** Usage by model */\n byModel: Array<{\n model: string;\n count: number;\n avgResponseTime: number;\n errorRate: number;\n }>;\n /** Feedback breakdown */\n feedback: {\n accepted: number;\n rejected: number;\n edited: number;\n thumbsUp: number;\n thumbsDown: number;\n };\n /** Trends */\n trends: TimeSeriesData;\n}\n\n// ============================================================================\n// TRACK PROMPT CLASS\n// ============================================================================\n\n/**\n * TrackPrompt - Track AI prompt interactions.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides prompt tracking including:\n * - Prompt submissions\n * - Response tracking\n * - User feedback\n * - Quality metrics\n * - Usage analytics\n * \n * @example\n * ```typescript\n * const trackPrompt = new TrackPrompt(webId, endpoint, apiKey);\n * \n * // Track prompt submission\n * await trackPrompt.trackSubmission({\n * promptId: 'prompt_123',\n * userId: 'user_456',\n * promptType: 'code_completion',\n * model: 'gpt-4',\n * inputTokens: 150\n * });\n * \n * // Track response\n * await trackPrompt.trackResponse({\n * promptId: 'prompt_123',\n * responseTime: 1200,\n * outputTokens: 300,\n * completed: true\n * });\n * \n * // Track user feedback\n * await trackPrompt.trackFeedback({\n * promptId: 'prompt_123',\n * feedbackType: 'accepted',\n * rating: 5\n * });\n * ```\n */\nexport class TrackPrompt extends FlowGridTransport {\n /**\n * Creates and tracks a prompt event.\n * \n * @param config - Prompt configuration\n * @returns Promise resolving to prompt response\n */\n async create(config: PromptConfig): Promise<PromptResponse> {\n return await this.init(config.eventName, {\n ...config.properties,\n type: \"prompt\",\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a prompt submission.\n * \n * @param config - Prompt submission configuration\n * @returns Promise resolving to prompt response\n */\n async trackSubmission(config: PromptSubmissionConfig): Promise<PromptResponse> {\n return await this.init(\"prompt_submitted\", {\n type: \"prompt\",\n subtype: \"submission\",\n promptId: config.promptId,\n userId: config.userId,\n promptType: config.promptType,\n model: config.model,\n inputTokens: config.inputTokens,\n conversationId: config.conversationId,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a prompt response.\n * \n * @param config - Prompt response configuration\n * @returns Promise resolving to prompt response\n */\n async trackResponse(config: PromptResponseConfig): Promise<PromptResponse> {\n return await this.init(\"prompt_response\", {\n type: \"prompt\",\n subtype: \"response\",\n promptId: config.promptId,\n responseTime: config.responseTime,\n outputTokens: config.outputTokens,\n completed: config.completed,\n error: config.error,\n qualityScore: config.qualityScore,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks user feedback on a prompt response.\n * \n * @param config - Prompt feedback configuration\n * @returns Promise resolving to prompt response\n */\n async trackFeedback(config: PromptFeedbackConfig): Promise<PromptResponse> {\n return await this.init(\"prompt_feedback\", {\n type: \"prompt\",\n subtype: \"feedback\",\n promptId: config.promptId,\n userId: config.userId,\n feedbackType: config.feedbackType,\n rating: config.rating,\n comment: config.comment,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a conversation/multi-turn prompt session.\n * \n * @param conversationId - Conversation ID\n * @param userId - User ID\n * @param turnCount - Number of turns\n * @param totalDuration - Total conversation duration (ms)\n * @returns Promise resolving to prompt response\n */\n async trackConversation(\n conversationId: string,\n userId: string,\n turnCount: number,\n totalDuration: number\n ): Promise<PromptResponse> {\n return await this.init(\"prompt_conversation\", {\n type: \"prompt\",\n subtype: \"conversation\",\n conversationId,\n userId,\n turnCount,\n totalDuration,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default TrackPrompt;\n","/**\n * @fileoverview Core - A/B Testing & Experiments\n * @module features/core/experiment\n * \n * @description\n * Track A/B tests, experiments, and variant assignments\n * to measure impact and optimize product decisions.\n * \n * @useCases\n * - Run A/B tests on features\n * - Track variant assignments\n * - Measure experiment impact\n * - Statistical significance analysis\n * - Multi-variant testing\n * \n * @metrics\n * - Conversion by variant\n * - Statistical significance\n * - Sample size per variant\n * - Experiment duration\n * - Impact on key metrics\n * \n * @chartData\n * - Bar chart: Conversion by variant\n * - Line chart: Metric trends by variant\n * - Table: Experiment results summary\n * - Confidence interval visualization\n * \n * @example\n * ```typescript\n * const experiment = new Experiment(webId, endpoint, apiKey);\n * \n * await experiment.track({\n * eventName: 'checkout_flow_test',\n * variant: 'simplified_v2',\n * properties: { userId: 'user_123' }\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { TimeSeriesData } from \"../../types\";\nimport { ServerCookieReader } from \"../../utils/identity\";\nimport { getCookie } from \"../../utils/storage\";\n\n// ============================================================================\n// HASHING UTILITY (FNV-1a)\n// ============================================================================\n\n/**\n * FNV-1a 32-bit hash — lightweight deterministic hash for variant assignment.\n * Ensures the same user always gets the same variant regardless of device.\n */\nfunction fnv1a(input: string): number {\n let hash = 0x811c9dc5; // FNV offset basis\n for (let i = 0; i < input.length; i++) {\n hash ^= input.charCodeAt(i);\n hash = (hash * 0x01000193) >>> 0; // FNV prime, unsigned 32-bit\n }\n return hash;\n}\n\n/**\n * Global holdout (guide §3) — a fixed, deterministic slice of all visitors\n * who are excluded from every experiment on the site. Lets teams measure\n * the combined lift of \"all active experiments\" against a clean baseline,\n * with zero server round-trips: each visitor's holdout membership is a pure\n * function of their id, so it's stable across page loads and experiments.\n */\nexport const HOLDOUT_PCT = 5;\n\nfunction isInHoldout(userId: string, holdoutPct: number = HOLDOUT_PCT): boolean {\n if (holdoutPct <= 0) return false;\n if (holdoutPct >= 100) return true;\n const hash = fnv1a('holdout::' + userId);\n return (hash % 10000) / 100 < holdoutPct;\n}\n\n/**\n * Mutual exclusion (guide §3) — experiments that could interact (e.g. two\n * tests on the same checkout flow) share an `exclusion_group` id in their\n * properties. Each visitor deterministically \"claims\" exactly one experiment\n * per group, so no one is ever in two experiments from the same group at once.\n */\nfunction filterMutuallyExclusive(\n userId: string,\n experimentId: string,\n exclusionGroup: string,\n groupMemberIds: string[]\n): boolean {\n const members = Array.from(new Set([...groupMemberIds, experimentId])).sort();\n if (members.length <= 1) return true;\n const hash = fnv1a('exclusion::' + exclusionGroup + '::' + userId);\n const claimed = members[hash % members.length];\n return claimed === experimentId;\n}\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for tracking an experiment-related event.\n *\n * Used when sending a generic experiment event with a specific variant\n * and associated properties.\n */\nexport interface ExperimentConfig {\n /** Logical experiment event name */\n eventName: string;\n\n /** Variant identifier associated with this event */\n variant: string;\n\n /** Additional contextual event data */\n properties: Record<string, unknown>;\n}\n\n/**\n * Defines the structure and metadata of an experiment.\n *\n * Used when creating or updating an experiment configuration.\n */\nexport interface ExperimentDefinition {\n /** Unique identifier for the experiment */\n experimentId: string;\n\n /** Human-readable experiment name */\n name: string;\n\n /** Optional description of experiment purpose */\n description?: string;\n\n /**\n * Variants participating in the experiment.\n *\n * Shape matches the verify endpoint and the client-side engine:\n * `{ id, weight }` (with optional human-readable `name`).\n */\n variants: Array<{\n /** Unique identifier for the variant */\n id: string;\n\n /** Traffic allocation weight (relative distribution) */\n weight: number;\n\n /** Optional human-readable variant name */\n name?: string;\n }>;\n\n /** Primary metric used to evaluate experiment success */\n primaryMetric: string;\n\n /** Optional secondary metrics */\n secondaryMetrics?: string[];\n\n /** Optional ISO start date */\n startDate?: string;\n\n /** Optional ISO end date */\n endDate?: string;\n}\n\n/**\n * Configuration for assigning a user to a specific experiment variant.\n *\n * Represents an explicit assignment event.\n */\nexport interface ExperimentAssignmentConfig {\n /** Experiment identifier */\n experimentId: string;\n\n /** Unique user identifier */\n userId: string;\n\n /** Assigned variant identifier */\n variantId: string;\n\n /**\n * Assignment origin:\n * - random: weighted allocation\n * - targeted: rule-based assignment\n * - override: manual or forced assignment\n */\n source?: 'random' | 'targeted' | 'override';\n}\n\n/**\n * Configuration for recording a conversion event within an experiment.\n *\n * Used to attribute metric changes to a specific variant.\n */\nexport interface ExperimentConversionConfig {\n /** Experiment identifier */\n experimentId: string;\n\n /** Unique user identifier */\n userId: string;\n\n /** Variant associated with the user */\n variantId: string;\n\n /** Metric name being recorded */\n metricName: string;\n\n /** Numeric metric value */\n value: number;\n}\n\n/**\n * Standardized response returned from experiment tracking operations.\n *\n * Ensures consistent structure across assignment, exposure,\n * definition, and conversion events.\n */\nexport interface ExperimentResponse {\n /** Event name associated with the request */\n eventName: string;\n\n /** Properties sent with the event */\n properties: Record<string, unknown>;\n\n /** Server acknowledgment metadata */\n response: {\n /** Indicates whether the event was successfully recorded */\n tracked: boolean;\n\n /** Optional experiment identifier */\n experimentId?: string;\n\n /** Optional variant identifier */\n variantId?: string;\n };\n\n /** ISO timestamp of the recorded event */\n timestamp: string;\n}\n\n/**\n * Aggregated results for a single experiment.\n *\n * Represents computed performance metrics per variant.\n */\nexport interface ExperimentResultsData {\n /** Experiment metadata */\n experiment: {\n experimentId: string;\n name: string;\n status: 'running' | 'paused' | 'completed';\n startDate: string;\n /** Duration in days */\n duration: number;\n };\n\n /** Variant-level performance metrics */\n variants: Array<{\n variantId: string;\n name: string;\n\n /** Total participants assigned */\n participants: number;\n\n /** Total recorded conversions */\n conversions: number;\n\n /** conversions / participants */\n conversionRate: number;\n\n /** Relative improvement compared to baseline */\n improvement: number;\n\n /** Statistical confidence level (0–1 or percentage depending on backend) */\n confidence: number;\n\n /** Indicates highest performing variant */\n isWinner: boolean;\n\n /** Indicates whether statistical threshold is met */\n isStatisticallySignificant: boolean;\n }>;\n\n /** Time-series metric trends per metric name */\n trends: Record<string, TimeSeriesData>;\n\n /** System-generated recommendation */\n recommendation: {\n action: 'ship_winner' | 'continue' | 'stop' | 'needs_more_data';\n winningVariant?: string;\n confidence: number;\n reason: string;\n };\n}\n\n/**\n * Aggregated analytics across all experiments.\n *\n * Provides high-level performance and activity overview.\n */\nexport interface ExperimentAnalyticsData {\n /** Platform-wide experiment summary */\n summary: {\n totalExperiments: number;\n activeExperiments: number;\n completedExperiments: number;\n avgExperimentDuration: number;\n winRate: number;\n };\n\n /** Currently active experiments */\n active: Array<{\n experimentId: string;\n name: string;\n participants: number;\n daysSinceStart: number;\n leadingVariant: string;\n confidence: number;\n }>;\n\n /** Recently completed experiments */\n recentResults: Array<{\n experimentId: string;\n name: string;\n winner: string;\n improvement: number;\n completedDate: string;\n }>;\n}\n\nexport interface VariantResponse {\n experimentId: string;\n userId?: string;\n variantId: string;\n assignedAt: string;\n}\n\n/**\n * A single declarative DOM change applied for an assigned variant — the no-code\n * rendering primitive. The engine applies these on `init()` so a marketer can\n * run a test (different headline, hidden banner, redirect to another page)\n * without an engineer hand-writing `if (variant === \"B\")` branching.\n */\nexport type VariantChange =\n | { type: 'redirect'; url: string }\n | { type: 'text'; selector: string; value: string }\n | { type: 'html'; selector: string; value: string }\n | { type: 'hide'; selector: string }\n | { type: 'style'; selector: string; property: string; value: string }\n | { type: 'addClass'; selector: string; value: string };\n\n/**\n * Local experiment variant definition used by the client-side\n * assignment engine (ported from sdk.js).\n */\nexport interface ExperimentVariant {\n /** Variant identifier */\n id: string;\n /** Traffic allocation weight */\n weight: number;\n /** Optional human-readable label (shown in the dashboard). */\n label?: string;\n /**\n * Split-URL / redirect test shortcut — visitors assigned this variant are\n * sent to `redirectUrl`. Equivalent to a single `{ type: 'redirect' }` change.\n */\n redirectUrl?: string;\n /** Declarative DOM changes applied when this variant is assigned. */\n changes?: VariantChange[];\n}\n\n/**\n * Lightweight experiment config passed to `initExperiments()`.\n */\nexport interface ExperimentInitConfig {\n /** Unique experiment identifier */\n id: string;\n /** Weighted variant list */\n variants: ExperimentVariant[];\n /** Optional properties forwarded to the verify endpoint */\n properties?: Record<string, unknown>;\n}\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst ASSIGNMENT_STORAGE_KEY = 'fg_exp_assignments';\nconst ASSIGNMENT_COOKIE_KEY = 'fg_exp_assignments';\nconst ASSIGNMENT_COOKIE_MAX_AGE_DAYS = 365;\n\n// ============================================================================\n// EXPERIMENT CLASS\n// ============================================================================\n\n/**\n * Experiment - A/B Testing & Experimentation SDK\n *\n * @extends FlowGridTransport\n *\n * @description\n * Provides structured experiment lifecycle management including:\n * - Experiment definition\n * - Deterministic or server-based variant assignment\n * - Exposure tracking\n * - Conversion tracking\n * - Statistical results & analysis\n *\n * This class enforces correct HTTP semantics:\n *\n * Exposure and conversion events are strictly POST to preserve:\n * - Statistical validity\n * - Unique participant counting\n * - Accurate conversion attribution\n * - Idempotent replay safety\n *\n * @lifecycle\n * 1. Define experiment\n * 2. Assign user to variant\n * 3. Track exposure (when user actually sees variant)\n * 4. Track conversions (primary/secondary metrics)\n * 5. Query results & statistical analysis\n *\n * @statistical_model\n * - Variant-level sample sizes tracked independently\n * - Conversion rates calculated per variant\n * - Confidence intervals computed server-side\n * - Statistical significance evaluated against control\n *\n * @guarantees\n * - Assignment is explicitly recorded\n * - Exposure tracking is immutable\n * - Conversion events are attributable to assigned variant\n * - Results queries never mutate experiment state\n *\n * @example\n * ```typescript\n * const experiment = new Experiment(webId, endpoint, apiKey);\n *\n * // 1. Define experiment\n * await experiment.define({\n * experimentId: 'checkout_v2',\n * name: 'Simplified Checkout',\n * variants: [\n * { variantId: 'control', name: 'Current', weight: 50 },\n * { variantId: 'treatment', name: 'Simplified', weight: 50 }\n * ],\n * primaryMetric: 'checkout_completed'\n * });\n *\n * // 2. Assign user\n * await experiment.assign({\n * experimentId: 'checkout_v2',\n * userId: 'user_123',\n * variantId: 'treatment'\n * });\n *\n * // 3. Track exposure\n * await experiment.trackExposure('checkout_v2', 'user_123', 'treatment');\n *\n * // 4. Track conversion\n * await experiment.trackConversion({\n * experimentId: 'checkout_v2',\n * userId: 'user_123',\n * variantId: 'treatment',\n * metricName: 'checkout_completed',\n * value: 1\n * });\n *\n * // 5. Fetch results\n * const results = await experiment.getResults('checkout_v2');\n * ```\n */\nexport class Experiment extends FlowGridTransport {\n\n // ---------------------------------------------------------------------------\n // CLIENT-SIDE EXPERIMENT ENGINE STATE\n // ---------------------------------------------------------------------------\n\n /** Verified experiment configs: { [experiment_id]: { variants, properties, verified } } */\n private experiments: Record<string, {\n variants: ExperimentVariant[];\n properties: Record<string, unknown>;\n verified: boolean;\n }> = {};\n\n /** Local variant assignments: { [experiment_id]: variant_id } */\n private assignments: Record<string, string> = {};\n\n // ---------------------------------------------------------------------------\n // CLIENT-SIDE ENGINE — SSR-safe persistence (localStorage + cookie mirror)\n // ---------------------------------------------------------------------------\n\n /**\n * Decode the cookie’s URL-encoded JSON payload into an assignment map.\n * Returns `{}` on any parse failure so a stale cookie never throws.\n */\n private parseAssignmentCookie(raw: string | null): Record<string, string> {\n if (!raw) return {};\n try {\n const decoded = decodeURIComponent(raw);\n const parsed = JSON.parse(decoded);\n return parsed && typeof parsed === 'object' ? parsed : {};\n } catch {\n return {};\n }\n }\n\n private readAssignmentCookie(): Record<string, string> {\n return this.parseAssignmentCookie(getCookie(ASSIGNMENT_COOKIE_KEY));\n }\n\n /**\n * Persist `this.assignments` to a first-party cookie so SSR requests on\n * subsequent navigations see the same variant. Best-effort — no-op in\n * non-browser contexts or when document.cookie is unavailable.\n */\n private writeAssignmentCookie(): void {\n try {\n if (typeof document === 'undefined') return;\n const value = encodeURIComponent(JSON.stringify(this.assignments));\n const maxAge = ASSIGNMENT_COOKIE_MAX_AGE_DAYS * 24 * 60 * 60;\n const sameSite = 'Lax';\n const secure = typeof location !== 'undefined' && location.protocol === 'https:'\n ? '; Secure'\n : '';\n document.cookie = `${ASSIGNMENT_COOKIE_KEY}=${value}; Path=/; Max-Age=${maxAge}; SameSite=${sameSite}${secure}`;\n } catch { /* SSR or restricted env */ }\n }\n\n private loadAssignments(): Record<string, string> {\n // 1. localStorage — authoritative on the client\n try {\n if (typeof localStorage !== 'undefined') {\n const stored = localStorage.getItem(ASSIGNMENT_STORAGE_KEY);\n if (stored) {\n const parsed = JSON.parse(stored);\n if (parsed && typeof parsed === 'object') return parsed;\n }\n }\n } catch { /* SSR or privacy mode */ }\n\n // 2. Cookie fallback — lets SSR + first-paint resolve a variant\n const cookieAssignments = this.readAssignmentCookie();\n if (Object.keys(cookieAssignments).length > 0) return cookieAssignments;\n\n return {};\n }\n\n private saveAssignments(): void {\n try {\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem(ASSIGNMENT_STORAGE_KEY, JSON.stringify(this.assignments));\n }\n } catch { /* SSR or privacy mode */ }\n // Mirror to cookie so the next SSR request can hydrate without flashing.\n this.writeAssignmentCookie();\n }\n\n /**\n * Inject assignments from server context (SSR/Node).\n * Use this when assignments were resolved server-side and need to be\n * available before localStorage is accessible.\n */\n injectAssignments(assignments: Record<string, string>): void {\n this.assignments = { ...this.assignments, ...assignments };\n }\n\n /**\n * Returns all current assignments (for SSR hydration).\n */\n getAssignments(): Record<string, string> {\n return { ...this.assignments };\n }\n\n /**\n * Hydrate assignments from a server-side cookie reader\n * (Next.js `cookies()`, Nuxt `useCookie()`, Hono ctx, etc.).\n *\n * @example\n * ```ts\n * import { cookies } from \"next/headers\";\n * fg.experiments.client.hydrateFromCookies(cookies());\n * const variant = fg.experiments.variant(\"checkout_v2\");\n * ```\n */\n hydrateFromCookies(reader: ServerCookieReader): void {\n const raw = reader.get(ASSIGNMENT_COOKIE_KEY);\n const value = typeof raw === 'string' ? raw : raw?.value ?? null;\n const parsed = this.parseAssignmentCookie(value ?? null);\n if (Object.keys(parsed).length > 0) this.injectAssignments(parsed);\n }\n\n /**\n * Hydrate assignments from a raw `Cookie` request header string.\n * Useful for runtimes without a structured cookie reader (Workers, Bun, etc.).\n */\n hydrateFromCookieHeader(cookieHeader: string | null | undefined): void {\n if (!cookieHeader) return;\n const parts = cookieHeader.split(/;\\s*/);\n const prefix = `${ASSIGNMENT_COOKIE_KEY}=`;\n const match = parts.find(p => p.startsWith(prefix));\n if (!match) return;\n const parsed = this.parseAssignmentCookie(match.slice(prefix.length));\n if (Object.keys(parsed).length > 0) this.injectAssignments(parsed);\n }\n\n /**\n * Serialise current assignments as a `Set-Cookie` header value.\n * Use server-side after a server-only assignment so the response can\n * seed the visitor’s browser with the same variant.\n */\n serializeAssignmentCookie(): string {\n const value = encodeURIComponent(JSON.stringify(this.assignments));\n const maxAge = ASSIGNMENT_COOKIE_MAX_AGE_DAYS * 24 * 60 * 60;\n return `${ASSIGNMENT_COOKIE_KEY}=${value}; Path=/; Max-Age=${maxAge}; SameSite=Lax`;\n }\n\n /**\n * Clear all locally cached assignments (cookie + localStorage + memory).\n * Useful for logout flows or test resets.\n */\n clearAssignments(): void {\n this.assignments = {};\n try {\n if (typeof localStorage !== 'undefined') localStorage.removeItem(ASSIGNMENT_STORAGE_KEY);\n } catch { /* noop */ }\n try {\n if (typeof document !== 'undefined') {\n document.cookie = `${ASSIGNMENT_COOKIE_KEY}=; Path=/; Max-Age=0; SameSite=Lax`;\n }\n } catch { /* noop */ }\n }\n\n // ---------------------------------------------------------------------------\n // CLIENT-SIDE ENGINE — stable anonymous ID\n // ---------------------------------------------------------------------------\n\n private static readonly ANON_ID_KEY = 'fg_anon_id';\n\n /**\n * Returns a stable anonymous ID. Generates a UUID on first call and\n * persists it in localStorage so the same anonymous visitor always\n * receives a consistent variant assignment.\n */\n private getOrCreateAnonId(): string {\n try {\n if (typeof localStorage !== 'undefined') {\n const existing = localStorage.getItem(Experiment.ANON_ID_KEY);\n if (existing) return existing;\n\n const id = typeof crypto !== 'undefined' && crypto.randomUUID\n ? crypto.randomUUID()\n : 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {\n const r = (Math.random() * 16) | 0;\n return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);\n });\n\n localStorage.setItem(Experiment.ANON_ID_KEY, id);\n return id;\n }\n } catch { /* SSR or privacy mode */ }\n // Non-browser fallback — still deterministic within process lifetime\n return 'anon_' + fnv1a(String(Math.random())).toString(36);\n }\n\n // ---------------------------------------------------------------------------\n // CLIENT-SIDE ENGINE — deterministic hash-based assignment\n // ---------------------------------------------------------------------------\n\n /**\n * Deterministic variant assignment using FNV-1a hash.\n * Same userId + experimentId always produces the same variant.\n */\n private deterministicAssign(userId: string, experimentId: string, variants: ExperimentVariant[]): string {\n const hashValue = fnv1a(userId + experimentId);\n let totalWeight = 0;\n for (const v of variants) totalWeight += v.weight;\n\n const bucket = (hashValue % 10000) / 10000 * totalWeight;\n let cumulative = 0;\n for (const v of variants) {\n cumulative += v.weight;\n if (bucket < cumulative) return v.id;\n }\n return variants[variants.length - 1].id;\n }\n\n private assignVariant(\n experimentId: string,\n variants: ExperimentVariant[],\n properties: Record<string, unknown> = {},\n exclusionGroupMembers: string[] = []\n ): void {\n // Reload from localStorage to handle cross-tab race conditions\n this.assignments = this.loadAssignments();\n\n const existing = this.assignments[experimentId];\n\n // Resolve a userId for the hash: prefer identityManager's visitorId, then fall back\n const userId = this.identityManager?.getVisitorId?.()\n || this.visitorId\n || this.getOrCreateAnonId();\n\n // \"Ship B\" short-circuit (guide §6 `initExperiment` pattern): once the\n // dashboard declares a winner, `properties.status`/`winner_id` on the\n // definition are enough to converge every visitor — new or already\n // bucketed — onto it. Stopping a test becomes a data change (one row\n // update, picked up on the next page load), not a redeploy.\n const status = typeof properties.status === 'string' ? properties.status : undefined;\n const winnerId = typeof properties.winner_id === 'string' ? properties.winner_id : undefined;\n if (status === 'complete' && winnerId && variants.some(v => v.id === winnerId)) {\n if (existing === winnerId) return;\n\n this.assignments[experimentId] = winnerId;\n this.saveAssignments();\n\n this.init('experiment_assignment', {\n type: 'experiment',\n subtype: 'assignment',\n experimentId,\n userId,\n variantId: winnerId,\n source: 'shipped_winner',\n assignmentSource: 'sdk_init',\n timestamp: new Date().toISOString(),\n }).catch(() => {});\n return;\n }\n\n if (existing && variants.some(v => v.id === existing)) {\n // Existing assignment still valid — keep it\n return;\n }\n\n // Traffic-splitting primitives (guide §3) — these only gate *new*\n // assignments. A visitor already bucketed into this experiment keeps\n // their assignment even if they'd now land in the holdout or lose an\n // exclusion-group claim; retroactively pulling people out would bias\n // the very results the test is trying to measure.\n if (isInHoldout(userId)) return;\n\n const exclusionGroup = typeof properties.exclusion_group === 'string' ? properties.exclusion_group : undefined;\n if (exclusionGroup && !filterMutuallyExclusive(userId, experimentId, exclusionGroup, exclusionGroupMembers)) {\n return;\n }\n\n // Deterministic assignment via hash\n const assignedVariantId = this.deterministicAssign(userId, experimentId, variants);\n this.assignments[experimentId] = assignedVariantId;\n this.saveAssignments();\n\n // Fire-and-forget assignment event.\n // The backend AssignmentHandlerNode requires `userId` in properties.\n this.init('experiment_assignment', {\n type: 'experiment',\n subtype: 'assignment',\n experimentId,\n userId,\n variantId: assignedVariantId,\n source: 'random',\n assignmentSource: 'sdk_init',\n timestamp: new Date().toISOString(),\n }).catch(() => {});\n }\n\n // ---------------------------------------------------------------------------\n // CLIENT-SIDE ENGINE — server verification\n // ---------------------------------------------------------------------------\n\n /**\n * Groups experiment ids by their `properties.exclusion_group`, so\n * `assignVariant` can deterministically pick exactly one experiment per\n * group for each visitor (guide §3 mutual exclusion).\n */\n private computeExclusionGroupMembers(\n items: { experiment_id: string; properties?: Record<string, unknown> }[]\n ): Map<string, string[]> {\n const groups = new Map<string, string[]>();\n for (const item of items) {\n const group = typeof item.properties?.exclusion_group === 'string' ? item.properties.exclusion_group : undefined;\n if (!group) continue;\n if (!groups.has(group)) groups.set(group, []);\n groups.get(group)!.push(item.experiment_id);\n }\n return groups;\n }\n\n /**\n * Assigns variants straight from the locally supplied configs — the\n * degraded path when the verify endpoint is unreachable OR returns an\n * error (429 rate limit, 5xx). An unverified local assignment that emits\n * events beats silently disabling the experiment for this visitor.\n */\n private assignFromLocalConfigs(configs: ExperimentInitConfig[]): void {\n const groupMembers = this.computeExclusionGroupMembers(\n configs.map(exp => ({ experiment_id: exp.id, properties: exp.properties }))\n );\n for (const exp of configs) {\n this.experiments[exp.id] = {\n variants: exp.variants,\n properties: exp.properties || {},\n verified: false,\n };\n const exclusionGroup = typeof exp.properties?.exclusion_group === 'string' ? exp.properties.exclusion_group : undefined;\n this.assignVariant(\n exp.id,\n exp.variants,\n exp.properties || {},\n exclusionGroup ? groupMembers.get(exclusionGroup) ?? [] : []\n );\n }\n }\n\n private async verifyExperiments(configs: ExperimentInitConfig[]): Promise<void> {\n const payload = {\n web_id: this.webId,\n experiments: configs.map(exp => ({\n experiment_id: exp.id,\n variants: exp.variants,\n properties: exp.properties || {},\n })),\n };\n\n try {\n const res = await fetch(`${this.endpoint}/api/v1/experiments/verify`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n });\n const response = await res.json();\n\n if (response.ok && Array.isArray(response.data)) {\n const groupMembers = this.computeExclusionGroupMembers(response.data);\n for (const item of response.data) {\n if (item.action !== 'error') {\n this.experiments[item.experiment_id] = {\n variants: item.variants,\n properties: item.properties ?? {},\n verified: true,\n };\n const exclusionGroup = typeof item.properties?.exclusion_group === 'string' ? item.properties.exclusion_group : undefined;\n this.assignVariant(\n item.experiment_id,\n item.variants,\n item.properties ?? {},\n exclusionGroup ? groupMembers.get(exclusionGroup) ?? [] : []\n );\n }\n }\n } else {\n // Server replied but refused (rate limit, validation, 5xx) — degrade\n // to local assignment rather than leaving the visitor unassigned.\n this.assignFromLocalConfigs(configs);\n }\n } catch {\n // Network failure — fall back to local config\n this.assignFromLocalConfigs(configs);\n }\n }\n\n // ---------------------------------------------------------------------------\n // CLIENT-SIDE ENGINE — public init\n // ---------------------------------------------------------------------------\n\n /**\n * Initialise the client-side experiment engine.\n *\n * Verifies experiment configs against the API, assigns variants using\n * weighted random allocation, and persists assignments in localStorage\n * under `fg_exp_assignments`.\n *\n * @param experiments - Array of experiment configs to register\n */\n async initExperiments(experiments: ExperimentInitConfig[]): Promise<void> {\n this.assignments = this.loadAssignments();\n\n // Anti-flicker: hide every element a variant might change *before* the\n // (async) verify round-trip, so visitors never see the original flash to\n // the variant. A safety timeout reveals everything even if init stalls.\n const selectors = this.collectChangeSelectors(experiments);\n if (selectors.length > 0) this.injectAntiFlicker(selectors);\n\n try {\n if (experiments.length > 0) {\n await this.verifyExperiments(experiments);\n }\n this.applyAssignedChanges();\n } finally {\n this.removeAntiFlicker();\n }\n }\n\n // ---------------------------------------------------------------------------\n // CLIENT-SIDE ENGINE — declarative variant rendering (no-code)\n // ---------------------------------------------------------------------------\n\n private antiFlickerStyleEl: HTMLStyleElement | null = null;\n private antiFlickerTimer: ReturnType<typeof setTimeout> | null = null;\n /** Experiments whose assigned variant has already had its changes applied + exposure fired. */\n private appliedExperiments = new Set<string>();\n\n /** Normalises a variant's `redirectUrl` shortcut + `changes` into one list. */\n private variantChanges(variant: ExperimentVariant | undefined): VariantChange[] {\n if (!variant) return [];\n const out: VariantChange[] = [];\n if (variant.redirectUrl) out.push({ type: 'redirect', url: variant.redirectUrl });\n if (Array.isArray(variant.changes)) out.push(...variant.changes);\n return out;\n }\n\n /** All non-redirect selectors across the passed configs — used for anti-flicker. */\n private collectChangeSelectors(experiments: ExperimentInitConfig[]): string[] {\n const set = new Set<string>();\n for (const exp of experiments) {\n for (const v of exp.variants) {\n for (const c of this.variantChanges(v)) {\n if (c.type !== 'redirect') set.add(c.selector);\n }\n }\n }\n return Array.from(set);\n }\n\n private injectAntiFlicker(selectors: string[]): void {\n if (typeof document === 'undefined' || this.antiFlickerStyleEl) return;\n try {\n const style = document.createElement('style');\n style.setAttribute('data-flowgrid-antiflicker', '');\n style.textContent = `${selectors.join(',')}{visibility:hidden !important}`;\n (document.head || document.documentElement).appendChild(style);\n this.antiFlickerStyleEl = style;\n // Never leave the page hidden — reveal after 4s no matter what.\n this.antiFlickerTimer = setTimeout(() => this.removeAntiFlicker(), 4000);\n } catch { /* ignore */ }\n }\n\n private removeAntiFlicker(): void {\n if (this.antiFlickerTimer) { clearTimeout(this.antiFlickerTimer); this.antiFlickerTimer = null; }\n try { this.antiFlickerStyleEl?.remove(); } catch { /* ignore */ }\n this.antiFlickerStyleEl = null;\n }\n\n private applyDomChange(change: Exclude<VariantChange, { type: 'redirect' }>): void {\n if (typeof document === 'undefined') return;\n const nodes = document.querySelectorAll<HTMLElement>(change.selector);\n nodes.forEach((el) => {\n switch (change.type) {\n case 'text': el.textContent = change.value; break;\n case 'html': el.innerHTML = change.value; break;\n case 'hide': el.style.display = 'none'; break;\n case 'style': el.style.setProperty(change.property, change.value); break;\n case 'addClass': el.classList.add(...change.value.split(/\\s+/).filter(Boolean)); break;\n }\n });\n }\n\n /**\n * Apply the assigned variant's declarative changes for every registered\n * experiment, then fire one exposure per experiment. Redirects take priority\n * and short-circuit (the new page re-runs init). DOM changes wait for the\n * document to be ready. Idempotent per experiment.\n */\n private applyAssignedChanges(): void {\n if (typeof window === 'undefined') return;\n\n for (const [experimentId, config] of Object.entries(this.experiments)) {\n if (this.appliedExperiments.has(experimentId)) continue;\n const variantId = this.assignments[experimentId];\n if (!variantId) continue;\n const variant = config.variants.find((v) => v.id === variantId);\n const changes = this.variantChanges(variant);\n\n // Redirect first — if we're not already on the target, go there. The\n // destination page's own init() handles exposure for the redirected view.\n const redirect = changes.find((c): c is Extract<VariantChange, { type: 'redirect' }> => c.type === 'redirect');\n if (redirect) {\n const target = new URL(redirect.url, window.location.href);\n if (target.href !== window.location.href) {\n this.appliedExperiments.add(experimentId);\n this.trackLocalExposure(experimentId).catch(() => {});\n window.location.replace(target.href);\n return;\n }\n }\n\n const domChanges = changes.filter(\n (c): c is Exclude<VariantChange, { type: 'redirect' }> => c.type !== 'redirect'\n );\n const run = () => domChanges.forEach((c) => this.applyDomChange(c));\n if (typeof document !== 'undefined' && document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', run, { once: true });\n } else {\n run();\n }\n\n this.appliedExperiments.add(experimentId);\n // Auto-fire exposure so no-code users get results without extra calls.\n this.trackLocalExposure(experimentId).catch(() => {});\n }\n }\n\n /**\n * Manually (re-)apply the assigned variant's changes for one experiment —\n * useful after client-side navigation re-renders the DOM.\n */\n applyVariant(experimentId: string): void {\n this.appliedExperiments.delete(experimentId);\n this.applyAssignedChanges();\n }\n\n /**\n * Zero-config init: fetch this site's running experiments from the server and\n * run them. This is what lets the no-build snippet run experiments with no\n * author JS — the dashboard is the only place experiments are configured.\n *\n * Best-effort: a network/permission failure is a silent no-op (the page just\n * shows its control experience), never a thrown error.\n */\n async initFromServer(): Promise<void> {\n try {\n const res = await fetch(\n `${this.endpoint}/api/v1/experiments/active?web_id=${encodeURIComponent(this.webId)}`\n );\n const json = await res.json();\n if (json?.ok && Array.isArray(json.data) && json.data.length > 0) {\n await this.initExperiments(json.data as ExperimentInitConfig[]);\n }\n } catch {\n /* offline, blocked, or no experiments — leave the control experience */\n }\n }\n\n /**\n * Returns the locally assigned variant for an experiment.\n *\n * Reads from the in-memory assignment map (backed by localStorage).\n * Returns `null` if no assignment exists.\n */\n getLocalVariant(experimentId: string): string | null {\n // Re-read from storage to stay in sync with other tabs\n this.assignments = this.loadAssignments();\n return this.assignments[experimentId] ?? null;\n }\n\n /**\n * Track a conversion against the locally assigned variant.\n *\n * No-op if the visitor has no assignment for this experiment.\n */\n async trackLocalConversion(\n experimentId: string,\n metricName: string = 'conversion',\n metricValue: number = 1\n ): Promise<void> {\n const variantId = this.getLocalVariant(experimentId);\n if (!variantId) return;\n\n // Backend ConversionHandlerNode requires `userId` — resolve from\n // identity manager or the persisted anonymous ID.\n const userId = this.identityManager?.getVisitorId?.()\n || this.visitorId\n || this.getOrCreateAnonId();\n\n await this.init('experiment_conversion', {\n type: 'experiment',\n subtype: 'conversion',\n experimentId,\n userId,\n variantId,\n metricName,\n value: metricValue,\n timestamp: new Date().toISOString(),\n });\n }\n\n /**\n * Track an exposure for the locally assigned variant, resolving the user id\n * automatically (identity manager → visitor id → persisted anonymous id).\n *\n * No-op if the visitor has no assignment for this experiment. Use this in the\n * browser so callers don't have to thread a user id through every component.\n */\n async trackLocalExposure(experimentId: string): Promise<void> {\n const variantId = this.getLocalVariant(experimentId);\n if (!variantId) return;\n\n const userId = this.identityManager?.getVisitorId?.()\n || this.visitorId\n || this.getOrCreateAnonId();\n\n await this.trackExposure(experimentId, userId, variantId);\n }\n\n // ---------------------------------------------------------------------------\n // SERVER-SIDE EXPERIMENT METHODS\n //\n // Backend supports write events only: definition, assignment, conversion,\n // exposure, plus the deprecated generic custom shim below. Read endpoints\n // (results / analytics / variant lookup) are not exposed via the SDK\n // functions router.\n // ---------------------------------------------------------------------------\n\n /**\n * @deprecated Prefer `assign()`, `trackExposure()`, or `trackConversion()`.\n * Retained as a write-only compatibility shim for generic experiment events.\n */\n async track(config: ExperimentConfig): Promise<ExperimentResponse> {\n return await this.init(config.eventName, {\n ...config.properties,\n type: \"experiment\",\n subtype: \"custom\",\n variant: config.variant,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Defines a new experiment.\n */\n async define(definition: ExperimentDefinition): Promise<ExperimentResponse> {\n return await this.init(\"experiment_definition\", {\n type: \"experiment\",\n subtype: \"definition\",\n ...definition,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Assigns a user to a variant.\n */\n async assign(config: ExperimentAssignmentConfig): Promise<ExperimentResponse> {\n return await this.init(\"experiment_assignment\", {\n type: \"experiment\",\n subtype: \"assignment\",\n experimentId: config.experimentId,\n userId: config.userId,\n variantId: config.variantId,\n source: config.source || \"random\",\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Records a conversion event for an experiment.\n */\n async trackConversion(config: ExperimentConversionConfig): Promise<ExperimentResponse> {\n return await this.init(\"experiment_conversion\", {\n type: \"experiment\",\n subtype: \"conversion\",\n experimentId: config.experimentId,\n userId: config.userId,\n variantId: config.variantId,\n metricName: config.metricName,\n value: config.value,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Records that a user was exposed to a variant.\n */\n async trackExposure(\n experimentId: string,\n userId: string,\n variantId: string\n ): Promise<ExperimentResponse> {\n return await this.init(\"experiment_exposure\", {\n type: \"experiment\",\n subtype: \"exposure\",\n experimentId,\n userId,\n variantId,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default Experiment;\n","/**\n * @fileoverview Product Analytics - Page View Tracking\n * @module features/analytics/page-views\n * \n * @description\n * Track page views with rich context including scroll depth, time on page,\n * referrer information, and user engagement signals.\n * \n * @useCases\n * - Track which pages users visit most frequently\n * - Measure time spent on content pages\n * - Identify high bounce rate pages\n * - Analyze user navigation patterns\n * - Monitor landing page performance\n * \n * @metrics\n * - Total page views\n * - Unique page views \n * - Average time on page\n * - Bounce rate by page\n * - Scroll depth distribution\n * - Entry/exit page analysis\n * \n * @chartData\n * - Line chart: Page views over time\n * - Bar chart: Top pages by views\n * - Heatmap: Scroll depth by page\n * - Funnel: Page flow analysis\n * \n * @example\n * ```typescript\n * const pageViews = new PageViews(webId, endpoint, apiKey);\n * \n * // Track a page view\n * await pageViews.track({\n * url: window.location.href,\n * path: window.location.pathname,\n * title: document.title,\n * referrer: document.referrer\n * });\n * \n * // Track with scroll depth\n * await pageViews.trackScrollDepth({\n * path: '/blog/article-1',\n * scrollDepth: 75\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { getWindow, getDocument, isBrowser, combineTeardowns, Teardown } from \"../../utils/browser\";\nimport { \n PageView, \n TrafficMetrics, \n TimeSeriesData, \n ChartDataPoint,\n DateRangeFilter,\n PaginationParams \n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for tracking a page view.\n */\nexport interface PageViewConfig {\n /** Full page URL */\n url: string;\n /** Page path (without domain) */\n path: string;\n /** Page title */\n title?: string;\n /** Referrer URL */\n referrer?: string;\n /** User ID (if authenticated) */\n userId?: string;\n /** Session ID */\n sessionId?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Configuration for tracking scroll depth.\n */\nexport interface ScrollDepthConfig {\n /** Page path */\n path: string;\n /** Scroll depth percentage (0-100) */\n scrollDepth: number;\n /** Time at this depth (seconds) */\n timeAtDepth?: number;\n}\n\n/**\n * Configuration for tracking time on page.\n */\nexport interface TimeOnPageConfig {\n /** Page path */\n path: string;\n /** Time spent in seconds */\n duration: number;\n /** Whether user engaged (scrolled, clicked, etc.) */\n engaged?: boolean;\n}\n\n/**\n * Page view response from API.\n */\nexport interface PageViewResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n pageViewId: string;\n tracked: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Page analytics data for charts.\n */\nexport interface PageAnalyticsData {\n /** Top pages */\n topPages: Array<{\n path: string;\n title: string;\n views: number;\n uniqueViews: number;\n avgTimeOnPage: number;\n bounceRate: number;\n }>;\n /** Page views time series */\n timeSeries: TimeSeriesData;\n /** Summary metrics */\n summary: {\n totalPageViews: number;\n uniquePageViews: number;\n avgTimeOnPage: number;\n avgScrollDepth: number;\n bounceRate: number;\n };\n}\n\n// ============================================================================\n// PAGE VIEWS CLASS\n// ============================================================================\n\n/**\n * PageViews - Track and analyze page view events.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides comprehensive page view tracking with support for:\n * - Basic page view events\n * - Scroll depth tracking\n * - Time on page measurement\n * - Exit intent detection\n * - Page engagement scoring\n * \n * @example\n * ```typescript\n * const pageViews = new PageViews(webId, endpoint, apiKey);\n * \n * // Basic page view\n * await pageViews.track({\n * url: 'https://example.com/products',\n * path: '/products',\n * title: 'Our Products'\n * });\n * ```\n */\nexport class PageViews extends FlowGridTransport {\n /**\n * Tracks a page view event.\n * \n * @param config - Page view configuration\n * @returns Promise resolving to the API response\n * \n * @example\n * ```typescript\n * await pageViews.track({\n * url: window.location.href,\n * path: window.location.pathname,\n * title: document.title\n * });\n * ```\n */\n async track(config: PageViewConfig): Promise<PageViewResponse> {\n return await this.init(\"page_view\", {\n type: \"page_view\",\n url: config.url,\n path: config.path,\n title: config.title || \"\",\n referrer: config.referrer || \"\",\n userId: config.userId,\n sessionId: config.sessionId,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks scroll depth on a page.\n * \n * @param config - Scroll depth configuration\n * @returns Promise resolving to the API response\n * \n * @example\n * ```typescript\n * // Track when user scrolls to 50%\n * await pageViews.trackScrollDepth({\n * path: '/article/how-to-guide',\n * scrollDepth: 50\n * });\n * ```\n */\n async trackScrollDepth(config: ScrollDepthConfig): Promise<PageViewResponse> {\n return await this.init(\"scroll_depth\", {\n type: \"scroll_depth\",\n path: config.path,\n scrollDepth: Math.min(100, Math.max(0, config.scrollDepth)),\n timeAtDepth: config.timeAtDepth,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks time spent on a page.\n * \n * @param config - Time on page configuration\n * @returns Promise resolving to the API response\n * \n * @example\n * ```typescript\n * // Track on page unload\n * window.addEventListener('beforeunload', () => {\n * pageViews.trackTimeOnPage({\n * path: '/pricing',\n * duration: 45,\n * engaged: true\n * });\n * });\n * ```\n */\n async trackTimeOnPage(config: TimeOnPageConfig): Promise<PageViewResponse> {\n return await this.init(\"time_on_page\", {\n type: \"time_on_page\",\n path: config.path,\n duration: config.duration,\n engaged: config.engaged ?? false,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a page exit event.\n * \n * @param path - Page path being exited\n * @param duration - Time spent on page\n * @param scrollDepth - Final scroll depth\n * @returns Promise resolving to the API response\n */\n async trackExit(\n path: string, \n duration: number, \n scrollDepth: number\n ): Promise<PageViewResponse> {\n return await this.init(\"page_exit\", {\n type: \"page_exit\",\n path,\n duration,\n scrollDepth,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Auto-track page views for both classic full-page navigations and SPA\n * route changes (history.pushState / replaceState / popstate). Also wires\n * scroll-depth thresholds (25/50/75/100%) and a final time-on-page event\n * when the page is hidden or unloaded.\n *\n * @returns A teardown function that detaches every listener.\n *\n * @example\n * ```ts\n * const stop = fg.pageViews.client.autoTrack({ userId: \"u_1\" });\n * ```\n */\n autoTrack(options: { userId?: string; sessionId?: string } = {}): Teardown {\n if (!isBrowser()) return () => { /* no-op on the server */ };\n const win = getWindow()!;\n const doc = getDocument()!;\n\n let currentPath = win.location.pathname;\n let pageStart = Date.now();\n let maxDepth = 0;\n const sentDepths = new Set<number>();\n\n const fireView = () => {\n void this.track({\n url: win.location.href,\n path: win.location.pathname,\n title: doc.title,\n referrer: doc.referrer,\n userId: options.userId,\n sessionId: options.sessionId,\n });\n };\n\n const flushTimeOnPage = () => {\n const duration = Math.round((Date.now() - pageStart) / 1000);\n if (duration <= 0) return;\n void this.trackTimeOnPage({ path: currentPath, duration, engaged: maxDepth > 10 });\n };\n\n const onRouteChange = () => {\n if (win.location.pathname === currentPath) return;\n flushTimeOnPage();\n currentPath = win.location.pathname;\n pageStart = Date.now();\n maxDepth = 0;\n sentDepths.clear();\n fireView();\n };\n\n // Patch history methods so we can detect SPA route changes.\n const origPush = win.history.pushState;\n const origReplace = win.history.replaceState;\n win.history.pushState = function (...args) {\n const r = origPush.apply(this, args);\n win.dispatchEvent(new Event(\"flowgrid:locationchange\"));\n return r;\n };\n win.history.replaceState = function (...args) {\n const r = origReplace.apply(this, args);\n win.dispatchEvent(new Event(\"flowgrid:locationchange\"));\n return r;\n };\n const popHandler = () => onRouteChange();\n const locHandler = () => onRouteChange();\n win.addEventListener(\"popstate\", popHandler);\n win.addEventListener(\"flowgrid:locationchange\", locHandler);\n\n // Scroll-depth thresholds.\n const onScroll = () => {\n const docEl = doc.documentElement;\n const scrollTop = win.scrollY || docEl.scrollTop;\n const height = Math.max(1, docEl.scrollHeight - win.innerHeight);\n const depth = Math.min(100, Math.round((scrollTop / height) * 100));\n if (depth > maxDepth) maxDepth = depth;\n for (const t of [25, 50, 75, 100]) {\n if (depth >= t && !sentDepths.has(t)) {\n sentDepths.add(t);\n void this.trackScrollDepth({ path: currentPath, scrollDepth: t });\n }\n }\n };\n win.addEventListener(\"scroll\", onScroll, { passive: true });\n\n // Flush on unload / visibility change.\n const onHide = () => { if (doc.visibilityState === \"hidden\") flushTimeOnPage(); };\n doc.addEventListener(\"visibilitychange\", onHide);\n win.addEventListener(\"pagehide\", flushTimeOnPage);\n\n // Initial view.\n fireView();\n\n return combineTeardowns(\n () => win.removeEventListener(\"popstate\", popHandler),\n () => win.removeEventListener(\"flowgrid:locationchange\", locHandler),\n () => win.removeEventListener(\"scroll\", onScroll),\n () => doc.removeEventListener(\"visibilitychange\", onHide),\n () => win.removeEventListener(\"pagehide\", flushTimeOnPage),\n () => { win.history.pushState = origPush; win.history.replaceState = origReplace; },\n );\n }\n}\n\nexport default PageViews;\n","/**\n * @fileoverview Product Analytics - Session Tracking\n * @module features/analytics/sessions\n * \n * @description\n * Track user sessions with comprehensive context including device info,\n * geographic location, traffic source, and engagement metrics.\n * \n * @useCases\n * - Monitor active user sessions in real-time\n * - Analyze session duration and depth\n * - Track user journey across pages\n * - Identify traffic sources and campaigns\n * - Segment users by device and location\n * \n * @metrics\n * - Total sessions\n * - Unique visitors\n * - Average session duration\n * - Pages per session\n * - Bounce rate\n * - New vs returning visitors\n * \n * @chartData\n * - Line chart: Sessions over time\n * - Pie chart: Traffic source breakdown\n * - Bar chart: Sessions by device type\n * - Geo map: Sessions by country\n * \n * @example\n * ```typescript\n * const sessions = new Sessions(webId, endpoint, apiKey);\n * \n * // Start a new session\n * const session = await sessions.start({\n * userId: 'user_123',\n * device: { type: 'desktop', browser: 'Chrome' },\n * source: 'google',\n * medium: 'organic'\n * });\n * \n * // End session\n * await sessions.end(session.sessionId);\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { getWindow, getDocument, isBrowser, combineTeardowns, Teardown } from \"../../utils/browser\";\nimport { \n Session,\n DeviceInfo, \n GeoLocation, \n UTMParams,\n TrafficMetrics,\n TimeSeriesData,\n DateRangeFilter,\n PaginationParams,\n UserIdentity\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for starting a new session.\n */\nexport interface SessionStartConfig {\n /** User identity */\n user?: UserIdentity;\n /** Device information */\n device?: DeviceInfo;\n /** Geographic location */\n location?: GeoLocation;\n /** Traffic source */\n source?: string;\n /** Traffic medium */\n medium?: string;\n /** Campaign name */\n campaign?: string;\n /** UTM parameters */\n utm?: UTMParams;\n /** Landing page URL */\n landingPage?: string;\n /** Referrer URL */\n referrer?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Session response from API.\n */\nexport interface SessionResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n sessionId: string;\n started: boolean;\n isNewVisitor: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Session end response.\n */\nexport interface SessionEndResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n sessionId: string;\n duration: number;\n pageViews: number;\n eventCount: number;\n };\n timestamp: string;\n}\n\n/**\n * Session analytics data for charts.\n */\nexport interface SessionAnalyticsData {\n /** Summary metrics */\n summary: {\n totalSessions: number;\n uniqueVisitors: number;\n newVisitors: number;\n returningVisitors: number;\n avgSessionDuration: number;\n avgPagesPerSession: number;\n bounceRate: number;\n };\n /** Sessions time series */\n timeSeries: TimeSeriesData;\n /** Sessions by source */\n bySource: Array<{\n source: string;\n sessions: number;\n percentage: number;\n }>;\n /** Sessions by device */\n byDevice: Array<{\n device: string;\n sessions: number;\n percentage: number;\n }>;\n /** Sessions by country */\n byCountry: Array<{\n country: string;\n countryCode: string;\n sessions: number;\n }>;\n}\n\n/**\n * Real-time session data.\n */\nexport interface RealTimeSessionData {\n /** Currently active sessions */\n activeSessions: number;\n /** Active users in last 5 minutes */\n activeUsers5min: number;\n /** Active users in last 30 minutes */\n activeUsers30min: number;\n /** Sessions by page (real-time) */\n sessionsByPage: Array<{ path: string; count: number }>;\n /** Sessions by country (real-time) */\n sessionsByCountry: Array<{ country: string; count: number }>;\n}\n\n// ============================================================================\n// SESSIONS CLASS\n// ============================================================================\n\n/**\n * Sessions - Track and analyze user sessions.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides comprehensive session tracking including:\n * - Session lifecycle (start, update, end)\n * - Device and location tracking\n * - Traffic source attribution\n * - Real-time session monitoring\n * - Session replay triggers\n * \n * @example\n * ```typescript\n * const sessions = new Sessions(webId, endpoint, apiKey);\n * \n * // Start session on page load\n * const session = await sessions.start({\n * user: { userId: 'user_123' },\n * device: { type: 'mobile', os: 'iOS' },\n * source: 'email',\n * campaign: 'summer_sale'\n * });\n * \n * // Get real-time data\n * const realTime = await sessions.getRealTime();\n * console.log(`${realTime.activeSessions} users online`);\n * ```\n */\nexport class Sessions extends FlowGridTransport {\n /**\n * Starts a new user session.\n * \n * @param config - Session configuration\n * @returns Promise resolving to session response with sessionId\n * \n * @example\n * ```typescript\n * const session = await sessions.start({\n * user: { userId: 'user_123' },\n * source: 'google',\n * medium: 'cpc',\n * campaign: 'brand_terms'\n * });\n * localStorage.setItem('sessionId', session.response.sessionId);\n * ```\n */\n async start(config: SessionStartConfig = {}): Promise<SessionResponse> {\n return await this.init(\"session_start\", {\n type: \"session_start\",\n user: config.user,\n device: config.device,\n location: config.location,\n source: config.source || config.utm?.utmSource,\n medium: config.medium || config.utm?.utmMedium,\n campaign: config.campaign || config.utm?.utmCampaign,\n utm: config.utm,\n landingPage: config.landingPage || (typeof window !== 'undefined' ? window.location.href : ''),\n referrer: config.referrer || (typeof document !== 'undefined' ? document.referrer : ''),\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Updates an existing session with new data.\n * \n * @param sessionId - Session identifier\n * @param properties - Properties to update\n * @returns Promise resolving to update response\n */\n async update(\n sessionId: string, \n properties: Record<string, unknown>\n ): Promise<SessionResponse> {\n return await this.init(\"session_update\", {\n type: \"session_update\",\n sessionId,\n ...properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Ends a user session.\n * \n * @param sessionId - Session identifier to end\n * @returns Promise resolving to session end response with metrics\n * \n * @example\n * ```typescript\n * window.addEventListener('beforeunload', async () => {\n * const sessionId = localStorage.getItem('sessionId');\n * if (sessionId) {\n * await sessions.end(sessionId);\n * }\n * });\n * ```\n */\n async end(sessionId: string): Promise<SessionEndResponse> {\n return await this.init(\"session_end\", {\n type: \"session_end\",\n sessionId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a heartbeat to keep session alive.\n * \n * @param sessionId - Session identifier\n * @returns Promise resolving to heartbeat response\n */\n async heartbeat(sessionId: string): Promise<SessionResponse> {\n return await this.init(\"session_heartbeat\", {\n type: \"session_heartbeat\",\n sessionId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Auto-manage a browser session: starts on call, sends a heartbeat on a\n * fixed interval, and ends the session on `pagehide`. The resolved\n * `sessionId` is stored on `sessionStorage` so reloads within the same tab\n * resume the same session.\n *\n * @param config Optional initial config forwarded to {@link Sessions.start}.\n * @param options.heartbeatMs Interval between heartbeats. Default 30s.\n * @returns A teardown function that ends the session and detaches listeners.\n */\n autoTrack(config: SessionStartConfig = {}, options: { heartbeatMs?: number } = {}): Teardown {\n if (!isBrowser()) return () => { /* no-op on the server */ };\n const win = getWindow()!;\n const doc = getDocument()!;\n const heartbeatMs = options.heartbeatMs ?? 30_000;\n\n const STORE_KEY = \"flowgrid:sessionId\";\n let sessionId: string | undefined;\n try { sessionId = win.sessionStorage.getItem(STORE_KEY) || undefined; } catch { /* private mode */ }\n\n const startPromise = sessionId\n ? Promise.resolve(sessionId)\n : this.start(config).then((r) => {\n sessionId = r.response.sessionId;\n try { win.sessionStorage.setItem(STORE_KEY, sessionId); } catch { /* ignore */ }\n return sessionId;\n }).catch(() => undefined);\n\n const beat = win.setInterval(() => {\n if (sessionId && doc.visibilityState === \"visible\") void this.heartbeat(sessionId);\n }, heartbeatMs);\n\n const onHide = () => { if (sessionId) void this.end(sessionId); };\n win.addEventListener(\"pagehide\", onHide);\n\n return combineTeardowns(\n () => win.clearInterval(beat),\n () => win.removeEventListener(\"pagehide\", onHide),\n () => { void startPromise.then(() => { if (sessionId) void this.end(sessionId); }); },\n () => { try { win.sessionStorage.removeItem(STORE_KEY); } catch { /* ignore */ } },\n );\n }\n}\n\nexport default Sessions;\n","/**\n * @fileoverview Product Analytics - Custom Event Tracking\n * @module features/analytics/events\n * \n * @description\n * Track custom events with flexible properties for any user action\n * or system event. Supports categorization, value tracking, and batching.\n * \n * @useCases\n * - Track button clicks and CTA interactions\n * - Monitor feature usage and engagement\n * - Capture form submissions and errors\n * - Log search queries and results\n * - Record video/audio playback events\n * \n * @metrics\n * - Event counts by type\n * - Event trends over time\n * - Unique users per event\n * - Event value aggregations\n * - Event-to-conversion correlation\n * \n * @chartData\n * - Bar chart: Top events by count\n * - Line chart: Event trends over time\n * - Heatmap: Events by hour/day\n * - Sankey: Event flow paths\n * \n * @example\n * ```typescript\n * const events = new Events(webId, endpoint, apiKey);\n * \n * // Track a button click\n * await events.track({\n * eventName: 'cta_clicked',\n * category: 'engagement',\n * action: 'click',\n * label: 'signup_hero'\n * });\n * \n * // Track with value\n * await events.track({\n * eventName: 'video_watched',\n * value: 120, // seconds\n * properties: { videoId: 'intro-video' }\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { \n CustomEvent,\n ClickEvent,\n FormEvent,\n SearchEvent,\n ErrorEvent,\n TimeSeriesData,\n DateRangeFilter,\n PaginationParams,\n ChartDataPoint\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for tracking a custom event.\n */\nexport interface EventTrackConfig {\n /** Event name (required) */\n eventName: string;\n /** Event category for grouping */\n category?: string;\n /** Event action */\n action?: string;\n /** Event label for sub-categorization */\n label?: string;\n /** Numeric value associated with event */\n value?: number;\n /** Non-interactive event (won't affect bounce rate) */\n nonInteraction?: boolean;\n /** User ID */\n userId?: string;\n /** Session ID */\n sessionId?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Batch event tracking configuration.\n */\nexport interface BatchEventConfig {\n /** Array of events to track */\n events: EventTrackConfig[];\n}\n\n/**\n * Event response from API.\n */\nexport interface EventResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n eventId: string;\n tracked: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Batch event response.\n */\nexport interface BatchEventResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n tracked: number;\n failed: number;\n eventIds: string[];\n };\n timestamp: string;\n}\n\n/**\n * Event analytics data for charts.\n */\nexport interface EventAnalyticsData {\n /** Top events by count */\n topEvents: Array<{\n eventName: string;\n category: string;\n count: number;\n uniqueUsers: number;\n totalValue: number;\n }>;\n /** Events time series */\n timeSeries: TimeSeriesData;\n /** Events by category */\n byCategory: Array<{\n category: string;\n count: number;\n percentage: number;\n }>;\n /** Summary metrics */\n summary: {\n totalEvents: number;\n uniqueEventTypes: number;\n eventsPerUser: number;\n totalValue: number;\n };\n}\n\n// ============================================================================\n// EVENTS CLASS\n// ============================================================================\n\n/**\n * Events - Track and analyze custom events.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides flexible custom event tracking including:\n * - Standard event tracking with categories\n * - Click and interaction tracking\n * - Form submission tracking\n * - Search event tracking\n * - Error and exception logging\n * - Batch event submission\n * \n * @example\n * ```typescript\n * const events = new Events(webId, endpoint, apiKey);\n * \n * // Track feature usage\n * await events.track({\n * eventName: 'feature_used',\n * category: 'features',\n * action: 'use',\n * label: 'export_pdf',\n * properties: { format: 'A4', pages: 5 }\n * });\n * ```\n */\nexport class Events extends FlowGridTransport {\n /**\n * Tracks a custom event.\n * \n * @param config - Event configuration\n * @returns Promise resolving to event response\n * \n * @example\n * ```typescript\n * await events.track({\n * eventName: 'upgrade_clicked',\n * category: 'monetization',\n * action: 'click',\n * label: 'pricing_page',\n * value: 99.99\n * });\n * ```\n */\n async track(config: EventTrackConfig): Promise<EventResponse> {\n return await this.init(\"custom_event\", {\n type: \"custom_event\",\n eventName: config.eventName,\n category: config.category || \"general\",\n action: config.action,\n label: config.label,\n value: config.value,\n nonInteraction: config.nonInteraction || false,\n userId: config.userId,\n sessionId: config.sessionId,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks multiple events in a single request.\n * \n * @param config - Batch configuration with events array\n * @returns Promise resolving to batch response\n * \n * @example\n * ```typescript\n * await events.trackBatch({\n * events: [\n * { eventName: 'step_1_complete', category: 'onboarding' },\n * { eventName: 'step_2_complete', category: 'onboarding' },\n * { eventName: 'step_3_complete', category: 'onboarding' }\n * ]\n * });\n * ```\n */\n async trackBatch(config: BatchEventConfig): Promise<BatchEventResponse> {\n const events = config.events.map(e => ({\n ...e,\n timestamp: new Date().toISOString()\n }));\n\n return await this.init(\"batch_events\", {\n type: \"batch_events\",\n events,\n count: events.length,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a click/interaction event.\n * \n * @param config - Click event configuration\n * @returns Promise resolving to event response\n * \n * @example\n * ```typescript\n * document.addEventListener('click', (e) => {\n * if (e.target.dataset.track) {\n * events.trackClick({\n * elementId: e.target.id,\n * elementText: e.target.textContent,\n * targetUrl: e.target.href\n * });\n * }\n * });\n * ```\n */\n async trackClick(config: ClickEvent): Promise<EventResponse> {\n return await this.init(\"click_event\", {\n type: \"click_event\",\n ...config,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a form submission event.\n * \n * @param config - Form event configuration\n * @returns Promise resolving to event response\n * \n * @example\n * ```typescript\n * form.addEventListener('submit', async (e) => {\n * await events.trackForm({\n * formId: 'contact-form',\n * formName: 'Contact Us',\n * success: true,\n * fields: ['name', 'email', 'message'],\n * completionTime: 45\n * });\n * });\n * ```\n */\n async trackForm(config: FormEvent): Promise<EventResponse> {\n return await this.init(\"form_event\", {\n type: \"form_event\",\n ...config,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a search event.\n * \n * @param config - Search event configuration\n * @returns Promise resolving to event response\n * \n * @example\n * ```typescript\n * await events.trackSearch({\n * query: 'wireless headphones',\n * category: 'products',\n * resultsCount: 42,\n * filters: { brand: 'Sony', priceRange: '100-200' }\n * });\n * ```\n */\n async trackSearch(config: SearchEvent): Promise<EventResponse> {\n return await this.init(\"search_event\", {\n type: \"search_event\",\n query: config.query,\n category: config.category,\n resultsCount: config.resultsCount,\n resultClicked: config.resultClicked,\n clickedPosition: config.clickedPosition,\n filters: config.filters,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks an error or exception.\n * \n * @param config - Error event configuration\n * @returns Promise resolving to event response\n * \n * @example\n * ```typescript\n * window.onerror = (message, source, line, col, error) => {\n * events.trackError({\n * message: message.toString(),\n * stack: error?.stack,\n * type: error?.name,\n * fatal: false,\n * context: source\n * });\n * };\n * ```\n */\n async trackError(config: ErrorEvent): Promise<EventResponse> {\n return await this.init(\"error_event\", {\n type: \"error_event\",\n message: config.message,\n stack: config.stack,\n errorType: config.type,\n fatal: config.fatal || false,\n context: config.context,\n userAction: config.userAction,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default Events;\n","/**\n * @fileoverview Product Analytics - User Identification & Traits\n * @module features/analytics/identify\n * \n * @description\n * Identify users and track their traits for personalization,\n * segmentation, and cohort analysis.\n * \n * @useCases\n * - Link anonymous sessions to authenticated users\n * - Store user profile data for segmentation\n * - Track user lifecycle changes (signup, upgrade, churn)\n * - Enable personalized experiences\n * - Build user cohorts for analysis\n * \n * @metrics\n * - Total identified users\n * - Anonymous to identified conversion rate\n * - Users by plan/segment\n * - User lifetime value distribution\n * - Trait distribution\n * \n * @chartData\n * - Pie chart: Users by plan\n * - Bar chart: Users by trait value\n * - Line chart: New identifications over time\n * - Table: User segments\n * \n * @example\n * ```typescript\n * const identify = new Identify(webId, endpoint, apiKey);\n * \n * // Identify user on login\n * await identify.user({\n * userId: 'user_123',\n * traits: {\n * email: 'user@example.com',\n * name: 'John Doe',\n * plan: 'premium',\n * createdAt: '2025-01-15'\n * }\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { getWindow, getDocument, isBrowser, type Teardown } from \"../../utils/browser\";\nimport {\n UserTraits,\n MoneyValue,\n TimeSeriesData\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for identifying a user.\n */\nexport interface IdentifyConfig {\n /** Unique user identifier */\n userId: string;\n /** Anonymous ID to link (optional) */\n anonymousId?: string;\n /** User traits/properties */\n traits?: UserTraits;\n}\n\n/**\n * Options for {@link Identify.autoTrackForms} passive identity capture.\n */\nexport interface PassiveIdentityOptions {\n /** Debounce for `input` events before reading a field (ms, default 800). */\n debounceMs?: number;\n /** Extra `path` regex on which NO inputs are captured (replaces the default). */\n excludePath?: RegExp;\n}\n\n/**\n * Configuration for aliasing user IDs.\n */\nexport interface AliasConfig {\n /** Previous user ID */\n previousId: string;\n /** New user ID */\n userId: string;\n}\n\n/**\n * Configuration for grouping users.\n */\nexport interface GroupConfig {\n /** User ID */\n userId: string;\n /** Group/company ID */\n groupId: string;\n /** Group traits */\n traits?: {\n name?: string;\n industry?: string;\n employees?: number;\n plan?: string;\n [key: string]: unknown;\n };\n}\n\n/**\n * Identify response from API.\n */\nexport interface IdentifyResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n userId: string;\n identified: boolean;\n isNewUser: boolean;\n linkedAnonymousId?: string;\n };\n timestamp: string;\n}\n\n/**\n * User analytics data for charts.\n */\nexport interface UserAnalyticsData {\n /** Summary metrics */\n summary: {\n totalUsers: number;\n identifiedUsers: number;\n anonymousUsers: number;\n newUsersThisPeriod: number;\n identificationRate: number;\n };\n /** Users by plan/segment */\n byPlan: Array<{\n plan: string;\n count: number;\n percentage: number;\n mrr?: MoneyValue;\n }>;\n /** New users time series */\n newUsersTimeSeries: TimeSeriesData;\n /** User lifecycle distribution */\n byLifecycleStage: Array<{\n stage: string;\n count: number;\n percentage: number;\n }>;\n}\n\n// ============================================================================\n// IDENTIFY CLASS\n// ============================================================================\n\n/**\n * Identify - User identification and trait management.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides user identification capabilities including:\n * - User identification on login/signup\n * - Anonymous to identified user linking\n * - User trait/property storage\n * - User aliasing for ID changes\n * - Group/company association\n * \n * @example\n * ```typescript\n * const identify = new Identify(webId, endpoint, apiKey);\n * \n * // On user signup\n * await identify.user({\n * userId: 'user_123',\n * anonymousId: getAnonymousId(),\n * traits: {\n * email: 'user@example.com',\n * name: 'Jane Smith',\n * plan: 'starter',\n * createdAt: new Date().toISOString()\n * }\n * });\n * \n * // Associate user with company\n * await identify.group({\n * userId: 'user_123',\n * groupId: 'company_456',\n * traits: { name: 'Acme Inc', employees: 50 }\n * });\n * ```\n */\nexport class Identify extends FlowGridTransport {\n /**\n * Identifies a user with traits.\n * \n * @param config - Identification configuration\n * @returns Promise resolving to identify response\n * \n * @example\n * ```typescript\n * // Basic identification\n * await identify.user({\n * userId: 'user_123',\n * traits: { email: 'user@example.com' }\n * });\n * \n * // With anonymous ID linking\n * await identify.user({\n * userId: 'user_123',\n * anonymousId: localStorage.getItem('anonId'),\n * traits: {\n * name: 'John Doe',\n * plan: 'premium',\n * ltv: { amount: 29900, currency: 'USD' }\n * }\n * });\n * ```\n */\n async user(config: IdentifyConfig): Promise<IdentifyResponse> {\n return await this.init(\"identify_user\", {\n type: \"identify\",\n userId: config.userId,\n anonymousId: config.anonymousId,\n traits: config.traits || {},\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Updates traits for an existing user.\n * \n * @param userId - User identifier\n * @param traits - Traits to update/add\n * @returns Promise resolving to identify response\n * \n * @example\n * ```typescript\n * // Update user plan after upgrade\n * await identify.updateTraits('user_123', {\n * plan: 'enterprise',\n * ltv: { amount: 99900, currency: 'USD' }\n * });\n * ```\n */\n async updateTraits(userId: string, traits: UserTraits): Promise<IdentifyResponse> {\n return await this.init(\"update_traits\", {\n type: \"update_traits\",\n userId,\n traits,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Creates an alias between two user IDs.\n * \n * @param config - Alias configuration\n * @returns Promise resolving to alias response\n * \n * @example\n * ```typescript\n * // When user ID changes (e.g., after migration)\n * await identify.alias({\n * previousId: 'old_user_id',\n * userId: 'new_user_id'\n * });\n * ```\n */\n async alias(config: AliasConfig): Promise<IdentifyResponse> {\n return await this.init(\"alias_user\", {\n type: \"alias\",\n previousId: config.previousId,\n userId: config.userId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Associates a user with a group/company.\n * \n * @param config - Group configuration\n * @returns Promise resolving to group response\n * \n * @example\n * ```typescript\n * await identify.group({\n * userId: 'user_123',\n * groupId: 'company_456',\n * traits: {\n * name: 'Acme Corporation',\n * industry: 'Technology',\n * employees: 500,\n * plan: 'enterprise'\n * }\n * });\n * ```\n */\n async group(config: GroupConfig): Promise<IdentifyResponse> {\n return await this.init(\"group_user\", {\n type: \"group\",\n userId: config.userId,\n groupId: config.groupId,\n traits: config.traits || {},\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Resets user identity (logout).\n * \n * @param userId - User ID to reset\n * @returns Promise resolving to reset response\n */\n async reset(userId: string): Promise<IdentifyResponse> {\n return await this.init(\"reset_identity\", {\n type: \"reset\",\n userId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Passive identity capture — the browser-side form-field tracker (parity with\n * `flowgrid.js`'s `setupPassiveIdentityCapture`). Watches inputs (initial scan\n * + a MutationObserver for fields added by modals/SPAs), classifies each by\n * name/id/placeholder/autocomplete, hashes PII (SHA-256) before it leaves the\n * browser, and posts the captured identity to the flowgrid-core identity store\n * (`POST /api/v1/identity/identify`) so the dashboard can map the anonymous\n * `visitor_id` to a person — and, with a CRM connected, to a known customer.\n *\n * Privacy: requires `analytics` consent; never attaches to password/card/cvv/\n * ssn/pin fields or anything marked `data-analytics=\"ignore\"` / `.no-track`;\n * skips sensitive paths (checkout/account-settings/admin). Off by default in\n * {@link FlowGrid.autoTrack} — enable with `autoTrack({ passiveIdentity: true })`.\n *\n * @returns a teardown that detaches every listener (no-op on the server).\n */\n autoTrackForms(options: PassiveIdentityOptions = {}): Teardown {\n if (!isBrowser()) return () => { /* no-op on the server */ };\n if (!FlowGridTransport.hasConsent(\"analytics\")) return () => { /* consent not granted */ };\n\n const win = getWindow()!;\n const doc = getDocument()!;\n const debounceMs = options.debounceMs ?? 800;\n\n const EXCLUDED_PATH_RE = options.excludePath ?? /\\/(checkout|account\\/settings|admin)(\\/|$)/i;\n const BLOCKED_RE = /(password|cardnumber|creditcard|\\bcard\\b|cvv|cvc|ssn|\\bpin\\b|securitycode)/i;\n const SKIP_RE = /(message|comment|note|feedback|search|query)/i;\n\n type FieldRule = { re: RegExp; trait: string; pii: boolean; identify: boolean };\n const FIELD_MAP: FieldRule[] = [\n { re: /(email|emailaddress)/, trait: \"email\", pii: true, identify: true },\n { re: /(phone|tel|mobile|cell)/, trait: \"phone\", pii: true, identify: false },\n { re: /(firstname|fname|givenname)/, trait: \"firstName\", pii: false, identify: false },\n { re: /(lastname|lname|surname|familyname)/, trait: \"lastName\", pii: false, identify: false },\n { re: /(company|organi[sz]ation|businessname)/, trait: \"company\", pii: false, identify: false },\n { re: /(streetaddress|addressline|^address$|^street$)/, trait: \"address\", pii: false, identify: false },\n { re: /(^city$|town)/, trait: \"city\", pii: false, identify: false },\n { re: /(postcode|postalcode|zipcode|^zip$)/, trait: \"postcode\", pii: false, identify: false },\n // A single full-name / username field maps to firstName; listed last so\n // dedicated first/last-name fields win when both exist.\n { re: /(fullname|^name$|username|yourname|contactname|displayname)/, trait: \"firstName\", pii: false, identify: false },\n ];\n\n const captured: Record<string, string> = {};\n const seen = new WeakSet<Element>();\n const debounceTimers = new WeakMap<Element, ReturnType<typeof setTimeout>>();\n const teardowns: Array<() => void> = [];\n\n const fieldEl = (el: Element) => el as HTMLInputElement;\n\n const normalize = (el: Element): string =>\n [fieldEl(el).name, el.id, el.getAttribute(\"placeholder\"), el.getAttribute(\"autocomplete\")]\n .filter(Boolean).join(\" \").toLowerCase().replace(/[\\s_-]/g, \"\");\n\n const isBlocked = (el: Element, key: string): boolean => {\n const input = fieldEl(el);\n if (input.type === \"password\" || input.type === \"hidden\") return true;\n if (el.getAttribute(\"data-analytics\") === \"ignore\") return true;\n if (el.classList && el.classList.contains(\"no-track\")) return true;\n return BLOCKED_RE.test(key);\n };\n\n const classify = (el: Element): FieldRule | { blocked: true } | null => {\n const key = normalize(el);\n if (!key) return null;\n if (isBlocked(el, key)) return { blocked: true };\n if (SKIP_RE.test(key)) return null;\n return FIELD_MAP.find((f) => f.re.test(key)) ?? null;\n };\n\n const sha256hex = async (value: string): Promise<string | null> => {\n try {\n const enc = new TextEncoder().encode(String(value).trim().toLowerCase());\n const buf = await win.crypto.subtle.digest(\"SHA-256\", enc);\n return Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n } catch {\n return null;\n }\n };\n\n const resolveVisitorId = (): string =>\n (this.identityManager ? this.identityManager.getVisitorId() : this.visitorId) || \"\";\n const resolveSessionId = (): string | null =>\n this.identityManager ? this.identityManager.getSessionId() : null;\n\n // Posts the captured identity to the flowgrid-core identity store. Raw email\n // stays server-side (CRM enrichment + dashboard display); the analytics\n // event store only ever sees hashes (the `field_captured` mirror below).\n const sendIdentity = () => {\n const visitorId = resolveVisitorId();\n if (!this.webId || !visitorId) return;\n if (!captured.email && !captured.emailHash && !captured.firstName && !captured.lastName && !captured.phone) return;\n\n const traits = {\n email: captured.email ?? null,\n email_hash: captured.emailHash ?? null,\n // Raw phone is only populated on an explicit form submit (consent);\n // passive blur/input keeps phone hash-only.\n phone: captured.phone ?? null,\n phone_hash: captured.phoneHash ?? null,\n first_name: captured.firstName ?? null,\n last_name: captured.lastName ?? null,\n company: captured.company ?? null,\n address: captured.address ?? null,\n city: captured.city ?? null,\n postcode: captured.postcode ?? null,\n };\n const url = `${this.endpoint}/api/v1/identity/identify`;\n const payload = JSON.stringify({\n web_id: this.webId,\n visitor_id: visitorId,\n session_id: resolveSessionId(),\n path_name: win.location.pathname,\n traits,\n });\n try {\n if (typeof navigator !== \"undefined\" && navigator.sendBeacon &&\n navigator.sendBeacon(url, new Blob([payload], { type: \"application/json\" }))) {\n // sent via beacon\n } else {\n void fetch(url, { method: \"POST\", headers: { \"Content-Type\": \"application/json\" }, body: payload, keepalive: true }).catch(() => { /* swallow */ });\n }\n } catch { /* swallow */ }\n };\n\n const captureField = (el: Element) => {\n const cls = classify(el);\n if (!cls || \"blocked\" in cls) return;\n const value = (fieldEl(el).value || \"\").trim();\n if (!value) return;\n\n if (cls.pii) {\n const toHash = cls.trait === \"phone\" ? value.replace(/[^\\d+]/g, \"\") : value;\n void sha256hex(toHash).then((hash) => {\n if (cls.trait === \"email\") { captured.email = value; if (hash) captured.emailHash = hash; }\n else if (cls.trait === \"phone\" && hash) { captured.phoneHash = hash; }\n if (cls.identify || captured.email) sendIdentity();\n });\n } else {\n captured[cls.trait] = value;\n if (captured.email) sendIdentity();\n }\n };\n\n const attach = (el: Element) => {\n if (seen.has(el)) return;\n const cls = classify(el);\n if (!cls || \"blocked\" in cls) return; // never listen to blocked / unknown fields\n seen.add(el);\n const onBlur = () => captureField(el);\n const onInput = () => {\n clearTimeout(debounceTimers.get(el));\n debounceTimers.set(el, setTimeout(() => captureField(el), debounceMs));\n };\n el.addEventListener(\"blur\", onBlur, { passive: true } as AddEventListenerOptions);\n el.addEventListener(\"input\", onInput, { passive: true } as AddEventListenerOptions);\n teardowns.push(() => {\n el.removeEventListener(\"blur\", onBlur);\n el.removeEventListener(\"input\", onInput);\n });\n };\n\n const scanPage = () => {\n if (EXCLUDED_PATH_RE.test(win.location.pathname)) return;\n doc.querySelectorAll(\"input, textarea, select\").forEach(attach);\n };\n\n // Explicit submit is treated as consent — capture the RAW phone (the one\n // identity field passive tracking keeps hash-only) so the CRM cascade can\n // match by phone, then flush. All other fields are already captured passively.\n const onSubmit = (e: Event) => {\n const form = e.target as HTMLElement | null;\n if (!form || form.tagName !== \"FORM\") return;\n if (EXCLUDED_PATH_RE.test(win.location.pathname)) return;\n let phoneOnSubmit = false;\n (form as HTMLFormElement).querySelectorAll(\"input, textarea, select\").forEach((el) => {\n const cls = classify(el);\n if (!cls || \"blocked\" in cls || cls.trait !== \"phone\") return;\n const raw = (fieldEl(el).value || \"\").trim();\n if (raw) { captured.phone = raw; phoneOnSubmit = true; }\n });\n if (phoneOnSubmit) sendIdentity();\n };\n doc.addEventListener(\"submit\", onSubmit, true);\n teardowns.push(() => doc.removeEventListener(\"submit\", onSubmit, true));\n\n scanPage();\n let mo: MutationObserver | null = null;\n try {\n mo = new MutationObserver(() => scanPage());\n if (doc.body) mo.observe(doc.body, { childList: true, subtree: true });\n } catch { /* MutationObserver unavailable */ }\n\n return () => {\n if (mo) try { mo.disconnect(); } catch { /* noop */ }\n for (const t of teardowns) { try { t(); } catch { /* noop */ } }\n };\n }\n\n}\n\nexport default Identify;\n","/**\n * @fileoverview Product Analytics - Funnel Analysis\n * @module features/analytics/funnels\n * \n * @description\n * Define and analyze conversion funnels to understand user journeys,\n * identify drop-off points, and optimize conversion rates.\n * \n * @useCases\n * - Track signup/onboarding completion rates\n * - Analyze checkout funnel performance\n * - Identify where users drop off in key flows\n * - Compare funnel performance across segments\n * - A/B test different funnel variations\n * \n * @metrics\n * - Step-by-step conversion rates\n * - Drop-off rates at each step\n * - Time to complete each step\n * - Overall funnel conversion rate\n * - Funnel completion time\n * \n * @chartData\n * - Funnel chart: Step-by-step visualization\n * - Bar chart: Conversion by step\n * - Line chart: Funnel performance over time\n * - Comparison: Funnel variants side-by-side\n * \n * @example\n * ```typescript\n * const funnels = new Funnels(webId, endpoint, apiKey);\n * \n * // Track funnel step\n * await funnels.trackStep({\n * funnelId: 'checkout_funnel',\n * step: 'add_payment',\n * stepNumber: 3,\n * userId: 'user_123'\n * });\n * \n * // Get funnel analysis\n * const analysis = await funnels.analyze('checkout_funnel');\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { \n FunnelStep,\n FunnelAnalysis,\n DateRangeFilter,\n TimeSeriesData\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Funnel definition configuration.\n */\nexport interface FunnelDefinition {\n /** Unique funnel identifier */\n funnelId: string;\n /** Funnel name for display */\n name: string;\n /** Funnel description */\n description?: string;\n /** Ordered list of step definitions */\n steps: Array<{\n /** Step name */\n name: string;\n /** Event(s) that trigger this step */\n events: string[];\n /** Step description */\n description?: string;\n /** Whether step is optional */\n optional?: boolean;\n }>;\n}\n\n/**\n * Configuration for tracking a funnel step.\n */\nexport interface FunnelStepConfig {\n /** Funnel identifier */\n funnelId: string;\n /** Step name or event */\n step: string;\n /** Step number (1-indexed) */\n stepNumber: number;\n /** User ID */\n userId?: string;\n /** Session ID */\n sessionId?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Funnel step response from API.\n */\nexport interface FunnelStepResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n funnelId: string;\n step: string;\n tracked: boolean;\n previousStep?: string;\n timeFromPrevious?: number;\n };\n timestamp: string;\n}\n\n/**\n * Funnel analytics data for charts.\n */\nexport interface FunnelAnalyticsData {\n /** Funnel definition */\n funnel: FunnelDefinition;\n /** Analysis results */\n analysis: FunnelAnalysis;\n /** Step-by-step data for visualization */\n visualization: Array<{\n step: string;\n entered: number;\n completed: number;\n conversionRate: number;\n dropOffRate: number;\n avgTime: number;\n }>;\n /** Performance over time */\n trendData: TimeSeriesData;\n}\n\n/**\n * Funnel comparison data.\n */\nexport interface FunnelComparisonData {\n /** Base funnel data */\n baseline: FunnelAnalyticsData;\n /** Comparison funnel data */\n comparison: FunnelAnalyticsData;\n /** Step-by-step comparison */\n stepComparison: Array<{\n step: string;\n baselineRate: number;\n comparisonRate: number;\n difference: number;\n significant: boolean;\n }>;\n}\n\n// ============================================================================\n// FUNNELS CLASS\n// ============================================================================\n\n/**\n * Funnels - Define and analyze conversion funnels.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides funnel analysis capabilities including:\n * - Funnel definition and management\n * - Step-by-step event tracking\n * - Conversion and drop-off analysis\n * - Time-to-completion tracking\n * - Segment-based funnel comparison\n * \n * @example\n * ```typescript\n * const funnels = new Funnels(webId, endpoint, apiKey);\n * \n * // Define a signup funnel\n * await funnels.define({\n * funnelId: 'signup_funnel',\n * name: 'User Signup',\n * steps: [\n * { name: 'Visit Landing', events: ['page_view_landing'] },\n * { name: 'Click Signup', events: ['signup_clicked'] },\n * { name: 'Submit Form', events: ['signup_submitted'] },\n * { name: 'Verify Email', events: ['email_verified'] }\n * ]\n * });\n * \n * // Analyze funnel performance\n * const analysis = await funnels.analyze('signup_funnel', {\n * range: 'last30days'\n * });\n * ```\n */\nexport class Funnels extends FlowGridTransport {\n /**\n * Defines a new funnel or updates existing.\n * \n * @param config - Funnel definition\n * @returns Promise resolving to definition response\n * \n * @example\n * ```typescript\n * await funnels.define({\n * funnelId: 'checkout_funnel',\n * name: 'Checkout Flow',\n * steps: [\n * { name: 'View Cart', events: ['view_cart'] },\n * { name: 'Begin Checkout', events: ['begin_checkout'] },\n * { name: 'Add Shipping', events: ['add_shipping_info'] },\n * { name: 'Add Payment', events: ['add_payment_info'] },\n * { name: 'Purchase', events: ['purchase'] }\n * ]\n * });\n * ```\n */\n async define(config: FunnelDefinition): Promise<FunnelStepResponse> {\n return await this.init(\"define_funnel\", {\n type: \"define_funnel\",\n funnelId: config.funnelId,\n name: config.name,\n description: config.description,\n steps: config.steps,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a funnel step completion.\n * \n * @param config - Funnel step configuration\n * @returns Promise resolving to step response\n * \n * @example\n * ```typescript\n * // Track user progressing through checkout\n * await funnels.trackStep({\n * funnelId: 'checkout_funnel',\n * step: 'add_shipping_info',\n * stepNumber: 3,\n * userId: 'user_123',\n * properties: { shippingMethod: 'express' }\n * });\n * ```\n */\n async trackStep(config: FunnelStepConfig): Promise<FunnelStepResponse> {\n return await this.init(\"funnel_step\", {\n type: \"funnel_step\",\n funnelId: config.funnelId,\n step: config.step,\n stepNumber: config.stepNumber,\n userId: config.userId,\n sessionId: config.sessionId,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks funnel completion.\n * \n * @param funnelId - Funnel identifier\n * @param userId - User who completed\n * @param properties - Completion properties\n * @returns Promise resolving to completion response\n */\n async trackComplete(\n funnelId: string,\n userId?: string,\n properties?: Record<string, unknown>\n ): Promise<FunnelStepResponse> {\n return await this.init(\"funnel_complete\", {\n type: \"funnel_complete\",\n funnelId,\n userId,\n ...properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks funnel abandonment.\n * \n * @param funnelId - Funnel identifier\n * @param lastStep - Last completed step\n * @param userId - User who abandoned\n * @param reason - Optional abandonment reason\n * @returns Promise resolving to abandonment response\n */\n async trackAbandon(\n funnelId: string,\n lastStep: string,\n userId?: string,\n reason?: string\n ): Promise<FunnelStepResponse> {\n return await this.init(\"funnel_abandon\", {\n type: \"funnel_abandon\",\n funnelId,\n lastStep,\n userId,\n reason,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Analyzes funnel performance.\n * \n * @param funnelId - Funnel identifier\n * @param filter - Date range filter\n * @returns Promise resolving to funnel analytics\n * \n * @example\n * ```typescript\n * const analysis = await funnels.analyze('checkout_funnel', {\n * range: 'last7days'\n * });\n * \n * // Funnel visualization data\n * const funnelData = analysis.visualization.map(step => ({\n * name: step.step,\n * value: step.entered,\n * conversion: step.conversionRate\n * }));\n * \n * // Overall conversion rate\n * console.log(`Conversion: ${analysis.analysis.overallConversionRate}%`);\n * ```\n */\n async analyze(\n funnelId: string,\n filter?: DateRangeFilter\n ): Promise<FunnelAnalyticsData> {\n return await this.init(\"analyze_funnel\", {\n type: \"query\",\n funnelId,\n filter: filter || { range: \"last30days\" },\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Compares two funnel periods or segments.\n * \n * @param funnelId - Funnel identifier\n * @param baselineFilter - Baseline period filter\n * @param comparisonFilter - Comparison period filter\n * @returns Promise resolving to comparison data\n * \n * @example\n * ```typescript\n * const comparison = await funnels.compare(\n * 'checkout_funnel',\n * { range: 'last30days' },\n * { range: 'last60days' }\n * );\n * \n * // Side-by-side comparison chart\n * const comparisonData = comparison.stepComparison.map(s => ({\n * step: s.step,\n * baseline: s.baselineRate,\n * comparison: s.comparisonRate\n * }));\n * ```\n */\n async compare(\n funnelId: string,\n baselineFilter: DateRangeFilter,\n comparisonFilter: DateRangeFilter\n ): Promise<FunnelComparisonData> {\n return await this.init(\"compare_funnel\", {\n type: \"query\",\n funnelId,\n baselineFilter,\n comparisonFilter,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Lists all defined funnels.\n * \n * @returns Promise resolving to funnel list\n */\n async list(): Promise<FunnelDefinition[]> {\n return await this.init(\"list_funnels\", {\n type: \"query\",\n timestamp: new Date().toISOString()\n });\n }\n}\n\nexport default Funnels;\n","/**\n * @fileoverview Product Analytics - Retention Analysis\n * @module features/analytics/retention\n * \n * @description\n * Track and analyze user retention using cohort analysis,\n * identify churn patterns, and measure user stickiness.\n * \n * @useCases\n * - Measure week-over-week retention rates\n * - Compare retention across user cohorts\n * - Identify churn risk factors\n * - Track feature adoption impact on retention\n * - Calculate DAU/MAU ratios (stickiness)\n * \n * @metrics\n * - Day 1, 7, 30 retention rates\n * - Cohort retention curves\n * - Churn rate by segment\n * - User resurrection rate\n * - DAU/WAU/MAU ratios\n * \n * @chartData\n * - Retention curve: Line chart over time\n * - Cohort matrix: Heatmap by cohort/period\n * - Bar chart: Retention by segment\n * - Trend line: Stickiness over time\n * \n * @example\n * ```typescript\n * const retention = new Retention(webId, endpoint, apiKey);\n * \n * // Track user activity\n * await retention.trackActivity({\n * userId: 'user_123',\n * action: 'feature_used'\n * });\n * \n * // Get cohort analysis\n * const cohorts = await retention.getCohortAnalysis({\n * range: 'last90days',\n * granularity: 'week'\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { \n RetentionCohort,\n UserLifecycleStage,\n LifecycleMetrics,\n DateRangeFilter,\n TimeGranularity,\n TimeSeriesData\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for tracking user activity.\n */\nexport interface ActivityConfig {\n /** User identifier */\n userId: string;\n /** Activity type/action */\n action?: string;\n /** Session ID */\n sessionId?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Configuration for cohort analysis.\n */\nexport interface CohortAnalysisConfig {\n /** Date range for analysis */\n filter?: DateRangeFilter;\n /** Cohort granularity */\n granularity?: TimeGranularity;\n /** Segment by trait */\n segmentBy?: string;\n /** Number of periods to show */\n periods?: number;\n}\n\n/**\n * Activity tracking response.\n */\nexport interface ActivityResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n userId: string;\n tracked: boolean;\n daysSinceFirstSeen: number;\n isReturning: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Cohort analysis data for charts.\n */\nexport interface CohortAnalysisData {\n /** Cohort data */\n cohorts: RetentionCohort[];\n /** Retention matrix for heatmap */\n matrix: Array<{\n cohort: string;\n periods: Array<{\n period: number;\n retained: number;\n retentionRate: number;\n }>;\n }>;\n /** Summary metrics */\n summary: {\n avgDay1Retention: number;\n avgDay7Retention: number;\n avgDay30Retention: number;\n overallRetentionTrend: 'improving' | 'declining' | 'stable';\n };\n}\n\n/**\n * User stickiness data.\n */\nexport interface StickinessData {\n /** DAU - Daily Active Users */\n dau: number;\n /** WAU - Weekly Active Users */\n wau: number;\n /** MAU - Monthly Active Users */\n mau: number;\n /** DAU/MAU ratio (stickiness) */\n dauMauRatio: number;\n /** DAU/WAU ratio */\n dauWauRatio: number;\n /** Stickiness trend over time */\n trendData: TimeSeriesData;\n}\n\n/**\n * Churn analysis data.\n */\nexport interface ChurnAnalysisData {\n /** Current churn rate */\n churnRate: number;\n /** Users at risk of churning */\n atRiskUsers: number;\n /** Users who churned this period */\n churnedUsers: number;\n /** Churn by segment */\n bySegment: Array<{\n segment: string;\n churnRate: number;\n count: number;\n }>;\n /** Common churn factors */\n churnFactors: Array<{\n factor: string;\n correlation: number;\n }>;\n /** Churn trend */\n trendData: TimeSeriesData;\n}\n\n// ============================================================================\n// RETENTION CLASS\n// ============================================================================\n\n/**\n * Retention - Track and analyze user retention.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides retention analysis capabilities including:\n * - User activity tracking\n * - Cohort-based retention analysis\n * - Stickiness metrics (DAU/MAU)\n * - Churn prediction and analysis\n * - Lifecycle stage tracking\n * \n * @example\n * ```typescript\n * const retention = new Retention(webId, endpoint, apiKey);\n * \n * // Track daily user activity\n * await retention.trackActivity({\n * userId: 'user_123',\n * action: 'daily_login'\n * });\n * \n * // Analyze cohort retention\n * const analysis = await retention.getCohortAnalysis({\n * granularity: 'week',\n * periods: 12\n * });\n * \n * // Cohort heatmap data\n * const heatmapData = analysis.matrix;\n * ```\n */\nexport class Retention extends FlowGridTransport {\n /**\n * Tracks user activity for retention calculation.\n * \n * @param config - Activity configuration\n * @returns Promise resolving to activity response\n * \n * @example\n * ```typescript\n * // Track on app open\n * await retention.trackActivity({\n * userId: 'user_123',\n * action: 'app_opened'\n * });\n * \n * // Track specific feature usage\n * await retention.trackActivity({\n * userId: 'user_123',\n * action: 'used_export',\n * properties: { format: 'pdf' }\n * });\n * ```\n */\n async trackActivity(config: ActivityConfig): Promise<ActivityResponse> {\n return await this.init(\"user_activity\", {\n type: \"user_activity\",\n userId: config.userId,\n action: config.action || \"active\",\n sessionId: config.sessionId,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks user lifecycle stage change.\n * \n * @param userId - User identifier\n * @param stage - New lifecycle stage\n * @param previousStage - Previous stage (optional)\n * @returns Promise resolving to stage change response\n */\n async trackLifecycleChange(\n userId: string,\n stage: UserLifecycleStage,\n previousStage?: UserLifecycleStage\n ): Promise<ActivityResponse> {\n return await this.init(\"lifecycle_change\", {\n type: \"lifecycle_change\",\n userId,\n stage,\n previousStage,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default Retention;\n","/**\n * @fileoverview Product Analytics - Revenue Attribution\n * @module features/analytics/attribution\n * \n * @description\n * Multi-touch attribution modeling to understand which marketing\n * channels and touchpoints drive conversions and revenue.\n * \n * @useCases\n * - Track marketing touchpoints across customer journey\n * - Calculate attribution for conversions\n * - Compare attribution models (first-touch, last-touch, linear)\n * - Optimize marketing spend allocation\n * - Understand channel effectiveness\n * \n * @metrics\n * - Attributed revenue by channel\n * - Conversion paths\n * - Assisted conversions\n * - Time to conversion\n * - Channel influence score\n * \n * @chartData\n * - Sankey diagram: Customer journey paths\n * - Bar chart: Revenue by channel\n * - Table: Attribution comparison by model\n * - Pie chart: Channel contribution\n * \n * @example\n * ```typescript\n * const attribution = new Attribution(webId, endpoint, apiKey);\n * \n * // Track touchpoint\n * await attribution.trackTouchpoint({\n * userId: 'user_123',\n * channel: 'google_ads',\n * campaign: 'brand_terms',\n * medium: 'cpc'\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { \n UTMParams,\n MoneyValue,\n DateRangeFilter,\n TimeSeriesData\n} from \"../../types\";\nimport { getWindow, getDocument, isBrowser } from \"../../utils/browser\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Attribution model types.\n */\nexport type AttributionModel = \n | 'first_touch'\n | 'last_touch'\n | 'linear'\n | 'time_decay'\n | 'position_based'\n | 'data_driven';\n\n/**\n * Marketing touchpoint configuration.\n */\nexport interface TouchpointConfig {\n /** User ID */\n userId: string;\n /** Marketing channel */\n channel: string;\n /** Campaign name */\n campaign?: string;\n /** Traffic medium */\n medium?: string;\n /** Traffic source */\n source?: string;\n /** Content/creative */\n content?: string;\n /** Search term */\n term?: string;\n /** Referrer URL */\n referrer?: string;\n /** Landing page */\n landingPage?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Conversion event for attribution.\n */\nexport interface ConversionEvent {\n /** User ID */\n userId: string;\n /** Conversion type */\n conversionType: 'purchase' | 'signup' | 'lead' | 'custom';\n /** Conversion value */\n value?: MoneyValue;\n /** Conversion identifier (e.g., order ID) */\n conversionId?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Touchpoint response from API.\n */\nexport interface TouchpointResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n touchpointId: string;\n userId: string;\n position: number;\n tracked: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Attribution report data.\n */\nexport interface AttributionReportData {\n /** Selected attribution model */\n model: AttributionModel;\n /** Attribution by channel */\n byChannel: Array<{\n channel: string;\n conversions: number;\n attributedRevenue: MoneyValue;\n assistedConversions: number;\n avgTouchpoints: number;\n firstTouchConversions: number;\n lastTouchConversions: number;\n }>;\n /** Top conversion paths */\n topPaths: Array<{\n path: string[];\n conversions: number;\n revenue: MoneyValue;\n avgTimeToConvert: number;\n }>;\n /** Summary metrics */\n summary: {\n totalConversions: number;\n totalRevenue: MoneyValue;\n avgTouchpointsPerConversion: number;\n avgTimeToConversion: number;\n topChannel: string;\n topCampaign: string;\n };\n /** Model comparison */\n modelComparison: Array<{\n model: AttributionModel;\n channelAttribution: Record<string, number>;\n }>;\n}\n\n// ============================================================================\n// ATTRIBUTION CLASS\n// ============================================================================\n\n/**\n * Attribution - Multi-touch attribution tracking.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides attribution capabilities including:\n * - Touchpoint tracking\n * - Conversion attribution\n * - Multiple attribution models\n * - Path analysis\n * - Channel effectiveness\n * \n * @example\n * ```typescript\n * const attribution = new Attribution(webId, endpoint, apiKey);\n * \n * // Track touchpoints throughout customer journey\n * await attribution.trackTouchpoint({\n * userId: 'user_123',\n * channel: 'email',\n * campaign: 'welcome_series',\n * medium: 'email'\n * });\n * \n * // Record conversion\n * await attribution.recordConversion({\n * userId: 'user_123',\n * conversionType: 'purchase',\n * value: { amount: 9999, currency: 'USD' },\n * conversionId: 'ORD-12345'\n * });\n * ```\n */\nexport class Attribution extends FlowGridTransport {\n /**\n * Tracks a marketing touchpoint.\n * \n * @param config - Touchpoint configuration\n * @returns Promise resolving to touchpoint response\n * \n * @example\n * ```typescript\n * await attribution.trackTouchpoint({\n * userId: 'user_123',\n * channel: 'facebook_ads',\n * campaign: 'retargeting_cart',\n * medium: 'paid_social',\n * landingPage: '/products/special-offer'\n * });\n * ```\n */\n async trackTouchpoint(config: TouchpointConfig): Promise<TouchpointResponse> {\n return await this.init(\"attribution_touchpoint\", {\n type: \"attribution_touchpoint\",\n userId: config.userId,\n channel: config.channel,\n campaign: config.campaign,\n medium: config.medium,\n source: config.source,\n content: config.content,\n term: config.term,\n referrer: config.referrer,\n landingPage: config.landingPage,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Records a conversion for attribution.\n * \n * @param event - Conversion event\n * @returns Promise resolving to conversion response\n * \n * @example\n * ```typescript\n * await attribution.recordConversion({\n * userId: 'user_123',\n * conversionType: 'purchase',\n * value: { amount: 14999, currency: 'USD' },\n * conversionId: 'ORD-98765'\n * });\n * ```\n */\n async recordConversion(event: ConversionEvent): Promise<TouchpointResponse> {\n return await this.init(\"attribution_conversion\", {\n type: \"attribution_conversion\",\n userId: event.userId,\n conversionType: event.conversionType,\n value: event.value,\n conversionId: event.conversionId,\n ...event.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Auto-capture a touchpoint from `utm_*` query params and `document.referrer`.\n * Browser-only; returns null on the server or when no UTM/referrer data is present.\n */\n captureFromUrl(userId: string): TouchpointConfig | null {\n if (!isBrowser()) return null;\n const win = getWindow();\n const doc = getDocument();\n if (!win || !doc) return null;\n\n const params = new URLSearchParams(win.location.search);\n const utm: UTMParams = {\n utmSource: params.get(\"utm_source\") ?? undefined,\n utmMedium: params.get(\"utm_medium\") ?? undefined,\n utmCampaign: params.get(\"utm_campaign\") ?? undefined,\n utmTerm: params.get(\"utm_term\") ?? undefined,\n utmContent: params.get(\"utm_content\") ?? undefined,\n };\n const referrer = doc.referrer || undefined;\n\n if (!utm.utmSource && !utm.utmMedium && !utm.utmCampaign && !referrer) {\n return null;\n }\n\n const config: TouchpointConfig = {\n userId,\n channel: utm.utmSource || (referrer ? \"referral\" : \"direct\"),\n campaign: utm.utmCampaign,\n medium: utm.utmMedium,\n source: utm.utmSource,\n content: utm.utmContent,\n term: utm.utmTerm,\n referrer,\n landingPage: win.location.pathname,\n };\n void this.trackTouchpoint(config);\n return config;\n }\n}\n\nexport default Attribution;\n","/**\n * @fileoverview Product Analytics - Heatmaps & Click Tracking\n * @module features/analytics/heatmaps\n * \n * @description\n * Track user interactions for heatmap visualization including\n * clicks, mouse movements, and scroll behavior.\n * \n * @useCases\n * - Visualize click patterns on pages\n * - Identify dead zones and ignored elements\n * - Analyze scroll depth and attention\n * - Optimize CTA placement\n * - Improve page layout based on behavior\n * \n * @metrics\n * - Click coordinates and density\n * - Scroll depth distribution\n * - Attention time by viewport\n * - Element interaction rates\n * - Rage clicks detection\n * \n * @chartData\n * - Heatmap: Click density overlay\n * - Scroll map: Attention by page section\n * - Element report: Interaction by element\n * - Rage click detection\n * \n * @example\n * ```typescript\n * const heatmaps = new Heatmaps(webId, endpoint, apiKey);\n * \n * // Track click\n * await heatmaps.trackClick({\n * pageUrl: '/pricing',\n * x: 450,\n * y: 320,\n * viewportWidth: 1920,\n * viewportHeight: 1080\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { DateRangeFilter } from \"../../types\";\nimport { getWindow, getDocument, isBrowser, combineTeardowns, Teardown } from \"../../utils/browser\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Click interaction data.\n */\nexport interface ClickData {\n /** Page URL */\n pageUrl: string;\n /** X coordinate (from left) */\n x: number;\n /** Y coordinate (from top) */\n y: number;\n /** Viewport width */\n viewportWidth: number;\n /** Viewport height */\n viewportHeight: number;\n /** Element selector clicked */\n elementSelector?: string;\n /** Element text (truncated) */\n elementText?: string;\n /** User ID */\n userId?: string;\n /** Session ID */\n sessionId?: string;\n}\n\n/**\n * Movement/hover data.\n */\nexport interface MovementData {\n /** Page URL */\n pageUrl: string;\n /** Movement path (array of x,y coordinates) */\n path: Array<{ x: number; y: number; t: number }>;\n /** Viewport dimensions */\n viewport: { width: number; height: number };\n /** Session ID */\n sessionId?: string;\n}\n\n/**\n * Scroll data.\n */\nexport interface ScrollData {\n /** Page URL */\n pageUrl: string;\n /** Maximum scroll depth reached (percentage) */\n maxScrollDepth: number;\n /** Page height */\n pageHeight: number;\n /** Viewport height */\n viewportHeight: number;\n /** Time at various scroll depths */\n timeAtDepths?: Record<number, number>;\n /** Session ID */\n sessionId?: string;\n}\n\n/**\n * Heatmap response from API.\n */\nexport interface HeatmapResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n recorded: boolean;\n interactionId: string;\n };\n timestamp: string;\n}\n\n/**\n * Heatmap data for visualization.\n */\nexport interface HeatmapData {\n /** Page URL */\n pageUrl: string;\n /** Click points for heatmap */\n clicks: Array<{\n x: number;\n y: number;\n count: number;\n normalizedX: number;\n normalizedY: number;\n }>;\n /** Scroll depth data */\n scrollDepth: Array<{\n depth: number;\n usersReached: number;\n percentageReached: number;\n avgTimeAtDepth: number;\n }>;\n /** Element interaction data */\n elements: Array<{\n selector: string;\n clicks: number;\n hovers: number;\n avgTimeHovered: number;\n }>;\n /** Rage click zones */\n rageClicks: Array<{\n x: number;\n y: number;\n count: number;\n element: string;\n }>;\n /** Summary */\n summary: {\n totalClicks: number;\n uniqueSessions: number;\n avgScrollDepth: number;\n rageClickRate: number;\n };\n}\n\n// ============================================================================\n// HEATMAPS CLASS\n// ============================================================================\n\n/**\n * Heatmaps - Track user interactions for heatmap visualization.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides heatmap data collection including:\n * - Click tracking with coordinates\n * - Mouse movement tracking\n * - Scroll depth tracking\n * - Element interaction tracking\n * - Rage click detection\n * \n * @example\n * ```typescript\n * const heatmaps = new Heatmaps(webId, endpoint, apiKey);\n * \n * // Track clicks\n * document.addEventListener('click', (e) => {\n * heatmaps.trackClick({\n * pageUrl: window.location.pathname,\n * x: e.clientX,\n * y: e.clientY + window.scrollY,\n * viewportWidth: window.innerWidth,\n * viewportHeight: window.innerHeight,\n * elementSelector: getSelector(e.target)\n * });\n * });\n * \n * // Get heatmap data for visualization\n * const data = await heatmaps.getData('/pricing', { range: 'last7days' });\n * ```\n */\nexport class Heatmaps extends FlowGridTransport {\n\n /** Throttle timestamps */\n private lastMovementTime = 0;\n private lastScrollTime = 0;\n\n /** Throttle intervals (ms) */\n private static readonly MOVEMENT_THROTTLE_MS = 100;\n private static readonly SCROLL_THROTTLE_MS = 500;\n\n /**\n * Tracks a click interaction.\n * \n * @param data - Click data\n * @returns Promise resolving to heatmap response\n */\n async trackClick(data: ClickData): Promise<HeatmapResponse> {\n return await this.init(\"heatmap_click\", {\n type: \"heatmap_click\",\n pageUrl: data.pageUrl,\n x: data.x,\n y: data.y,\n normalizedX: data.x / data.viewportWidth,\n normalizedY: data.y / data.viewportHeight,\n viewportWidth: data.viewportWidth,\n viewportHeight: data.viewportHeight,\n elementSelector: data.elementSelector,\n elementText: data.elementText?.substring(0, 50),\n userId: data.userId,\n sessionId: data.sessionId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks mouse movement path.\n * \n * @param data - Movement data\n * @returns Promise resolving to heatmap response\n */\n async trackMovement(data: MovementData): Promise<HeatmapResponse> {\n const now = Date.now();\n if (now - this.lastMovementTime < Heatmaps.MOVEMENT_THROTTLE_MS) {\n return { eventName: 'heatmap_movement', properties: {}, response: { recorded: false, interactionId: '' }, timestamp: new Date().toISOString() };\n }\n this.lastMovementTime = now;\n\n return await this.init(\"heatmap_movement\", {\n type: \"heatmap_movement\",\n pageUrl: data.pageUrl,\n path: data.path,\n viewport: data.viewport,\n sessionId: data.sessionId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks scroll behavior.\n * \n * @param data - Scroll data\n * @returns Promise resolving to heatmap response\n */\n async trackScroll(data: ScrollData): Promise<HeatmapResponse> {\n const now = Date.now();\n if (now - this.lastScrollTime < Heatmaps.SCROLL_THROTTLE_MS) {\n return { eventName: 'heatmap_scroll', properties: {}, response: { recorded: false, interactionId: '' }, timestamp: new Date().toISOString() };\n }\n this.lastScrollTime = now;\n\n return await this.init(\"heatmap_scroll\", {\n type: \"heatmap_scroll\",\n pageUrl: data.pageUrl,\n maxScrollDepth: data.maxScrollDepth,\n pageHeight: data.pageHeight,\n viewportHeight: data.viewportHeight,\n timeAtDepths: data.timeAtDepths,\n sessionId: data.sessionId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks rage clicks (repeated clicks in frustration).\n * \n * @param pageUrl - Page URL\n * @param x - X coordinate\n * @param y - Y coordinate\n * @param clickCount - Number of rapid clicks\n * @returns Promise resolving to heatmap response\n */\n async trackRageClick(\n pageUrl: string,\n x: number,\n y: number,\n clickCount: number\n ): Promise<HeatmapResponse> {\n return await this.init(\"heatmap_rage_click\", {\n type: \"heatmap_rage_click\",\n pageUrl,\n x,\n y,\n clickCount,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Auto-collect heatmap interactions: clicks (with element selector + text)\n * and scroll depth on the current page. Mouse-move tracking is opt-in\n * because the volume can be very high.\n *\n * @param options.movement Subscribe to throttled mousemove sampling.\n * @param options.userId Attached to every event.\n * @returns Teardown function detaching all listeners.\n */\n autoTrack(options: { movement?: boolean; userId?: string; sessionId?: string } = {}): Teardown {\n if (!isBrowser()) return () => { /* no-op on the server */ };\n const win = getWindow()!;\n const doc = getDocument()!;\n\n const selectorOf = (el: Element | null): string => {\n if (!el) return \"\";\n if (el.id) return `#${el.id}`;\n const tag = el.tagName.toLowerCase();\n const cls = (el.className && typeof el.className === \"string\")\n ? \".\" + el.className.trim().split(/\\s+/).slice(0, 2).join(\".\")\n : \"\";\n return `${tag}${cls}`;\n };\n\n const onClick = (e: MouseEvent) => {\n const target = e.target as Element | null;\n void this.trackClick({\n pageUrl: win.location.pathname,\n x: e.clientX,\n y: e.clientY + win.scrollY,\n viewportWidth: win.innerWidth,\n viewportHeight: win.innerHeight,\n elementSelector: selectorOf(target),\n elementText: (target?.textContent || \"\").trim().slice(0, 50),\n userId: options.userId,\n sessionId: options.sessionId,\n });\n };\n doc.addEventListener(\"click\", onClick, { capture: true, passive: true });\n\n let maxDepth = 0;\n let scrollFlushScheduled = false;\n const flushScroll = () => {\n scrollFlushScheduled = false;\n const docEl = doc.documentElement;\n void this.trackScroll({\n pageUrl: win.location.pathname,\n maxScrollDepth: maxDepth,\n pageHeight: docEl.scrollHeight,\n viewportHeight: win.innerHeight,\n sessionId: options.sessionId,\n });\n };\n const onScroll = () => {\n const docEl = doc.documentElement;\n const top = win.scrollY || docEl.scrollTop;\n const height = Math.max(1, docEl.scrollHeight - win.innerHeight);\n const depth = Math.min(100, Math.round((top / height) * 100));\n if (depth > maxDepth) maxDepth = depth;\n if (!scrollFlushScheduled) {\n scrollFlushScheduled = true;\n win.addEventListener(\"pagehide\", flushScroll, { once: true });\n }\n };\n win.addEventListener(\"scroll\", onScroll, { passive: true });\n\n let moveTeardown: (() => void) | undefined;\n if (options.movement) {\n let buffer: Array<{ x: number; y: number; t: number }> = [];\n const start = Date.now();\n const onMove = (e: MouseEvent) => {\n buffer.push({ x: e.clientX, y: e.clientY, t: Date.now() - start });\n if (buffer.length >= 50) {\n const path = buffer; buffer = [];\n void this.trackMovement({\n pageUrl: win.location.pathname,\n path,\n viewport: { width: win.innerWidth, height: win.innerHeight },\n sessionId: options.sessionId,\n });\n }\n };\n doc.addEventListener(\"mousemove\", onMove, { passive: true });\n moveTeardown = () => doc.removeEventListener(\"mousemove\", onMove);\n }\n\n return combineTeardowns(\n () => doc.removeEventListener(\"click\", onClick, { capture: true } as EventListenerOptions),\n () => win.removeEventListener(\"scroll\", onScroll),\n () => win.removeEventListener(\"pagehide\", flushScroll),\n moveTeardown,\n );\n }\n}\n\nexport default Heatmaps;\n","/**\n * @fileoverview Product Analytics - Performance Monitoring\n * @module features/analytics/performance\n * \n * @description\n * Track web performance metrics including Core Web Vitals,\n * page load times, and resource performance.\n * \n * @useCases\n * - Monitor Core Web Vitals (LCP, FID, CLS)\n * - Track page load performance\n * - Identify slow pages and resources\n * - Correlate performance with conversions\n * - Monitor API response times\n * \n * @metrics\n * - Largest Contentful Paint (LCP)\n * - First Input Delay (FID)\n * - Cumulative Layout Shift (CLS)\n * - Time to First Byte (TTFB)\n * - First Contentful Paint (FCP)\n * \n * @chartData\n * - Line chart: Core Web Vitals over time\n * - Bar chart: Slowest pages\n * - Histogram: Load time distribution\n * - Gauge: Performance score\n * \n * @example\n * ```typescript\n * const performance = new Performance(webId, endpoint, apiKey);\n * \n * // Track Core Web Vitals\n * await performance.trackWebVitals({\n * lcp: 2500,\n * fid: 100,\n * cls: 0.1,\n * pageUrl: '/products'\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { DateRangeFilter, TimeSeriesData } from \"../../types\";\nimport { getWindow, isBrowser, combineTeardowns, Teardown } from \"../../utils/browser\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Core Web Vitals data.\n */\nexport interface WebVitalsData {\n /** Largest Contentful Paint (ms) */\n lcp?: number;\n /** First Input Delay (ms) */\n fid?: number;\n /** Cumulative Layout Shift (score) */\n cls?: number;\n /** First Contentful Paint (ms) */\n fcp?: number;\n /** Time to First Byte (ms) */\n ttfb?: number;\n /** Interaction to Next Paint (ms) */\n inp?: number;\n /** Page URL */\n pageUrl: string;\n /** User ID */\n userId?: string;\n /** Connection type */\n connectionType?: 'slow-2g' | '2g' | '3g' | '4g' | 'wifi' | 'unknown';\n /** Device type */\n deviceType?: 'mobile' | 'tablet' | 'desktop';\n}\n\n/**\n * Page load timing data.\n */\nexport interface PageLoadData {\n /** Page URL */\n pageUrl: string;\n /** DNS lookup time (ms) */\n dnsTime?: number;\n /** TCP connection time (ms) */\n tcpTime?: number;\n /** Time to first byte (ms) */\n ttfb?: number;\n /** DOM content loaded time (ms) */\n domContentLoaded?: number;\n /** Full page load time (ms) */\n loadTime?: number;\n /** DOM interactive time (ms) */\n domInteractive?: number;\n /** Number of resources */\n resourceCount?: number;\n /** Total transfer size (bytes) */\n transferSize?: number;\n}\n\n/**\n * Resource timing data.\n */\nexport interface ResourceData {\n /** Resource URL */\n resourceUrl: string;\n /** Resource type */\n resourceType: 'script' | 'stylesheet' | 'image' | 'font' | 'fetch' | 'xhr' | 'other';\n /** Duration (ms) */\n duration: number;\n /** Transfer size (bytes) */\n transferSize?: number;\n /** Page URL where resource was loaded */\n pageUrl: string;\n}\n\n/**\n * Performance response from API.\n */\nexport interface PerformanceResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n recorded: boolean;\n performanceScore?: number;\n };\n timestamp: string;\n}\n\n/**\n * Performance analytics data.\n */\nexport interface PerformanceAnalyticsData {\n /** Core Web Vitals summary */\n webVitals: {\n lcp: { p50: number; p75: number; p95: number; good: number; needsImprovement: number; poor: number };\n fid: { p50: number; p75: number; p95: number; good: number; needsImprovement: number; poor: number };\n cls: { p50: number; p75: number; p95: number; good: number; needsImprovement: number; poor: number };\n inp: { p50: number; p75: number; p95: number; good: number; needsImprovement: number; poor: number };\n };\n /** Performance by page */\n byPage: Array<{\n pageUrl: string;\n avgLoadTime: number;\n avgLCP: number;\n avgFID: number;\n avgCLS: number;\n sampleSize: number;\n }>;\n /** Performance by device */\n byDevice: Array<{\n device: string;\n avgLoadTime: number;\n avgLCP: number;\n sampleSize: number;\n }>;\n /** Slowest resources */\n slowResources: Array<{\n resourceUrl: string;\n resourceType: string;\n avgDuration: number;\n occurrences: number;\n }>;\n /** Performance trends */\n trends: {\n lcp: TimeSeriesData;\n fid: TimeSeriesData;\n cls: TimeSeriesData;\n };\n /** Overall score (0-100) */\n performanceScore: number;\n}\n\n// ============================================================================\n// PERFORMANCE CLASS\n// ============================================================================\n\n/**\n * Performance - Track web performance metrics.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides performance monitoring including:\n * - Core Web Vitals tracking\n * - Page load timing\n * - Resource performance\n * - API response times\n * - Performance scoring\n * \n * @example\n * ```typescript\n * const performance = new Performance(webId, endpoint, apiKey);\n * \n * // Track Web Vitals (use web-vitals library)\n * import { onLCP, onFID, onCLS } from 'web-vitals';\n * \n * onLCP((metric) => {\n * performance.trackWebVitals({\n * lcp: metric.value,\n * pageUrl: window.location.pathname\n * });\n * });\n * ```\n */\nexport class PerformanceTracking extends FlowGridTransport {\n /**\n * Tracks Core Web Vitals.\n * \n * @param data - Web Vitals data\n * @returns Promise resolving to performance response\n */\n async trackWebVitals(data: WebVitalsData): Promise<PerformanceResponse> {\n return await this.init(\"web_vitals\", {\n type: \"web_vitals\",\n ...data,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks page load timing.\n * \n * @param data - Page load data\n * @returns Promise resolving to performance response\n */\n async trackPageLoad(data: PageLoadData): Promise<PerformanceResponse> {\n return await this.init(\"page_load\", {\n type: \"page_load\",\n ...data,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks resource performance.\n * \n * @param data - Resource data\n * @returns Promise resolving to performance response\n */\n async trackResource(data: ResourceData): Promise<PerformanceResponse> {\n return await this.init(\"resource_timing\", {\n type: \"resource_timing\",\n ...data,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks API call performance.\n * \n * @param endpoint - API endpoint\n * @param duration - Response time (ms)\n * @param success - Whether call succeeded\n * @param statusCode - HTTP status code\n * @returns Promise resolving to performance response\n */\n async trackApiCall(\n endpoint: string,\n duration: number,\n success: boolean,\n statusCode?: number\n ): Promise<PerformanceResponse> {\n return await this.init(\"api_timing\", {\n type: \"api_timing\",\n endpoint,\n duration,\n success,\n statusCode,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Auto-collect Core Web Vitals, navigation timing, and resource timing\n * directly from the browser. Safe no-op outside the browser.\n *\n * @param options.userId Optional user identifier attached to every metric.\n * @param options.resources When true, also subscribes to PerformanceObserver\n * for `resource` entries. Default: false (noisy).\n * @param options.minDuration Only forward resources slower than this (ms). Default 200.\n * @returns A teardown function that detaches all observers/listeners.\n *\n * @example\n * ```ts\n * const stop = fg.performance.client.autoTrack({ userId: \"u_1\" });\n * // ...later\n * stop();\n * ```\n */\n autoTrack(options: {\n userId?: string;\n resources?: boolean;\n minDuration?: number;\n } = {}): Teardown {\n if (!isBrowser()) return () => { /* no-op on the server */ };\n\n const win = getWindow()!;\n const pageUrl = win.location.pathname;\n const teardowns: Array<() => void> = [];\n\n // --- Web Vitals via the `web-vitals` library (dynamic import keeps it\n // out of server bundles even if tree-shaking misses it).\n let cancelled = false;\n void import(/* webpackChunkName: \"flowgrid-vitals\" */ \"web-vitals\").then((wv) => {\n if (cancelled) return;\n const send = (metric: { name: string; value: number }) => {\n const key = metric.name.toLowerCase() as keyof Pick<WebVitalsData, \"lcp\" | \"fid\" | \"cls\" | \"fcp\" | \"ttfb\" | \"inp\">;\n const payload: WebVitalsData = { pageUrl, userId: options.userId };\n (payload[key] as number | undefined) = metric.value;\n void this.trackWebVitals(payload);\n };\n wv.onLCP?.(send);\n wv.onCLS?.(send);\n wv.onINP?.(send);\n wv.onFCP?.(send);\n wv.onTTFB?.(send);\n // FID was removed in web-vitals v4+, but we still surface it if present\n (wv as { onFID?: (cb: (m: { name: string; value: number }) => void) => void }).onFID?.(send);\n }).catch(() => { /* library missing — silently skip */ });\n teardowns.push(() => { cancelled = true; });\n\n // --- PerformanceNavigationTiming → trackPageLoad\n const reportNavigation = () => {\n const nav = win.performance?.getEntriesByType?.(\"navigation\")?.[0] as\n PerformanceNavigationTiming | undefined;\n if (!nav) return;\n void this.trackPageLoad({\n pageUrl,\n dnsTime: Math.max(0, nav.domainLookupEnd - nav.domainLookupStart),\n tcpTime: Math.max(0, nav.connectEnd - nav.connectStart),\n ttfb: Math.max(0, nav.responseStart - nav.requestStart),\n domContentLoaded: nav.domContentLoadedEventEnd,\n loadTime: nav.loadEventEnd,\n domInteractive: nav.domInteractive,\n transferSize: nav.transferSize,\n });\n };\n if (win.document.readyState === \"complete\") {\n reportNavigation();\n } else {\n const onLoad = () => reportNavigation();\n win.addEventListener(\"load\", onLoad, { once: true });\n teardowns.push(() => win.removeEventListener(\"load\", onLoad));\n }\n\n // --- Resource timing (opt-in)\n if (options.resources && typeof win.PerformanceObserver === \"function\") {\n const min = options.minDuration ?? 200;\n const obs = new win.PerformanceObserver((list) => {\n for (const entry of list.getEntries() as PerformanceResourceTiming[]) {\n if (entry.duration < min) continue;\n const initiator = entry.initiatorType as ResourceData[\"resourceType\"];\n void this.trackResource({\n resourceUrl: entry.name,\n resourceType: ([\n \"script\", \"stylesheet\", \"image\", \"font\", \"fetch\", \"xhr\",\n ].includes(initiator) ? initiator : \"other\"),\n duration: Math.round(entry.duration),\n transferSize: entry.transferSize,\n pageUrl,\n });\n }\n });\n try {\n obs.observe({ type: \"resource\", buffered: true });\n teardowns.push(() => obs.disconnect());\n } catch { /* unsupported */ }\n }\n\n return combineTeardowns(...teardowns);\n }\n}\n\nexport { PerformanceTracking as Performance };\nexport default PerformanceTracking;\n","/**\n * @fileoverview Session Replay (rrweb) capture.\n * @module features/analytics/replay\n *\n * @description\n * Browser-only DOM session replay powered by [rrweb](https://github.com/rrweb-io/rrweb).\n * Records DOM mutations, mouse, scroll, input, and viewport events, batches them\n * into chunks, and ships them to the FlowGrid backend at `call=replay_chunk`.\n *\n * Loaded dynamically so it is only pulled into the bundle when `start()` is\n * called in a browser environment.\n *\n * @example\n * ```ts\n * const stop = await fg.replay.start({\n * sampleRate: 1.0,\n * maskAllInputs: true,\n * });\n * // ...later\n * stop();\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { isBrowser, getDocument, type Teardown } from \"../../utils/browser\";\n\n// ============================================================================\n// CONSENT\n// ============================================================================\n\nconst REPLAY_CONSENT_KEY = \"flowgrid:replayConsent\";\n\n/**\n * Returns true if the user has previously granted replay consent\n * (persisted in localStorage across sessions).\n */\nexport function hasReplayConsent(): boolean {\n if (!isBrowser()) return false;\n try {\n return localStorage.getItem(REPLAY_CONSENT_KEY) === \"granted\";\n } catch {\n return false;\n }\n}\n\n/**\n * Persist replay consent in localStorage.\n * Call this from your cookie/consent banner \"Accept\" handler.\n * Once granted, the banner will not need to re-prompt on subsequent visits\n * and `SessionReplay.start()` will proceed without being blocked.\n *\n * @example\n * ```ts\n * // In your consent banner \"Accept All\" click handler:\n * grantReplayConsent();\n * ```\n */\nexport function grantReplayConsent(): void {\n if (!isBrowser()) return;\n try {\n localStorage.setItem(REPLAY_CONSENT_KEY, \"granted\");\n } catch { /* storage unavailable */ }\n}\n\n/**\n * Revoke replay consent and remove the persisted value.\n * Recording will be blocked on the next `start()` call when\n * `requireConsent: true` is set.\n */\nexport function revokeReplayConsent(): void {\n if (!isBrowser()) return;\n try {\n localStorage.removeItem(REPLAY_CONSENT_KEY);\n } catch { /* storage unavailable */ }\n}\n\n// ============================================================================\n// CONFIG\n// ============================================================================\n\nexport interface SessionReplayOptions {\n\n /** Explicit enable switch. Pass `false` to keep the recorder disabled. */\n enabled?: boolean;\n\n /**\n * Gate recording on explicit user consent.\n * When `true`, `start()` will only proceed if `hasReplayConsent()` returns\n * `true` (localStorage) or `FlowGridTransport.hasConsent(\"analytics\")` is set.\n * Call `grantReplayConsent()` from your consent banner to unblock recording.\n * Default `false` (backwards-compatible — does not break existing installs).\n */\n requireConsent?: boolean;\n\n /** Sampling rate 0.0-1.0. Default 1.0 (record every session). */\n sampleRate?: number;\n\n /** Existing session ID to attach the recording to. Auto-generated otherwise. */\n sessionId?: string;\n\n /** User ID for the recording, if known. */\n userId?: string;\n\n /** Mask all `<input>`, `<textarea>` and `[contenteditable]` values. Default true. */\n maskAllInputs?: boolean;\n\n /** Mask text content under elements matching this CSS class. Default `\"fg-mask\"`. */\n maskTextClass?: string | RegExp;\n\n /** Skip recording elements matching this CSS class entirely. Default `\"fg-block\"`. */\n blockClass?: string | RegExp;\n\n /** Ignore events on elements matching this CSS class. Default `\"fg-ignore\"`. */\n ignoreClass?: string;\n\n /** Flush buffered events at most every N ms. Default 5000. */\n flushIntervalMs?: number;\n\n /** Force flush when buffer reaches N bytes (approximate). Default 512KB. */\n flushBytes?: number;\n\n /** Maximum recording length per session in ms. Default 30 minutes. */\n maxDurationMs?: number;\n\n /** Hard cap on total bytes captured per session. Default 10MB. */\n maxBytes?: number;\n\n /** Invoked when recorder state changes or lifecycle metadata is updated. */\n onStatus?: (status: SessionReplayStatus) => void;\n\n /** Invoked after a chunk is accepted for transport. */\n onFlush?: (metadata: SessionReplayFlushMetadata) => void;\n\n /** Invoked when events are dropped due to limits or serialization issues. */\n onDrop?: (metadata: SessionReplayDropMetadata) => void;\n\n /** Invoked when recorder setup or teardown fails. */\n onError?: (error: Error, context: SessionReplayErrorContext) => void;\n\n}\n\nexport type SessionReplayState =\n | \"idle\"\n | \"starting\"\n | \"recording\"\n | \"stopped\"\n | \"disabled\"\n | \"sampled_out\"\n | \"error\";\n\nexport type SessionReplayFlushReason =\n | \"interval\"\n | \"size\"\n | \"visibility_hidden\"\n | \"pagehide\"\n | \"manual\"\n | \"stop\"\n | \"max_duration\"\n | \"max_bytes\";\n\nexport interface SessionReplayStatus {\n state: SessionReplayState;\n sessionId?: string;\n userId?: string;\n startedAt?: string;\n sequence: number;\n bufferedEvents: number;\n bufferedBytes: number;\n totalBytes: number;\n droppedEvents: number;\n recorderVersion: string;\n lastFlushReason?: SessionReplayFlushReason;\n lastError?: string;\n}\n\nexport interface SessionReplayFlushMetadata {\n sessionId: string;\n userId?: string;\n sequence: number;\n eventsCount: number;\n byteSize: number;\n reason: SessionReplayFlushReason;\n isFinal: boolean;\n droppedEvents: number;\n capturedAt: string;\n idempotencyKey: string;\n}\n\nexport interface SessionReplayDropMetadata {\n sessionId?: string;\n reason: \"serialization_error\" | \"max_bytes\";\n droppedEvents: number;\n byteSize?: number;\n maxBytes?: number;\n}\n\nexport interface SessionReplayErrorContext {\n phase: \"import\" | \"record\" | \"flush\" | \"stop\";\n sessionId?: string;\n}\n\nexport interface SessionReplaySendChunkOptions {\n userId?: string;\n sequence?: number;\n useBeacon?: boolean;\n startedAt?: string;\n capturedAt?: string;\n pageUrl?: string;\n isFinal?: boolean;\n reason?: SessionReplayFlushReason;\n privacy?: ReplayPrivacyMetadata;\n droppedEvents?: number;\n recorderVersion?: string;\n}\n\nexport interface ReplayPrivacyMetadata {\n masking_mode: \"all_inputs\" | \"selective\" | \"custom\";\n blocked_selectors: string[];\n redacted_fields: string[];\n consent_state: \"granted\" | \"denied\" | \"unknown\";\n sampling_state: \"sampled_in\" | \"sampled_out\";\n}\n\ninterface ActiveRecording {\n sessionId: string;\n userId?: string;\n startedAtMs: number;\n startedAt: string;\n sequence: number;\n buffer: unknown[];\n bufferBytes: number;\n totalBytes: number;\n droppedEvents: number;\n stopped: boolean;\n /** Set to true once the first FullSnapshot (rrweb event type 2) is in the buffer. */\n initialSnapshotCaptured: boolean;\n flushTimer: ReturnType<typeof setInterval>;\n durationGuard: ReturnType<typeof setTimeout>;\n stopRecording?: Teardown;\n onVisibility: () => void;\n onPageHide: () => void;\n options: Required<Pick<SessionReplayOptions, \"flushIntervalMs\" | \"flushBytes\" | \"maxDurationMs\" | \"maxBytes\" | \"maskAllInputs\">> & SessionReplayOptions;\n}\n\nconst DEFAULT_FLUSH_MS = 5_000;\nconst DEFAULT_FLUSH_BYTES = 512 * 1024;\nconst DEFAULT_MAX_DURATION_MS = 30 * 60 * 1000;\nconst DEFAULT_MAX_BYTES = 10 * 1024 * 1024;\nconst STORAGE_KEY = \"flowgrid:replaySessionId\";\nconst RECORDER_VERSION = \"rrweb@2\";\n\n// ============================================================================\n// SESSION REPLAY CLASS\n// ============================================================================\n\nexport class SessionReplay extends FlowGridTransport {\n private disabled = false;\n private active: ActiveRecording | null = null;\n private state: SessionReplayState = \"idle\";\n private boundUserId?: string;\n private lastError?: string;\n\n /**\n * Begin capturing the current page as a replay session. Returns a teardown\n * function that flushes the final buffer (via `sendBeacon` when possible)\n * and detaches the recorder.\n *\n * No-op on the server or when the browser fails sampling.\n */\n async start(options: SessionReplayOptions = {}): Promise<Teardown> {\n\n if (options.enabled === false || this.disabled) {\n this.setState(\"disabled\", options);\n return () => {};\n }\n\n if (this.active && !this.active.stopped) {\n this.emitStatus(this.active.options);\n return () => this.stop(\"manual\");\n }\n\n if (!isBrowser()) return () => {};\n\n // Consent gate: when requireConsent is true, recording only proceeds if the\n // user has explicitly granted consent (localStorage key or transport consent).\n if (options.requireConsent === true) {\n const consented = hasReplayConsent() || FlowGridTransport.hasConsent(\"analytics\");\n if (!consented) {\n this.setState(\"disabled\", options);\n return () => {};\n }\n }\n\n this.setState(\"starting\", options);\n\n const sampleRate = options.sampleRate ?? 1.0;\n if (sampleRate < 1 && Math.random() > sampleRate) {\n this.setState(\"sampled_out\", options);\n return () => {};\n }\n\n let rrweb: typeof import(\"rrweb\");\n try {\n rrweb = await import(/* webpackChunkName: \"flowgrid-replay\" */ \"rrweb\");\n } catch (error) {\n // Surface the failure — silent import errors are the #1 reason replays\n // never start without any indication in the dashboard.\n if (typeof console !== \"undefined\") {\n console.error(\"[FlowGrid] Session replay failed to load rrweb:\", error);\n }\n this.handleError(this.toError(error), { phase: \"import\" }, options);\n return () => {};\n }\n\n const doc = getDocument();\n if (!doc) return () => {};\n\n const sessionId = options.sessionId ?? this.resolveSessionId();\n const normalizedOptions = this.normalizeOptions(options);\n const startedAtMs = Date.now();\n \n const active: ActiveRecording = {\n sessionId,\n userId: options.userId ?? this.boundUserId,\n startedAtMs,\n startedAt: new Date(startedAtMs).toISOString(),\n sequence: 0,\n buffer: [],\n bufferBytes: 0,\n totalBytes: 0,\n droppedEvents: 0,\n stopped: false,\n initialSnapshotCaptured: false,\n flushTimer: setInterval(() => { void this.flush(\"interval\"); }, normalizedOptions.flushIntervalMs),\n durationGuard: setTimeout(() => this.stop(\"max_duration\"), normalizedOptions.maxDurationMs),\n onVisibility: () => {\n if (doc.visibilityState === \"hidden\") void this.flush(\"visibility_hidden\", { useBeacon: true });\n },\n onPageHide: () => { void this.flush(\"pagehide\", { useBeacon: true }); },\n options: normalizedOptions,\n };\n\n this.active = active;\n\n try {\n\n active.stopRecording = rrweb.record({\n emit: (event: unknown) => this.captureEvent(event),\n maskAllInputs: normalizedOptions.maskAllInputs,\n maskTextClass: normalizedOptions.maskTextClass ?? \"fg-mask\",\n blockClass: normalizedOptions.blockClass ?? \"fg-block\",\n ignoreClass: normalizedOptions.ignoreClass ?? \"fg-ignore\",\n // Emit a new FullSnapshot every 200 incremental events OR every 30\n // seconds, whichever comes first. The time-based gate ensures the\n // player always has a recent snapshot to seek from even in low-activity\n // sessions where 200 events accumulate very slowly.\n checkoutEveryNth: 200,\n checkoutEveryNms: 30_000,\n // Reduce snapshot payload size: don't inline images or collect fonts\n // in the snapshot — they can bloat the first chunk significantly and\n // increase the chance of the Tinybird 1 MB limit being hit on chunk 0.\n inlineImages: false,\n collectFonts: false,\n });\n\n } catch (error) {\n\n this.handleError(this.toError(error), { \n phase: \"record\", sessionId \n }, normalizedOptions);\n\n active.stopped = true;\n\n this.cleanupActive(\"stop\");\n\n this.active = null;\n\n return () => {};\n\n }\n\n doc.addEventListener(\"visibilitychange\", active.onVisibility);\n window.addEventListener(\"pagehide\", active.onPageHide);\n\n this.setState(\"recording\", normalizedOptions);\n\n return () => this.stop(\"manual\");\n }\n\n /** Enable replay capture and start recording immediately. */\n enable(options: SessionReplayOptions = {}): Promise<Teardown> {\n this.disabled = false;\n return this.start({ ...options, enabled: true });\n }\n\n /** Disable replay capture and stop any active recording. */\n disable(): void {\n this.disabled = true;\n this.stop(\"manual\");\n this.setState(\"disabled\", this.active?.options);\n }\n\n /** Stop recording and flush the final chunk with lifecycle metadata. */\n stop(reason: SessionReplayFlushReason = \"stop\"): void {\n const active = this.active;\n if (!active || active.stopped) return;\n active.stopped = true;\n this.cleanupActive(reason);\n void this.flush(reason, { useBeacon: true, isFinal: true });\n this.setState(\"stopped\", active.options, reason);\n this.active = null;\n }\n\n /** Flush buffered rrweb events without stopping the recorder. */\n async flush(\n reason: SessionReplayFlushReason = \"manual\",\n options: { useBeacon?: boolean; isFinal?: boolean } = {},\n ): Promise<void> {\n\n const active = this.active;\n if (!active || active.buffer.length === 0) return;\n\n const events = active.buffer;\n const eventsCount = events.length;\n const byteSize = active.bufferBytes;\n const sequence = active.sequence++;\n active.buffer = [];\n active.bufferBytes = 0;\n\n const capturedAt = new Date().toISOString();\n const idempotencyKey = this.createIdempotencyKey(active.sessionId, sequence, byteSize);\n const metadata: SessionReplayFlushMetadata = {\n sessionId: active.sessionId,\n userId: active.userId,\n sequence,\n eventsCount,\n byteSize,\n reason,\n isFinal: options.isFinal === true,\n droppedEvents: active.droppedEvents,\n capturedAt,\n idempotencyKey,\n };\n\n try {\n await this.postReplayChunk(active.sessionId, events, {\n userId: active.userId,\n sequence,\n useBeacon: options.useBeacon,\n startedAt: active.startedAt,\n capturedAt,\n pageUrl: this.getPageUrl(),\n isFinal: metadata.isFinal,\n reason,\n privacy: this.createPrivacyMetadata(active.options, \"sampled_in\"),\n droppedEvents: active.droppedEvents,\n recorderVersion: RECORDER_VERSION,\n }, idempotencyKey);\n active.options.onFlush?.(metadata);\n this.emitStatus(active.options, reason);\n } catch (error) {\n this.handleError(this.toError(error), { phase: \"flush\", sessionId: active.sessionId }, active.options);\n }\n }\n\n /** Bind a known user to subsequent chunks in the active recording. */\n identify(userId: string): void {\n this.boundUserId = userId;\n if (this.active) {\n this.active.userId = userId;\n this.emitStatus(this.active.options);\n }\n }\n\n /** Current recorder health/lifecycle state. */\n getStatus(): SessionReplayStatus {\n return this.createStatus(this.active?.options);\n }\n\n /**\n * Manually persist a single rrweb event chunk. Most callers should use\n * `start()` instead; this is exposed for tests and advanced integrations\n * that drive `rrweb.record()` themselves.\n */\n async sendChunk(\n sessionId: string,\n events: unknown[],\n options: SessionReplaySendChunkOptions = {},\n ): Promise<void> {\n if (events.length === 0) return;\n await this.postReplayChunk(sessionId, events, options);\n }\n\n private async postReplayChunk(\n sessionId: string,\n events: unknown[],\n options: SessionReplaySendChunkOptions,\n idempotencyKey = this.createIdempotencyKey(sessionId, options.sequence ?? 0, this.measureEvents(events)),\n ): Promise<void> {\n await this.postRaw(\n \"replay_chunk\",\n {\n type: \"replay_chunk\",\n session_id: sessionId,\n user_id: options.userId,\n sequence: options.sequence ?? 0,\n started_at: options.startedAt,\n captured_at: options.capturedAt ?? new Date().toISOString(),\n events,\n page_url: options.pageUrl ?? this.getPageUrl(),\n idempotency_key: idempotencyKey,\n compression: \"none\",\n is_final: options.isFinal === true,\n chunk_kind: options.isFinal ? \"final\" : \"partial\",\n flush_reason: options.reason ?? \"manual\",\n dropped_events: options.droppedEvents ?? 0,\n recorder_version: options.recorderVersion ?? RECORDER_VERSION,\n privacy: options.privacy ?? this.createPrivacyMetadata(undefined, \"sampled_in\"),\n },\n { useBeacon: options.useBeacon },\n );\n }\n\n private captureEvent(event: unknown): void {\n const active = this.active;\n if (!active || active.stopped) return;\n\n let size = 0;\n try {\n size = JSON.stringify(event).length;\n } catch {\n active.droppedEvents++;\n active.options.onDrop?.({\n sessionId: active.sessionId,\n reason: \"serialization_error\",\n droppedEvents: active.droppedEvents,\n });\n return;\n }\n\n if (active.totalBytes + size > active.options.maxBytes) {\n active.droppedEvents++;\n active.options.onDrop?.({\n sessionId: active.sessionId,\n reason: \"max_bytes\",\n droppedEvents: active.droppedEvents,\n byteSize: active.totalBytes + size,\n maxBytes: active.options.maxBytes,\n });\n this.stop(\"max_bytes\");\n return;\n }\n\n active.totalBytes += size;\n active.buffer.push(event);\n active.bufferBytes += size;\n\n // FullSnapshot (rrweb type 2) is always the first event emitted by rrweb.record().\n // Flush it immediately in the next microtask rather than waiting for the\n // 5-second interval — this guarantees sequence 0 is transmitted right away\n // and reduces the window in which a Tinybird network failure can lose the\n // only snapshot the player needs to initialize.\n if (!active.initialSnapshotCaptured && (event as { type?: number }).type === 2) {\n active.initialSnapshotCaptured = true;\n Promise.resolve().then(() => { void this.flush(\"manual\"); });\n return;\n }\n\n if (active.bufferBytes >= active.options.flushBytes) {\n void this.flush(\"size\");\n }\n }\n\n private cleanupActive(reason: SessionReplayFlushReason): void {\n const active = this.active;\n if (!active) return;\n const doc = getDocument();\n clearInterval(active.flushTimer);\n clearTimeout(active.durationGuard);\n doc?.removeEventListener(\"visibilitychange\", active.onVisibility);\n window.removeEventListener(\"pagehide\", active.onPageHide);\n try {\n active.stopRecording?.();\n } catch (error) {\n this.handleError(this.toError(error), { phase: \"stop\", sessionId: active.sessionId }, active.options);\n }\n this.emitStatus(active.options, reason);\n }\n\n private normalizeOptions(options: SessionReplayOptions): ActiveRecording[\"options\"] {\n return {\n ...options,\n flushIntervalMs: options.flushIntervalMs ?? DEFAULT_FLUSH_MS,\n flushBytes: options.flushBytes ?? DEFAULT_FLUSH_BYTES,\n maxDurationMs: options.maxDurationMs ?? DEFAULT_MAX_DURATION_MS,\n maxBytes: options.maxBytes ?? DEFAULT_MAX_BYTES,\n maskAllInputs: options.maskAllInputs ?? true,\n };\n }\n\n private createStatus(options?: SessionReplayOptions, reason?: SessionReplayFlushReason): SessionReplayStatus {\n const active = this.active;\n return {\n state: this.state,\n sessionId: active?.sessionId,\n userId: active?.userId ?? this.boundUserId,\n startedAt: active?.startedAt,\n sequence: active?.sequence ?? 0,\n bufferedEvents: active?.buffer.length ?? 0,\n bufferedBytes: active?.bufferBytes ?? 0,\n totalBytes: active?.totalBytes ?? 0,\n droppedEvents: active?.droppedEvents ?? 0,\n recorderVersion: RECORDER_VERSION,\n lastFlushReason: reason,\n lastError: this.lastError,\n };\n }\n\n private setState(state: SessionReplayState, options?: SessionReplayOptions, reason?: SessionReplayFlushReason): void {\n this.state = state;\n this.emitStatus(options, reason);\n }\n\n private emitStatus(options?: SessionReplayOptions, reason?: SessionReplayFlushReason): void {\n options?.onStatus?.(this.createStatus(options, reason));\n }\n\n private handleError(error: Error, context: SessionReplayErrorContext, options?: SessionReplayOptions): void {\n this.lastError = error.message;\n this.state = \"error\";\n options?.onError?.(error, context);\n this.emitStatus(options);\n }\n\n private createPrivacyMetadata(\n options?: SessionReplayOptions,\n samplingState: ReplayPrivacyMetadata[\"sampling_state\"] = \"sampled_in\",\n ): ReplayPrivacyMetadata {\n // Check both the persistent localStorage consent and the in-memory transport\n // consent gate so either path to granting consent is reflected accurately.\n const consentGranted = hasReplayConsent() || FlowGridTransport.hasConsent(\"analytics\");\n return {\n masking_mode: options?.maskAllInputs === false ? \"selective\" : \"all_inputs\",\n blocked_selectors: this.selectorList(options?.blockClass ?? \"fg-block\"),\n redacted_fields: this.selectorList(options?.maskTextClass ?? \"fg-mask\"),\n consent_state: consentGranted ? \"granted\" : \"denied\",\n sampling_state: samplingState,\n };\n }\n\n private selectorList(selector: string | RegExp): string[] {\n return typeof selector === \"string\" ? [selector] : [selector.source];\n }\n\n private getPageUrl(): string {\n try {\n return typeof location !== \"undefined\" ? location.href : \"\";\n } catch {\n return \"\";\n }\n }\n\n private createIdempotencyKey(sessionId: string, sequence: number, byteSize: number): string {\n return `${this.webId}:${sessionId}:${sequence}:${byteSize}`;\n }\n\n private measureEvents(events: unknown[]): number {\n try {\n return JSON.stringify(events).length;\n } catch {\n return 0;\n }\n }\n\n private toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n }\n\n private resolveSessionId(): string {\n try {\n const existing = sessionStorage.getItem(STORAGE_KEY);\n if (existing) return existing;\n const fresh = this.generateId();\n sessionStorage.setItem(STORAGE_KEY, fresh);\n return fresh;\n } catch {\n return this.generateId();\n }\n }\n\n private generateId(): string {\n const cryptoObj = (typeof crypto !== \"undefined\" ? crypto : undefined) as Crypto | undefined;\n if (cryptoObj?.randomUUID) return cryptoObj.randomUUID();\n return `rep_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;\n }\n}\n\nexport default SessionReplay;\n","/**\n * @fileoverview Ecommerce - Product Tracking\n * @module features/ecommerce/products\n * \n * @description\n * Track product interactions including views, impressions, clicks,\n * and product list interactions.\n * \n * @useCases\n * - Track product page views\n * - Measure product list/search impressions\n * - Analyze product click-through rates\n * - Monitor product detail engagement\n * - Compare product performance\n * \n * @metrics\n * - Product views\n * - Product impressions\n * - Click-through rate\n * - View-to-cart rate\n * - Revenue per product\n * \n * @chartData\n * - Bar chart: Top products by views\n * - Line chart: Product views over time\n * - Table: Product performance comparison\n * - Scatter: Price vs conversion rate\n * \n * @example\n * ```typescript\n * const products = new Products(webId, endpoint, apiKey);\n * \n * // Track product view\n * await products.view({\n * productId: 'SKU-123',\n * name: 'Premium Headphones',\n * price: 299.99,\n * currency: 'USD',\n * category: 'Electronics/Audio'\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { \n Product,\n ProductList,\n ProductPerformance,\n TimeSeriesData,\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for tracking a product view.\n */\nexport interface ProductViewConfig extends Product {\n /** User ID (if authenticated) */\n userId?: string;\n /** Session ID */\n sessionId?: string;\n /** How user arrived at product (search, category, recommendation) */\n source?: 'search' | 'category' | 'recommendation' | 'direct' | 'ad';\n /** Search query if from search */\n searchQuery?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Configuration for tracking product impressions.\n */\nexport interface ProductImpressionConfig {\n /** List information */\n list: ProductList;\n /** User ID */\n userId?: string;\n /** Session ID */\n sessionId?: string;\n}\n\n/**\n * Configuration for tracking product clicks.\n */\nexport interface ProductClickConfig {\n /** Product clicked */\n product: Product;\n /** List where product was clicked */\n listId?: string;\n /** Position in list */\n position?: number;\n /** User ID */\n userId?: string;\n}\n\n/**\n * Product event response from API.\n */\nexport interface ProductResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n productId: string;\n tracked: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Product analytics data for charts.\n */\nexport interface ProductAnalyticsData {\n /** Top products by various metrics */\n topProducts: {\n byViews: ProductPerformance[];\n byRevenue: ProductPerformance[];\n byConversion: ProductPerformance[];\n };\n /** Product views time series */\n viewsTimeSeries: TimeSeriesData;\n /** Summary metrics */\n summary: {\n totalProductViews: number;\n uniqueProductsViewed: number;\n avgViewsPerProduct: number;\n topCategory: string;\n topBrand: string;\n };\n /** Performance by category */\n byCategory: Array<{\n category: string;\n views: number;\n revenue: number;\n conversionRate: number;\n }>;\n}\n\n// ============================================================================\n// PRODUCTS CLASS\n// ============================================================================\n\n/**\n * Products - Track product interactions.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides product tracking capabilities including:\n * - Product page views\n * - Product list impressions\n * - Product clicks from lists\n * - Product detail interactions\n * - Quick view tracking\n * \n * @example\n * ```typescript\n * const products = new Products(webId, endpoint, apiKey);\n * \n * // Track product page view\n * await products.view({\n * productId: 'PROD-001',\n * name: 'Wireless Earbuds',\n * price: 79.99,\n * currency: 'USD',\n * category: 'Electronics/Audio',\n * brand: 'AudioTech'\n * });\n * \n * // Track list impressions\n * await products.impression({\n * list: {\n * listId: 'search-results',\n * listName: 'Search Results',\n * products: searchResults\n * }\n * });\n * ```\n */\nexport class Products extends FlowGridTransport {\n /**\n * Tracks a product page view.\n * \n * @param config - Product view configuration\n * @returns Promise resolving to product response\n * \n * @example\n * ```typescript\n * await products.view({\n * productId: 'SKU-12345',\n * name: 'Premium Widget',\n * price: 49.99,\n * currency: 'USD',\n * category: 'Widgets/Premium',\n * brand: 'WidgetCo',\n * source: 'search',\n * searchQuery: 'premium widget'\n * });\n * ```\n */\n async view(config: ProductViewConfig): Promise<ProductResponse> {\n return await this.init(\"product_view\", {\n type: \"ecommerce\",\n subtype: \"product_view\",\n product: {\n productId: config.productId,\n name: config.name,\n brand: config.brand,\n category: config.category,\n variant: config.variant,\n price: config.price,\n currency: config.currency,\n url: config.url,\n imageUrl: config.imageUrl,\n rating: config.rating,\n reviewCount: config.reviewCount,\n stockStatus: config.stockStatus,\n attributes: config.attributes\n },\n source: config.source,\n searchQuery: config.searchQuery,\n userId: config.userId,\n sessionId: config.sessionId,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks product list impressions.\n * \n * @param config - Impression configuration\n * @returns Promise resolving to impression response\n * \n * @example\n * ```typescript\n * // Track products shown in search results\n * await products.impression({\n * list: {\n * listId: 'search-results',\n * listName: 'Search Results',\n * products: products.map((p, i) => ({ ...p, position: i + 1 }))\n * }\n * });\n * ```\n */\n async impression(config: ProductImpressionConfig): Promise<ProductResponse> {\n return await this.init(\"product_impression\", {\n type: \"ecommerce\",\n subtype: \"product_impression\",\n list: config.list,\n productCount: config.list.products.length,\n userId: config.userId,\n sessionId: config.sessionId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a product click from a list.\n * \n * @param config - Click configuration\n * @returns Promise resolving to click response\n * \n * @example\n * ```typescript\n * await products.click({\n * product: selectedProduct,\n * listId: 'homepage-featured',\n * position: 3\n * });\n * ```\n */\n async click(config: ProductClickConfig): Promise<ProductResponse> {\n return await this.init(\"product_click\", {\n type: \"ecommerce\",\n subtype: \"product_click\",\n product: config.product,\n listId: config.listId,\n position: config.position,\n userId: config.userId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a quick view interaction (modal product view).\n * \n * @param product - Product being quick-viewed\n * @param listId - List where quick view was triggered\n * @returns Promise resolving to quick view response\n */\n async quickView(product: Product, listId?: string): Promise<ProductResponse> {\n return await this.init(\"product_quick_view\", {\n type: \"ecommerce\",\n subtype: \"product_quick_view\",\n product,\n listId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks product detail interaction (zoom, read reviews, etc.).\n * \n * @param productId - Product identifier\n * @param action - Interaction type\n * @param properties - Additional properties\n * @returns Promise resolving to interaction response\n */\n async interaction(\n productId: string,\n action: 'zoom' | 'read_reviews' | 'size_guide' | 'compare' | 'share' | 'notify_stock',\n properties?: Record<string, unknown>\n ): Promise<ProductResponse> {\n return await this.init(\"product_interaction\", {\n type: \"ecommerce\",\n subtype: \"product_interaction\",\n productId,\n action,\n ...properties,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default Products;\n","/**\n * @fileoverview Ecommerce - Cart Tracking\n * @module features/ecommerce/cart\n * \n * @description\n * Track shopping cart interactions including add/remove items,\n * cart views, and cart abandonment.\n * \n * @useCases\n * - Track add to cart events\n * - Monitor cart modifications\n * - Detect cart abandonment\n * - Analyze popular cart items\n * - Measure cart value trends\n * \n * @metrics\n * - Add to cart rate\n * - Average cart value\n * - Cart abandonment rate\n * - Items per cart\n * - Cart-to-checkout rate\n * \n * @chartData\n * - Line chart: Cart value over time\n * - Bar chart: Top added products\n * - Pie chart: Abandonment reasons\n * - Funnel: Cart to checkout flow\n * \n * @example\n * ```typescript\n * const cart = new CartTracking(webId, endpoint, apiKey);\n * \n * // Track add to cart\n * await cart.addItem({\n * product: { productId: 'SKU-123', name: 'Widget', price: 29.99, currency: 'USD' },\n * quantity: 2,\n * cartId: 'cart_abc'\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { \n Product,\n Cart,\n CartItem,\n CartAnalytics,\n CurrencyCode,\n DateRangeFilter,\n TimeSeriesData\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for adding item to cart.\n */\nexport interface AddToCartConfig {\n /** Product being added */\n product: Product;\n /** Quantity to add */\n quantity: number;\n /** Cart identifier */\n cartId?: string;\n /** User ID */\n userId?: string;\n /** Source of add (product page, quick add, etc.) */\n source?: 'product_page' | 'quick_add' | 'wishlist' | 'recommendation' | 'reorder';\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Configuration for removing item from cart.\n */\nexport interface RemoveFromCartConfig {\n /** Product being removed */\n product: Product;\n /** Quantity to remove */\n quantity: number;\n /** Cart identifier */\n cartId?: string;\n /** User ID */\n userId?: string;\n /** Reason for removal */\n reason?: 'user_action' | 'out_of_stock' | 'price_change' | 'auto_expired';\n}\n\n/**\n * Configuration for updating cart item.\n */\nexport interface UpdateCartItemConfig {\n /** Product being updated */\n product: Product;\n /** Previous quantity */\n previousQuantity: number;\n /** New quantity */\n newQuantity: number;\n /** Cart identifier */\n cartId?: string;\n}\n\n/**\n * Configuration for cart view.\n */\nexport interface ViewCartConfig {\n /** Current cart state */\n cart: Cart;\n /** User ID */\n userId?: string;\n}\n\n/**\n * Cart event response from API.\n */\nexport interface CartResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n cartId: string;\n tracked: boolean;\n cartValue: number;\n itemCount: number;\n };\n timestamp: string;\n}\n\n/**\n * Cart analytics data for charts.\n */\nexport interface CartAnalyticsData {\n /** Cart metrics summary */\n summary: {\n totalAddToCarts: number;\n totalRemoveFromCarts: number;\n averageCartValue: number;\n averageItemsPerCart: number;\n abandonmentRate: number;\n cartToCheckoutRate: number;\n };\n /** Add to cart trends */\n addToCartTimeSeries: TimeSeriesData;\n /** Top products added to cart */\n topAddedProducts: Array<{\n product: Pick<Product, 'productId' | 'name' | 'category'>;\n addCount: number;\n removeCount: number;\n netAdds: number;\n }>;\n /** Abandonment analysis */\n abandonment: {\n rate: number;\n avgAbandonedValue: number;\n topAbandonedProducts: Array<{\n product: Pick<Product, 'productId' | 'name'>;\n abandonCount: number;\n }>;\n };\n}\n\n// ============================================================================\n// CART TRACKING CLASS\n// ============================================================================\n\n/**\n * CartTracking - Track shopping cart interactions.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides cart tracking capabilities including:\n * - Add to cart events\n * - Remove from cart events\n * - Cart quantity updates\n * - Cart view tracking\n * - Cart abandonment detection\n * \n * @example\n * ```typescript\n * const cart = new CartTracking(webId, endpoint, apiKey);\n * \n * // Track add to cart\n * await cart.addItem({\n * product: {\n * productId: 'PROD-001',\n * name: 'Widget Pro',\n * price: 99.99,\n * currency: 'USD'\n * },\n * quantity: 1,\n * source: 'product_page'\n * });\n * \n * // Track cart view\n * await cart.view({\n * cart: currentCart\n * });\n * ```\n */\nexport class CartTracking extends FlowGridTransport {\n /**\n * Tracks adding an item to cart.\n * \n * @param config - Add to cart configuration\n * @returns Promise resolving to cart response\n * \n * @example\n * ```typescript\n * await cart.addItem({\n * product: {\n * productId: 'SKU-123',\n * name: 'Premium Widget',\n * price: 49.99,\n * currency: 'USD',\n * category: 'Widgets'\n * },\n * quantity: 2,\n * source: 'product_page'\n * });\n * ```\n */\n async addItem(config: AddToCartConfig): Promise<CartResponse> {\n const lineTotal = config.product.price * config.quantity - (config.product.discount || 0);\n \n return await this.init(\"add_to_cart\", {\n type: \"ecommerce\",\n subtype: \"add_to_cart\",\n product: config.product,\n quantity: config.quantity,\n lineTotal,\n cartId: config.cartId,\n userId: config.userId,\n source: config.source,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks removing an item from cart.\n * \n * @param config - Remove from cart configuration\n * @returns Promise resolving to cart response\n * \n * @example\n * ```typescript\n * await cart.removeItem({\n * product: productToRemove,\n * quantity: 1,\n * cartId: 'cart_123',\n * reason: 'user_action'\n * });\n * ```\n */\n async removeItem(config: RemoveFromCartConfig): Promise<CartResponse> {\n return await this.init(\"remove_from_cart\", {\n type: \"ecommerce\",\n subtype: \"remove_from_cart\",\n product: config.product,\n quantity: config.quantity,\n cartId: config.cartId,\n userId: config.userId,\n reason: config.reason || \"user_action\",\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks cart item quantity update.\n * \n * @param config - Update configuration\n * @returns Promise resolving to cart response\n * \n * @example\n * ```typescript\n * await cart.updateItem({\n * product: item.product,\n * previousQuantity: 1,\n * newQuantity: 3,\n * cartId: 'cart_123'\n * });\n * ```\n */\n async updateItem(config: UpdateCartItemConfig): Promise<CartResponse> {\n return await this.init(\"update_cart_item\", {\n type: \"ecommerce\",\n subtype: \"update_cart_item\",\n product: config.product,\n previousQuantity: config.previousQuantity,\n newQuantity: config.newQuantity,\n quantityChange: config.newQuantity - config.previousQuantity,\n cartId: config.cartId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a cart view event.\n * \n * @param config - View cart configuration\n * @returns Promise resolving to cart response\n * \n * @example\n * ```typescript\n * await cart.view({\n * cart: {\n * cartId: 'cart_123',\n * items: cartItems,\n * subtotal: 149.97,\n * total: 164.97,\n * currency: 'USD',\n * itemCount: 3\n * }\n * });\n * ```\n */\n async view(config: ViewCartConfig): Promise<CartResponse> {\n return await this.init(\"view_cart\", {\n type: \"ecommerce\",\n subtype: \"view_cart\",\n cartId: config.cart.cartId,\n cart: config.cart,\n cartValue: config.cart.total,\n itemCount: config.cart.itemCount,\n userId: config.userId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks cart abandonment.\n * \n * @param cart - Abandoned cart state\n * @param reason - Abandonment reason (if known)\n * @returns Promise resolving to cart response\n */\n async abandon(\n cart: Cart,\n reason?: 'exit' | 'timeout' | 'price_too_high' | 'shipping_cost' | 'other'\n ): Promise<CartResponse> {\n return await this.init(\"cart_abandoned\", {\n type: \"ecommerce\",\n subtype: \"cart_abandoned\",\n cart,\n cartValue: cart.total,\n itemCount: cart.itemCount,\n reason,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Applies a coupon to the cart.\n * \n * @param cartId - Cart identifier\n * @param couponCode - Coupon code applied\n * @param success - Whether coupon was valid\n * @param discount - Discount amount (if successful)\n * @returns Promise resolving to cart response\n */\n async applyCoupon(\n cartId: string,\n couponCode: string,\n success: boolean,\n discount?: number\n ): Promise<CartResponse> {\n return await this.init(\"apply_coupon\", {\n type: \"ecommerce\",\n subtype: \"apply_coupon\",\n cartId,\n couponCode,\n success,\n discount,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default CartTracking;\n","/**\n * @fileoverview Ecommerce - Checkout Tracking\n * @module features/ecommerce/checkout\n * \n * @description\n * Track checkout flow progress, payment attempts, and checkout\n * optimization opportunities.\n * \n * @useCases\n * - Track checkout step progression\n * - Identify checkout drop-off points\n * - Monitor payment success rates\n * - Analyze shipping method preferences\n * - Optimize checkout UX\n * \n * @metrics\n * - Checkout initiation rate\n * - Step completion rates\n * - Payment success rate\n * - Checkout abandonment rate\n * - Average time to complete\n * \n * @chartData\n * - Funnel chart: Checkout steps\n * - Bar chart: Abandonment by step\n * - Pie chart: Payment methods\n * - Line chart: Checkout completions over time\n * \n * @example\n * ```typescript\n * const checkout = new Checkout(webId, endpoint, apiKey);\n * \n * // Begin checkout\n * await checkout.begin({\n * cart: shoppingCart,\n * userId: 'user_123'\n * });\n * \n * // Track step progress\n * await checkout.step({\n * step: 'add_payment_info',\n * stepNumber: 3,\n * cart: shoppingCart\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { \n Cart,\n CheckoutStep,\n ShippingInfo,\n PaymentInfo,\n TimeSeriesData\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for beginning checkout.\n */\nexport interface BeginCheckoutConfig {\n /** Cart being checked out */\n cart: Cart;\n /** User ID */\n userId?: string;\n /** Session ID */\n sessionId?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Configuration for tracking checkout step.\n */\nexport interface CheckoutStepConfig {\n /** Checkout step */\n step: CheckoutStep;\n /** Step number */\n stepNumber: number;\n /** Cart state at this step */\n cart: Cart;\n /** Option selected at this step (e.g., shipping method) */\n option?: string;\n /** User ID */\n userId?: string;\n}\n\n/**\n * Configuration for adding shipping info.\n */\nexport interface AddShippingConfig {\n /** Cart being checked out */\n cart: Cart;\n /** Shipping information */\n shipping: ShippingInfo;\n /** User ID */\n userId?: string;\n}\n\n/**\n * Configuration for adding payment info.\n */\nexport interface AddPaymentConfig {\n /** Cart being checked out */\n cart: Cart;\n /** Payment information (non-sensitive) */\n payment: PaymentInfo;\n /** User ID */\n userId?: string;\n}\n\n/**\n * Checkout event response from API.\n */\nexport interface CheckoutResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n checkoutId: string;\n step: string;\n tracked: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Checkout analytics data for charts.\n */\nexport interface CheckoutAnalyticsData {\n /** Checkout funnel data */\n funnel: Array<{\n step: CheckoutStep;\n stepNumber: number;\n started: number;\n completed: number;\n conversionRate: number;\n dropOffRate: number;\n avgTimeToComplete: number;\n }>;\n /** Summary metrics */\n summary: {\n checkoutsStarted: number;\n checkoutsCompleted: number;\n overallConversionRate: number;\n avgCheckoutTime: number;\n avgOrderValue: number;\n abandonmentRate: number;\n };\n /** Checkouts over time */\n timeSeries: TimeSeriesData;\n /** Shipping methods used */\n shippingMethods: Array<{\n method: string;\n count: number;\n percentage: number;\n avgShippingCost: number;\n }>;\n /** Payment methods used */\n paymentMethods: Array<{\n method: string;\n count: number;\n percentage: number;\n successRate: number;\n }>;\n /** Abandonment analysis */\n abandonmentByStep: Array<{\n step: CheckoutStep;\n abandonments: number;\n percentage: number;\n avgCartValue: number;\n }>;\n}\n\n// ============================================================================\n// CHECKOUT CLASS\n// ============================================================================\n\n/**\n * Checkout - Track checkout flow and payments.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides checkout tracking capabilities including:\n * - Checkout initiation\n * - Step-by-step progress\n * - Shipping info capture\n * - Payment info capture\n * - Checkout abandonment\n * \n * @example\n * ```typescript\n * const checkout = new Checkout(webId, endpoint, apiKey);\n * \n * // Start checkout\n * await checkout.begin({ cart: shoppingCart });\n * \n * // Track shipping step\n * await checkout.addShipping({\n * cart: shoppingCart,\n * shipping: {\n * method: 'express',\n * carrier: 'FedEx',\n * cost: 9.99\n * }\n * });\n * \n * // Track payment step\n * await checkout.addPayment({\n * cart: shoppingCart,\n * payment: {\n * method: 'credit_card',\n * cardBrand: 'Visa'\n * }\n * });\n * ```\n */\nexport class Checkout extends FlowGridTransport {\n /**\n * Tracks checkout initiation.\n * \n * @param config - Begin checkout configuration\n * @returns Promise resolving to checkout response\n * \n * @example\n * ```typescript\n * await checkout.begin({\n * cart: {\n * cartId: 'cart_123',\n * items: cartItems,\n * total: 199.99,\n * currency: 'USD',\n * itemCount: 3\n * }\n * });\n * ```\n */\n async begin(config: BeginCheckoutConfig): Promise<CheckoutResponse> {\n return await this.init(\"begin_checkout\", {\n type: \"ecommerce\",\n subtype: \"begin_checkout\",\n cart: config.cart,\n cartValue: config.cart.total,\n itemCount: config.cart.itemCount,\n userId: config.userId,\n sessionId: config.sessionId,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks checkout step progress.\n * \n * @param config - Checkout step configuration\n * @returns Promise resolving to checkout response\n * \n * @example\n * ```typescript\n * await checkout.step({\n * step: 'add_shipping_info',\n * stepNumber: 2,\n * cart: shoppingCart,\n * option: 'express_shipping'\n * });\n * ```\n */\n async step(config: CheckoutStepConfig): Promise<CheckoutResponse> {\n return await this.init(\"checkout_step\", {\n type: \"ecommerce\",\n subtype: \"checkout_step\",\n step: config.step,\n stepNumber: config.stepNumber,\n cart: config.cart,\n option: config.option,\n userId: config.userId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks adding shipping information.\n * \n * @param config - Shipping info configuration\n * @returns Promise resolving to checkout response\n * \n * @example\n * ```typescript\n * await checkout.addShipping({\n * cart: shoppingCart,\n * shipping: {\n * method: 'standard',\n * carrier: 'USPS',\n * cost: 5.99,\n * estimatedDelivery: '2026-02-07'\n * }\n * });\n * ```\n */\n async addShipping(config: AddShippingConfig): Promise<CheckoutResponse> {\n return await this.init(\"add_shipping_info\", {\n type: \"ecommerce\",\n subtype: \"shipping_info\",\n cart: config.cart,\n shipping: config.shipping,\n shippingMethod: config.shipping.method,\n shippingCost: config.shipping.cost,\n userId: config.userId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks adding payment information.\n * \n * @param config - Payment info configuration\n * @returns Promise resolving to checkout response\n * \n * @example\n * ```typescript\n * await checkout.addPayment({\n * cart: shoppingCart,\n * payment: {\n * method: 'credit_card',\n * provider: 'stripe',\n * cardBrand: 'Mastercard',\n * cardLast4: '4242'\n * }\n * });\n * ```\n */\n async addPayment(config: AddPaymentConfig): Promise<CheckoutResponse> {\n return await this.init(\"add_payment_info\", {\n type: \"ecommerce\",\n subtype: \"payment_info\",\n cart: config.cart,\n payment: config.payment,\n paymentMethod: config.payment.method,\n userId: config.userId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks checkout abandonment.\n * \n * @param cart - Cart at abandonment\n * @param step - Last completed step\n * @param reason - Abandonment reason (if known)\n * @returns Promise resolving to checkout response\n */\n async abandon(\n cart: Cart,\n step: CheckoutStep,\n reason?: string\n ): Promise<CheckoutResponse> {\n return await this.init(\"checkout_abandoned\", {\n type: \"ecommerce\",\n subtype: \"checkout_abandoned\",\n cart,\n step,\n cartValue: cart.total,\n reason,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks payment attempt (success or failure).\n * \n * @param success - Whether payment succeeded\n * @param cart - Cart being paid for\n * @param payment - Payment information\n * @param errorCode - Error code (if failed)\n * @returns Promise resolving to checkout response\n */\n async paymentAttempt(\n success: boolean,\n cart: Cart,\n payment: PaymentInfo,\n errorCode?: string\n ): Promise<CheckoutResponse> {\n return await this.init(\"payment_attempt\", {\n type: \"ecommerce\",\n subtype: \"payment_attempt\",\n success,\n cart,\n payment,\n errorCode,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default Checkout;\n","/**\n * @fileoverview Ecommerce - Purchase/Transaction Tracking\n * @module features/ecommerce/purchases\n * \n * @description\n * Track completed purchases, transactions, and revenue with\n * full order details and customer information.\n * \n * @useCases\n * - Track completed purchases\n * - Calculate revenue metrics\n * - Monitor order values\n * - Analyze purchase patterns\n * - Track customer lifetime value\n * \n * @metrics\n * - Total revenue\n * - Number of orders\n * - Average order value\n * - Revenue per customer\n * - Items per order\n * \n * @chartData\n * - Line chart: Revenue over time\n * - Bar chart: Revenue by product/category\n * - KPI cards: Revenue, AOV, Orders\n * - Table: Recent transactions\n * \n * @example\n * ```typescript\n * const purchases = new Purchases(webId, endpoint, apiKey);\n * \n * // Track a purchase\n * await purchases.track({\n * order: {\n * orderId: 'ORD-12345',\n * items: orderItems,\n * total: 299.99,\n * currency: 'USD'\n * }\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { \n Order,\n CartItem,\n ShippingInfo,\n PaymentInfo,\n RevenueMetrics,\n MoneyValue,\n CurrencyCode,\n DateRangeFilter,\n TimeSeriesData,\n PaginationParams\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for tracking a purchase.\n */\nexport interface PurchaseConfig {\n /** Order details */\n order: Order;\n /** User ID */\n userId?: string;\n /** Session ID */\n sessionId?: string;\n /** Attribution data */\n attribution?: {\n source?: string;\n medium?: string;\n campaign?: string;\n affiliate?: string;\n };\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Purchase event response from API.\n */\nexport interface PurchaseResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n orderId: string;\n tracked: boolean;\n revenue: number;\n isFirstPurchase: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Revenue analytics data for charts.\n */\nexport interface RevenueAnalyticsData {\n /** Revenue summary */\n summary: {\n totalRevenue: MoneyValue;\n netRevenue: MoneyValue;\n totalOrders: number;\n averageOrderValue: MoneyValue;\n itemsSold: number;\n uniqueCustomers: number;\n repeatPurchaseRate: number;\n };\n /** Revenue time series */\n revenueTimeSeries: TimeSeriesData;\n /** Orders time series */\n ordersTimeSeries: TimeSeriesData;\n /** Revenue by category */\n byCategory: Array<{\n category: string;\n revenue: number;\n orders: number;\n percentage: number;\n }>;\n /** Revenue by product */\n byProduct: Array<{\n productId: string;\n productName: string;\n revenue: number;\n unitsSold: number;\n }>;\n /** Revenue by geography */\n byCountry: Array<{\n country: string;\n revenue: number;\n orders: number;\n }>;\n /** Customer segments */\n customerSegments: Array<{\n segment: 'new' | 'returning' | 'vip';\n customers: number;\n revenue: number;\n avgOrderValue: number;\n }>;\n}\n\n/**\n * Recent orders data.\n */\nexport interface RecentOrdersData {\n orders: Array<{\n orderId: string;\n total: MoneyValue;\n itemCount: number;\n status: string;\n createdAt: string;\n customer?: {\n userId: string;\n isNew: boolean;\n };\n }>;\n pagination: {\n page: number;\n limit: number;\n total: number;\n hasMore: boolean;\n };\n}\n\n// ============================================================================\n// PURCHASES CLASS\n// ============================================================================\n\n/**\n * Purchases - Track completed purchases and revenue.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides purchase tracking capabilities including:\n * - Purchase completion events\n * - Revenue tracking\n * - Order details capture\n * - Customer LTV tracking\n * - Attribution tracking\n * \n * @example\n * ```typescript\n * const purchases = new Purchases(webId, endpoint, apiKey);\n * \n * // Track completed purchase\n * await purchases.track({\n * order: {\n * orderId: 'ORD-12345',\n * items: cartItems,\n * subtotal: 279.99,\n * discountTotal: 30.00,\n * shippingTotal: 9.99,\n * taxTotal: 26.00,\n * total: 285.98,\n * currency: 'USD',\n * shipping: { method: 'express', cost: 9.99 },\n * payment: { method: 'credit_card' }\n * },\n * userId: 'user_123'\n * });\n * ```\n */\nexport class Purchases extends FlowGridTransport {\n /**\n * Tracks a completed purchase.\n * \n * @param config - Purchase configuration\n * @returns Promise resolving to purchase response\n * \n * @example\n * ```typescript\n * await purchases.track({\n * order: {\n * orderId: 'ORD-98765',\n * items: [\n * { productId: 'PROD-1', name: 'Widget', price: 29.99, quantity: 2, lineTotal: 59.98 }\n * ],\n * subtotal: 59.98,\n * discountTotal: 0,\n * shippingTotal: 5.99,\n * taxTotal: 5.40,\n * total: 71.37,\n * currency: 'USD'\n * },\n * attribution: {\n * source: 'google',\n * medium: 'cpc',\n * campaign: 'spring_sale'\n * }\n * });\n * ```\n */\n async track(config: PurchaseConfig): Promise<PurchaseResponse> {\n const order = config.order;\n \n return await this.init(\"purchase\", {\n type: \"ecommerce\",\n subtype: \"purchase\",\n order: {\n orderId: order.orderId,\n items: order.items,\n subtotal: order.subtotal,\n discountTotal: order.discountTotal,\n shippingTotal: order.shippingTotal,\n taxTotal: order.taxTotal,\n total: order.total,\n currency: order.currency,\n shipping: order.shipping,\n payment: order.payment,\n coupons: order.coupons,\n affiliation: order.affiliation\n },\n revenue: order.total,\n itemCount: order.items.reduce((sum, i) => sum + i.quantity, 0),\n userId: config.userId,\n sessionId: config.sessionId,\n attribution: config.attribution,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks order status change.\n * \n * @param orderId - Order identifier\n * @param newStatus - New order status\n * @param previousStatus - Previous status (optional)\n * @returns Promise resolving to status change response\n */\n async statusChange(\n orderId: string,\n newStatus: Order['status'],\n previousStatus?: Order['status']\n ): Promise<PurchaseResponse> {\n return await this.init(\"order_status_change\", {\n type: \"ecommerce\",\n subtype: \"order_status_change\",\n orderId,\n newStatus,\n previousStatus,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks order shipped event.\n * \n * @param orderId - Order identifier\n * @param shipping - Shipping details with tracking\n * @returns Promise resolving to shipped response\n */\n async shipped(orderId: string, shipping: ShippingInfo): Promise<PurchaseResponse> {\n return await this.init(\"order_shipped\", {\n type: \"ecommerce\",\n subtype: \"order_shipped\",\n orderId,\n shipping,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks order delivered event.\n * \n * @param orderId - Order identifier\n * @param deliveredAt - Delivery timestamp\n * @returns Promise resolving to delivered response\n */\n async delivered(orderId: string, deliveredAt?: string): Promise<PurchaseResponse> {\n return await this.init(\"order_delivered\", {\n type: \"ecommerce\",\n subtype: \"order_delivered\",\n orderId,\n deliveredAt: deliveredAt || new Date().toISOString(),\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default Purchases;\n","/**\n * @fileoverview Ecommerce - Refund Tracking\n * @module features/ecommerce/refunds\n * \n * @description\n * Track refunds, returns, and order cancellations with\n * reason analysis and revenue impact.\n * \n * @useCases\n * - Track full and partial refunds\n * - Analyze refund reasons\n * - Monitor return rates by product\n * - Calculate net revenue after refunds\n * - Identify problematic products\n * \n * @metrics\n * - Total refunds\n * - Refund rate\n * - Average refund value\n * - Refunds by reason\n * - Net revenue impact\n * \n * @chartData\n * - Bar chart: Refunds by reason\n * - Line chart: Refund trends\n * - Table: Products with highest return rates\n * - KPI: Refund rate vs benchmark\n * \n * @example\n * ```typescript\n * const refunds = new Refunds(webId, endpoint, apiKey);\n * \n * // Track a refund\n * await refunds.track({\n * orderId: 'ORD-12345',\n * refundId: 'REF-001',\n * amount: 49.99,\n * currency: 'USD',\n * reason: 'defective',\n * isFullRefund: false\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { \n Refund,\n CartItem,\n MoneyValue,\n CurrencyCode,\n DateRangeFilter,\n TimeSeriesData\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for tracking a refund.\n */\nexport interface RefundConfig extends Refund {\n /** User ID */\n userId?: string;\n /** Initiated by (customer or admin) */\n initiatedBy?: 'customer' | 'admin' | 'system';\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Refund event response from API.\n */\nexport interface RefundResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n refundId: string;\n orderId: string;\n tracked: boolean;\n refundedAmount: number;\n };\n timestamp: string;\n}\n\n/**\n * Refund analytics data for charts.\n */\nexport interface RefundAnalyticsData {\n /** Summary metrics */\n summary: {\n totalRefunds: number;\n totalRefundValue: MoneyValue;\n refundRate: number;\n averageRefundValue: MoneyValue;\n fullRefundCount: number;\n partialRefundCount: number;\n };\n /** Refunds time series */\n timeSeries: TimeSeriesData;\n /** Refunds by reason */\n byReason: Array<{\n reason: string;\n count: number;\n totalValue: number;\n percentage: number;\n }>;\n /** Products with highest refund rates */\n topRefundedProducts: Array<{\n productId: string;\n productName: string;\n refundCount: number;\n refundValue: number;\n refundRate: number;\n }>;\n /** Revenue impact */\n revenueImpact: {\n grossRevenue: MoneyValue;\n refundedAmount: MoneyValue;\n netRevenue: MoneyValue;\n refundImpactPercent: number;\n };\n}\n\n// ============================================================================\n// REFUNDS CLASS\n// ============================================================================\n\n/**\n * Refunds - Track refunds and returns.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides refund tracking capabilities including:\n * - Full refund tracking\n * - Partial refund tracking\n * - Refund reason analysis\n * - Return rate monitoring\n * - Revenue impact calculation\n * \n * @example\n * ```typescript\n * const refunds = new Refunds(webId, endpoint, apiKey);\n * \n * // Track full refund\n * await refunds.track({\n * orderId: 'ORD-12345',\n * refundId: 'REF-001',\n * amount: 149.99,\n * currency: 'USD',\n * reason: 'customer_request',\n * isFullRefund: true\n * });\n * \n * // Track partial refund with items\n * await refunds.track({\n * orderId: 'ORD-12345',\n * refundId: 'REF-002',\n * items: [refundedItem],\n * amount: 49.99,\n * currency: 'USD',\n * reason: 'defective',\n * isFullRefund: false\n * });\n * ```\n */\nexport class Refunds extends FlowGridTransport {\n /**\n * Tracks a refund event.\n * \n * @param config - Refund configuration\n * @returns Promise resolving to refund response\n * \n * @example\n * ```typescript\n * await refunds.track({\n * orderId: 'ORD-98765',\n * refundId: 'REF-123',\n * items: [\n * { productId: 'PROD-1', name: 'Widget', price: 29.99, quantity: 1, lineTotal: 29.99 }\n * ],\n * amount: 29.99,\n * currency: 'USD',\n * reason: 'wrong_item',\n * isFullRefund: false,\n * initiatedBy: 'customer'\n * });\n * ```\n */\n async track(config: RefundConfig): Promise<RefundResponse> {\n return await this.init(\"refund\", {\n type: \"ecommerce\",\n subtype: \"refund\",\n orderId: config.orderId,\n refundId: config.refundId,\n items: config.items,\n amount: config.amount,\n currency: config.currency,\n reason: config.reason,\n isFullRefund: config.isFullRefund,\n userId: config.userId,\n initiatedBy: config.initiatedBy,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a refund request (before processing).\n * \n * @param orderId - Original order ID\n * @param items - Items requested for refund\n * @param reason - Refund reason\n * @returns Promise resolving to request response\n */\n async request(\n orderId: string,\n items?: CartItem[],\n reason?: string\n ): Promise<RefundResponse> {\n return await this.init(\"refund_requested\", {\n type: \"ecommerce\",\n subtype: \"refund_requested\",\n orderId,\n items,\n reason,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks refund approval.\n * \n * @param refundId - Refund identifier\n * @param approvedAmount - Approved refund amount\n * @returns Promise resolving to approval response\n */\n async approve(refundId: string, approvedAmount: number): Promise<RefundResponse> {\n return await this.init(\"refund_approved\", {\n type: \"ecommerce\",\n subtype: \"refund_approved\",\n refundId,\n approvedAmount,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks refund denial.\n * \n * @param refundId - Refund identifier\n * @param denialReason - Reason for denial\n * @returns Promise resolving to denial response\n */\n async deny(refundId: string, denialReason: string): Promise<RefundResponse> {\n return await this.init(\"refund_denied\", {\n type: \"ecommerce\",\n subtype: \"refund_denied\",\n refundId,\n denialReason,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks refund completion (money returned).\n * \n * @param refundId - Refund identifier\n * @param processedAmount - Amount actually refunded\n * @returns Promise resolving to completion response\n */\n async complete(refundId: string, processedAmount: number): Promise<RefundResponse> {\n return await this.init(\"refund_completed\", {\n type: \"ecommerce\",\n subtype: \"refund_completed\",\n refundId,\n processedAmount,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default Refunds;\n","/**\n * @fileoverview Ecommerce - Promotion Tracking\n * @module features/ecommerce/promotions\n * \n * @description\n * Track promotional content, coupon usage, and discount\n * effectiveness across your ecommerce store.\n * \n * @useCases\n * - Track promotional banner views\n * - Measure promotion click-through rates\n * - Analyze coupon usage and effectiveness\n * - Monitor discount impact on revenue\n * - Compare promotion performance\n * \n * @metrics\n * - Promotion impressions\n * - Promotion CTR\n * - Coupon redemption rate\n * - Revenue attributed to promotions\n * - Discount impact on margins\n * \n * @chartData\n * - Bar chart: Top promotions by revenue\n * - Line chart: Coupon usage over time\n * - Table: Promotion performance comparison\n * - Pie chart: Revenue by promotion type\n * \n * @example\n * ```typescript\n * const promotions = new Promotions(webId, endpoint, apiKey);\n * \n * // Track promotion view\n * await promotions.view({\n * promotionId: 'PROMO-SUMMER',\n * name: 'Summer Sale 20% Off',\n * creative: 'homepage-banner'\n * });\n * \n * // Track coupon usage\n * await promotions.couponUsed({\n * code: 'SAVE20',\n * discountValue: 20,\n * orderId: 'ORD-123'\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { \n Promotion,\n MoneyValue,\n DateRangeFilter,\n TimeSeriesData\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for tracking promotion view.\n */\nexport interface PromotionViewConfig extends Promotion {\n /** User ID */\n userId?: string;\n /** Page where promotion was shown */\n page?: string;\n}\n\n/**\n * Configuration for tracking promotion click.\n */\nexport interface PromotionClickConfig extends Promotion {\n /** User ID */\n userId?: string;\n /** Destination URL */\n destinationUrl?: string;\n}\n\n/**\n * Configuration for tracking coupon usage.\n */\nexport interface CouponUsageConfig {\n /** Coupon code */\n code: string;\n /** Order ID where coupon was applied */\n orderId: string;\n /** Discount amount */\n discountValue: number;\n /** Discount type */\n discountType: 'percentage' | 'fixed';\n /** Order value before discount */\n orderValueBefore: number;\n /** Order value after discount */\n orderValueAfter: number;\n /** User ID */\n userId?: string;\n}\n\n/**\n * Promotion event response from API.\n */\nexport interface PromotionResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n promotionId: string;\n tracked: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Promotion analytics data for charts.\n */\nexport interface PromotionAnalyticsData {\n /** Summary metrics */\n summary: {\n totalPromotionViews: number;\n totalPromotionClicks: number;\n overallCTR: number;\n totalCouponsUsed: number;\n totalDiscountGiven: MoneyValue;\n revenueFromPromotions: MoneyValue;\n };\n /** Top performing promotions */\n topPromotions: Array<{\n promotionId: string;\n name: string;\n views: number;\n clicks: number;\n ctr: number;\n conversions: number;\n revenue: number;\n }>;\n /** Coupon performance */\n coupons: Array<{\n code: string;\n usageCount: number;\n totalDiscount: number;\n avgOrderValue: number;\n conversionLift: number;\n }>;\n /** Promotions over time */\n timeSeries: TimeSeriesData;\n /** Revenue impact */\n revenueImpact: {\n promotedRevenue: MoneyValue;\n nonPromotedRevenue: MoneyValue;\n promotionRevenueShare: number;\n avgDiscountPercent: number;\n };\n}\n\n// ============================================================================\n// PROMOTIONS CLASS\n// ============================================================================\n\n/**\n * Promotions - Track promotions and coupons.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides promotion tracking capabilities including:\n * - Promotion/banner impressions\n * - Promotion clicks\n * - Coupon applications\n * - Coupon success/failure\n * - Revenue attribution\n * \n * @example\n * ```typescript\n * const promotions = new Promotions(webId, endpoint, apiKey);\n * \n * // Track banner impression\n * await promotions.view({\n * promotionId: 'FLASH-SALE',\n * name: 'Flash Sale - 30% Off',\n * creative: 'hero-banner',\n * position: 'homepage-top'\n * });\n * \n * // Track banner click\n * await promotions.click({\n * promotionId: 'FLASH-SALE',\n * name: 'Flash Sale - 30% Off',\n * destinationUrl: '/sale'\n * });\n * ```\n */\nexport class Promotions extends FlowGridTransport {\n /**\n * Tracks promotion view/impression.\n * \n * @param config - Promotion view configuration\n * @returns Promise resolving to promotion response\n * \n * @example\n * ```typescript\n * await promotions.view({\n * promotionId: 'SUMMER-2026',\n * name: 'Summer Collection Launch',\n * creative: 'collection-banner',\n * position: 'homepage-hero',\n * page: '/home'\n * });\n * ```\n */\n async view(config: PromotionViewConfig): Promise<PromotionResponse> {\n return await this.init(\"promotion_view\", {\n type: \"ecommerce\",\n subtype: \"promotion_view\",\n promotionId: config.promotionId,\n name: config.name,\n creative: config.creative,\n position: config.position,\n page: config.page,\n userId: config.userId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks promotion click.\n * \n * @param config - Promotion click configuration\n * @returns Promise resolving to promotion response\n * \n * @example\n * ```typescript\n * await promotions.click({\n * promotionId: 'SUMMER-2026',\n * name: 'Summer Collection Launch',\n * destinationUrl: '/collections/summer-2026'\n * });\n * ```\n */\n async click(config: PromotionClickConfig): Promise<PromotionResponse> {\n return await this.init(\"promotion_click\", {\n type: \"ecommerce\",\n subtype: \"promotion_click\",\n promotionId: config.promotionId,\n name: config.name,\n creative: config.creative,\n position: config.position,\n destinationUrl: config.destinationUrl,\n userId: config.userId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks successful coupon usage.\n * \n * @param config - Coupon usage configuration\n * @returns Promise resolving to coupon response\n * \n * @example\n * ```typescript\n * await promotions.couponUsed({\n * code: 'SAVE20',\n * orderId: 'ORD-12345',\n * discountValue: 20,\n * discountType: 'percentage',\n * orderValueBefore: 100,\n * orderValueAfter: 80\n * });\n * ```\n */\n async couponUsed(config: CouponUsageConfig): Promise<PromotionResponse> {\n return await this.init(\"coupon_used\", {\n type: \"ecommerce\",\n subtype: \"coupon_used\",\n code: config.code,\n orderId: config.orderId,\n discountValue: config.discountValue,\n discountType: config.discountType,\n orderValueBefore: config.orderValueBefore,\n orderValueAfter: config.orderValueAfter,\n savings: config.orderValueBefore - config.orderValueAfter,\n userId: config.userId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks coupon application attempt (including failures).\n * \n * @param code - Coupon code attempted\n * @param success - Whether coupon was valid\n * @param failureReason - Reason for failure (if failed)\n * @returns Promise resolving to attempt response\n */\n async couponAttempt(\n code: string,\n success: boolean,\n failureReason?: 'expired' | 'invalid' | 'minimum_not_met' | 'already_used' | 'not_applicable'\n ): Promise<PromotionResponse> {\n return await this.init(\"coupon_attempt\", {\n type: \"ecommerce\",\n subtype: \"coupon_attempt\",\n code,\n success,\n failureReason,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default Promotions;\n","/**\n * @fileoverview Ecommerce - Wishlist Tracking\n * @module features/ecommerce/wishlist\n * \n * @description\n * Track wishlist interactions to understand purchase intent\n * and drive conversions from saved items.\n * \n * @useCases\n * - Track wishlist additions\n * - Monitor wishlist to cart conversions\n * - Identify popular wishlist items\n * - Trigger price drop notifications\n * - Analyze wishlist behavior patterns\n * \n * @metrics\n * - Wishlist additions\n * - Wishlist to cart rate\n * - Wishlist to purchase rate\n * - Average items per wishlist\n * - Top wishlisted products\n * \n * @chartData\n * - Bar chart: Top wishlisted products\n * - Line chart: Wishlist activity over time\n * - Funnel: Wishlist to purchase conversion\n * - Table: Wishlist conversion rates by product\n * \n * @example\n * ```typescript\n * const wishlist = new WishlistTracking(webId, endpoint, apiKey);\n * \n * // Add to wishlist\n * await wishlist.add({\n * product: { productId: 'PROD-123', name: 'Widget', price: 49.99, currency: 'USD' },\n * wishlistId: 'wl_user123'\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { \n Product,\n Wishlist,\n WishlistItem,\n DateRangeFilter,\n TimeSeriesData\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Configuration for adding to wishlist.\n */\nexport interface AddToWishlistConfig {\n /** Product being added */\n product: Product;\n /** Wishlist ID */\n wishlistId?: string;\n /** User ID */\n userId?: string;\n /** Source of add */\n source?: 'product_page' | 'quick_add' | 'list_view' | 'search';\n}\n\n/**\n * Configuration for removing from wishlist.\n */\nexport interface RemoveFromWishlistConfig {\n /** Product being removed */\n product: Product;\n /** Wishlist ID */\n wishlistId?: string;\n /** User ID */\n userId?: string;\n /** Reason for removal */\n reason?: 'user_action' | 'purchased' | 'moved_to_cart' | 'out_of_stock';\n}\n\n/**\n * Wishlist event response from API.\n */\nexport interface WishlistResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n wishlistId: string;\n productId: string;\n tracked: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Wishlist analytics data for charts.\n */\nexport interface WishlistAnalyticsData {\n /** Summary metrics */\n summary: {\n totalWishlistAdds: number;\n totalWishlistRemoves: number;\n activeWishlists: number;\n avgItemsPerWishlist: number;\n wishlistToCartRate: number;\n wishlistToPurchaseRate: number;\n };\n /** Top wishlisted products */\n topProducts: Array<{\n product: Pick<Product, 'productId' | 'name' | 'category' | 'price'>;\n wishlistCount: number;\n conversionToCart: number;\n conversionToPurchase: number;\n }>;\n /** Wishlist activity time series */\n timeSeries: TimeSeriesData;\n /** Conversion funnel */\n conversionFunnel: {\n wishlisted: number;\n addedToCart: number;\n purchased: number;\n wishlistToCartRate: number;\n cartToPurchaseRate: number;\n };\n}\n\n// ============================================================================\n// WISHLIST TRACKING CLASS\n// ============================================================================\n\n/**\n * WishlistTracking - Track wishlist interactions.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides wishlist tracking capabilities including:\n * - Add to wishlist events\n * - Remove from wishlist events\n * - Wishlist views\n * - Move to cart tracking\n * - Wishlist sharing\n * \n * @example\n * ```typescript\n * const wishlist = new WishlistTracking(webId, endpoint, apiKey);\n * \n * // Add product to wishlist\n * await wishlist.add({\n * product: {\n * productId: 'PROD-001',\n * name: 'Designer Bag',\n * price: 299.99,\n * currency: 'USD'\n * },\n * source: 'product_page'\n * });\n * \n * // Track wishlist view\n * await wishlist.view({\n * wishlistId: 'wl_user123',\n * items: wishlistItems\n * });\n * ```\n */\nexport class WishlistTracking extends FlowGridTransport {\n /**\n * Tracks adding product to wishlist.\n * \n * @param config - Add to wishlist configuration\n * @returns Promise resolving to wishlist response\n * \n * @example\n * ```typescript\n * await wishlist.add({\n * product: {\n * productId: 'SKU-789',\n * name: 'Premium Sneakers',\n * price: 159.99,\n * currency: 'USD',\n * category: 'Footwear'\n * },\n * source: 'product_page'\n * });\n * ```\n */\n async add(config: AddToWishlistConfig): Promise<WishlistResponse> {\n return await this.init(\"add_to_wishlist\", {\n type: \"ecommerce\",\n subtype: \"add_to_wishlist\",\n product: config.product,\n wishlistId: config.wishlistId,\n userId: config.userId,\n source: config.source,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks removing product from wishlist.\n * \n * @param config - Remove from wishlist configuration\n * @returns Promise resolving to wishlist response\n * \n * @example\n * ```typescript\n * await wishlist.remove({\n * product: productToRemove,\n * wishlistId: 'wl_user123',\n * reason: 'moved_to_cart'\n * });\n * ```\n */\n async remove(config: RemoveFromWishlistConfig): Promise<WishlistResponse> {\n return await this.init(\"remove_from_wishlist\", {\n type: \"ecommerce\",\n subtype: \"remove_from_wishlist\",\n product: config.product,\n wishlistId: config.wishlistId,\n userId: config.userId,\n reason: config.reason || \"user_action\",\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks wishlist view.\n * \n * @param wishlist - Wishlist being viewed\n * @returns Promise resolving to wishlist response\n */\n async view(wishlist: Wishlist): Promise<WishlistResponse> {\n return await this.init(\"view_wishlist\", {\n type: \"ecommerce\",\n subtype: \"view_wishlist\",\n wishlistId: wishlist.wishlistId,\n itemCount: wishlist.items.length,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks moving item from wishlist to cart.\n * \n * @param product - Product being moved\n * @param wishlistId - Source wishlist\n * @returns Promise resolving to wishlist response\n */\n async moveToCart(product: Product, wishlistId?: string): Promise<WishlistResponse> {\n return await this.init(\"wishlist_to_cart\", {\n type: \"ecommerce\",\n subtype: \"wishlist_to_cart\",\n product,\n wishlistId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks wishlist sharing.\n * \n * @param wishlistId - Wishlist being shared\n * @param method - Share method\n * @returns Promise resolving to wishlist response\n */\n async share(\n wishlistId: string,\n method: 'email' | 'link' | 'social'\n ): Promise<WishlistResponse> {\n return await this.init(\"share_wishlist\", {\n type: \"ecommerce\",\n subtype: \"share_wishlist\",\n wishlistId,\n method,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default WishlistTracking;\n","/**\n * @fileoverview Ecommerce - Customer Lifetime Value (LTV)\n * @module features/ecommerce/ltv\n * \n * @description\n * Calculate and track customer lifetime value to understand\n * customer worth and optimize acquisition costs.\n * \n * @useCases\n * - Calculate customer LTV\n * - Segment customers by value\n * - Track LTV trends over time\n * - Identify high-value customers\n * - Optimize acquisition spend (LTV:CAC)\n * \n * @metrics\n * - Average LTV\n * - LTV by cohort\n * - LTV by segment\n * - LTV:CAC ratio\n * - Customer value distribution\n * \n * @chartData\n * - Line chart: LTV by cohort over time\n * - Bar chart: LTV by segment\n * - Histogram: Customer value distribution\n * - KPI: Average LTV, LTV:CAC\n * \n * @example\n * ```typescript\n * const ltv = new CustomerLTV(webId, endpoint, apiKey);\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { \n MoneyValue,\n CurrencyCode,\n DateRangeFilter,\n TimeSeriesData\n} from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Customer value data.\n */\nexport interface CustomerValueData {\n /** Customer/user ID */\n userId: string;\n /** Total revenue from customer */\n totalRevenue: MoneyValue;\n /** Number of orders */\n orderCount: number;\n /** Average order value */\n averageOrderValue: MoneyValue;\n /** Predicted future value */\n predictedLTV: MoneyValue;\n /** Customer segment */\n segment: 'low' | 'medium' | 'high' | 'vip';\n /** Days since first purchase */\n customerAge: number;\n /** Days since last purchase */\n daysSinceLastPurchase: number;\n /** Purchase frequency (orders per month) */\n purchaseFrequency: number;\n}\n\n/**\n * Configuration for recording acquisition cost.\n */\nexport interface AcquisitionCostConfig {\n /** Customer/user ID */\n userId: string;\n /** Acquisition cost */\n cost: MoneyValue;\n /** Acquisition channel */\n channel: string;\n /** Campaign (if applicable) */\n campaign?: string;\n}\n\n/**\n * LTV event response from API.\n */\nexport interface LTVResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n userId: string;\n ltv: number;\n tracked: boolean;\n };\n timestamp: string;\n}\n\n/**\n * LTV analytics data for charts.\n */\nexport interface LTVAnalyticsData {\n /** Summary metrics */\n summary: {\n averageLTV: MoneyValue;\n medianLTV: MoneyValue;\n totalCustomerValue: MoneyValue;\n averageCAC: MoneyValue;\n ltvCacRatio: number;\n averageOrderValue: MoneyValue;\n averageOrdersPerCustomer: number;\n };\n /** LTV by customer segment */\n bySegment: Array<{\n segment: string;\n customerCount: number;\n averageLTV: MoneyValue;\n totalRevenue: MoneyValue;\n percentageOfCustomers: number;\n percentageOfRevenue: number;\n }>;\n /** LTV by cohort */\n byCohort: Array<{\n cohort: string;\n customerCount: number;\n averageLTV: MoneyValue;\n ltvProgression: number[];\n }>;\n /** LTV by acquisition channel */\n byChannel: Array<{\n channel: string;\n customers: number;\n averageLTV: MoneyValue;\n averageCAC: MoneyValue;\n ltvCacRatio: number;\n roi: number;\n }>;\n /** LTV distribution */\n distribution: Array<{\n range: string;\n count: number;\n percentage: number;\n }>;\n /** LTV trend over time */\n trendData: TimeSeriesData;\n}\n\n// ============================================================================\n// CUSTOMER LTV CLASS\n// ============================================================================\n\n/**\n * CustomerLTV - Calculate and track customer lifetime value.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides LTV tracking capabilities including:\n * - Customer value calculation\n * - Acquisition cost tracking\n * - LTV:CAC ratio analysis\n * - Customer segmentation by value\n * - Cohort-based LTV analysis\n * \n * @example\n * ```typescript\n * const ltv = new CustomerLTV(webId, endpoint, apiKey);\n * \n * // Get individual customer value\n * const value = await ltv.getCustomerValue('user_123');\n * console.log(`LTV: $${value.totalRevenue.amount / 100}`);\n * \n * // Record acquisition cost\n * await ltv.recordAcquisitionCost({\n * userId: 'user_123',\n * cost: { amount: 5000, currency: 'USD' },\n * channel: 'facebook_ads',\n * campaign: 'spring_campaign'\n * });\n * ```\n */\nexport class CustomerLTV extends FlowGridTransport {\n /**\n * Records customer acquisition cost.\n * \n * @param config - Acquisition cost configuration\n * @returns Promise resolving to LTV response\n * \n * @example\n * ```typescript\n * await ltv.recordAcquisitionCost({\n * userId: 'user_123',\n * cost: { amount: 2500, currency: 'USD' }, // $25.00\n * channel: 'google_ads',\n * campaign: 'brand_terms'\n * });\n * ```\n */\n async recordAcquisitionCost(config: AcquisitionCostConfig): Promise<LTVResponse> {\n return await this.init(\"record_acquisition_cost\", {\n type: \"ecommerce\",\n subtype: \"acquisition_cost\",\n userId: config.userId,\n cost: config.cost,\n channel: config.channel,\n campaign: config.campaign,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Updates customer segment based on value.\n * \n * @param userId - Customer/user identifier\n * @param segment - New segment\n * @returns Promise resolving to LTV response\n */\n async updateSegment(\n userId: string,\n segment: 'low' | 'medium' | 'high' | 'vip'\n ): Promise<LTVResponse> {\n return await this.init(\"update_customer_segment\", {\n type: \"ecommerce\",\n subtype: \"update_customer_segment\",\n userId,\n segment,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default CustomerLTV;\n","/**\n * @fileoverview Ecommerce - Search Analytics\n * @module features/ecommerce/search\n * \n * @description\n * Track site search behavior to improve product discovery\n * and conversion from search results.\n * \n * @useCases\n * - Track search queries and results\n * - Identify zero-result searches\n * - Measure search-to-purchase conversion\n * - Analyze popular search terms\n * - Optimize search suggestions\n * \n * @metrics\n * - Search volume\n * - Zero-result rate\n * - Search refinement rate\n * - Click-through rate from search\n * - Conversion from search\n * \n * @chartData\n * - Word cloud: Popular search terms\n * - Bar chart: Top queries\n * - Table: Zero-result queries\n * - Funnel: Search to purchase\n * \n * @example\n * ```typescript\n * const search = new SearchAnalytics(webId, endpoint, apiKey);\n * \n * // Track search\n * await search.track({\n * query: 'wireless headphones',\n * resultsCount: 42,\n * filters: { brand: 'Sony' }\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { Product, DateRangeFilter, TimeSeriesData } from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Search query configuration.\n */\nexport interface SearchConfig {\n /** Search query */\n query: string;\n /** Number of results */\n resultsCount: number;\n /** Search category/scope */\n category?: string;\n /** Applied filters */\n filters?: Record<string, string | string[]>;\n /** Sort order */\n sortBy?: string;\n /** Page number */\n page?: number;\n /** User ID */\n userId?: string;\n /** Session ID */\n sessionId?: string;\n}\n\n/**\n * Search result click configuration.\n */\nexport interface SearchClickConfig {\n /** Original search query */\n query: string;\n /** Product clicked */\n product: Product;\n /** Position in results */\n position: number;\n /** Results page number */\n page?: number;\n}\n\n/**\n * Search response from API.\n */\nexport interface SearchResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n searchId: string;\n tracked: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Search analytics data.\n */\nexport interface SearchAnalyticsData {\n /** Summary metrics */\n summary: {\n totalSearches: number;\n uniqueSearchers: number;\n zeroResultSearches: number;\n zeroResultRate: number;\n avgResultsPerSearch: number;\n searchToClickRate: number;\n searchToConversionRate: number;\n };\n /** Top search queries */\n topQueries: Array<{\n query: string;\n searches: number;\n clicks: number;\n conversions: number;\n ctr: number;\n conversionRate: number;\n }>;\n /** Zero-result queries */\n zeroResultQueries: Array<{\n query: string;\n occurrences: number;\n lastSearched: string;\n }>;\n /** Popular filters */\n popularFilters: Array<{\n filterName: string;\n filterValue: string;\n usageCount: number;\n }>;\n /** Search trends */\n trends: TimeSeriesData;\n /** Search to purchase funnel */\n funnel: {\n searches: number;\n resultsViewed: number;\n productClicked: number;\n addedToCart: number;\n purchased: number;\n };\n}\n\n// ============================================================================\n// SEARCH ANALYTICS CLASS\n// ============================================================================\n\n/**\n * SearchAnalytics - Track site search behavior.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides search analytics including:\n * - Search query tracking\n * - Result click tracking\n * - Zero-result detection\n * - Search refinement tracking\n * - Search to conversion analysis\n * \n * @example\n * ```typescript\n * const search = new SearchAnalytics(webId, endpoint, apiKey);\n * \n * // Track search\n * await search.track({\n * query: 'running shoes',\n * resultsCount: 156,\n * filters: { size: '10', color: 'black' },\n * sortBy: 'price_asc'\n * });\n * \n * // Track click from search results\n * await search.trackClick({\n * query: 'running shoes',\n * product: clickedProduct,\n * position: 3\n * });\n * ```\n */\nexport class SearchAnalytics extends FlowGridTransport {\n /**\n * Tracks a search query.\n * \n * @param config - Search configuration\n * @returns Promise resolving to search response\n */\n async track(config: SearchConfig): Promise<SearchResponse> {\n return await this.init(\"site_search\", {\n type: \"ecommerce\",\n subtype: \"site_search\",\n query: config.query,\n resultsCount: config.resultsCount,\n hasResults: config.resultsCount > 0,\n category: config.category,\n filters: config.filters,\n sortBy: config.sortBy,\n page: config.page || 1,\n userId: config.userId,\n sessionId: config.sessionId,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks a click on search result.\n * \n * @param config - Search click configuration\n * @returns Promise resolving to search response\n */\n async trackClick(config: SearchClickConfig): Promise<SearchResponse> {\n return await this.init(\"search_result_click\", {\n type: \"ecommerce\",\n subtype: \"search_result_click\",\n query: config.query,\n product: config.product,\n position: config.position,\n page: config.page || 1,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks search refinement (modifying search).\n * \n * @param originalQuery - Original search query\n * @param refinedQuery - New/refined query\n * @param refinementType - Type of refinement\n * @returns Promise resolving to search response\n */\n async trackRefinement(\n originalQuery: string,\n refinedQuery: string,\n refinementType: 'query_change' | 'add_filter' | 'remove_filter' | 'sort_change'\n ): Promise<SearchResponse> {\n return await this.init(\"search_refinement\", {\n type: \"ecommerce\",\n subtype: \"search_refinement\",\n originalQuery,\n refinedQuery,\n refinementType,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks autocomplete/suggestion selection.\n * \n * @param partialQuery - Partial query typed\n * @param selectedSuggestion - Suggestion selected\n * @param suggestionPosition - Position of suggestion\n * @returns Promise resolving to search response\n */\n async trackSuggestion(\n partialQuery: string,\n selectedSuggestion: string,\n suggestionPosition: number\n ): Promise<SearchResponse> {\n return await this.init(\"search_suggestion\", {\n type: \"ecommerce\",\n subtype: \"search_suggestion\",\n partialQuery,\n selectedSuggestion,\n suggestionPosition,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default SearchAnalytics;\n","/**\n * @fileoverview Ecommerce - Subscription & Recurring Revenue\n * @module features/ecommerce/subscriptions\n * \n * @description\n * Track subscription lifecycle events for SaaS and subscription\n * ecommerce businesses.\n * \n * @useCases\n * - Track subscription signups\n * - Monitor subscription changes (upgrades/downgrades)\n * - Track cancellations and churn\n * - Calculate MRR/ARR metrics\n * - Analyze subscription cohorts\n * \n * @metrics\n * - Monthly Recurring Revenue (MRR)\n * - Annual Recurring Revenue (ARR)\n * - Churn rate\n * - Expansion revenue\n * - Net revenue retention\n * \n * @chartData\n * - Line chart: MRR over time\n * - Waterfall: MRR movement\n * - Bar chart: Revenue by plan\n * - Cohort: Subscription retention\n * \n * @example\n * ```typescript\n * const subscriptions = new Subscriptions(webId, endpoint, apiKey);\n * \n * // Track subscription start\n * await subscriptions.start({\n * subscriptionId: 'sub_123',\n * userId: 'user_456',\n * plan: 'pro',\n * mrr: { amount: 4900, currency: 'USD' },\n * billingCycle: 'monthly'\n * });\n * ```\n */\n\nimport { FlowGridTransport } from \"../../client/transport\";\nimport { MoneyValue, CurrencyCode, DateRangeFilter, TimeSeriesData } from \"../../types\";\n\n// ============================================================================\n// CONFIGURATION TYPES\n// ============================================================================\n\n/**\n * Subscription start configuration.\n */\nexport interface SubscriptionStartConfig {\n /** Subscription ID */\n subscriptionId: string;\n /** User ID */\n userId: string;\n /** Plan name */\n plan: string;\n /** Monthly recurring revenue */\n mrr: MoneyValue;\n /** Billing cycle */\n billingCycle: 'monthly' | 'quarterly' | 'yearly';\n /** Trial period (if applicable) */\n trialDays?: number;\n /** Coupon applied */\n coupon?: string;\n /** Custom properties */\n properties?: Record<string, unknown>;\n}\n\n/**\n * Subscription change configuration.\n */\nexport interface SubscriptionChangeConfig {\n /** Subscription ID */\n subscriptionId: string;\n /** User ID */\n userId: string;\n /** Previous plan */\n previousPlan: string;\n /** New plan */\n newPlan: string;\n /** Previous MRR */\n previousMrr: MoneyValue;\n /** New MRR */\n newMrr: MoneyValue;\n /** Change type */\n changeType: 'upgrade' | 'downgrade' | 'cross_grade';\n}\n\n/**\n * Subscription cancellation configuration.\n */\nexport interface SubscriptionCancelConfig {\n /** Subscription ID */\n subscriptionId: string;\n /** User ID */\n userId: string;\n /** Plan being cancelled */\n plan: string;\n /** Lost MRR */\n lostMrr: MoneyValue;\n /** Cancellation reason */\n reason?: string;\n /** Feedback provided */\n feedback?: string;\n /** Whether cancelled immediately or at period end */\n immediate: boolean;\n}\n\n/**\n * Subscription response from API.\n */\nexport interface SubscriptionResponse {\n eventName: string;\n properties: Record<string, unknown>;\n response: {\n subscriptionId: string;\n tracked: boolean;\n };\n timestamp: string;\n}\n\n/**\n * Subscription analytics data.\n */\nexport interface SubscriptionAnalyticsData {\n /** Summary metrics */\n summary: {\n totalMrr: MoneyValue;\n totalArr: MoneyValue;\n activeSubscriptions: number;\n newSubscriptions: number;\n churnedSubscriptions: number;\n churnRate: number;\n expansionMrr: MoneyValue;\n contractionMrr: MoneyValue;\n netMrrMovement: MoneyValue;\n netRevenueRetention: number;\n avgRevenuePerUser: MoneyValue;\n };\n /** MRR by plan */\n byPlan: Array<{\n plan: string;\n subscriptions: number;\n mrr: MoneyValue;\n percentage: number;\n }>;\n /** MRR waterfall */\n mrrMovement: {\n startingMrr: MoneyValue;\n newMrr: MoneyValue;\n expansionMrr: MoneyValue;\n contractionMrr: MoneyValue;\n churnedMrr: MoneyValue;\n endingMrr: MoneyValue;\n };\n /** MRR trend */\n mrrTrend: TimeSeriesData;\n /** Churn by reason */\n churnByReason: Array<{\n reason: string;\n count: number;\n lostMrr: MoneyValue;\n }>;\n /** Trial conversion rate */\n trialMetrics: {\n trialsStarted: number;\n trialsConverted: number;\n conversionRate: number;\n };\n}\n\n// ============================================================================\n// SUBSCRIPTIONS CLASS\n// ============================================================================\n\n/**\n * Subscriptions - Track subscription lifecycle events.\n * \n * @extends FlowGridTransport\n * \n * @description\n * Provides subscription tracking including:\n * - Subscription starts (new MRR)\n * - Plan changes (expansion/contraction)\n * - Cancellations (churned MRR)\n * - Renewals\n * - Trial conversions\n * \n * @example\n * ```typescript\n * const subscriptions = new Subscriptions(webId, endpoint, apiKey);\n * \n * // New subscription\n * await subscriptions.start({\n * subscriptionId: 'sub_abc123',\n * userId: 'user_456',\n * plan: 'professional',\n * mrr: { amount: 9900, currency: 'USD' },\n * billingCycle: 'monthly',\n * trialDays: 14\n * });\n * \n * // Upgrade\n * await subscriptions.change({\n * subscriptionId: 'sub_abc123',\n * userId: 'user_456',\n * previousPlan: 'professional',\n * newPlan: 'enterprise',\n * previousMrr: { amount: 9900, currency: 'USD' },\n * newMrr: { amount: 29900, currency: 'USD' },\n * changeType: 'upgrade'\n * });\n * ```\n */\nexport class Subscriptions extends FlowGridTransport {\n /**\n * Tracks subscription start.\n * \n * @param config - Subscription start configuration\n * @returns Promise resolving to subscription response\n */\n async start(config: SubscriptionStartConfig): Promise<SubscriptionResponse> {\n return await this.init(\"subscription_start\", {\n type: \"ecommerce\",\n subtype: \"subscription_start\",\n subscriptionId: config.subscriptionId,\n userId: config.userId,\n plan: config.plan,\n mrr: config.mrr,\n billingCycle: config.billingCycle,\n trialDays: config.trialDays,\n coupon: config.coupon,\n isTrialing: (config.trialDays || 0) > 0,\n ...config.properties,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks subscription plan change.\n * \n * @param config - Subscription change configuration\n * @returns Promise resolving to subscription response\n */\n async change(config: SubscriptionChangeConfig): Promise<SubscriptionResponse> {\n const mrrDiff = config.newMrr.amount - config.previousMrr.amount;\n \n return await this.init(\"subscription_change\", {\n type: \"ecommerce\",\n subtype: \"subscription_change\",\n subscriptionId: config.subscriptionId,\n userId: config.userId,\n previousPlan: config.previousPlan,\n newPlan: config.newPlan,\n previousMrr: config.previousMrr,\n newMrr: config.newMrr,\n mrrChange: mrrDiff,\n changeType: config.changeType,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks subscription cancellation.\n * \n * @param config - Cancellation configuration\n * @returns Promise resolving to subscription response\n */\n async cancel(config: SubscriptionCancelConfig): Promise<SubscriptionResponse> {\n return await this.init(\"subscription_cancel\", {\n type: \"ecommerce\",\n subtype: \"subscription_cancel\",\n subscriptionId: config.subscriptionId,\n userId: config.userId,\n plan: config.plan,\n lostMrr: config.lostMrr,\n reason: config.reason,\n feedback: config.feedback,\n immediate: config.immediate,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks subscription renewal.\n * \n * @param subscriptionId - Subscription ID\n * @param userId - User ID\n * @param mrr - Renewed MRR\n * @returns Promise resolving to subscription response\n */\n async renew(\n subscriptionId: string,\n userId: string,\n mrr: MoneyValue\n ): Promise<SubscriptionResponse> {\n return await this.init(\"subscription_renew\", {\n type: \"ecommerce\",\n subtype: \"subscription_renew\",\n subscriptionId,\n userId,\n mrr,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks trial conversion to paid.\n * \n * @param subscriptionId - Subscription ID\n * @param userId - User ID\n * @param plan - Converted to plan\n * @param mrr - New MRR\n * @returns Promise resolving to subscription response\n */\n async trialConverted(\n subscriptionId: string,\n userId: string,\n plan: string,\n mrr: MoneyValue\n ): Promise<SubscriptionResponse> {\n return await this.init(\"trial_converted\", {\n type: \"ecommerce\",\n subtype: \"trial_converted\",\n subscriptionId,\n userId,\n plan,\n mrr,\n timestamp: new Date().toISOString()\n });\n }\n\n /**\n * Tracks trial expiration without conversion.\n * \n * @param subscriptionId - Subscription ID\n * @param userId - User ID\n * @returns Promise resolving to subscription response\n */\n async trialExpired(\n subscriptionId: string,\n userId: string\n ): Promise<SubscriptionResponse> {\n return await this.init(\"trial_expired\", {\n type: \"ecommerce\",\n subtype: \"trial_expired\",\n subscriptionId,\n userId,\n timestamp: new Date().toISOString()\n });\n }\n\n}\n\nexport default Subscriptions;\n","/**\n * Activation shortcuts — one helper per event, positional args, sensible defaults.\n * Underlying class accessible via `.client`.\n */\nimport { Activation } from \"../../features/core/activation\";\n\nexport class ActivationTracker {\n constructor(public readonly client: Activation) {}\n\n /** New user signup. */\n signup(userId: string, method: string, source?: string, props?: Record<string, unknown>) {\n return this.client.trackSignup(userId, method, source, props);\n }\n\n /** Step in an onboarding flow (e.g. `step 2 of 5`). */\n onboardingStep(\n userId: string,\n stepId: string,\n stepName: string,\n stepOrder: number,\n totalSteps: number,\n extra?: { skipped?: boolean; timeSpent?: number; properties?: Record<string, unknown> }\n ) {\n return this.client.trackOnboardingStep({\n userId,\n stepId,\n stepName,\n stepOrder,\n totalSteps,\n ...extra,\n });\n }\n\n /** All onboarding steps finished. */\n onboardingComplete(userId: string, totalTimeSpent: number, stepsCompleted: number, stepsSkipped = 0) {\n return this.client.completeOnboarding(userId, totalTimeSpent, stepsCompleted, stepsSkipped);\n }\n\n /** First meaningful action after signup (\"aha\" moment). */\n firstAction(userId: string, actionName: string, timeSinceSignup: number, properties?: Record<string, unknown>) {\n return this.client.trackFirstAction({ userId, actionName, timeSinceSignup, properties });\n }\n\n /** Generic activation event. */\n event(eventName: string, properties: Record<string, unknown> = {}) {\n return this.client.create({ eventName, properties });\n }\n\n}\n","/**\n * Feature usage shortcuts.\n *\n * Semantic helpers over `track(\"feature_usage\", { featureId, featureName, action })`.\n * Pick the verb that describes what happened — the dashboard funnel maps each\n * action onto a lifecycle stage (a *viewed* feature is Discovered, a *used* one\n * is Adopted). No hidden state, no guessing: you send exactly what you call.\n */\nimport { TrackFeature } from \"../../features/core/track-feature\";\n\nexport type FeatureAction = \"viewed\" | \"used\" | \"completed\" | \"abandoned\";\n\nexport class FeatureTracker {\n constructor(public readonly client: TrackFeature) {}\n\n /** Emit a `feature_usage` event with an explicit action (defaults to \"used\"). */\n record(featureId: string, featureName: string, action: FeatureAction = \"used\", properties?: Record<string, unknown>) {\n return this.client.trackUsage({ featureId, featureName, action, properties });\n }\n\n /** Visitor opened / looked at the feature. Funnel → Discovered + Viewed. */\n viewed(featureId: string, featureName: string, properties?: Record<string, unknown>) {\n return this.record(featureId, featureName, \"viewed\", properties);\n }\n\n /** Visitor used the feature. Funnel → Adopted. */\n used(featureId: string, featureName: string, properties?: Record<string, unknown>) {\n return this.record(featureId, featureName, \"used\", properties);\n }\n\n /** Visitor finished the feature's job successfully. Funnel → Completed. */\n completed(featureId: string, featureName: string, properties?: Record<string, unknown>) {\n return this.record(featureId, featureName, \"completed\", properties);\n }\n\n /** Visitor started but gave up. Funnel → Abandonment. */\n abandoned(featureId: string, featureName: string, properties?: Record<string, unknown>) {\n return this.record(featureId, featureName, \"abandoned\", properties);\n }\n\n /** Create / register a feature event (advanced — see `client.create`). */\n create(userId: string, action: string, featureName: string, properties?: Record<string, unknown>) {\n return this.client.create({ userId, action, properties: { featureName, ...properties } });\n }\n}\n","/**\n * AI prompt tracking shortcuts.\n */\nimport { TrackPrompt, PromptFeedbackConfig } from \"../../features/core/track-prompt\";\n\nexport class PromptTracker {\n constructor(public readonly client: TrackPrompt) {}\n\n /** A user submitted a prompt. */\n submitted(promptId: string, promptType: string, properties?: Record<string, unknown>) {\n return this.client.trackSubmission({ promptId, promptType, properties });\n }\n\n /** The model returned a response. */\n responded(promptId: string, responseTime: number, completed: boolean, properties?: Record<string, unknown>) {\n return this.client.trackResponse({ promptId, responseTime, completed, properties });\n }\n\n /** User feedback on a prompt response (thumbs up/down etc.). */\n feedback(promptId: string, feedbackType: PromptFeedbackConfig[\"feedbackType\"], properties?: Record<string, unknown>) {\n return this.client.trackFeedback({ promptId, feedbackType, properties });\n }\n\n /** Multi-turn conversation summary. */\n conversation(conversationId: string, userId: string, turnCount: number, totalDuration: number) {\n return this.client.trackConversation(conversationId, userId, turnCount, totalDuration);\n }\n\n /** Generic prompt event. */\n event(eventName: string, properties: Record<string, unknown> = {}) {\n return this.client.create({ eventName, properties });\n }\n}\n","/**\n * Experiment tracker — A/B testing API.\n *\n * Covers the full A/B test lifecycle:\n * 1. `define()` — register an experiment (one-time admin setup).\n * 2. `init()` — initialise the client-side engine (browser only).\n * 3. `assign()` / `variant()` — get a user's variant.\n * 4. `exposure()` — log that the user saw the assigned variant.\n * 5. `conversion()` — log a success metric.\n *\n * The browser flow is the simplest:\n * ```ts\n * await fg.experiments.init([{ experimentId, variants: [...] }]);\n * const variant = fg.experiments.variant(experimentId);\n * if (variant === \"B\") renderNewCheckout();\n * await fg.experiments.exposure(experimentId, userId, variant);\n * // …user converts…\n * await fg.experiments.conversion(experimentId, userId, variant, \"purchase\", 1);\n * ```\n */\nimport {\n Experiment,\n ExperimentDefinition,\n ExperimentInitConfig,\n} from \"../../features/core/experiment\";\nimport type { ServerCookieReader } from \"../../utils/identity\";\n\nexport class ExperimentTracker {\n constructor(public readonly client: Experiment) {}\n\n // ---------------------------------------------------------------------------\n // SETUP\n // ---------------------------------------------------------------------------\n\n /**\n * Define an experiment. Run this once during admin setup.\n *\n * @example\n * ```ts\n * await fg.experiments.define({\n * experimentId: \"checkout_v2\",\n * name: \"New checkout flow\",\n * variants: [\n * { id: \"control\", weight: 50 },\n * { id: \"B\", weight: 50 },\n * ],\n * });\n * ```\n */\n define(definition: ExperimentDefinition) {\n return this.client.define(definition);\n }\n\n /**\n * Initialise the client-side engine (browser only).\n *\n * Verifies experiments, performs weighted-random assignment, and\n * persists assignments in `localStorage` so the same visitor always\n * sees the same variant.\n */\n init(experiments: ExperimentInitConfig[]) {\n return this.client.initExperiments(experiments);\n }\n\n /**\n * Zero-config init — fetch this site's running experiments from the server\n * and run them automatically (variant assignment + redirect/DOM changes +\n * exposure). No experiment config needed in code: the dashboard is the source\n * of truth. This is what powers the no-build snippet path.\n *\n * @example\n * ```ts\n * await fg.experiments.initFromServer();\n * ```\n */\n initFromServer() {\n return this.client.initFromServer();\n }\n\n // ---------------------------------------------------------------------------\n // ASSIGNMENT\n // ---------------------------------------------------------------------------\n\n /**\n * Synchronously read the locally-cached variant assignment.\n * Returns `null` if no assignment exists. Browser-only.\n */\n variant(experimentId: string): string | null {\n return this.client.getLocalVariant(experimentId);\n }\n\n /**\n * Explicitly assign (or override) a user's variant. Use this for\n * targeted assignments or admin overrides.\n */\n assign(\n experimentId: string,\n userId: string,\n variantId: string,\n source: \"random\" | \"targeted\" | \"override\" = \"targeted\"\n ) {\n return this.client.assign({ experimentId, userId, variantId, source });\n }\n\n // ---------------------------------------------------------------------------\n // EXPOSURE & CONVERSION\n // ---------------------------------------------------------------------------\n\n /**\n * Record that a user was exposed to (saw / used) their variant.\n * Call this when the variant is rendered.\n */\n exposure(experimentId: string, userId: string, variantId: string) {\n return this.client.trackExposure(experimentId, userId, variantId);\n }\n\n /**\n * Browser-side exposure for the locally-assigned variant — resolves both the\n * variant and the user id for you. Call this right after rendering the\n * variant; no-op if the visitor has no assignment yet.\n *\n * @example\n * ```ts\n * await fg.experiments.init([{ id: \"checkout_v2\", variants: [...] }]);\n * await fg.experiments.exposureAuto(\"checkout_v2\");\n * ```\n */\n exposureAuto(experimentId: string) {\n return this.client.trackLocalExposure(experimentId);\n }\n\n /**\n * Record a conversion against a specific user's variant (server-side).\n *\n * @example\n * ```ts\n * await fg.experiments.conversion(\"checkout_v2\", \"u_1\", \"B\", \"purchase\", 49.99);\n * ```\n */\n conversion(\n experimentId: string,\n userId: string,\n variantId: string,\n metricName: string,\n value: number = 1\n ) {\n return this.client.trackConversion({\n experimentId,\n userId,\n variantId,\n metricName,\n value,\n });\n }\n\n /**\n * Browser-side conversion using the locally-cached variant assignment.\n * No-op if the visitor has no assignment for this experiment.\n *\n * @example\n * ```ts\n * await fg.experiments.localConversion(\"checkout_v2\", \"purchase\", 49.99);\n * ```\n */\n localConversion(experimentId: string, metricName: string = \"conversion\", value: number = 1) {\n return this.client.trackLocalConversion(experimentId, metricName, value);\n }\n\n // ---------------------------------------------------------------------------\n // SSR / COOKIE HYDRATION\n //\n // The underlying engine mirrors assignments to a `fg_exp_assignments`\n // cookie so that server-rendered HTML can pick the right variant before\n // any JS executes. These helpers wire the cookie into common runtimes.\n // ---------------------------------------------------------------------------\n\n /**\n * Hydrate assignments from a server cookie reader (Next.js `cookies()`,\n * Nuxt `useCookie()`, etc.). Call this once per request before reading\n * `variant()` server-side.\n *\n * @example\n * ```ts\n * import { cookies } from \"next/headers\";\n * fg.experiments.hydrateFromCookies(cookies());\n * const variant = fg.experiments.variant(\"checkout_v2\");\n * ```\n */\n hydrateFromCookies(reader: ServerCookieReader) {\n this.client.hydrateFromCookies(reader);\n }\n\n /**\n * Hydrate assignments from a raw `Cookie` header (Workers, Bun, Hono, etc.).\n */\n hydrateFromCookieHeader(cookieHeader: string | null | undefined) {\n this.client.hydrateFromCookieHeader(cookieHeader);\n }\n\n /**\n * Inject assignments resolved server-side. Use when the assignment was\n * computed without a cookie (e.g. logged-in user fetched from DB).\n */\n injectAssignments(assignments: Record<string, string>) {\n this.client.injectAssignments(assignments);\n }\n\n /**\n * Snapshot of all known assignments. Useful for SSR payload hand-off.\n */\n getAssignments(): Record<string, string> {\n return this.client.getAssignments();\n }\n\n /**\n * Build a `Set-Cookie` header value seeding the visitor's browser with\n * the current assignment map.\n *\n * @example\n * ```ts\n * res.headers.set(\"Set-Cookie\", fg.experiments.serializeAssignmentCookie());\n * ```\n */\n serializeAssignmentCookie(): string {\n return this.client.serializeAssignmentCookie();\n }\n\n /** Wipe all locally-cached assignments (cookie + localStorage). */\n clearAssignments() {\n this.client.clearAssignments();\n }\n}\n","/**\n * Page view shortcuts. Browser fields auto-detected when available.\n */\nimport { PageViews } from \"../../features/analytics/page-views\";\n\nfunction browserDefaults() {\n const isBrowser = typeof window !== \"undefined\" && typeof document !== \"undefined\";\n return {\n url: isBrowser ? window.location.href : \"\",\n path: isBrowser ? window.location.pathname : \"\",\n title: isBrowser ? document.title : undefined,\n referrer: isBrowser ? document.referrer : undefined,\n };\n}\n\nexport class PageViewTracker {\n constructor(public readonly client: PageViews) {}\n\n /**\n * Track a page view. With no args, auto-detects from `window`/`document`.\n * Pass a path or full URL to override.\n */\n view(\n pathOrUrl?: string,\n properties?: Record<string, unknown>,\n opts?: { title?: string; referrer?: string; userId?: string; sessionId?: string }\n ) {\n const d = browserDefaults();\n const isFullUrl = pathOrUrl && /^https?:\\/\\//i.test(pathOrUrl);\n const url = isFullUrl ? pathOrUrl! : d.url;\n const path = isFullUrl ? new URL(pathOrUrl!).pathname : (pathOrUrl ?? d.path);\n return this.client.track({\n url,\n path,\n title: opts?.title ?? d.title,\n referrer: opts?.referrer ?? d.referrer,\n userId: opts?.userId,\n sessionId: opts?.sessionId,\n properties,\n });\n }\n\n /** Record scroll depth (0-100). */\n scroll(path: string, scrollDepth: number, timeAtDepth?: number) {\n return this.client.trackScrollDepth({ path, scrollDepth, timeAtDepth });\n }\n\n /** Record time spent on a page (seconds). */\n time(path: string, duration: number) {\n return this.client.trackTimeOnPage({ path, duration });\n }\n\n /** Record page exit. */\n exit(path: string, duration: number, scrollDepth: number) {\n return this.client.trackExit(path, duration, scrollDepth);\n }\n\n /** Auto-track SPA route changes, scroll-depth thresholds, and time-on-page. */\n autoTrack(options?: { userId?: string; sessionId?: string }) {\n return this.client.autoTrack(options);\n }\n}\n","/**\n * Session lifecycle shortcuts.\n */\nimport { Sessions } from \"../../features/analytics/sessions\";\n\nexport class SessionTracker {\n constructor(public readonly client: Sessions) {}\n\n /** Start a new session. */\n start(opts?: Parameters<Sessions[\"start\"]>[0]) {\n return this.client.start(opts);\n }\n\n /** Update an active session with new properties. */\n update(sessionId: string, properties: Record<string, unknown>) {\n return this.client.update(sessionId, properties);\n }\n\n /** End a session. */\n end(sessionId: string) {\n return this.client.end(sessionId);\n }\n\n /** Heartbeat to keep a session alive. */\n heartbeat(sessionId: string) {\n return this.client.heartbeat(sessionId);\n }\n\n /** Auto-manage browser session lifecycle (start, heartbeat, end on pagehide). */\n autoTrack(config?: Parameters<Sessions[\"autoTrack\"]>[0], options?: { heartbeatMs?: number }) {\n return this.client.autoTrack(config, options);\n }\n}\n","/**\n * Custom event shortcuts.\n */\nimport { Events } from \"../../features/analytics/events\";\n\nexport class EventTracker {\n constructor(public readonly client: Events) {}\n\n /** Track a custom event. */\n track(eventName: string, properties?: Record<string, unknown>, opts?: { userId?: string; sessionId?: string }) {\n return this.client.track({ eventName, properties, userId: opts?.userId, sessionId: opts?.sessionId });\n }\n\n /** Track many events in one request. */\n batch(events: Array<{ eventName: string; properties?: Record<string, unknown> }>) {\n return this.client.trackBatch({ events });\n }\n\n /** Track a click on a tagged element. */\n click(elementId: string, elementText: string, properties?: Record<string, unknown>) {\n return this.client.trackClick({ elementId, elementText, properties });\n }\n\n /** Track a form interaction. */\n form(formId: string, formName: string, properties?: Record<string, unknown>) {\n return this.client.trackForm({ formId, formName, properties });\n }\n\n /** Track a search query. */\n search(query: string, category: string, properties?: Record<string, unknown>) {\n return this.client.trackSearch({ query, category, properties });\n }\n\n /** Track a runtime error. */\n error(message: string, type: string, properties?: Record<string, unknown>) {\n return this.client.trackError({ message, type, properties });\n }\n}\n","/**\n * User identification shortcuts.\n */\nimport { Identify } from \"../../features/analytics/identify\";\n\nexport class IdentifyTracker {\n constructor(public readonly client: Identify) {}\n\n /** Identify a user with traits. */\n user(userId: string, traits?: Record<string, unknown>, anonymousId?: string) {\n return this.client.user({ userId, anonymousId, traits: traits });\n }\n\n /** Update traits on an existing user. */\n updateTraits(userId: string, traits: Record<string, unknown>) {\n return this.client.updateTraits(userId, traits);\n }\n\n /** Alias an anonymous ID to a known user ID. */\n alias(previousId: string, userId: string) {\n return this.client.alias({ previousId, userId });\n }\n\n /** Associate a user with a group/company. */\n group(userId: string, groupId: string, traits?: Record<string, unknown>) {\n return this.client.group({ userId, groupId, traits: traits });\n }\n}\n","/**\n * Funnel shortcuts.\n */\nimport { Funnels } from \"../../features/analytics/funnels\";\n\nexport class FunnelTracker {\n constructor(public readonly client: Funnels) {}\n\n /** Define a new funnel (admin / setup). */\n define(definition: Parameters<Funnels[\"define\"]>[0]) {\n return this.client.define(definition);\n }\n\n /** Record a step completion in a funnel. */\n step(funnelId: string, step: string, stepNumber: number, userId?: string, properties?: Record<string, unknown>) {\n return this.client.trackStep({ funnelId, step, stepNumber, userId, properties });\n }\n\n /** Mark a funnel as completed. */\n complete(funnelId: string, userId?: string, properties?: Record<string, unknown>) {\n return this.client.trackComplete(funnelId, userId, properties);\n }\n\n /** Mark a funnel as abandoned. */\n abandon(funnelId: string, lastStep: string, userId?: string, reason?: string) {\n return this.client.trackAbandon(funnelId, lastStep, userId, reason);\n }\n}\n","/**\n * Retention shortcuts.\n */\nimport { Retention } from \"../../features/analytics/retention\";\n\nexport class RetentionTracker {\n constructor(public readonly client: Retention) {}\n\n /** Record that a user was active. */\n active(userId: string, properties?: Record<string, unknown>) {\n return this.client.trackActivity({ userId, properties });\n }\n}\n","/**\n * Multi-touch attribution shortcuts.\n */\nimport { Attribution, ConversionEvent } from \"../../features/analytics/attribution\";\n\nexport class AttributionTracker {\n constructor(public readonly client: Attribution) {}\n\n /** Record a marketing touchpoint. */\n touchpoint(userId: string, channel: string, properties?: Record<string, unknown>) {\n return this.client.trackTouchpoint({ userId, channel, properties });\n }\n\n /** Record a conversion that should be attributed. */\n conversion(userId: string, conversionType: ConversionEvent[\"conversionType\"], properties?: Record<string, unknown>) {\n return this.client.recordConversion({ userId, conversionType, properties });\n }\n\n /** Auto-capture a touchpoint from `utm_*` params + `document.referrer`. */\n captureFromUrl(userId: string) {\n return this.client.captureFromUrl(userId);\n }\n}\n","/**\n * Heatmap shortcuts.\n */\nimport { Heatmaps } from \"../../features/analytics/heatmaps\";\n\nexport class HeatmapTracker {\n constructor(public readonly client: Heatmaps) {}\n\n /** Record a click point. */\n click(pageUrl: string, x: number, y: number, viewportWidth: number, viewportHeight: number) {\n return this.client.trackClick({ pageUrl, x, y, viewportWidth, viewportHeight });\n }\n\n /** Record a mouse-movement path. */\n movement(pageUrl: string, path: Array<{ x: number; y: number; t: number }>, viewport: { width: number; height: number }) {\n return this.client.trackMovement({ pageUrl, path, viewport });\n }\n\n /** Record scroll behaviour. */\n scroll(pageUrl: string, maxScrollDepth: number, pageHeight: number, viewportHeight: number) {\n return this.client.trackScroll({ pageUrl, maxScrollDepth, pageHeight, viewportHeight });\n }\n\n /** Auto-attach click + scroll listeners (and optionally mousemove). */\n autoTrack(options?: { movement?: boolean; userId?: string; sessionId?: string }) {\n return this.client.autoTrack(options);\n }\n}\n","/**\n * Performance shortcuts.\n */\nimport { PerformanceTracking } from \"../../features/analytics/performance\";\n\ntype ResourceType = \"script\" | \"stylesheet\" | \"image\" | \"font\" | \"xhr\" | \"fetch\" | \"other\";\n\nexport class PerformanceTracker {\n constructor(public readonly client: PerformanceTracking) {}\n\n /** Report Core Web Vitals (LCP/FID/CLS/INP/TTFB). */\n webVitals(pageUrl: string, vitals: Record<string, number>) {\n return this.client.trackWebVitals({ pageUrl, ...vitals });\n }\n\n /** Report page-load timing. */\n pageLoad(pageUrl: string, timings: Record<string, number>) {\n return this.client.trackPageLoad({ pageUrl, ...timings });\n }\n\n /** Report a single resource load. */\n resource(pageUrl: string, resourceUrl: string, resourceType: ResourceType, duration: number) {\n return this.client.trackResource({ pageUrl, resourceUrl, resourceType, duration });\n }\n\n /** Auto-collect Core Web Vitals + navigation timing from the browser. */\n autoTrack(options?: { userId?: string; resources?: boolean; minDuration?: number }) {\n return this.client.autoTrack(options);\n }\n}\n","/**\n * Session Replay shortcut wrapper.\n */\nimport {\n SessionReplay,\n type SessionReplayFlushMetadata,\n type SessionReplayFlushReason,\n type SessionReplayOptions,\n type SessionReplayStatus,\n} from \"../../features/analytics/replay\";\nimport type { Teardown } from \"../../utils/browser\";\n\nexport class ReplayTracker {\n constructor(public readonly client: SessionReplay) {}\n\n /**\n * Start recording the current page. Returns a teardown that stops capture\n * and flushes the final buffer.\n *\n * @example\n * ```ts\n * const stop = await fg.replay.start({ sampleRate: 1.0, maskAllInputs: true });\n * ```\n */\n start(options?: SessionReplayOptions): Promise<Teardown> {\n return this.client.start(options ?? {});\n }\n\n /** Enable replay and start recording immediately. */\n enable(options?: SessionReplayOptions): Promise<Teardown> {\n return this.client.enable(options ?? {});\n }\n\n /** Disable replay and stop any active recorder. */\n disable(): void {\n this.client.disable();\n }\n\n /** Stop the active recorder and flush a final chunk. */\n stop(reason?: SessionReplayFlushReason): void {\n this.client.stop(reason);\n }\n\n /** Flush buffered events without stopping recording. */\n flush(reason?: SessionReplayFlushReason): Promise<void> {\n return this.client.flush(reason ?? \"manual\");\n }\n\n /** Bind a known user ID to subsequent replay chunks. */\n identify(userId: string): void {\n this.client.identify(userId);\n }\n\n /** Read recorder lifecycle and health state. */\n status(): SessionReplayStatus {\n return this.client.getStatus();\n }\n\n /** Send a pre-recorded rrweb chunk for custom recorder integrations. */\n sendChunk(\n sessionId: string,\n events: unknown[],\n options?: Parameters<SessionReplay[\"sendChunk\"]>[2],\n ): Promise<void> {\n return this.client.sendChunk(sessionId, events, options ?? {});\n }\n}\n\nexport type { SessionReplayFlushMetadata, SessionReplayFlushReason, SessionReplayOptions, SessionReplayStatus };\n","/**\n * Product shortcuts.\n */\nimport { Products } from \"../../features/ecommerce/products\";\nimport type { Product } from \"../../types\";\n\nexport class ProductTracker {\n constructor(public readonly client: Products) {}\n\n /** Track a product detail view. */\n view(product: Pick<Product, \"productId\" | \"name\" | \"price\" | \"currency\"> & Partial<Product>, properties?: Record<string, unknown>) {\n return this.client.view({ ...product, properties });\n }\n\n /** Track a product list impression. */\n impression(listId: string, listName: string, products: Product[]) {\n return this.client.impression({ list: { listId, listName, products } });\n }\n\n /** Track a click on a product in a list. */\n click(product: Product, listId: string, position: number) {\n return this.client.click({ product, listId, position });\n }\n}\n","/**\n * Cart shortcuts.\n */\nimport { CartTracking } from \"../../features/ecommerce/cart\";\nimport type { Product, Cart } from \"../../types\";\n\nexport class CartTracker {\n constructor(public readonly client: CartTracking) {}\n\n /** Add a product to cart. */\n add(product: Product, quantity = 1, cartId?: string) {\n return this.client.addItem({ product, quantity, cartId });\n }\n\n /** Remove a product from cart. */\n remove(product: Product, quantity = 1, cartId?: string) {\n return this.client.removeItem({ product, quantity, cartId });\n }\n\n /** Change the quantity of a cart item. */\n update(product: Product, previousQuantity: number, newQuantity: number, cartId?: string) {\n return this.client.updateItem({ product, previousQuantity, newQuantity, cartId });\n }\n\n /** Track a cart view. */\n view(cart: Cart) {\n return this.client.view({ cart });\n }\n}\n","/**\n * Checkout shortcuts.\n */\nimport { Checkout } from \"../../features/ecommerce/checkout\";\nimport type { Cart, ShippingInfo, PaymentInfo, CheckoutStep } from \"../../types\";\n\nexport class CheckoutTracker {\n constructor(public readonly client: Checkout) {}\n\n /** Begin checkout for the current cart. */\n begin(cart: Cart) {\n return this.client.begin({ cart });\n }\n\n /** Move to a checkout step. */\n step(step: CheckoutStep, stepNumber: number, cart: Cart) {\n return this.client.step({ step, stepNumber, cart });\n }\n\n /** Add shipping info. */\n shipping(cart: Cart, shipping: ShippingInfo) {\n return this.client.addShipping({ cart, shipping });\n }\n\n /** Add payment info. */\n payment(cart: Cart, payment: PaymentInfo) {\n return this.client.addPayment({ cart, payment });\n }\n}\n","/**\n * Purchase shortcuts.\n */\nimport { Purchases } from \"../../features/ecommerce/purchases\";\nimport type { Order } from \"../../types\";\n\nexport class PurchaseTracker {\n constructor(public readonly client: Purchases) {}\n\n /** Track a completed purchase. */\n complete(order: Order) {\n return this.client.track({ order });\n }\n}\n","/**\n * Refund shortcuts.\n */\nimport { Refunds } from \"../../features/ecommerce/refunds\";\nimport type { CartItem } from \"../../types\";\n\nexport class RefundTracker {\n constructor(public readonly client: Refunds) {}\n\n /** Customer requested a refund. */\n request(orderId: string, items?: CartItem[], reason?: string) {\n return this.client.request(orderId, items, reason);\n }\n\n /** Refund approved. */\n approve(refundId: string, approvedAmount: number) {\n return this.client.approve(refundId, approvedAmount);\n }\n\n /** Refund denied. */\n deny(refundId: string, reason: string) {\n return this.client.deny(refundId, reason);\n }\n\n /** Track a completed refund (partial or full). */\n complete(\n orderId: string,\n refundId: string,\n amount: number,\n currency: string,\n reason: string,\n isFullRefund = false\n ) {\n return this.client.track({ orderId, refundId, amount, currency, reason, isFullRefund });\n }\n}\n","/**\n * Promotion shortcuts.\n */\nimport { Promotions } from \"../../features/ecommerce/promotions\";\n\nexport class PromotionTracker {\n constructor(public readonly client: Promotions) {}\n\n /** A user saw a promotion. */\n view(promotionId: string, name: string, creative: string, position: string) {\n return this.client.view({ promotionId, name, creative, position });\n }\n\n /** A user clicked a promotion. */\n click(promotionId: string, name: string, creative: string, position: string) {\n return this.client.click({ promotionId, name, creative, position });\n }\n\n /** A coupon was applied. */\n coupon(\n code: string,\n orderId: string,\n discountValue: number,\n discountType: \"percentage\" | \"fixed\",\n orderValueBefore: number,\n orderValueAfter: number\n ) {\n return this.client.couponUsed({\n code,\n orderId,\n discountValue,\n discountType,\n orderValueBefore,\n orderValueAfter,\n });\n }\n}\n","/**\n * Wishlist shortcuts.\n */\nimport { WishlistTracking } from \"../../features/ecommerce/wishlist\";\nimport type { Product, Wishlist } from \"../../types\";\n\nexport class WishlistTracker {\n constructor(public readonly client: WishlistTracking) {}\n\n /** Add a product to wishlist. */\n add(product: Product, wishlistId?: string) {\n return this.client.add({ product, wishlistId });\n }\n\n /** Remove a product from wishlist. */\n remove(product: Product, wishlistId?: string) {\n return this.client.remove({ product, wishlistId });\n }\n\n /** Track a wishlist view. */\n view(wishlist: Wishlist) {\n return this.client.view(wishlist);\n }\n\n /** Move a wishlist item into the cart. */\n moveToCart(product: Product, wishlistId?: string) {\n return this.client.moveToCart(product, wishlistId);\n }\n\n /** Wishlist was shared. */\n share(wishlistId: string, method: \"email\" | \"link\" | \"social\") {\n return this.client.share(wishlistId, method);\n }\n}\n","/**\n * Customer LTV shortcuts.\n */\nimport { CustomerLTV } from \"../../features/ecommerce/ltv\";\nimport type { MoneyValue } from \"../../types\";\n\ntype LTVSegment = \"low\" | \"medium\" | \"high\" | \"vip\";\n\nexport class LTVTracker {\n constructor(public readonly client: CustomerLTV) {}\n\n /** Record a customer acquisition cost (CAC). */\n acquisitionCost(userId: string, cost: MoneyValue, channel: string) {\n return this.client.recordAcquisitionCost({ userId, cost, channel });\n }\n\n /** Move a user into a different LTV segment. */\n segment(userId: string, newSegment: LTVSegment) {\n return this.client.updateSegment(userId, newSegment);\n }\n}\n","/**\n * Site search shortcuts.\n */\nimport { SearchAnalytics } from \"../../features/ecommerce/search\";\nimport type { Product } from \"../../types\";\n\ntype RefinementType = \"query_change\" | \"add_filter\" | \"remove_filter\" | \"sort_change\";\n\nexport class SearchTracker {\n constructor(public readonly client: SearchAnalytics) {}\n\n /** A user searched. */\n query(query: string, resultsCount: number, extra?: { category?: string; filters?: Record<string, string | string[]>; userId?: string; sessionId?: string }) {\n return this.client.track({ query, resultsCount, ...extra });\n }\n\n /** A user clicked a search result. */\n click(query: string, product: Product, position: number) {\n return this.client.trackClick({ query, product, position });\n }\n\n /** A user refined an existing search. */\n refine(originalQuery: string, refinedQuery: string, refinementType: RefinementType) {\n return this.client.trackRefinement(originalQuery, refinedQuery, refinementType);\n }\n\n /** Autocomplete suggestion accepted. */\n suggestion(partialQuery: string, selectedSuggestion: string, suggestionPosition: number) {\n return this.client.trackSuggestion(partialQuery, selectedSuggestion, suggestionPosition);\n }\n}\n","/**\n * Subscription shortcuts.\n */\nimport { Subscriptions } from \"../../features/ecommerce/subscriptions\";\nimport type { MoneyValue } from \"../../types\";\n\nexport class SubscriptionTracker {\n constructor(public readonly client: Subscriptions) {}\n\n /** New subscription started. */\n start(\n subscriptionId: string,\n userId: string,\n plan: string,\n mrr: MoneyValue,\n billingCycle: \"monthly\" | \"quarterly\" | \"yearly\"\n ) {\n return this.client.start({ subscriptionId, userId, plan, mrr, billingCycle });\n }\n\n /** Plan changed (upgrade / downgrade). */\n change(\n subscriptionId: string,\n userId: string,\n previousPlan: string,\n newPlan: string,\n previousMrr: MoneyValue,\n newMrr: MoneyValue,\n changeType: \"upgrade\" | \"downgrade\" | \"cross_grade\"\n ) {\n return this.client.change({\n subscriptionId,\n userId,\n previousPlan,\n newPlan,\n previousMrr,\n newMrr,\n changeType,\n });\n }\n\n /** Subscription cancelled. */\n cancel(subscriptionId: string, userId: string, plan: string, lostMrr: MoneyValue, immediate = false) {\n return this.client.cancel({ subscriptionId, userId, plan, lostMrr, immediate });\n }\n\n /** Subscription renewed. */\n renew(subscriptionId: string, userId: string, mrr: MoneyValue) {\n return this.client.renew(subscriptionId, userId, mrr);\n }\n\n /** Trial converted to paid. */\n trialConverted(\n subscriptionId: string,\n userId: string,\n plan: string,\n mrr: MoneyValue\n ) {\n return this.client.trialConverted(subscriptionId, userId, plan, mrr);\n }\n\n /** Trial expired without converting. */\n trialExpired(subscriptionId: string, userId: string) {\n return this.client.trialExpired(subscriptionId, userId);\n }\n}\n","/**\n * @fileoverview Unified FlowGrid SDK facade.\n * @module flowgrid\n *\n * @description\n * A single class that aggregates every feature module so consumers can\n * instantiate the SDK once and access all tracking domains from one object.\n *\n * Every namespace returns an ergonomic *shortcut* wrapper exposing\n * positional-arg methods for the 80% case. The raw underlying class is\n * always available on `.client` for advanced configuration.\n *\n * @example\n * ```ts\n * import { FlowGrid } from \"flowgrid-sdk\";\n *\n * const fg = new FlowGrid(\"web_123\", \"https://core.flow-grid.xyz\", \"sk_live_xxx\");\n *\n * // Top-level shortcuts (the 80% case)\n * fg.track(\"signup_completed\", { plan: \"pro\" });\n * fg.page();\n * fg.identifyUser(\"u_1\", { email: \"alice@example.com\" });\n *\n * // Per-feature shortcuts\n * fg.cart.add({ productId: \"sku_1\", name: \"Tee\", price: 29, currency: \"USD\" });\n * fg.experiments.assign(\"checkout_v2\", \"u_1\");\n *\n * // Power-user escape hatch — raw class with full config surface\n * fg.events.client.track({ eventName: \"x\", sessionId: \"...\", category: \"...\" });\n * ```\n */\n\nimport { FlowGridTransport, FlowGridTransportConfig } from \"./client/transport\";\nimport { IdentityManager } from \"./utils/identity\";\n\nimport { Activation, TrackFeature, TrackPrompt, Experiment } from \"./features/core\";\nimport {\n PageViews,\n Sessions,\n Events,\n Identify,\n Funnels,\n Retention,\n Attribution,\n Heatmaps,\n PerformanceTracking,\n SessionReplay,\n} from \"./features/analytics\";\nimport {\n Products,\n CartTracking,\n Checkout,\n Purchases,\n Refunds,\n Promotions,\n WishlistTracking,\n CustomerLTV,\n SearchAnalytics,\n Subscriptions,\n} from \"./features/ecommerce\";\n// Enterprise feature + tracker classes are dynamically imported at runtime so\n// webpack can split them into a separate async chunk. The type-only imports\n// below give TypeScript full type information without adding any runtime bytes\n// to the initial bundle.\nimport type {\n Engagement,\n ChurnAnalytics,\n Monetization,\n MultiPathFunnels,\n SupportAnalytics,\n AcquisitionAnalytics,\n PathAnalytics,\n SecurityAnalytics,\n} from \"./features/enterprise\";\nimport type * as _EnterpriseFeaturesNS from \"./features/enterprise\";\n\nimport type {\n EngagementTracker,\n CohortTracker,\n ChurnTracker,\n MonetizationTracker,\n MultiPathFunnelTracker,\n SupportTracker,\n AcquisitionTracker,\n PathTracker,\n AlertTracker,\n SecurityTracker,\n ForecastingTracker,\n} from \"./tracking/enterprise\";\nimport type * as _EnterpriseTrackingNS from \"./tracking/enterprise\";\n\n// Import directly from sub-barrels (not the top-level ./tracking barrel) so\n// the static module graph never reaches ./tracking/enterprise and its\n// dependencies can be tree-shaken / placed only in the async chunk.\nimport { ActivationTracker, ExperimentTracker, FeatureTracker, PromptTracker } from \"./tracking/core\";\nimport {\n EventTracker, PageViewTracker, SessionTracker, IdentifyTracker,\n FunnelTracker, RetentionTracker, AttributionTracker,\n HeatmapTracker, PerformanceTracker, ReplayTracker,\n} from \"./tracking/analytics\";\nimport {\n ProductTracker, CartTracker, CheckoutTracker, PurchaseTracker,\n RefundTracker, PromotionTracker, WishlistTracker,\n SubscriptionTracker, LTVTracker, SearchTracker,\n} from \"./tracking/ecommerce\";\n\n// ---- Enterprise lazy-load state (module-level singleton) ----\nlet _enterpriseFeatures: typeof _EnterpriseFeaturesNS | null = null;\nlet _enterpriseTracking: typeof _EnterpriseTrackingNS | null = null;\n\nfunction loadEnterpriseModules(): Promise<void> {\n if (_enterpriseFeatures && _enterpriseTracking) return Promise.resolve();\n return Promise.all([\n import(/* webpackChunkName: \"flowgrid-enterprise\" */ \"./features/enterprise\"),\n import(/* webpackChunkName: \"flowgrid-enterprise\" */ \"./tracking/enterprise\"),\n ]).then(([features, tracking]) => {\n _enterpriseFeatures = features as typeof _EnterpriseFeaturesNS;\n _enterpriseTracking = tracking as typeof _EnterpriseTrackingNS;\n });\n}\n\ntype Ctor<T> = new (\n webId: string,\n endpoint: string,\n apiKey: string,\n visitorId?: string,\n identityManager?: IdentityManager,\n config?: FlowGridTransportConfig\n) => T;\n\nexport type FlowGridTrackDomain =\n | \"event\"\n | \"activation\"\n | \"feature\"\n | \"prompt\"\n | \"experiment\"\n | \"ecommerce\"\n | \"analytics\"\n | \"retention\"\n | \"attribution\"\n | \"funnel\"\n | \"engagement\"\n | \"churn\"\n | \"monetization\"\n | \"support\"\n | \"acquisition\"\n | \"paths\"\n | \"security\"\n | \"multiPathFunnels\";\n\nexport interface FlowGridTrackOptions {\n userId?: string;\n sessionId?: string;\n /** Force a specific module when an event name is intentionally generic. */\n domain?: FlowGridTrackDomain;\n}\n\n/**\n * Property shapes for every event name the `.track()` router understands.\n * Required fields are enforced at the call site; extra fields are always\n * permitted via the `Record<string, unknown>` intersection.\n */\nexport interface FlowGridEventMap {\n // ---- Activation ----\n signup_completed: { userId: string; method: string; source?: string } & Record<string, unknown>;\n onboarding_step: { userId: string; stepId: string; stepName: string; stepOrder: number; totalSteps: number } & Record<string, unknown>;\n onboarding_completed: { userId: string; totalTimeSpent: number; stepsCompleted: number; stepsSkipped?: number } & Record<string, unknown>;\n first_action: { userId: string; actionName: string; timeSinceSignup: number } & Record<string, unknown>;\n // ---- Feature ----\n feature_usage: { featureId: string; featureName: string; action?: string } & Record<string, unknown>;\n feature_viewed: { featureId: string; featureName: string } & Record<string, unknown>;\n feature_used: { featureId: string; featureName: string } & Record<string, unknown>;\n feature_completed: { featureId: string; featureName: string } & Record<string, unknown>;\n feature_abandoned: { featureId: string; featureName: string } & Record<string, unknown>;\n // ---- Prompt ----\n prompt_submitted: { promptId: string; promptType: string } & Record<string, unknown>;\n prompt_response: { promptId: string; responseTime: number; completed: boolean } & Record<string, unknown>;\n prompt_feedback: { promptId: string; feedbackType: string } & Record<string, unknown>;\n prompt_conversation: { conversationId: string; userId: string; turnCount: number; totalDuration: number } & Record<string, unknown>;\n // ---- Experiment ----\n experiment_assignment: { experimentId: string; userId: string; variantId: string } & Record<string, unknown>;\n experiment_exposure: { experimentId: string; userId: string; variantId: string } & Record<string, unknown>;\n experiment_conversion: { experimentId: string; userId: string; variantId: string; metricName: string } & Record<string, unknown>;\n // ---- Analytics ----\n click_event: { elementId: string; elementText: string } & Record<string, unknown>;\n form_event: { formId: string; formName: string } & Record<string, unknown>;\n search_event: { query: string; category: string } & Record<string, unknown>;\n error_event: { message: string; type: string } & Record<string, unknown>;\n funnel_step: { funnelId: string; step: string; stepNumber: number } & Record<string, unknown>;\n funnel_complete: { funnelId: string } & Record<string, unknown>;\n funnel_abandon: { funnelId: string; lastStep: string } & Record<string, unknown>;\n user_activity: { userId: string } & Record<string, unknown>;\n attribution_touchpoint: { userId: string; channel: string } & Record<string, unknown>;\n attribution_conversion: { userId: string; conversionType: string } & Record<string, unknown>;\n // ---- Ecommerce ----\n product_view: { productId: string; name: string; price: number; currency: string } & Record<string, unknown>;\n product_impression: { list: Record<string, unknown> } & Record<string, unknown>;\n product_click: { productId: string; name: string; price: number; currency: string; listId: string; position: number } & Record<string, unknown>;\n add_to_cart: { productId: string; name: string; price: number; currency: string; quantity: number } & Record<string, unknown>;\n remove_from_cart: { productId: string; name: string; price: number; currency: string } & Record<string, unknown>;\n update_cart_item: { product: Record<string, unknown>; previousQuantity: number; newQuantity: number } & Record<string, unknown>;\n view_cart: { cart: Record<string, unknown> } & Record<string, unknown>;\n begin_checkout: { cart: Record<string, unknown> } & Record<string, unknown>;\n checkout_step: { step: string; stepNumber: number; cart: Record<string, unknown> } & Record<string, unknown>;\n add_shipping_info: { cart: Record<string, unknown>; shipping: Record<string, unknown> } & Record<string, unknown>;\n add_payment_info: { cart: Record<string, unknown>; payment: Record<string, unknown> } & Record<string, unknown>;\n purchase: { order: Record<string, unknown> } & Record<string, unknown>;\n refund: { orderId: string; refundId: string; amount: number; currency: string; isFullRefund: boolean } & Record<string, unknown>;\n refund_requested: { orderId: string } & Record<string, unknown>;\n refund_approved: { refundId: string; approvedAmount: number } & Record<string, unknown>;\n refund_denied: { refundId: string; reason: string } & Record<string, unknown>;\n refund_completed: { refundId: string; processedAmount: number } & Record<string, unknown>;\n promotion_view: { promotionId: string; name: string; creative: string; position: number } & Record<string, unknown>;\n promotion_click: { promotionId: string; name: string; creative: string; position: number } & Record<string, unknown>;\n coupon_used: { code: string; orderId: string; discountValue: number; discountType: string; orderValueBefore: number; orderValueAfter: number } & Record<string, unknown>;\n add_to_wishlist: { productId: string; name: string; price: number; currency: string } & Record<string, unknown>;\n remove_from_wishlist: { productId: string; name: string; price: number; currency: string } & Record<string, unknown>;\n view_wishlist: { wishlist: Record<string, unknown> } & Record<string, unknown>;\n wishlist_to_cart: { productId: string; name: string; price: number; currency: string } & Record<string, unknown>;\n share_wishlist: { wishlistId: string; method: string } & Record<string, unknown>;\n subscription_start: { subscriptionId: string; userId: string; plan: string; mrr: Record<string, unknown>; billingCycle: string } & Record<string, unknown>;\n subscription_change: { subscriptionId: string; userId: string; previousPlan: string; newPlan: string; previousMrr: Record<string, unknown>; newMrr: Record<string, unknown>; changeType: string } & Record<string, unknown>;\n subscription_cancel: { subscriptionId: string; userId: string; plan: string; lostMrr: Record<string, unknown>; immediate: boolean } & Record<string, unknown>;\n subscription_renew: { subscriptionId: string; userId: string; mrr: Record<string, unknown> } & Record<string, unknown>;\n trial_converted: { subscriptionId: string; userId: string; plan: string; mrr: Record<string, unknown> } & Record<string, unknown>;\n trial_expired: { subscriptionId: string; userId: string } & Record<string, unknown>;\n record_acquisition_cost: { userId: string; cost: Record<string, unknown>; channel: string } & Record<string, unknown>;\n update_customer_segment: { userId: string; newSegment: string } & Record<string, unknown>;\n site_search: { query: string; resultsCount: number } & Record<string, unknown>;\n search_result_click: { query: string; position: number; productId: string; name: string; price: number; currency: string } & Record<string, unknown>;\n search_refinement: { originalQuery: string; refinedQuery: string; refinementType: string } & Record<string, unknown>;\n search_suggestion: { partialQuery: string; selectedSuggestion: string; suggestionPosition: number } & Record<string, unknown>;\n // ---- Enterprise: Engagement ----\n \"engagement.dwell_time\": { featureId: string; dwellTimeMs: number } & Record<string, unknown>;\n \"engagement_dwell_time\": { featureId: string; dwellTimeMs: number } & Record<string, unknown>;\n \"engagement.session_depth\": { sessionId: string; pagesVisited: number; featuresUsed: number; actionsTaken: number; durationMs: number } & Record<string, unknown>;\n \"engagement_session_depth\": { sessionId: string; pagesVisited: number; featuresUsed: number; actionsTaken: number; durationMs: number } & Record<string, unknown>;\n \"engagement.content_consumption\": { contentId: string; contentType: string; percentageConsumed: number; timeSpentMs: number } & Record<string, unknown>;\n \"engagement_content_consumption\": { contentId: string; contentType: string; percentageConsumed: number; timeSpentMs: number } & Record<string, unknown>;\n \"engagement.user_activity\": { userId: string; activityType: string; durationMs: number } & Record<string, unknown>;\n \"engagement_user_activity\": { userId: string; activityType: string; durationMs: number } & Record<string, unknown>;\n // ---- Enterprise: Churn ----\n \"churn.usage_decay\": { userId: string; metric: string; periods: unknown } & Record<string, unknown>;\n \"churn_usage_decay\": { userId: string; metric: string; periods: unknown } & Record<string, unknown>;\n \"churn.nps\": { userId: string; score: number } & Record<string, unknown>;\n \"churn_nps\": { userId: string; score: number } & Record<string, unknown>;\n \"churn.churn_event\": { userId: string; churnType: string } & Record<string, unknown>;\n \"churn_churn_event\": { userId: string; churnType: string } & Record<string, unknown>;\n \"churn.prevention_action\": { userId: string; actionType: string } & Record<string, unknown>;\n \"churn_prevention_action\": { userId: string; actionType: string } & Record<string, unknown>;\n // ---- Enterprise: Monetization ----\n \"monetization.revenue\": { userId: string; amount: number; currency: string; type: string } & Record<string, unknown>;\n \"monetization_revenue\": { userId: string; amount: number; currency: string; type: string } & Record<string, unknown>;\n // ---- Enterprise: Support ----\n \"support.ticket_created\": { ticketId: string; userId: string; subject: string; category: string; priority: string; channel: string } & Record<string, unknown>;\n \"support_ticket_created\": { ticketId: string; userId: string; subject: string; category: string; priority: string; channel: string } & Record<string, unknown>;\n \"support.ticket_updated\": { ticketId: string; status: string } & Record<string, unknown>;\n \"support_ticket_updated\": { ticketId: string; status: string } & Record<string, unknown>;\n \"support.csat\": { ticketId: string; userId: string; score: number } & Record<string, unknown>;\n \"support_csat\": { ticketId: string; userId: string; score: number } & Record<string, unknown>;\n \"support.feature_complaint\": { userId: string; featureId: string; complaintType: string; severity: string } & Record<string, unknown>;\n \"support_feature_complaint\": { userId: string; featureId: string; complaintType: string; severity: string } & Record<string, unknown>;\n // ---- Enterprise: Acquisition ----\n \"acquisition.acquired\": { userId: string; source: string } & Record<string, unknown>;\n \"acquisition_acquired\": { userId: string; source: string } & Record<string, unknown>;\n \"acquisition.lifecycle_transition\": { userId: string; fromStage: string; toStage: string } & Record<string, unknown>;\n \"acquisition_lifecycle_transition\": { userId: string; fromStage: string; toStage: string } & Record<string, unknown>;\n \"acquisition.campaign_spend\": { campaignId: string; amount: Record<string, unknown>; date: string } & Record<string, unknown>;\n \"acquisition_campaign_spend\": { campaignId: string; amount: Record<string, unknown>; date: string } & Record<string, unknown>;\n // ---- Enterprise: Paths ----\n \"paths.transition\": { userId: string; fromScreen: string; toScreen: string } & Record<string, unknown>;\n \"paths_transition\": { userId: string; fromScreen: string; toScreen: string } & Record<string, unknown>;\n \"paths.feature_sequence\": { userId: string; feature: string; order: number } & Record<string, unknown>;\n \"paths_feature_sequence\": { userId: string; feature: string; order: number } & Record<string, unknown>;\n // ---- Enterprise: Security ----\n \"security.login_attempt\": { userId: string; success: boolean } & Record<string, unknown>;\n \"security_login_attempt\": { userId: string; success: boolean } & Record<string, unknown>;\n \"security.role_change\": { userId: string; changedBy: string; previousRole: string; newRole: string } & Record<string, unknown>;\n \"security_role_change\": { userId: string; changedBy: string; previousRole: string; newRole: string } & Record<string, unknown>;\n \"security.permission_change\": { userId: string; changedBy: string; permission: string; action: string } & Record<string, unknown>;\n \"security_permission_change\": { userId: string; changedBy: string; permission: string; action: string } & Record<string, unknown>;\n \"security.data_access\": { userId: string; resourceType: string; accessType: string } & Record<string, unknown>;\n \"security_data_access\": { userId: string; resourceType: string; accessType: string } & Record<string, unknown>;\n \"security.compliance_event\": { userId: string; eventType: string } & Record<string, unknown>;\n \"security_compliance_event\": { userId: string; eventType: string } & Record<string, unknown>;\n // ---- Enterprise: Multi-path Funnels ----\n \"multi_path_funnels.track_step\": { funnelId: string; step: string; userId: string } & Record<string, unknown>;\n \"multi_path_funnels_track_step\": { funnelId: string; step: string; userId: string } & Record<string, unknown>;\n \"multi_path_funnels.track_micro_conversion\": { conversionId: string; userId: string } & Record<string, unknown>;\n \"multi_path_funnels_track_micro_conversion\": { conversionId: string; userId: string } & Record<string, unknown>;\n}\n\n/** Union of every event name the `.track()` router recognises. */\nexport type FlowGridEventName = keyof FlowGridEventMap;\n\ntype FlowGridKnownTrackFields = Partial<{\n userId: string;\n sessionId: string;\n method: string;\n source: string;\n featureId: string;\n featureName: string;\n action: string;\n promptId: string;\n promptType: string;\n responseTime: number;\n completed: boolean;\n feedbackType: string;\n conversationId: string;\n turnCount: number;\n totalDuration: number;\n experimentId: string;\n variantId: string;\n variant: string;\n metricName: string;\n value: number;\n elementId: string;\n elementText: string;\n formId: string;\n formName: string;\n query: string;\n category: string;\n message: string;\n type: string;\n funnelId: string;\n step: string;\n stepNumber: number;\n lastStep: string;\n channel: string;\n conversionType: string;\n list: Record<string, unknown>;\n product: Record<string, unknown>;\n productId: string;\n name: string;\n price: number;\n currency: string;\n position: number;\n quantity: number;\n previousQuantity: number;\n newQuantity: number;\n cart: Record<string, unknown>;\n shipping: Record<string, unknown>;\n payment: Record<string, unknown>;\n order: Record<string, unknown>;\n orderId: string;\n refundId: string;\n amount: unknown;\n approvedAmount: number;\n processedAmount: number;\n reason: string;\n promotionId: string;\n creative: string;\n code: string;\n discountValue: number;\n discountType: string;\n orderValueBefore: number;\n orderValueAfter: number;\n wishlist: Record<string, unknown>;\n wishlistId: string;\n subscriptionId: string;\n plan: string;\n mrr: Record<string, unknown>;\n billingCycle: string;\n previousPlan: string;\n newPlan: string;\n previousMrr: Record<string, unknown>;\n newMrr: Record<string, unknown>;\n changeType: string;\n lostMrr: Record<string, unknown>;\n cost: Record<string, unknown>;\n newSegment: string;\n resultsCount: number;\n originalQuery: string;\n refinedQuery: string;\n refinementType: string;\n partialQuery: string;\n selectedSuggestion: string;\n suggestionPosition: number;\n dwellTimeMs: number;\n pagesVisited: number;\n featuresUsed: number;\n actionsTaken: number;\n durationMs: number;\n contentId: string;\n contentType: string;\n percentageConsumed: number;\n timeSpentMs: number;\n metric: string;\n periods: unknown;\n score: number;\n churnType: string;\n actionType: string;\n activityType: string;\n ticketId: string;\n subject: string;\n priority: string;\n status: string;\n complaintType: string;\n severity: string;\n fromStage: string;\n toStage: string;\n campaignId: string;\n date: string;\n fromScreen: string;\n toScreen: string;\n feature: string;\n success: boolean;\n isFullRefund: boolean;\n immediate: boolean;\n changedBy: string;\n previousRole: string;\n newRole: string;\n permission: string;\n resourceType: string;\n accessType: string;\n eventType: string;\n conversionId: string;\n}>;\n\nexport type FlowGridTrackProperties = Record<string, unknown> & FlowGridKnownTrackFields;\n\ntype TrackDispatchResult = { handled: true; value: unknown } | { handled: false };\ntype UnifiedFeatureAction = \"viewed\" | \"used\" | \"completed\" | \"abandoned\";\n\n/** The lifecycle action a {@link FeatureHandle} emits. */\nexport type FeatureHandleAction = UnifiedFeatureAction;\n\n/**\n * A feature bound to its id/name (+ optional category) via\n * {@link FlowGrid.defineFeature}. Call the verb that describes what happened;\n * each emits a `feature_usage` event through the unified `.track()` router.\n */\nexport interface FeatureHandle {\n readonly featureId: string;\n readonly featureName: string;\n /** Visitor opened / looked at the feature → Discovered + Viewed. */\n viewed(properties?: Record<string, unknown>): unknown;\n /** Visitor used the feature → Adopted. */\n used(properties?: Record<string, unknown>): unknown;\n /** Visitor finished the feature's job → Completed. */\n completed(properties?: Record<string, unknown>): unknown;\n /** Visitor started but gave up → Abandonment. */\n abandoned(properties?: Record<string, unknown>): unknown;\n /** Escape hatch — emit any action explicitly. */\n track(action: FeatureHandleAction, properties?: Record<string, unknown>): unknown;\n}\n\nconst UNHANDLED_TRACK: TrackDispatchResult = { handled: false };\nconst PRODUCT_KEYS = [\n \"productId\",\n \"name\",\n \"brand\",\n \"category\",\n \"variant\",\n \"price\",\n \"currency\",\n \"quantity\",\n \"discount\",\n \"coupon\",\n \"position\",\n \"url\",\n \"imageUrl\",\n \"rating\",\n \"reviewCount\",\n \"stockStatus\",\n \"attributes\",\n] as const;\n\nfunction handled(value: unknown): TrackDispatchResult {\n return { handled: true, value };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction stringValue(props: FlowGridTrackProperties, key: string): string | undefined {\n const value = props[key];\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction numberValue(props: FlowGridTrackProperties, key: string): number | undefined {\n const value = props[key];\n return typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n}\n\nfunction booleanValue(props: FlowGridTrackProperties, key: string): boolean | undefined {\n const value = props[key];\n return typeof value === \"boolean\" ? value : undefined;\n}\n\nfunction recordValue(props: FlowGridTrackProperties, key: string): FlowGridTrackProperties | undefined {\n const value = props[key];\n return isRecord(value) ? value as FlowGridTrackProperties : undefined;\n}\n\nfunction hasValue(props: FlowGridTrackProperties, key: string): boolean {\n const value = props[key];\n return value !== undefined && value !== null && !(typeof value === \"string\" && value.length === 0);\n}\n\nfunction omitProperties(props: FlowGridTrackProperties, keys: readonly string[]): FlowGridTrackProperties {\n const next: FlowGridTrackProperties = {};\n const blocked = new Set([...keys, \"domain\"]);\n for (const [key, value] of Object.entries(props)) {\n if (!blocked.has(key)) next[key] = value;\n }\n return next;\n}\n\nfunction productFrom(props: FlowGridTrackProperties): FlowGridTrackProperties | undefined {\n const nested = recordValue(props, \"product\");\n if (nested) return { ...nested, currency: nested.currency ?? \"USD\" };\n\n if (!hasValue(props, \"productId\") || !hasValue(props, \"name\") || !hasValue(props, \"price\")) {\n return undefined;\n }\n\n const product: FlowGridTrackProperties = { currency: \"USD\" };\n for (const key of PRODUCT_KEYS) {\n if (props[key] !== undefined) (product as Record<string, unknown>)[key] = props[key];\n }\n return product;\n}\n\n// ============================================================================\n// PUBLIC INIT TYPES\n// ============================================================================\n\n/** Granular auto-instrumentation toggles. */\nexport interface AutoTrackOptions {\n sessions?: boolean;\n pageViews?: boolean;\n performance?: boolean | { resources?: boolean; minDuration?: number };\n heatmaps?: boolean | { movement?: boolean };\n attribution?: boolean;\n replay?: boolean | import(\"./features/analytics/replay\").SessionReplayOptions;\n /** Auto-track session depth + user activity transitions. Default on. */\n engagement?: boolean | { idleAfterMs?: number; pollMs?: number };\n}\n\n/** Top-level config for `FlowGrid.init()`. */\nexport interface FlowGridInitConfig {\n\n /** Public web/site identifier (e.g. `\"wx_abc123\"`). Required. */\n webId: string;\n\n /**\n * Public API key. Optional — when omitted, the SDK runs in `data-site`-only\n * mode (web_id body-auth), matching the core `flowgrid.js` snippet. This is\n * what lets a no-build / script-tag install run experiments. Provide a key\n * for full server-authenticated ingestion.\n */\n apiKey?: string;\n\n /**\n * API base URL. Defaults to `\"https://core.flow-grid.xyz\"` — most users never\n * need to set this.\n */\n endpoint?: string;\n\n /**\n * Identify the user up-front so every subsequent event is attributed.\n * Equivalent to calling `fg.identifyUser(userId, traits)` after init.\n */\n user?: { userId: string; traits?: Record<string, unknown> };\n\n /**\n * Browser auto-instrumentation. `true` (default) turns on engagement and\n * session replay. Pageviews, sessions, heatmaps, performance, and attribution\n * are handled by the flowgrid.js core script and are off by default here.\n * Pass an object to control individual features, or `false` to opt out completely.\n */\n autoTrack?: boolean | AutoTrackOptions;\n\n /**\n * Session replay (rrweb). Off by default — opt in by passing `true` or an\n * options object. Requires the `rrweb` package (already a dependency).\n */\n replay?: boolean | import(\"./features/analytics/replay\").SessionReplayOptions;\n\n /**\n * Initial consent state. Equivalent to calling\n * `FlowGrid.setConsent({ analytics, marketing })` before any tracking.\n */\n consent?: { analytics?: boolean; marketing?: boolean };\n\n /** Low-level transport tunables (sampling, DNT, bot detection). */\n transport?: FlowGridTransportConfig;\n\n /** Optional pre-resolved visitor ID (server-side hydration). */\n visitorId?: string;\n\n /** Optional identity manager (advanced — visitor/session lifecycle). */\n identityManager?: IdentityManager;\n \n}\n\nconst DEFAULT_ENDPOINT = \"https://core.flow-grid.xyz\";\n\n/**\n * Unified FlowGrid SDK client. Instantiate once, use everywhere.\n *\n * Each feature is exposed as a lazily-initialised shortcut wrapper, so the\n * SDK has zero overhead for unused features.\n */\nexport class FlowGrid {\n /** Singleton created by {@link FlowGrid.init}. */\n private static _instance: FlowGrid | null = null;\n /** Teardown for the auto-instrumentation started during `init()`. */\n private _autoTrackStop: (() => void) | null = null;\n\n private readonly webId: string;\n private readonly endpoint: string;\n private readonly apiKey: string;\n private readonly visitorId?: string;\n private readonly identityManager?: IdentityManager;\n private readonly config?: FlowGridTransportConfig;\n\n private readonly _raw = new Map<string, unknown>();\n private readonly _wrapped = new Map<string, unknown>();\n\n /**\n * One-line init. Idempotent — calling twice returns the same instance and\n * logs a warning instead of starting auto-instrumentation again.\n *\n * @example\n * ```ts\n * // Painless: one line, sane defaults, everything on.\n * const fg = FlowGrid.init({ webId: \"wx_xxx\", apiKey: \"pk_xxx\" });\n *\n * // Or be explicit:\n * const fg = FlowGrid.init({\n * webId: \"wx_xxx\",\n * apiKey: \"pk_xxx\",\n * user: { userId: \"u_1\", traits: { plan: \"pro\" } },\n * replay: { sampleRate: 0.25, maskAllInputs: true },\n * consent: { analytics: true },\n * });\n * ```\n */\n static init(config: FlowGridInitConfig): FlowGrid {\n if (FlowGrid._instance) {\n if (typeof console !== \"undefined\") {\n console.warn(\"[FlowGrid] init() called more than once — returning existing instance.\");\n }\n return FlowGrid._instance;\n }\n\n if (!config?.webId) {\n throw new Error(\"FlowGrid.init: `webId` is required.\");\n }\n\n if (config.consent) {\n FlowGrid.setConsent(config.consent);\n }\n\n const fg = new FlowGrid(\n config.webId,\n config.endpoint ?? DEFAULT_ENDPOINT,\n config.apiKey ?? \"\",\n config.visitorId,\n config.identityManager,\n config.transport,\n );\n FlowGrid._instance = fg;\n\n // Pre-fetch the enterprise chunk immediately so getters are ready by the\n // time user code first accesses them (typically well after init returns).\n void loadEnterpriseModules();\n\n // Identify the user before auto-instrumentation so the very first events\n // (session_start, page_view) carry the userId.\n if (config.user?.userId) {\n void fg.identifyUser(config.user.userId, config.user.traits);\n }\n\n const autoTrack = config.autoTrack;\n const replay = config.replay;\n const autoOpts: AutoTrackOptions =\n autoTrack === false ? { sessions: false, pageViews: false, performance: false, heatmaps: false, attribution: false, engagement: false, replay: false }\n : autoTrack === true || autoTrack === undefined ? { sessions: false, pageViews: false, performance: false, heatmaps: false, attribution: false }\n : autoTrack;\n\n // Top-level `replay` always wins over autoTrack defaults. Previously this\n // only applied when autoOpts.replay was undefined, which meant\n // `init({ autoTrack: false, replay: true })` silently disabled replay.\n if (replay !== undefined) {\n autoOpts.replay = replay;\n }\n\n fg._autoTrackStop = fg.autoTrack({\n ...autoOpts,\n userId: config.user?.userId ?? (autoOpts as { userId?: string }).userId,\n });\n\n return fg;\n }\n\n /** Returns the singleton created by {@link FlowGrid.init}, if any. */\n static instance(): FlowGrid | null {\n return FlowGrid._instance;\n }\n\n /**\n * Await the enterprise feature chunk. Call this before accessing any\n * enterprise namespace (`fg.engagement`, `fg.churn`, etc.) if you need\n * guaranteed availability rather than relying on the background pre-fetch\n * started by `init()`.\n */\n static loadEnterprise(): Promise<void> {\n return loadEnterpriseModules();\n }\n\n /**\n * Reset the singleton — primarily for tests / hot-reload. Tears down any\n * active auto-instrumentation.\n */\n static reset(): void {\n if (FlowGrid._instance?._autoTrackStop) {\n try { FlowGrid._instance._autoTrackStop(); } catch { /* noop */ }\n }\n FlowGrid._instance = null;\n }\n\n /**\n * Explicit constructor for advanced cases (multi-tenant SSR, tests). Most\n * apps should use the static {@link FlowGrid.init} instead.\n */\n constructor(\n webId: string,\n endpoint: string,\n apiKey: string,\n visitorId?: string,\n identityManager?: IdentityManager,\n config?: FlowGridTransportConfig\n ) {\n this.webId = webId;\n this.endpoint = endpoint;\n this.apiKey = apiKey;\n this.visitorId = visitorId;\n this.identityManager = identityManager ?? new IdentityManager(visitorId ? { visitorId } : undefined);\n this.config = config;\n }\n\n private raw<T>(key: string, Klass: Ctor<T>): T {\n let instance = this._raw.get(key) as T | undefined;\n if (!instance) {\n instance = new Klass(\n this.webId,\n this.endpoint,\n this.apiKey,\n this.visitorId,\n this.identityManager,\n this.config\n );\n this._raw.set(key, instance);\n }\n return instance;\n }\n\n private wrap<W, T>(key: string, Klass: Ctor<T>, Wrapper: new (client: T) => W): W {\n let instance = this._wrapped.get(key) as W | undefined;\n if (!instance) {\n instance = new Wrapper(this.raw(key, Klass));\n this._wrapped.set(key, instance);\n }\n return instance;\n }\n\n // ---- Core ----\n get activation(): ActivationTracker { return this.wrap(\"activation\", Activation, ActivationTracker); }\n get features(): FeatureTracker { return this.wrap(\"features\", TrackFeature, FeatureTracker); }\n get prompts(): PromptTracker { return this.wrap(\"prompts\", TrackPrompt, PromptTracker); }\n get experiments(): ExperimentTracker { return this.wrap(\"experiments\", Experiment, ExperimentTracker); }\n\n // ---- Analytics ----\n get pageViews(): PageViewTracker { return this.wrap(\"pageViews\", PageViews, PageViewTracker); }\n get sessions(): SessionTracker { return this.wrap(\"sessions\", Sessions, SessionTracker); }\n get events(): EventTracker { return this.wrap(\"events\", Events, EventTracker); }\n get identify(): IdentifyTracker { return this.wrap(\"identify\", Identify, IdentifyTracker); }\n get funnels(): FunnelTracker { return this.wrap(\"funnels\", Funnels, FunnelTracker); }\n get retention(): RetentionTracker { return this.wrap(\"retention\", Retention, RetentionTracker); }\n get attribution(): AttributionTracker { return this.wrap(\"attribution\", Attribution, AttributionTracker); }\n get heatmaps(): HeatmapTracker { return this.wrap(\"heatmaps\", Heatmaps, HeatmapTracker); }\n get performance(): PerformanceTracker { return this.wrap(\"performance\", PerformanceTracking, PerformanceTracker); }\n get replay(): ReplayTracker { return this.wrap(\"replay\", SessionReplay, ReplayTracker); }\n\n // ---- Ecommerce ----\n get products(): ProductTracker { return this.wrap(\"products\", Products, ProductTracker); }\n get cart(): CartTracker { return this.wrap(\"cart\", CartTracking, CartTracker); }\n get checkout(): CheckoutTracker { return this.wrap(\"checkout\", Checkout, CheckoutTracker); }\n get purchases(): PurchaseTracker { return this.wrap(\"purchases\", Purchases, PurchaseTracker); }\n get refunds(): RefundTracker { return this.wrap(\"refunds\", Refunds, RefundTracker); }\n get promotions(): PromotionTracker { return this.wrap(\"promotions\", Promotions, PromotionTracker); }\n get wishlist(): WishlistTracker { return this.wrap(\"wishlist\", WishlistTracking, WishlistTracker); }\n get ltv(): LTVTracker { return this.wrap(\"ltv\", CustomerLTV, LTVTracker); }\n get search(): SearchTracker { return this.wrap(\"search\", SearchAnalytics, SearchTracker); }\n get subscriptions(): SubscriptionTracker { return this.wrap(\"subscriptions\", Subscriptions, SubscriptionTracker); }\n\n // ---- Enterprise ----\n // These getters pull classes from the lazily-loaded enterprise chunk.\n // The chunk is pre-fetched by init() so it's almost always ready by the\n // time user code accesses these properties. loadEnterprise() is available\n // for the rare case where callers need to await readiness explicitly.\n private _requireEnterprise() {\n const ent = _enterpriseFeatures;\n const entT = _enterpriseTracking;\n if (!ent || !entT) {\n throw new Error(\n \"[FlowGrid] Enterprise features are still loading. \" +\n \"await FlowGrid.loadEnterprise() before accessing enterprise namespaces directly.\"\n );\n }\n return { ent, entT };\n }\n\n get engagement(): EngagementTracker { const { ent, entT } = this._requireEnterprise(); return this.wrap(\"engagement\", ent.Engagement, entT.EngagementTracker); }\n get cohorts(): CohortTracker { const { ent, entT } = this._requireEnterprise(); return this.wrap(\"cohorts\", ent.Cohorts, entT.CohortTracker); }\n get churn(): ChurnTracker { const { ent, entT } = this._requireEnterprise(); return this.wrap(\"churn\", ent.ChurnAnalytics, entT.ChurnTracker); }\n get monetization(): MonetizationTracker { const { ent, entT } = this._requireEnterprise(); return this.wrap(\"monetization\", ent.Monetization, entT.MonetizationTracker); }\n get multiPathFunnels(): MultiPathFunnelTracker { const { ent, entT } = this._requireEnterprise(); return this.wrap(\"multiPathFunnels\", ent.MultiPathFunnels, entT.MultiPathFunnelTracker); }\n get support(): SupportTracker { const { ent, entT } = this._requireEnterprise(); return this.wrap(\"support\", ent.SupportAnalytics, entT.SupportTracker); }\n get acquisition(): AcquisitionTracker { const { ent, entT } = this._requireEnterprise(); return this.wrap(\"acquisition\", ent.AcquisitionAnalytics, entT.AcquisitionTracker); }\n get paths(): PathTracker { const { ent, entT } = this._requireEnterprise(); return this.wrap(\"paths\", ent.PathAnalytics, entT.PathTracker); }\n get alerts(): AlertTracker { const { ent, entT } = this._requireEnterprise(); return this.wrap(\"alerts\", ent.AlertsAnalytics, entT.AlertTracker); }\n get security(): SecurityTracker { const { ent, entT } = this._requireEnterprise(); return this.wrap(\"security\", ent.SecurityAnalytics, entT.SecurityTracker); }\n get forecasting(): ForecastingTracker { const { ent, entT } = this._requireEnterprise(); return this.wrap(\"forecasting\", ent.Forecasting, entT.ForecastingTracker); }\n\n // ---- Consent passthrough (static on transport) ----\n static setConsent = FlowGridTransport.setConsent.bind(FlowGridTransport);\n static hasConsent = FlowGridTransport.hasConsent.bind(FlowGridTransport);\n\n // ==========================================================================\n // TOP-LEVEL SHORTCUTS — the 80% case\n // ==========================================================================\n\n /**\n * Track a semantic event. The `eventName` must be one of the recognised\n * {@link FlowGridEventName} literals — TypeScript will error on unknown\n * names and narrow `properties` to the required shape for that event.\n *\n * @example\n * ```ts\n * fg.track(\"signup_completed\", { userId: \"u_1\", method: \"google\" });\n * fg.track(\"add_to_cart\", { productId: \"sku_1\", name: \"Tee\", price: 29, currency: \"USD\", quantity: 1 });\n * ```\n */\n track<E extends FlowGridEventName>(\n eventName: E,\n properties: FlowGridEventMap[E],\n options?: FlowGridTrackOptions\n ): unknown;\n track(\n eventName: string,\n properties: FlowGridTrackProperties = {},\n options: FlowGridTrackOptions = {}\n ) {\n const contextualProperties = this.withTrackContext(properties, options);\n const routed = this.dispatchTrack(eventName, contextualProperties, Boolean(options.domain), options.domain);\n if (routed.handled) return routed.value;\n return this.events.track(eventName, properties, options);\n }\n\n private withTrackContext(\n properties: FlowGridTrackProperties,\n options: FlowGridTrackOptions\n ): FlowGridTrackProperties {\n return {\n ...properties,\n ...(options.userId !== undefined && properties.userId === undefined ? { userId: options.userId } : {}),\n ...(options.sessionId !== undefined && properties.sessionId === undefined ? { sessionId: options.sessionId } : {}),\n };\n }\n\n private requireTrackProperties(\n eventName: string,\n properties: FlowGridTrackProperties,\n keys: readonly string[],\n strict: boolean\n ): boolean {\n const missing = keys.filter(key => !hasValue(properties, key));\n if (missing.length === 0) return true;\n if (strict) {\n throw new Error(`FlowGrid.track(\"${eventName}\") missing required properties: ${missing.join(\", \")}`);\n }\n return false;\n }\n\n private dispatchTrack(\n eventName: string,\n properties: FlowGridTrackProperties,\n strict: boolean,\n domain?: FlowGridTrackDomain\n ): TrackDispatchResult {\n const dispatchers: Array<() => TrackDispatchResult> = [];\n\n if (!domain || domain === \"activation\") dispatchers.push(() => this.dispatchActivationTrack(eventName, properties, strict));\n\n if (!domain || domain === \"feature\") dispatchers.push(() => this.dispatchFeatureTrack(eventName, properties, strict));\n\n if (!domain || domain === \"prompt\") dispatchers.push(() => this.dispatchPromptTrack(eventName, properties, strict));\n\n if (!domain || domain === \"experiment\") dispatchers.push(() => this.dispatchExperimentTrack(eventName, properties, strict));\n\n if (!domain || domain === \"analytics\" || domain === \"funnel\" || domain === \"retention\" || domain === \"attribution\") {\n dispatchers.push(() => this.dispatchAnalyticsTrack(eventName, properties, strict, domain));\n }\n\n if (!domain || domain === \"ecommerce\") dispatchers.push(() => this.dispatchEcommerceTrack(eventName, properties, strict));\n\n if (\n !domain ||\n domain === \"engagement\" ||\n domain === \"churn\" ||\n domain === \"monetization\" ||\n domain === \"support\" ||\n domain === \"acquisition\" ||\n domain === \"paths\" ||\n domain === \"security\" ||\n domain === \"multiPathFunnels\"\n ) {\n dispatchers.push(() => this.dispatchEnterpriseTrack(eventName, properties, strict, domain));\n }\n\n for (const dispatch of dispatchers) {\n const result = dispatch();\n if (result.handled) return result;\n }\n\n return UNHANDLED_TRACK;\n }\n\n private dispatchActivationTrack(\n eventName: string,\n properties: FlowGridTrackProperties,\n strict: boolean\n ): TrackDispatchResult {\n switch (eventName) {\n case \"signup_completed\": {\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"method\"], strict)) return UNHANDLED_TRACK;\n return handled(this.activation.signup(\n stringValue(properties, \"userId\")!,\n stringValue(properties, \"method\")!,\n stringValue(properties, \"source\"),\n omitProperties(properties, [\"userId\", \"method\", \"source\"])\n ));\n }\n case \"onboarding_step\": {\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"stepId\", \"stepName\", \"stepOrder\", \"totalSteps\"], strict)) return UNHANDLED_TRACK;\n return handled(this.activation.onboardingStep(\n stringValue(properties, \"userId\")!,\n stringValue(properties, \"stepId\")!,\n stringValue(properties, \"stepName\")!,\n numberValue(properties, \"stepOrder\")!,\n numberValue(properties, \"totalSteps\")!,\n {\n skipped: booleanValue(properties, \"skipped\"),\n timeSpent: numberValue(properties, \"timeSpent\"),\n properties: omitProperties(properties, [\"userId\", \"stepId\", \"stepName\", \"stepOrder\", \"totalSteps\", \"skipped\", \"timeSpent\"]),\n }\n ));\n }\n case \"onboarding_completed\": {\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"totalTimeSpent\", \"stepsCompleted\"], strict)) return UNHANDLED_TRACK;\n return handled(this.activation.onboardingComplete(\n stringValue(properties, \"userId\")!,\n numberValue(properties, \"totalTimeSpent\")!,\n numberValue(properties, \"stepsCompleted\")!,\n numberValue(properties, \"stepsSkipped\") ?? 0\n ));\n }\n case \"first_action\": {\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"actionName\", \"timeSinceSignup\"], strict)) return UNHANDLED_TRACK;\n return handled(this.activation.firstAction(\n stringValue(properties, \"userId\")!,\n stringValue(properties, \"actionName\")!,\n numberValue(properties, \"timeSinceSignup\")!,\n omitProperties(properties, [\"userId\", \"actionName\", \"timeSinceSignup\"])\n ));\n }\n default:\n if (strict) return handled(this.activation.event(eventName, properties));\n return UNHANDLED_TRACK;\n }\n }\n\n private dispatchFeatureTrack(\n eventName: string,\n properties: FlowGridTrackProperties,\n strict: boolean\n ): TrackDispatchResult {\n\n const actions: Record<string, UnifiedFeatureAction> = {\n feature_viewed: \"viewed\",\n feature_used: \"used\",\n feature_completed: \"completed\",\n feature_abandoned: \"abandoned\",\n };\n\n const action = actions[eventName] ?? (eventName === \"feature_usage\" \n ? properties.action : undefined);\n\n if (eventName === \"feature_usage\" || action) {\n if (!this.requireTrackProperties(eventName, properties, [\"featureId\", \"featureName\"], strict)) return UNHANDLED_TRACK;\n return handled(this.features.client.trackUsage({\n ...properties,\n action: typeof action === \"string\" ? action : \"used\",\n // Emit the lifecycle-specific event name on the wire (feature_viewed,\n // feature_completed, …). For the generic `feature_usage` entry point\n // this is just \"feature_usage\", preserving the prior behaviour.\n eventName,\n } as Parameters<TrackFeature[\"trackUsage\"]>[0]));\n }\n\n if (strict) {\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"featureName\"], true)) return UNHANDLED_TRACK;\n return handled(this.features.create(\n stringValue(properties, \"userId\")!,\n eventName,\n stringValue(properties, \"featureName\")!,\n omitProperties(properties, [\"userId\", \"featureName\"])\n ));\n }\n\n return UNHANDLED_TRACK;\n }\n\n private dispatchPromptTrack(\n eventName: string,\n properties: FlowGridTrackProperties,\n strict: boolean\n ): TrackDispatchResult {\n switch (eventName) {\n case \"prompt_submitted\":\n if (!this.requireTrackProperties(eventName, properties, [\"promptId\", \"promptType\"], strict)) return UNHANDLED_TRACK;\n return handled(this.prompts.client.trackSubmission(properties as Parameters<TrackPrompt[\"trackSubmission\"]>[0]));\n case \"prompt_response\":\n if (!this.requireTrackProperties(eventName, properties, [\"promptId\", \"responseTime\", \"completed\"], strict)) return UNHANDLED_TRACK;\n return handled(this.prompts.client.trackResponse(properties as Parameters<TrackPrompt[\"trackResponse\"]>[0]));\n case \"prompt_feedback\":\n if (!this.requireTrackProperties(eventName, properties, [\"promptId\", \"feedbackType\"], strict)) return UNHANDLED_TRACK;\n return handled(this.prompts.client.trackFeedback(properties as Parameters<TrackPrompt[\"trackFeedback\"]>[0]));\n case \"prompt_conversation\":\n if (!this.requireTrackProperties(eventName, properties, [\"conversationId\", \"userId\", \"turnCount\", \"totalDuration\"], strict)) return UNHANDLED_TRACK;\n return handled(this.prompts.conversation(\n stringValue(properties, \"conversationId\")!,\n stringValue(properties, \"userId\")!,\n numberValue(properties, \"turnCount\")!,\n numberValue(properties, \"totalDuration\")!\n ));\n default:\n if (strict || eventName.startsWith(\"prompt_\")) return handled(this.prompts.event(eventName, properties));\n return UNHANDLED_TRACK;\n }\n }\n\n private dispatchExperimentTrack(\n eventName: string,\n properties: FlowGridTrackProperties,\n strict: boolean\n ): TrackDispatchResult {\n switch (eventName) {\n case \"experiment_assignment\":\n if (!this.requireTrackProperties(eventName, properties, [\"experimentId\", \"userId\", \"variantId\"], strict)) return UNHANDLED_TRACK;\n return handled(this.experiments.client.assign(properties as Parameters<Experiment[\"assign\"]>[0]));\n case \"experiment_exposure\":\n if (!this.requireTrackProperties(eventName, properties, [\"experimentId\", \"userId\", \"variantId\"], strict)) return UNHANDLED_TRACK;\n return handled(this.experiments.exposure(\n stringValue(properties, \"experimentId\")!,\n stringValue(properties, \"userId\")!,\n stringValue(properties, \"variantId\")!\n ));\n case \"experiment_conversion\":\n if (!this.requireTrackProperties(eventName, properties, [\"experimentId\", \"userId\", \"variantId\", \"metricName\"], strict)) return UNHANDLED_TRACK;\n return handled(this.experiments.client.trackConversion({\n ...properties,\n value: numberValue(properties, \"value\") ?? 1,\n } as Parameters<Experiment[\"trackConversion\"]>[0]));\n default:\n if (strict || eventName.startsWith(\"experiment_\")) {\n if (!this.requireTrackProperties(eventName, properties, [\"variant\"], strict)) return UNHANDLED_TRACK;\n return handled(this.experiments.client.track({\n eventName,\n variant: stringValue(properties, \"variant\")!,\n properties: omitProperties(properties, [\"variant\"]),\n }));\n }\n return UNHANDLED_TRACK;\n }\n }\n\n private dispatchAnalyticsTrack(\n eventName: string,\n properties: FlowGridTrackProperties,\n strict: boolean,\n domain?: FlowGridTrackDomain\n ): TrackDispatchResult {\n switch (eventName) {\n case \"click_event\":\n if (!this.requireTrackProperties(eventName, properties, [\"elementId\", \"elementText\"], strict)) return UNHANDLED_TRACK;\n return handled(this.events.client.trackClick(properties as Parameters<Events[\"trackClick\"]>[0]));\n case \"form_event\":\n if (!this.requireTrackProperties(eventName, properties, [\"formId\", \"formName\"], strict)) return UNHANDLED_TRACK;\n return handled(this.events.client.trackForm(properties as Parameters<Events[\"trackForm\"]>[0]));\n case \"search_event\":\n if (!this.requireTrackProperties(eventName, properties, [\"query\", \"category\"], strict)) return UNHANDLED_TRACK;\n return handled(this.events.client.trackSearch(properties as Parameters<Events[\"trackSearch\"]>[0]));\n case \"error_event\":\n if (!this.requireTrackProperties(eventName, properties, [\"message\", \"type\"], strict)) return UNHANDLED_TRACK;\n return handled(this.events.client.trackError(properties as Parameters<Events[\"trackError\"]>[0]));\n case \"funnel_step\":\n if (!this.requireTrackProperties(eventName, properties, [\"funnelId\", \"step\", \"stepNumber\"], strict)) return UNHANDLED_TRACK;\n return handled(this.funnels.client.trackStep(properties as Parameters<Funnels[\"trackStep\"]>[0]));\n case \"funnel_complete\":\n if (!this.requireTrackProperties(eventName, properties, [\"funnelId\"], strict)) return UNHANDLED_TRACK;\n return handled(this.funnels.complete(stringValue(properties, \"funnelId\")!, stringValue(properties, \"userId\"), omitProperties(properties, [\"funnelId\", \"userId\"])));\n case \"funnel_abandon\":\n if (!this.requireTrackProperties(eventName, properties, [\"funnelId\", \"lastStep\"], strict)) return UNHANDLED_TRACK;\n return handled(this.funnels.abandon(stringValue(properties, \"funnelId\")!, stringValue(properties, \"lastStep\")!, stringValue(properties, \"userId\"), stringValue(properties, \"reason\")));\n case \"user_activity\":\n if (domain && domain !== \"retention\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\"], strict)) return UNHANDLED_TRACK;\n return handled(this.retention.client.trackActivity(properties as Parameters<Retention[\"trackActivity\"]>[0]));\n case \"attribution_touchpoint\":\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"channel\"], strict)) return UNHANDLED_TRACK;\n return handled(this.attribution.client.trackTouchpoint(properties as Parameters<Attribution[\"trackTouchpoint\"]>[0]));\n case \"attribution_conversion\":\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"conversionType\"], strict)) return UNHANDLED_TRACK;\n return handled(this.attribution.client.recordConversion(properties as Parameters<Attribution[\"recordConversion\"]>[0]));\n default:\n return UNHANDLED_TRACK;\n }\n }\n\n private dispatchEcommerceTrack(\n eventName: string,\n properties: FlowGridTrackProperties,\n strict: boolean\n ): TrackDispatchResult {\n switch (eventName) {\n case \"product_view\": {\n const product = productFrom(properties);\n if (!product) {\n if (strict) throw new Error(`FlowGrid.track(\"${eventName}\") missing required properties: product or productId, name, price`);\n return UNHANDLED_TRACK;\n }\n return handled(this.products.client.view({\n ...product,\n ...omitProperties(properties, [\"product\", ...PRODUCT_KEYS]),\n } as Parameters<Products[\"view\"]>[0]));\n }\n case \"product_impression\":\n if (!this.requireTrackProperties(eventName, properties, [\"list\"], strict)) return UNHANDLED_TRACK;\n return handled(this.products.client.impression(properties as unknown as Parameters<Products[\"impression\"]>[0]));\n case \"product_click\": {\n const product = productFrom(properties);\n if (!product || !this.requireTrackProperties(eventName, properties, [\"listId\", \"position\"], strict)) return UNHANDLED_TRACK;\n return handled(this.products.client.click({\n ...properties,\n product,\n } as Parameters<Products[\"click\"]>[0]));\n }\n case \"add_to_cart\":\n case \"remove_from_cart\": {\n const product = productFrom(properties);\n if (!product) {\n if (strict) throw new Error(`FlowGrid.track(\"${eventName}\") missing required properties: product or productId, name, price`);\n return UNHANDLED_TRACK;\n }\n const config = {\n ...omitProperties(properties, [\"product\", ...PRODUCT_KEYS]),\n product,\n quantity: numberValue(properties, \"quantity\") ?? 1,\n };\n return handled(eventName === \"add_to_cart\"\n ? this.cart.client.addItem(config as Parameters<CartTracking[\"addItem\"]>[0])\n : this.cart.client.removeItem(config as Parameters<CartTracking[\"removeItem\"]>[0]));\n }\n case \"update_cart_item\": {\n const product = productFrom(properties);\n if (!product || !this.requireTrackProperties(eventName, properties, [\"previousQuantity\", \"newQuantity\"], strict)) return UNHANDLED_TRACK;\n return handled(this.cart.client.updateItem({\n ...omitProperties(properties, [\"product\", ...PRODUCT_KEYS]),\n product,\n } as Parameters<CartTracking[\"updateItem\"]>[0]));\n }\n case \"view_cart\":\n if (!this.requireTrackProperties(eventName, properties, [\"cart\"], strict)) return UNHANDLED_TRACK;\n return handled(this.cart.client.view(properties as unknown as Parameters<CartTracking[\"view\"]>[0]));\n case \"begin_checkout\":\n if (!this.requireTrackProperties(eventName, properties, [\"cart\"], strict)) return UNHANDLED_TRACK;\n return handled(this.checkout.client.begin(properties as unknown as Parameters<Checkout[\"begin\"]>[0]));\n case \"checkout_step\":\n if (!this.requireTrackProperties(eventName, properties, [\"step\", \"stepNumber\", \"cart\"], strict)) return UNHANDLED_TRACK;\n return handled(this.checkout.client.step(properties as unknown as Parameters<Checkout[\"step\"]>[0]));\n case \"add_shipping_info\":\n if (!this.requireTrackProperties(eventName, properties, [\"cart\", \"shipping\"], strict)) return UNHANDLED_TRACK;\n return handled(this.checkout.client.addShipping(properties as unknown as Parameters<Checkout[\"addShipping\"]>[0]));\n case \"add_payment_info\":\n if (!this.requireTrackProperties(eventName, properties, [\"cart\", \"payment\"], strict)) return UNHANDLED_TRACK;\n return handled(this.checkout.client.addPayment(properties as unknown as Parameters<Checkout[\"addPayment\"]>[0]));\n case \"purchase\":\n if (!this.requireTrackProperties(eventName, properties, [\"order\"], strict)) return UNHANDLED_TRACK;\n return handled(this.purchases.client.track(properties as unknown as Parameters<Purchases[\"track\"]>[0]));\n case \"refund\":\n if (!this.requireTrackProperties(eventName, properties, [\"orderId\", \"refundId\", \"amount\", \"currency\"], strict)) return UNHANDLED_TRACK;\n return handled(this.refunds.client.track(properties as unknown as Parameters<Refunds[\"track\"]>[0]));\n case \"refund_requested\":\n if (!this.requireTrackProperties(eventName, properties, [\"orderId\"], strict)) return UNHANDLED_TRACK;\n return handled(this.refunds.client.request(stringValue(properties, \"orderId\")!, properties.items as Parameters<Refunds[\"request\"]>[1], stringValue(properties, \"reason\")));\n case \"refund_approved\":\n if (!this.requireTrackProperties(eventName, properties, [\"refundId\", \"approvedAmount\"], strict)) return UNHANDLED_TRACK;\n return handled(this.refunds.client.approve(stringValue(properties, \"refundId\")!, numberValue(properties, \"approvedAmount\")!));\n case \"refund_denied\":\n if (!this.requireTrackProperties(eventName, properties, [\"refundId\", \"reason\"], strict)) return UNHANDLED_TRACK;\n return handled(this.refunds.client.deny(stringValue(properties, \"refundId\")!, stringValue(properties, \"reason\")!));\n case \"refund_completed\":\n if (!this.requireTrackProperties(eventName, properties, [\"refundId\", \"processedAmount\"], strict)) return UNHANDLED_TRACK;\n return handled(this.refunds.client.complete(stringValue(properties, \"refundId\")!, numberValue(properties, \"processedAmount\")!));\n case \"promotion_view\":\n if (!this.requireTrackProperties(eventName, properties, [\"promotionId\", \"name\", \"creative\", \"position\"], strict)) return UNHANDLED_TRACK;\n return handled(this.promotions.client.view(properties as Parameters<Promotions[\"view\"]>[0]));\n case \"promotion_click\":\n if (!this.requireTrackProperties(eventName, properties, [\"promotionId\", \"name\", \"creative\", \"position\"], strict)) return UNHANDLED_TRACK;\n return handled(this.promotions.client.click(properties as Parameters<Promotions[\"click\"]>[0]));\n case \"coupon_used\":\n if (!this.requireTrackProperties(eventName, properties, [\"code\", \"orderId\", \"discountValue\", \"discountType\", \"orderValueBefore\", \"orderValueAfter\"], strict)) return UNHANDLED_TRACK;\n return handled(this.promotions.client.couponUsed(properties as Parameters<Promotions[\"couponUsed\"]>[0]));\n case \"add_to_wishlist\":\n case \"remove_from_wishlist\": {\n const product = productFrom(properties);\n if (!product) {\n if (strict) throw new Error(`FlowGrid.track(\"${eventName}\") missing required properties: product or productId, name, price`);\n return UNHANDLED_TRACK;\n }\n const config = { ...omitProperties(properties, [\"product\", ...PRODUCT_KEYS]), product };\n return handled(eventName === \"add_to_wishlist\"\n ? this.wishlist.client.add(config as Parameters<WishlistTracking[\"add\"]>[0])\n : this.wishlist.client.remove(config as Parameters<WishlistTracking[\"remove\"]>[0]));\n }\n case \"view_wishlist\":\n if (!this.requireTrackProperties(eventName, properties, [\"wishlist\"], strict)) return UNHANDLED_TRACK;\n return handled(this.wishlist.client.view(recordValue(properties, \"wishlist\") as unknown as Parameters<WishlistTracking[\"view\"]>[0]));\n case \"wishlist_to_cart\": {\n const product = productFrom(properties);\n if (!product) {\n if (strict) throw new Error(`FlowGrid.track(\"${eventName}\") missing required properties: product or productId, name, price`);\n return UNHANDLED_TRACK;\n }\n return handled(this.wishlist.client.moveToCart(product as Parameters<WishlistTracking[\"moveToCart\"]>[0], stringValue(properties, \"wishlistId\")));\n }\n case \"share_wishlist\":\n if (!this.requireTrackProperties(eventName, properties, [\"wishlistId\", \"method\"], strict)) return UNHANDLED_TRACK;\n return handled(this.wishlist.client.share(stringValue(properties, \"wishlistId\")!, stringValue(properties, \"method\") as Parameters<WishlistTracking[\"share\"]>[1]));\n case \"subscription_start\":\n if (!this.requireTrackProperties(eventName, properties, [\"subscriptionId\", \"userId\", \"plan\", \"mrr\", \"billingCycle\"], strict)) return UNHANDLED_TRACK;\n return handled(this.subscriptions.client.start(properties as unknown as Parameters<Subscriptions[\"start\"]>[0]));\n case \"subscription_change\":\n if (!this.requireTrackProperties(eventName, properties, [\"subscriptionId\", \"userId\", \"previousPlan\", \"newPlan\", \"previousMrr\", \"newMrr\", \"changeType\"], strict)) return UNHANDLED_TRACK;\n return handled(this.subscriptions.client.change(properties as unknown as Parameters<Subscriptions[\"change\"]>[0]));\n case \"subscription_cancel\":\n if (!this.requireTrackProperties(eventName, properties, [\"subscriptionId\", \"userId\", \"plan\", \"lostMrr\"], strict)) return UNHANDLED_TRACK;\n return handled(this.subscriptions.client.cancel(properties as unknown as Parameters<Subscriptions[\"cancel\"]>[0]));\n case \"subscription_renew\":\n if (!this.requireTrackProperties(eventName, properties, [\"subscriptionId\", \"userId\", \"mrr\"], strict)) return UNHANDLED_TRACK;\n return handled(this.subscriptions.client.renew(stringValue(properties, \"subscriptionId\")!, stringValue(properties, \"userId\")!, recordValue(properties, \"mrr\") as unknown as Parameters<Subscriptions[\"renew\"]>[2]));\n case \"trial_converted\":\n if (!this.requireTrackProperties(eventName, properties, [\"subscriptionId\", \"userId\", \"plan\", \"mrr\"], strict)) return UNHANDLED_TRACK;\n return handled(this.subscriptions.client.trialConverted(stringValue(properties, \"subscriptionId\")!, stringValue(properties, \"userId\")!, stringValue(properties, \"plan\")!, recordValue(properties, \"mrr\") as unknown as Parameters<Subscriptions[\"trialConverted\"]>[3]));\n case \"trial_expired\":\n if (!this.requireTrackProperties(eventName, properties, [\"subscriptionId\", \"userId\"], strict)) return UNHANDLED_TRACK;\n return handled(this.subscriptions.client.trialExpired(stringValue(properties, \"subscriptionId\")!, stringValue(properties, \"userId\")!));\n case \"record_acquisition_cost\":\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"cost\", \"channel\"], strict)) return UNHANDLED_TRACK;\n return handled(this.ltv.client.recordAcquisitionCost(properties as unknown as Parameters<CustomerLTV[\"recordAcquisitionCost\"]>[0]));\n case \"update_customer_segment\":\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"newSegment\"], strict)) return UNHANDLED_TRACK;\n return handled(this.ltv.client.updateSegment(stringValue(properties, \"userId\")!, stringValue(properties, \"newSegment\") as Parameters<CustomerLTV[\"updateSegment\"]>[1]));\n case \"site_search\":\n if (!this.requireTrackProperties(eventName, properties, [\"query\", \"resultsCount\"], strict)) return UNHANDLED_TRACK;\n return handled(this.search.client.track(properties as Parameters<SearchAnalytics[\"track\"]>[0]));\n case \"search_result_click\": {\n const product = productFrom(properties);\n if (!product || !this.requireTrackProperties(eventName, properties, [\"query\", \"position\"], strict)) return UNHANDLED_TRACK;\n return handled(this.search.client.trackClick({ ...properties, product } as Parameters<SearchAnalytics[\"trackClick\"]>[0]));\n }\n case \"search_refinement\":\n if (!this.requireTrackProperties(eventName, properties, [\"originalQuery\", \"refinedQuery\", \"refinementType\"], strict)) return UNHANDLED_TRACK;\n return handled(this.search.client.trackRefinement(stringValue(properties, \"originalQuery\")!, stringValue(properties, \"refinedQuery\")!, stringValue(properties, \"refinementType\") as Parameters<SearchAnalytics[\"trackRefinement\"]>[2]));\n case \"search_suggestion\":\n if (!this.requireTrackProperties(eventName, properties, [\"partialQuery\", \"selectedSuggestion\", \"suggestionPosition\"], strict)) return UNHANDLED_TRACK;\n return handled(this.search.client.trackSuggestion(stringValue(properties, \"partialQuery\")!, stringValue(properties, \"selectedSuggestion\")!, numberValue(properties, \"suggestionPosition\")!));\n default:\n return UNHANDLED_TRACK;\n }\n }\n\n private dispatchEnterpriseTrack(\n eventName: string,\n properties: FlowGridTrackProperties,\n strict: boolean,\n domain?: FlowGridTrackDomain\n ): TrackDispatchResult {\n // Enterprise chunk not yet loaded — let the event fall through to events.track().\n if (!_enterpriseFeatures || !_enterpriseTracking) return UNHANDLED_TRACK;\n\n switch (eventName) {\n\n case \"engagement.dwell_time\":\n case \"engagement_dwell_time\":\n if (domain && domain !== \"engagement\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"featureId\", \"dwellTimeMs\"], strict)) return UNHANDLED_TRACK;\n return handled(this.engagement.client.trackDwellTime(properties as Parameters<Engagement[\"trackDwellTime\"]>[0]));\n\n case \"engagement.session_depth\":\n case \"engagement_session_depth\":\n if (domain && domain !== \"engagement\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"sessionId\", \"pagesVisited\", \"featuresUsed\", \"actionsTaken\", \"durationMs\"], strict)) return UNHANDLED_TRACK;\n return handled(this.engagement.client.trackSessionDepth(properties as Parameters<Engagement[\"trackSessionDepth\"]>[0]));\n\n case \"engagement.content_consumption\":\n case \"engagement_content_consumption\":\n if (domain && domain !== \"engagement\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"contentId\", \"contentType\", \"percentageConsumed\", \"timeSpentMs\"], strict)) return UNHANDLED_TRACK;\n return handled(this.engagement.client.trackContentConsumption(properties as Parameters<Engagement[\"trackContentConsumption\"]>[0]));\n\n case \"engagement.user_activity\":\n case \"engagement_user_activity\":\n if (domain && domain !== \"engagement\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"activityType\", \"durationMs\"], strict)) return UNHANDLED_TRACK;\n return handled(this.engagement.client.trackUserActivity(properties as unknown as Parameters<Engagement[\"trackUserActivity\"]>[0]));\n\n case \"churn.usage_decay\":\n case \"churn_usage_decay\":\n if (domain && domain !== \"churn\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"metric\", \"periods\"], strict)) return UNHANDLED_TRACK;\n return handled(this.churn.client.trackUsageDecay(properties as Parameters<ChurnAnalytics[\"trackUsageDecay\"]>[0]));\n\n case \"churn.nps\":\n case \"churn_nps\":\n if (domain && domain !== \"churn\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"score\"], strict)) return UNHANDLED_TRACK;\n return handled(this.churn.client.trackNPS(properties as Parameters<ChurnAnalytics[\"trackNPS\"]>[0]));\n\n case \"churn.churn_event\":\n case \"churn_churn_event\":\n if (domain && domain !== \"churn\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"churnType\"], strict)) return UNHANDLED_TRACK;\n return handled(this.churn.client.trackChurn(properties as Parameters<ChurnAnalytics[\"trackChurn\"]>[0]));\n\n case \"churn.prevention_action\":\n case \"churn_prevention_action\":\n if (domain && domain !== \"churn\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"actionType\"], strict)) return UNHANDLED_TRACK;\n return handled(this.churn.client.trackPreventionAction(properties as Parameters<ChurnAnalytics[\"trackPreventionAction\"]>[0]));\n\n case \"monetization.revenue\":\n case \"monetization_revenue\":\n if (domain && domain !== \"monetization\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"amount\", \"currency\", \"type\"], strict)) return UNHANDLED_TRACK;\n return handled(this.monetization.client.trackRevenue(properties as Parameters<Monetization[\"trackRevenue\"]>[0]));\n\n case \"support.ticket_created\":\n case \"support_ticket_created\":\n if (domain && domain !== \"support\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"ticketId\", \"userId\", \"subject\", \"category\", \"priority\", \"channel\"], strict)) return UNHANDLED_TRACK;\n return handled(this.support.client.trackTicket(properties as Parameters<SupportAnalytics[\"trackTicket\"]>[0]));\n\n case \"support.ticket_updated\":\n case \"support_ticket_updated\":\n if (domain && domain !== \"support\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"ticketId\", \"status\"], strict)) return UNHANDLED_TRACK;\n return handled(this.support.client.updateTicket(properties as Parameters<SupportAnalytics[\"updateTicket\"]>[0]));\n\n case \"support.csat\":\n case \"support_csat\":\n if (domain && domain !== \"support\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"ticketId\", \"userId\", \"score\"], strict)) return UNHANDLED_TRACK;\n return handled(this.support.client.trackCSAT(properties as Parameters<SupportAnalytics[\"trackCSAT\"]>[0]));\n\n case \"support.feature_complaint\":\n case \"support_feature_complaint\":\n if (domain && domain !== \"support\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"featureId\", \"complaintType\", \"severity\"], strict)) return UNHANDLED_TRACK;\n return handled(this.support.client.trackFeatureComplaint(properties as Parameters<SupportAnalytics[\"trackFeatureComplaint\"]>[0]));\n\n case \"acquisition.acquired\":\n case \"acquisition_acquired\":\n if (domain && domain !== \"acquisition\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"source\"], strict)) return UNHANDLED_TRACK;\n return handled(this.acquisition.client.trackAcquisition(properties as Parameters<AcquisitionAnalytics[\"trackAcquisition\"]>[0]));\n\n case \"acquisition.lifecycle_transition\":\n case \"acquisition_lifecycle_transition\":\n if (domain && domain !== \"acquisition\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"fromStage\", \"toStage\"], strict)) return UNHANDLED_TRACK;\n return handled(this.acquisition.client.trackLifecycleTransition(properties as Parameters<AcquisitionAnalytics[\"trackLifecycleTransition\"]>[0]));\n\n case \"acquisition.campaign_spend\":\n case \"acquisition_campaign_spend\":\n if (domain && domain !== \"acquisition\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"campaignId\", \"amount\", \"date\"], strict)) return UNHANDLED_TRACK;\n return handled(this.acquisition.client.trackCampaignSpend(properties as unknown as Parameters<AcquisitionAnalytics[\"trackCampaignSpend\"]>[0]));\n\n case \"paths.transition\":\n case \"paths_transition\":\n if (domain && domain !== \"paths\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"fromScreen\", \"toScreen\"], strict)) return UNHANDLED_TRACK;\n return handled(this.paths.client.trackTransition(properties as Parameters<PathAnalytics[\"trackTransition\"]>[0]));\n\n case \"paths.feature_sequence\":\n case \"paths_feature_sequence\":\n if (domain && domain !== \"paths\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"feature\", \"order\"], strict)) return UNHANDLED_TRACK;\n return handled(this.paths.client.trackFeatureSequence(properties as unknown as Parameters<PathAnalytics[\"trackFeatureSequence\"]>[0]));\n\n case \"security.login_attempt\":\n case \"security_login_attempt\":\n if (domain && domain !== \"security\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"success\"], strict)) return UNHANDLED_TRACK;\n return handled(this.security.client.trackLoginAttempt(properties as Parameters<SecurityAnalytics[\"trackLoginAttempt\"]>[0]));\n\n case \"security.role_change\":\n case \"security_role_change\":\n if (domain && domain !== \"security\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"changedBy\", \"previousRole\", \"newRole\"], strict)) return UNHANDLED_TRACK;\n return handled(this.security.client.trackRoleChange(properties as Parameters<SecurityAnalytics[\"trackRoleChange\"]>[0]));\n\n case \"security.permission_change\":\n case \"security_permission_change\":\n if (domain && domain !== \"security\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"changedBy\", \"permission\", \"action\"], strict)) return UNHANDLED_TRACK;\n return handled(this.security.client.trackPermissionChange(properties as Parameters<SecurityAnalytics[\"trackPermissionChange\"]>[0]));\n\n case \"security.data_access\":\n case \"security_data_access\":\n if (domain && domain !== \"security\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"resourceType\", \"accessType\"], strict)) return UNHANDLED_TRACK;\n return handled(this.security.client.trackDataAccess(properties as Parameters<SecurityAnalytics[\"trackDataAccess\"]>[0]));\n \n case \"security.compliance_event\":\n case \"security_compliance_event\":\n if (domain && domain !== \"security\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"userId\", \"eventType\"], strict)) return UNHANDLED_TRACK;\n return handled(this.security.client.trackComplianceEvent(properties as Parameters<SecurityAnalytics[\"trackComplianceEvent\"]>[0]));\n\n case \"multi_path_funnels.track_step\":\n case \"multi_path_funnels_track_step\":\n if (domain && domain !== \"multiPathFunnels\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"funnelId\", \"step\", \"userId\"], strict)) return UNHANDLED_TRACK;\n return handled(this.multiPathFunnels.client.trackStep(properties as Parameters<MultiPathFunnels[\"trackStep\"]>[0]));\n\n case \"multi_path_funnels.track_micro_conversion\":\n case \"multi_path_funnels_track_micro_conversion\":\n if (domain && domain !== \"multiPathFunnels\") return UNHANDLED_TRACK;\n if (!this.requireTrackProperties(eventName, properties, [\"conversionId\", \"userId\"], strict)) return UNHANDLED_TRACK;\n return handled(this.multiPathFunnels.client.trackMicroConversion(properties as Parameters<MultiPathFunnels[\"trackMicroConversion\"]>[0]));\n\n default:\n return UNHANDLED_TRACK;\n \n }\n }\n\n /**\n * Record an experiment exposure for a user.\n *\n * For the browser-cached flow, prefer:\n * ```ts\n * await fg.experiments.init([{ experimentId, variants: [...] }]);\n * const variant = fg.experiments.variant(experimentId);\n * fg.experiment(\"checkout_v2\", { userId, variant });\n * ```\n */\n experiment(\n experimentId: string,\n options: { variant: string; userId: string }\n ) {\n return this.experiments.exposure(experimentId, options.userId, options.variant);\n }\n\n /**\n * Identify a user.\n *\n * @example\n * ```ts\n * fg.identifyUser(\"u_1\", { plan: \"pro\", email: \"alice@example.com\" });\n * ```\n */\n identifyUser(userId: string, traits?: Record<string, unknown>, anonymousId?: string) {\n return this.identify.user(userId, traits, anonymousId);\n }\n\n /**\n * Bind a feature's `id` + `name` (and optional `category`) once, and get back\n * a small set of semantic helpers — `viewed` / `used` / `completed` /\n * `abandoned` — so you don't repeat the id/name on every call. Each helper is\n * a thin, explicit wrapper over `track(\"feature_usage\", …)` (full\n * routing/consent/transport). No hidden state, no lifecycle guessing: every\n * call sends exactly the action you name. The dashboard does the interpreting\n * (a *viewed* feature is Discovered, a *used* one is Adopted).\n *\n * @example\n * ```ts\n * const agent = fg.defineFeature(\"ai-agent-builder\", \"AI Agent Builder\", { category: \"automation\" });\n * agent.viewed(); // → action: \"viewed\" (Discovered + Viewed)\n * agent.used({ model: \"opus\" }); // → action: \"used\" (Adopted)\n * agent.completed(); // → action: \"completed\"\n * agent.abandoned(); // → action: \"abandoned\"\n * ```\n */\n defineFeature(\n featureId: string,\n featureName: string,\n opts: { category?: string } = {},\n ): FeatureHandle {\n const emit = (action: FeatureHandleAction, properties?: Record<string, unknown>) =>\n this.track(\n \"feature_usage\",\n {\n featureId,\n featureName,\n action,\n ...(opts.category ? { category: opts.category } : {}),\n ...(properties ?? {}),\n },\n { domain: \"feature\" },\n );\n return {\n featureId,\n featureName,\n viewed: (properties) => emit(\"viewed\", properties),\n used: (properties) => emit(\"used\", properties),\n completed: (properties) => emit(\"completed\", properties),\n abandoned: (properties) => emit(\"abandoned\", properties),\n track: (action, properties) => emit(action, properties),\n };\n }\n\n /**\n * Track a page view. Auto-detects URL/path/title/referrer in the browser.\n *\n * @example\n * ```ts\n * fg.page(); // auto-detect everything\n * fg.page(\"/pricing\", { plan: \"pro\" }); // override path + properties\n * ```\n */\n page(\n pathOrUrl?: string,\n properties?: Record<string, unknown>,\n options?: { title?: string; referrer?: string; userId?: string; sessionId?: string }\n ) {\n return this.pageViews.view(pathOrUrl, properties, options);\n }\n\n /**\n * Enable browser-side auto-instrumentation in a single call.\n *\n * On by default (opt-out with `false`):\n * - **Engagement** — session depth, action count, and active/passive/idle transitions\n * - **Replay** — DOM session recording via rrweb\n *\n * Off by default (these are handled by the flowgrid.js core script):\n * - **Sessions**, **Page views**, **Performance**, **Heatmaps**, **Attribution**\n *\n * Pass `false` for any feature to disable it. Returns a single teardown\n * function that detaches every listener; safe no-op on the server.\n *\n * @example\n * ```ts\n * const stop = fg.autoTrack({ userId: \"u_1\" });\n * // ...later\n * stop();\n * ```\n */\n autoTrack(options: {\n userId?: string;\n sessions?: boolean;\n pageViews?: boolean;\n performance?: boolean | { resources?: boolean; minDuration?: number };\n heatmaps?: boolean | { movement?: boolean };\n attribution?: boolean;\n replay?: boolean | import(\"./features/analytics/replay\").SessionReplayOptions;\n engagement?: boolean | { idleAfterMs?: number; pollMs?: number };\n passiveIdentity?: boolean | import(\"./features/analytics/identify\").PassiveIdentityOptions;\n } = {}): () => void {\n const teardowns: Array<() => void> = [];\n const ctx = { userId: options.userId };\n\n if (options.sessions === true) {\n teardowns.push(this.sessions.autoTrack(ctx.userId ? { user: { userId: ctx.userId } } : {}));\n }\n if (options.pageViews === true) {\n teardowns.push(this.pageViews.autoTrack(ctx));\n }\n if (options.performance === true || (options.performance !== false && typeof options.performance === \"object\")) {\n const perfOpts = typeof options.performance === \"object\" ? options.performance : {};\n teardowns.push(this.performance.autoTrack({ ...perfOpts, userId: ctx.userId }));\n }\n if (options.heatmaps === true || (options.heatmaps !== false && typeof options.heatmaps === \"object\")) {\n const hmOpts = typeof options.heatmaps === \"object\" ? options.heatmaps : {};\n teardowns.push(this.heatmaps.autoTrack({ ...hmOpts, userId: ctx.userId }));\n }\n if (options.engagement !== false) {\n const engOpts = typeof options.engagement === \"object\" ? options.engagement : {};\n // Engagement lives in the enterprise chunk — start async (mirrors replay pattern).\n let resolvedEngStop: (() => void) | null = null;\n let engStopRequested = false;\n void loadEnterpriseModules().then(() => {\n if (engStopRequested) return;\n resolvedEngStop = this.engagement.client.autoTrack({ ...engOpts, userId: ctx.userId });\n });\n teardowns.push(() => {\n engStopRequested = true;\n if (resolvedEngStop) try { resolvedEngStop(); } catch { /* noop */ }\n });\n }\n if (options.attribution === true && ctx.userId) {\n this.attribution.captureFromUrl(ctx.userId);\n }\n // Passive identity capture is off by default — the flowgrid.js core script\n // already runs it for snippet installs; SDK-only sites opt in here.\n if (options.passiveIdentity === true || (options.passiveIdentity !== false && typeof options.passiveIdentity === \"object\")) {\n const piOpts = typeof options.passiveIdentity === \"object\" ? options.passiveIdentity : {};\n teardowns.push(this.identify.client.autoTrackForms(piOpts));\n }\n if (options.replay !== false) {\n const replayOpts = typeof options.replay === \"object\" ? options.replay : {};\n // start() is async; track its resolved teardown so the outer stop()\n // tears down recording even if the user calls it before rrweb resolved.\n let resolvedStop: (() => void) | null = null;\n let stopRequested = false;\n void this.replay.start({ userId: ctx.userId, ...replayOpts }).then(stop => {\n if (stopRequested) { try { stop(); } catch { /* noop */ } }\n else { resolvedStop = stop; }\n });\n teardowns.push(() => {\n stopRequested = true;\n if (resolvedStop) { try { resolvedStop(); } catch { /* noop */ } }\n });\n }\n\n return () => { for (const t of teardowns) { try { t(); } catch { /* swallow */ } } };\n }\n}\n\nexport default FlowGrid;\n","/**\n * @fileoverview Declarative, attribute-driven experiment tracking (no-code).\n * @module features/core/declarativeExperiments\n *\n * @description\n * Lets a non-technical user (WordPress / Wix / Squarespace marketer) wire\n * experiment conversions, page-level goals, and exposures using ONLY HTML\n * attributes — no JavaScript. It does not contain any experiment logic itself:\n * it reads the visitor's already-assigned variant from the real engine\n * (`Experiment` in `experiment.ts`) and calls its public tracking methods, so\n * the deterministic assignment, anti-flicker, holdout and exposure semantics\n * (\"dynamic integrity\") are untouched. This is purely the wiring layer.\n *\n * @attributes\n * data-fg-goal=\"<experimentId>\" Mark an element as a conversion trigger for\n * that experiment. Use \"*\" for every experiment\n * the visitor is currently bucketed into.\n * data-fg-metric=\"signup\" Metric name recorded (default \"conversion\").\n * data-fg-value=\"49\" Numeric value, e.g. order total (default 1).\n * data-fg-on=\"click\" Trigger: click (default) | load | submit | view.\n * click — on element click\n * load — once on page load (e.g. thank-you page)\n * submit — on the element's (or nearest) form submit\n * view — once when scrolled into view\n * data-fg-event=\"<name>\" Fire a generic `track(name)` event on the same\n * trigger (page-level/interaction tracking). Can be\n * combined with or used instead of data-fg-goal.\n *\n * @example\n * ```html\n * <!-- count a click on the signup button as a conversion for the hero test -->\n * <button data-fg-goal=\"homepage-hero-headline\" data-fg-metric=\"signup\">Start free</button>\n *\n * <!-- a thank-you page: count the visit as a conversion for every active test -->\n * <body data-fg-goal=\"*\" data-fg-on=\"load\" data-fg-metric=\"purchase\">\n * ```\n *\n * Robust to dynamically-injected DOM: a MutationObserver re-scans on changes, so\n * page builders and SPA route changes keep working with zero extra calls.\n */\n\n/** Minimal surface of the FlowGrid instance this layer depends on. */\nexport interface DeclarativeExperimentHost {\n experiments: {\n // NOTE: this is the ExperimentTracker wrapper's method name (`localConversion`),\n // not the underlying client's (`trackLocalConversion`). Getting this wrong is\n // a silent no-op at runtime (the call is undefined), so keep it in sync with\n // src/lib/tracking/core/experiment.ts.\n localConversion(experimentId: string, metricName?: string, value?: number): Promise<void> | void;\n getAssignments(): Record<string, string>;\n };\n track(eventName: string, properties?: Record<string, unknown>): unknown;\n}\n\nconst WIRED_ATTR = \"data-fg-wired\";\nconst GOAL_ATTR = \"data-fg-goal\";\nconst EVENT_ATTR = \"data-fg-event\";\n\ntype Trigger = \"click\" | \"load\" | \"submit\" | \"view\";\n\nfunction parseTrigger(raw: string | null): Trigger {\n switch ((raw || \"\").toLowerCase()) {\n case \"load\": return \"load\";\n case \"submit\": return \"submit\";\n case \"view\": return \"view\";\n default: return \"click\";\n }\n}\n\n/** Resolve the experiment ids a `data-fg-goal` value targets. \"*\" = all active. */\nfunction resolveExperimentIds(host: DeclarativeExperimentHost, goal: string): string[] {\n const trimmed = goal.trim();\n if (trimmed === \"*\") {\n try { return Object.keys(host.experiments.getAssignments()); } catch { return []; }\n }\n // Allow a comma-separated list of ids for multi-experiment conversions.\n return trimmed.split(\",\").map((s) => s.trim()).filter(Boolean);\n}\n\n/**\n * Run the element's goal + event side effects once. `trackLocalConversion` is a\n * safe no-op when the visitor isn't bucketed into a given experiment (control,\n * holdout, or no running test), so firing for every resolved id is harmless.\n */\nfunction fire(host: DeclarativeExperimentHost, el: Element): void {\n const goal = el.getAttribute(GOAL_ATTR);\n if (goal) {\n const metric = el.getAttribute(\"data-fg-metric\") || \"conversion\";\n const rawValue = el.getAttribute(\"data-fg-value\");\n const value = rawValue != null && rawValue.trim() !== \"\" && Number.isFinite(Number(rawValue))\n ? Number(rawValue)\n : 1;\n for (const id of resolveExperimentIds(host, goal)) {\n try { void host.experiments.localConversion(id, metric, value); } catch { /* ignore */ }\n }\n }\n\n const eventName = el.getAttribute(EVENT_ATTR);\n if (eventName) {\n try { \n host.track(eventName, { source: \"declarative\", trigger: el.getAttribute(\"data-fg-on\") || \"click\" }); \n } catch { /* ignore */ }\n }\n}\n\n/**\n * Wire every `[data-fg-goal]` / `[data-fg-event]` element under `root` that\n * isn't already wired, according to its trigger. Idempotent per element.\n */\nexport function wireDeclarativeExperiments(host: DeclarativeExperimentHost): {\n rescan: () => void;\n flushLoad: () => void;\n disconnect: () => void;\n} {\n if (typeof document === \"undefined\") {\n return { rescan: () => {}, flushLoad: () => {}, disconnect: () => {} };\n }\n\n // Whether the engine has had a chance to resolve assignments. `load`/`view`\n // goals discovered before this is true are deferred so a conversion is never\n // recorded before the visitor is bucketed.\n let assignmentsReady = false;\n const pendingImmediate: Element[] = [];\n let viewObserver: IntersectionObserver | null = null;\n\n const fireOrDefer = (el: Element) => {\n if (assignmentsReady) fire(host, el);\n else pendingImmediate.push(el);\n };\n\n const wireElement = (el: Element) => {\n if (el.hasAttribute(WIRED_ATTR)) return;\n el.setAttribute(WIRED_ATTR, \"\");\n const trigger = parseTrigger(el.getAttribute(\"data-fg-on\"));\n\n switch (trigger) {\n case \"click\":\n el.addEventListener(\"click\", () => fire(host, el));\n break;\n case \"submit\": {\n const form = el.tagName === \"FORM\" ? (el as HTMLFormElement) : el.closest(\"form\");\n (form || el).addEventListener(\"submit\", () => fire(host, el));\n break;\n }\n case \"view\":\n if (typeof IntersectionObserver !== \"undefined\") {\n if (!viewObserver) {\n viewObserver = new IntersectionObserver((entries, obs) => {\n for (const entry of entries) {\n if (entry.isIntersecting) {\n obs.unobserve(entry.target);\n fireOrDefer(entry.target);\n }\n }\n }, { threshold: 0.5 });\n }\n viewObserver.observe(el);\n } else {\n fireOrDefer(el); // no IO support — count it as seen\n }\n break;\n case \"load\":\n fireOrDefer(el);\n break;\n }\n };\n\n const scan = (root: ParentNode = document) => {\n const sel = `[${GOAL_ATTR}]:not([${WIRED_ATTR}]),[${EVENT_ATTR}]:not([${WIRED_ATTR}])`;\n root.querySelectorAll(sel).forEach(wireElement);\n };\n\n scan();\n\n // Re-scan when the DOM changes (page builders, SPA navigation, lazy content).\n const observer = typeof MutationObserver !== \"undefined\"\n ? new MutationObserver((mutations) => {\n for (const m of mutations) {\n m.addedNodes.forEach((node) => {\n if (node.nodeType === 1) scan(node as Element);\n });\n }\n })\n : null;\n observer?.observe(document.documentElement, { childList: true, subtree: true });\n\n return {\n rescan: () => scan(),\n /**\n * Mark assignments as resolved and fire any `load`/`view` goals that were\n * discovered before the engine had bucketed the visitor. Call once\n * `initFromServer()` settles. Idempotent.\n */\n flushLoad: () => {\n if (assignmentsReady) return;\n assignmentsReady = true;\n const queued = pendingImmediate.splice(0, pendingImmediate.length);\n for (const el of queued) fire(host, el);\n },\n disconnect: () => {\n try { observer?.disconnect(); } catch { /* ignore */ }\n try { viewObserver?.disconnect(); } catch { /* ignore */ }\n },\n };\n}\n","/**\n * @fileoverview FlowGrid SDK - Browser Bundle\n * @module browser\n *\n * @description\n * Minimal script-tag facade over the unified FlowGrid SDK. Browser users get a\n * tiny top-level API for the common path, while advanced users can call\n * `FlowGrid.instance()` and use the full SDK namespaces.\n *\n * @example\n * ```html\n * <script src=\"https://cdn.flow-grid.xyz/flowgrid.min.js\"></script>\n * <script>\n * FlowGrid.init({ webId: 'your-web-id', apiKey: 'your-api-key' });\n * FlowGrid.track('button_click', { button: 'signup' });\n * FlowGrid.page();\n * FlowGrid.identify('user_123', { plan: 'pro' });\n * </script>\n * ```\n */\n\nimport { FlowGrid, FlowGridInitConfig } from \"./lib/flowgrid\";\nimport type { ConsentCategory } from \"./lib/client/transport\";\nimport type { Order, Product } from \"./lib/types\";\nimport { wireDeclarativeExperiments } from \"./lib/features/core/declarativeExperiments\";\n\ntype BrowserProduct = Pick<Product, \"productId\" | \"name\" | \"price\"> & Partial<Product>;\n\ntype BrowserOrder = {\n orderId: string;\n total: number;\n currency?: Product[\"currency\"];\n items: Array<Pick<Product, \"productId\" | \"name\" | \"price\"> & Partial<Product> & { quantity: number }>;\n subtotal?: number;\n discountTotal?: number;\n shippingTotal?: number;\n taxTotal?: number;\n};\n\ntype BrowserFacade = {\n init(config: FlowGridInitConfig): FlowGrid;\n instance(): FlowGrid | null;\n reset(): void;\n setConsent(consent: Partial<Record<ConsentCategory, boolean>>): void;\n hasConsent(category: ConsentCategory): boolean;\n autoTrack(options?: Parameters<FlowGrid[\"autoTrack\"]>[0]): () => void;\n track: FlowGrid[\"track\"];\n page: FlowGrid[\"page\"];\n identify: FlowGrid[\"identifyUser\"];\n defineFeature: FlowGrid[\"defineFeature\"];\n experiment: FlowGrid[\"experiment\"];\n wireExperiments(): void;\n productView(product: BrowserProduct, properties?: Record<string, unknown>): ReturnType<FlowGrid[\"products\"][\"view\"]>;\n addToCart(product: BrowserProduct, quantity?: number, cartId?: string): ReturnType<FlowGrid[\"cart\"][\"add\"]>;\n purchase(order: BrowserOrder): ReturnType<FlowGrid[\"purchases\"][\"complete\"]>;\n};\n\nfunction getInstance(): FlowGrid {\n const instance = FlowGrid.instance();\n if (!instance) {\n throw new Error(\"FlowGrid not initialized. Call FlowGrid.init() first.\");\n }\n return instance;\n}\n\nfunction normalizeProduct(product: BrowserProduct): Product {\n return {\n ...product,\n currency: product.currency ?? \"USD\",\n };\n}\n\nfunction normalizeOrder(order: BrowserOrder): Order {\n const currency = order.currency ?? \"USD\";\n const items = order.items.map((item) => ({\n ...item,\n currency: item.currency ?? currency,\n quantity: item.quantity,\n lineTotal: item.price * item.quantity,\n }));\n const subtotal = order.subtotal ?? items.reduce((sum, item) => sum + item.lineTotal, 0);\n\n return {\n orderId: order.orderId,\n items,\n subtotal,\n discountTotal: order.discountTotal ?? 0,\n shippingTotal: order.shippingTotal ?? 0,\n taxTotal: order.taxTotal ?? 0,\n total: order.total,\n currency,\n };\n}\n\nconst browserFlowGrid: BrowserFacade = {\n init(config) {\n return FlowGrid.init(config);\n },\n\n instance() {\n return FlowGrid.instance();\n },\n\n reset() {\n FlowGrid.reset();\n },\n\n setConsent(consent) {\n FlowGrid.setConsent(consent);\n },\n\n hasConsent(category) {\n return FlowGrid.hasConsent(category);\n },\n\n autoTrack(options) {\n return getInstance().autoTrack(options);\n },\n\n track(eventName, properties, options) {\n return getInstance().track(eventName, properties, options);\n },\n\n page(pathOrUrl, properties, options) {\n return getInstance().page(pathOrUrl, properties, options);\n },\n\n identify(userId, traits, anonymousId) {\n return getInstance().identifyUser(userId, traits, anonymousId);\n },\n\n defineFeature(featureId, featureName, opts) {\n return getInstance().defineFeature(featureId, featureName, opts);\n },\n\n experiment(experimentId, options) {\n return getInstance().experiment(experimentId, options);\n },\n\n // Manually (re-)scan the DOM for `data-fg-*` experiment attributes. The\n // no-build path wires this automatically; SPA/programmatic users call it\n // after a client-side navigation re-renders the page.\n wireExperiments() {\n const wiring = wireDeclarativeExperiments(getInstance());\n wiring.flushLoad();\n },\n\n productView(product, properties) {\n return getInstance().products.view(normalizeProduct(product), properties);\n },\n\n addToCart(product, quantity = 1, cartId) {\n return getInstance().cart.add(normalizeProduct(product), quantity, cartId);\n },\n\n purchase(order) {\n return getInstance().purchases.complete(normalizeOrder(order));\n },\n};\n\ndeclare global {\n interface Window {\n FlowGrid: BrowserFacade;\n flowgrid: BrowserFacade;\n /**\n * Optional full config for the no-build auto-init path. Set this *before*\n * the `<script>` tag to enable replay, autoTrack tuning, user identify,\n * consent, custom endpoint/transport, etc. without writing an `init()` call.\n * `webId` may be omitted here and supplied via `data-site` instead.\n */\n FlowGridConfig?: Partial<FlowGridInitConfig>;\n }\n}\n\n/**\n * Read init config from the loading `<script>` tag's data attributes, so a\n * no-build install can self-initialize:\n *\n * ```html\n * <script src=\"https://cdn.flow-grid.xyz/flowgrid.min.js\" data-site=\"YOUR_WEBSITE_ID\"></script>\n * ```\n *\n * Config sources, lowest → highest precedence:\n * 1. `window.FlowGridConfig` — a full {@link FlowGridInitConfig} object set\n * *before* the tag loads. This is how the no-build path enables replay,\n * autoTrack tuning, user identify, consent, custom transport, etc. — every\n * option, declaratively, with no imperative `init()` call.\n * 2. `data-*` attributes on the tag (`data-site`/`data-website-id`/`data-web-id`,\n * `data-api-key`, `data-endpoint`, `data-replay`).\n *\n * `data-manual` suppresses auto-init entirely (call `FlowGrid.init()` yourself).\n * `apiKey` is optional (web_id-only mode) but recommended. Returns `null` when\n * there's no resolvable web id or auto-init is suppressed, leaving the manual\n * `FlowGrid.init({...})` path fully in control.\n */\nfunction readAutoInitConfig(): { config: FlowGridInitConfig; runExperiments: boolean } | null {\n const el =\n typeof document !== \"undefined\"\n ? (document.currentScript as HTMLScriptElement | null)\n : null;\n const attr = (name: string): string | null =>\n el && typeof el.getAttribute === \"function\" ? el.getAttribute(name) : null;\n\n // Programmatic users opt out of auto-init and configure everything in code.\n if (attr(\"data-manual\") != null) return null;\n\n const fromGlobal =\n typeof window !== \"undefined\" ? window.FlowGridConfig : undefined;\n\n const webId =\n attr(\"data-site\") ||\n attr(\"data-website-id\") ||\n attr(\"data-web-id\") ||\n fromGlobal?.webId;\n if (!webId) return null;\n\n // Spread the global config first so its replay/autoTrack/user/consent/transport\n // survive; data-* attributes (and the resolved webId) win where present.\n const config: FlowGridInitConfig = {\n ...fromGlobal,\n webId,\n apiKey: attr(\"data-api-key\") ?? fromGlobal?.apiKey,\n endpoint: attr(\"data-endpoint\") ?? fromGlobal?.endpoint,\n };\n\n // Simple on/off replay via attribute (the options-object form goes through\n // FlowGridConfig). Don't override an explicit FlowGridConfig.replay.\n const replayAttr = attr(\"data-replay\");\n if (replayAttr != null && config.replay === undefined) {\n config.replay = replayAttr !== \"false\";\n }\n\n return { config, runExperiments: attr(\"data-no-experiments\") == null };\n}\n\nif (typeof window !== \"undefined\") {\n // `FlowGrid` is always the SDK facade. `flowgrid` (lowercase) may already be\n // claimed by the core flowgrid.js snippet (a different API) — don't clobber it.\n window.FlowGrid = browserFlowGrid;\n if (typeof window.flowgrid === \"undefined\") {\n window.flowgrid = browserFlowGrid;\n }\n\n // Auto-init from FlowGridConfig / data-* (no-build path). Skipped entirely if\n // an instance already exists (a synchronous manual init won) or `data-manual`\n // is set — so full programmatic configuration is never locked out.\n const auto = readAutoInitConfig();\n if (auto && !FlowGrid.instance()) {\n try {\n const fg = FlowGrid.init(auto.config);\n // Zero-config experiments: assign + apply the site's dashboard-configured\n // tests with no author JS. Opt out with `data-no-experiments` on the tag.\n if (auto.runExperiments) {\n // Wire the declarative `data-fg-*` attribute layer now (click/submit/view\n // listeners + a MutationObserver for dynamically-injected DOM), then flush\n // any page-level `load` goals once assignment settles — so a marketer needs\n // only HTML attributes to record conversions, never JavaScript.\n const wiring = wireDeclarativeExperiments(fg);\n fg.experiments\n .initFromServer()\n .catch(() => {})\n .then(() => wiring.flushLoad());\n }\n } catch {\n /* init surfaces its own error; never throw from the bundle's top level */\n }\n }\n}\n\nexport { FlowGrid };\nexport type { BrowserFacade, BrowserOrder, BrowserProduct };\nexport default browserFlowGrid;\n"],"names":["root","factory","exports","module","define","amd","this","inProgress","dataWebpackPrefix","FlowGridTransport","constructor","webId","endpoint","apiKey","visitorId","identityManager","config","trim","Error","normalizeEndpoint","transportConfig","botDetection","respectDNT","sampleRate","drainEventBuffer","setConsent","consent","Object","assign","hasConsent","category","isLikelyBot","navigator","webdriver","test","userAgent","isDNTEnabled","globalPrivacyControl","doNotTrack","getDeviceContext","window","loc","location","doc","document","undefined","_screen_width","screen","width","_screen_height","height","_device_type","_language","language","_url","href","_page","pathname","_referrer","referrer","_title","title","getUtmContext","params","URLSearchParams","search","out","name","ATTRIBUTION_KEYS","value","get","sendViaBeacon","eventName","properties","sendBeacon","resolvedVisitorId","getVisitorId","callType","type","url","encodeURIComponent","payload","JSON","stringify","web_id","visitor_id","event_name","Blob","postRaw","body","options","analytics","isUnload","useBeacon","payloadSize","length","useKeepalive","beaconBody","parse","api_key","console","warn","err","res","fetch","method","headers","keepalive","ok","status","statusText","bufferEvent","localStorage","raw","getItem","EVENT_BUFFER_KEY","buffer","MAX_BUFFER_EVENTS","serialized","TextEncoder","encode","MAX_BUFFER_SIZE","push","setItem","removeItem","evt","catch","init","validateInput","_bot","enriched","executeRequest","initFeature","extraHeaders","attempt","MAX_RETRIES","controller","AbortController","timeoutId","setTimeout","abort","TIMEOUT_MS","signal","clearTimeout","backoff","text","safeRead","json","error","message","includes","ceiling","Math","min","pow","ms","random","Promise","resolve","MAX_PAYLOAD_SIZE","sensitiveKeys","lower","toLowerCase","key","clean","startsWith","replace","marketing","getWindow","getDocument","isBrowser","combineTeardowns","fns","fn","getCookie","match","cookie","RegExp","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__","m","d","definition","o","defineProperty","enumerable","f","e","chunkId","all","keys","reduce","promises","u","g","globalThis","Function","obj","prop","prototype","hasOwnProperty","call","l","done","script","needAttach","scripts","getElementsByTagName","i","s","getAttribute","createElement","charset","nc","setAttribute","src","onScriptComplete","prev","event","onerror","onload","timeout","doneFns","parentNode","removeChild","forEach","bind","target","head","appendChild","r","Symbol","toStringTag","scriptUrl","importScripts","currentScript","tagName","toUpperCase","p","installedChunks","j","installedChunkData","promise","reject","errorType","realSrc","request","webpackJsonpCallback","parentChunkLoadingFunction","data","chunkIds","moreModules","runtime","some","id","chunkLoadingGlobal","LEGACY_VISITOR_COOKIE","SESSION_KEY","SESSION_TOUCH_KEY","IdentityManager","sessionId","memoryStore","cookieDomain","visitorMaxAgeDays","sessionTimeoutMinutes","storage","detectBrowser","serverCookies","compat","resolveVisitorId","getSessionId","getMemorySessionId","existing","sessionStorage","lastTouch","Date","now","parseInt","String","newSession","generateSessionId","setVisitorId","persistVisitorId","clearSession","clearAll","deleteCookie","visitorCookieName","trackJsId","legacyId","readCookie","readLocalStorage","removeLegacyCookie","cookieId","lsId","serverVal","readServerCookie","newId","generateVisitorId","cookieName","writeCookie","result","maxAgeDays","maxAge","domain","secure","protocol","uuidV4","randomHex","crypto","randomUUID","c","toString","bytes","Uint8Array","getRandomValues","floor","Array","from","b","padStart","join","Activation","create","timestamp","toISOString","trackOnboardingStep","subtype","userId","stepId","stepName","stepOrder","totalSteps","progress","round","timeSpent","skipped","completeOnboarding","totalTimeSpent","stepsCompleted","stepsSkipped","completionRate","trackFirstAction","actionName","timeSinceSignup","source","trackSignup","TrackFeature","action","trackUsage","featureId","featureName","duration","success","TrackPrompt","trackSubmission","promptId","promptType","model","inputTokens","conversationId","trackResponse","responseTime","outputTokens","completed","qualityScore","trackFeedback","feedbackType","rating","comment","trackConversation","turnCount","totalDuration","fnv1a","input","hash","charCodeAt","ASSIGNMENT_STORAGE_KEY","ASSIGNMENT_COOKIE_KEY","Experiment","experiments","assignments","antiFlickerStyleEl","antiFlickerTimer","appliedExperiments","Set","parseAssignmentCookie","decoded","decodeURIComponent","parsed","readAssignmentCookie","writeAssignmentCookie","ASSIGNMENT_COOKIE_MAX_AGE_DAYS","sameSite","loadAssignments","stored","cookieAssignments","saveAssignments","injectAssignments","getAssignments","hydrateFromCookies","reader","hydrateFromCookieHeader","cookieHeader","parts","split","prefix","find","slice","serializeAssignmentCookie","clearAssignments","getOrCreateAnonId","ANON_ID_KEY","deterministicAssign","experimentId","variants","hashValue","totalWeight","v","weight","bucket","cumulative","assignVariant","exclusionGroupMembers","winnerId","winner_id","variantId","assignmentSource","holdoutPct","isInHoldout","exclusionGroup","exclusion_group","groupMemberIds","members","sort","filterMutuallyExclusive","assignedVariantId","computeExclusionGroupMembers","items","groups","Map","item","group","has","set","experiment_id","assignFromLocalConfigs","configs","groupMembers","map","exp","verified","verifyExperiments","response","isArray","initExperiments","selectors","collectChangeSelectors","injectAntiFlicker","applyAssignedChanges","removeAntiFlicker","variantChanges","variant","redirectUrl","changes","add","selector","style","textContent","documentElement","remove","applyDomChange","change","querySelectorAll","el","innerHTML","display","setProperty","property","classList","filter","Boolean","entries","redirect","URL","trackLocalExposure","domChanges","run","readyState","addEventListener","once","applyVariant","delete","initFromServer","getLocalVariant","trackLocalConversion","metricName","metricValue","trackExposure","track","trackConversion","PageViews","path","trackScrollDepth","scrollDepth","max","timeAtDepth","trackTimeOnPage","engaged","trackExit","autoTrack","win","currentPath","pageStart","maxDepth","sentDepths","fireView","flushTimeOnPage","onRouteChange","clear","origPush","history","pushState","origReplace","replaceState","args","apply","dispatchEvent","Event","popHandler","locHandler","onScroll","docEl","scrollTop","scrollY","scrollHeight","innerHeight","depth","t","passive","onHide","visibilityState","removeEventListener","Sessions","start","user","device","utm","utmSource","medium","utmMedium","campaign","utmCampaign","landingPage","update","end","heartbeat","heartbeatMs","STORE_KEY","startPromise","then","beat","setInterval","clearInterval","Events","label","nonInteraction","trackBatch","events","count","trackClick","trackForm","trackSearch","query","resultsCount","resultClicked","clickedPosition","filters","trackError","stack","fatal","context","userAction","Identify","anonymousId","traits","updateTraits","alias","previousId","groupId","reset","autoTrackForms","debounceMs","EXCLUDED_PATH_RE","excludePath","BLOCKED_RE","SKIP_RE","FIELD_MAP","re","trait","pii","identify","captured","seen","WeakSet","debounceTimers","WeakMap","teardowns","classify","normalize","contains","isBlocked","blocked","resolveSessionId","sendIdentity","email","emailHash","firstName","lastName","phone","email_hash","phone_hash","phoneHash","first_name","last_name","company","address","city","postcode","session_id","path_name","captureField","cls","async","enc","buf","subtle","digest","sha256hex","attach","onBlur","onInput","scanPage","onSubmit","form","phoneOnSubmit","mo","MutationObserver","observe","childList","subtree","disconnect","Funnels","funnelId","description","steps","trackStep","step","stepNumber","trackComplete","trackAbandon","lastStep","reason","analyze","range","compare","baselineFilter","comparisonFilter","list","Retention","trackActivity","trackLifecycleChange","stage","previousStage","Attribution","trackTouchpoint","channel","content","term","recordConversion","conversionType","conversionId","captureFromUrl","utmTerm","utmContent","Heatmaps","lastMovementTime","lastScrollTime","pageUrl","x","y","normalizedX","viewportWidth","normalizedY","viewportHeight","elementSelector","elementText","substring","trackMovement","MOVEMENT_THROTTLE_MS","recorded","interactionId","viewport","trackScroll","SCROLL_THROTTLE_MS","maxScrollDepth","pageHeight","timeAtDepths","trackRageClick","clickCount","onClick","clientX","clientY","innerWidth","className","capture","scrollFlushScheduled","flushScroll","top","moveTeardown","movement","onMove","PerformanceTracking","trackWebVitals","trackPageLoad","trackResource","trackApiCall","statusCode","cancelled","wv","send","metric","onLCP","onCLS","onINP","onFCP","onTTFB","onFID","reportNavigation","nav","performance","getEntriesByType","dnsTime","domainLookupEnd","domainLookupStart","tcpTime","connectEnd","connectStart","ttfb","responseStart","requestStart","domContentLoaded","domContentLoadedEventEnd","loadTime","loadEventEnd","domInteractive","transferSize","onLoad","resources","PerformanceObserver","minDuration","obs","entry","getEntries","initiator","initiatorType","resourceUrl","resourceType","buffered","hasReplayConsent","STORAGE_KEY","RECORDER_VERSION","SessionReplay","disabled","active","state","enabled","setState","stopped","emitStatus","stop","requireConsent","rrweb","handleError","toError","phase","normalizedOptions","normalizeOptions","startedAtMs","boundUserId","startedAt","sequence","bufferBytes","totalBytes","droppedEvents","initialSnapshotCaptured","flushTimer","flush","flushIntervalMs","durationGuard","maxDurationMs","onVisibility","onPageHide","stopRecording","record","emit","captureEvent","maskAllInputs","maskTextClass","blockClass","ignoreClass","checkoutEveryNth","checkoutEveryNms","inlineImages","collectFonts","cleanupActive","enable","disable","isFinal","eventsCount","byteSize","capturedAt","idempotencyKey","createIdempotencyKey","metadata","postReplayChunk","getPageUrl","privacy","createPrivacyMetadata","recorderVersion","onFlush","getStatus","createStatus","sendChunk","measureEvents","user_id","started_at","captured_at","page_url","idempotency_key","compression","is_final","chunk_kind","flush_reason","dropped_events","recorder_version","size","onDrop","maxBytes","flushBytes","bufferedEvents","bufferedBytes","lastFlushReason","lastError","onStatus","onError","samplingState","consentGranted","masking_mode","blocked_selectors","selectorList","redacted_fields","consent_state","sampling_state","fresh","generateId","cryptoObj","Products","view","product","productId","brand","price","currency","imageUrl","reviewCount","stockStatus","attributes","searchQuery","impression","productCount","products","click","listId","position","quickView","interaction","CartTracking","addItem","lineTotal","quantity","discount","cartId","updateItem","previousQuantity","newQuantity","quantityChange","cart","cartValue","total","itemCount","abandon","applyCoupon","couponCode","Checkout","begin","option","addShipping","shipping","shippingMethod","shippingCost","cost","addPayment","payment","paymentMethod","paymentAttempt","errorCode","Purchases","order","orderId","subtotal","discountTotal","shippingTotal","taxTotal","coupons","affiliation","revenue","sum","attribution","statusChange","newStatus","previousStatus","shipped","delivered","deliveredAt","Refunds","refundId","amount","isFullRefund","initiatedBy","approve","approvedAmount","deny","denialReason","complete","processedAmount","Promotions","promotionId","creative","page","destinationUrl","couponUsed","code","discountValue","discountType","orderValueBefore","orderValueAfter","savings","couponAttempt","failureReason","WishlistTracking","wishlistId","wishlist","moveToCart","share","CustomerLTV","recordAcquisitionCost","updateSegment","segment","SearchAnalytics","hasResults","sortBy","trackRefinement","originalQuery","refinedQuery","refinementType","trackSuggestion","partialQuery","selectedSuggestion","suggestionPosition","Subscriptions","subscriptionId","plan","mrr","billingCycle","trialDays","coupon","isTrialing","mrrDiff","newMrr","previousMrr","previousPlan","newPlan","mrrChange","changeType","cancel","lostMrr","feedback","immediate","renew","trialConverted","trialExpired","ActivationTracker","client","signup","props","onboardingStep","extra","onboardingComplete","firstAction","FeatureTracker","viewed","used","abandoned","PromptTracker","submitted","responded","conversation","ExperimentTracker","exposure","exposureAuto","conversion","localConversion","PageViewTracker","pathOrUrl","opts","browserDefaults","isFullUrl","scroll","time","exit","SessionTracker","EventTracker","batch","elementId","formId","formName","IdentifyTracker","FunnelTracker","RetentionTracker","AttributionTracker","touchpoint","HeatmapTracker","PerformanceTracker","webVitals","vitals","pageLoad","timings","resource","ReplayTracker","ProductTracker","listName","CartTracker","CheckoutTracker","PurchaseTracker","RefundTracker","PromotionTracker","WishlistTracker","LTVTracker","acquisitionCost","newSegment","SearchTracker","refine","suggestion","SubscriptionTracker","_enterpriseFeatures","_enterpriseTracking","loadEnterpriseModules","features","tracking","UNHANDLED_TRACK","handled","PRODUCT_KEYS","stringValue","numberValue","Number","isFinite","booleanValue","recordValue","isRecord","hasValue","omitProperties","next","productFrom","nested","FlowGrid","_instance","fg","transport","identifyUser","replay","autoOpts","sessions","pageViews","heatmaps","engagement","_autoTrackStop","instance","loadEnterprise","_raw","_wrapped","Klass","wrap","Wrapper","activation","prompts","funnels","retention","checkout","purchases","refunds","promotions","ltv","subscriptions","_requireEnterprise","ent","entT","Engagement","EngagementTracker","cohorts","Cohorts","CohortTracker","churn","ChurnAnalytics","ChurnTracker","monetization","Monetization","MonetizationTracker","multiPathFunnels","MultiPathFunnels","MultiPathFunnelTracker","support","SupportAnalytics","SupportTracker","acquisition","AcquisitionAnalytics","AcquisitionTracker","paths","PathAnalytics","PathTracker","alerts","AlertsAnalytics","AlertTracker","security","SecurityAnalytics","SecurityTracker","forecasting","Forecasting","ForecastingTracker","contextualProperties","withTrackContext","routed","dispatchTrack","requireTrackProperties","strict","missing","dispatchers","dispatchActivationTrack","dispatchFeatureTrack","dispatchPromptTrack","dispatchExperimentTrack","dispatchAnalyticsTrack","dispatchEcommerceTrack","dispatchEnterpriseTrack","dispatch","feature_viewed","feature_used","feature_completed","feature_abandoned","trackDwellTime","trackSessionDepth","trackContentConsumption","trackUserActivity","trackUsageDecay","trackNPS","trackChurn","trackPreventionAction","trackRevenue","trackTicket","updateTicket","trackCSAT","trackFeatureComplaint","trackAcquisition","trackLifecycleTransition","trackCampaignSpend","trackTransition","trackFeatureSequence","trackLoginAttempt","trackRoleChange","trackPermissionChange","trackDataAccess","trackComplianceEvent","trackMicroConversion","experiment","defineFeature","ctx","perfOpts","hmOpts","engOpts","resolvedEngStop","engStopRequested","passiveIdentity","piOpts","replayOpts","resolvedStop","stopRequested","WIRED_ATTR","GOAL_ATTR","EVENT_ATTR","fire","host","goal","rawValue","trimmed","resolveExperimentIds","trigger","wireDeclarativeExperiments","rescan","flushLoad","assignmentsReady","pendingImmediate","viewObserver","fireOrDefer","wireElement","hasAttribute","parseTrigger","closest","IntersectionObserver","isIntersecting","unobserve","threshold","scan","sel","observer","mutations","addedNodes","node","nodeType","queued","splice","getInstance","normalizeProduct","browserFlowGrid","wireExperiments","productView","addToCart","purchase","normalizeOrder","flowgrid","auto","attr","fromGlobal","FlowGridConfig","replayAttr","runExperiments","readAutoInitConfig","wiring"],"sourceRoot":""}