@layers/client 1.4.11 → 2.0.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"src-DnOWq7k2.js","names":["params: URLSearchParams","clickIdParam: string | undefined","clickIdValue: string | undefined","utms: Partial<Pick<AttributionData, (typeof UTM_PARAMS)[number]>>","data: AttributionData","props: Record<string, string>","props: Record<string, string>","overlayElement: HTMLDivElement | null","updateInterval: ReturnType<typeof setInterval> | null","lines: string[]","params: Record<string, string>","lastUrl: string","properties: EventProperties","props: EventProperties","appOpenProps: Record<string, unknown>","ctx: DeviceContext","data: WebAttributionData","merged: Record<string, unknown>","properties: Record<string, unknown>","context: DeviceContext","SDK_VERSION: string"],"sources":["../src/attribution.ts","../src/capi.ts","../src/debug-overlay.ts","../src/deep-links.ts","../src/standard-events.ts","../src/commerce.ts","../src/index.ts"],"sourcesContent":["const CLICK_ID_PARAMS = [\n 'fbclid',\n 'gclid',\n 'gbraid',\n 'wbraid',\n 'ttclid',\n 'msclkid',\n 'rclid'\n] as const;\nconst UTM_PARAMS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term'] as const;\nconst STORAGE_KEY = 'layers_attribution';\nconst TTL_MS = 30 * 24 * 60 * 60 * 1000; // 30 days\n\nexport interface AttributionData {\n click_id_param?: string;\n click_id_value?: string;\n utm_source?: string;\n utm_medium?: string;\n utm_campaign?: string;\n utm_content?: string;\n utm_term?: string;\n referrer_url?: string;\n captured_at: number;\n}\n\n/**\n * Capture attribution signals from the current page URL and referrer.\n * Persists to localStorage with a 30-day TTL. Click IDs take priority:\n * if a new click ID is present, the entire record is overwritten.\n */\nexport function captureAttribution(): void {\n if (typeof window === 'undefined' || typeof localStorage === 'undefined') return;\n\n let params: URLSearchParams;\n try {\n params = new URLSearchParams(window.location.search);\n } catch {\n return;\n }\n\n // Check for click IDs first — any click ID overwrites the stored record\n let clickIdParam: string | undefined;\n let clickIdValue: string | undefined;\n for (const param of CLICK_ID_PARAMS) {\n const value = params.get(param);\n if (value) {\n clickIdParam = param;\n clickIdValue = value;\n break; // first match wins\n }\n }\n\n // Collect UTM params\n const utms: Partial<Pick<AttributionData, (typeof UTM_PARAMS)[number]>> = {};\n let hasUtm = false;\n for (const param of UTM_PARAMS) {\n const value = params.get(param);\n if (value) {\n utms[param] = value;\n hasUtm = true;\n }\n }\n\n const referrer =\n typeof document !== 'undefined' && document.referrer ? document.referrer : undefined;\n\n // Nothing to capture\n if (!clickIdParam && !hasUtm && !referrer) return;\n\n const existing = getAttribution();\n\n // If a new click ID is present, overwrite entirely\n if (clickIdParam) {\n const data: AttributionData = {\n click_id_param: clickIdParam,\n ...(clickIdValue != null && { click_id_value: clickIdValue }),\n ...utms,\n ...(referrer != null && { referrer_url: referrer }),\n captured_at: Date.now()\n };\n writeAttribution(data);\n return;\n }\n\n // If UTMs are present, overwrite (fresh campaign visit)\n if (hasUtm) {\n const data: AttributionData = {\n // Preserve existing click ID if no new one\n ...(existing?.click_id_param != null && { click_id_param: existing.click_id_param }),\n ...(existing?.click_id_value != null && { click_id_value: existing.click_id_value }),\n ...utms,\n ...(referrer != null && { referrer_url: referrer }),\n captured_at: Date.now()\n };\n writeAttribution(data);\n return;\n }\n\n // Only referrer and no existing attribution — store it\n if (!existing) {\n const data: AttributionData = {\n ...(referrer != null && { referrer_url: referrer }),\n captured_at: Date.now()\n };\n writeAttribution(data);\n }\n}\n\n/**\n * Read stored attribution data, returning null if missing or expired.\n */\nexport function getAttribution(): AttributionData | null {\n if (typeof localStorage === 'undefined') return null;\n\n try {\n const raw = localStorage.getItem(STORAGE_KEY);\n if (!raw) return null;\n\n const data: AttributionData = JSON.parse(raw);\n if (Date.now() - data.captured_at > TTL_MS) {\n localStorage.removeItem(STORAGE_KEY);\n return null;\n }\n return data;\n } catch {\n return null;\n }\n}\n\n/**\n * Return a flat property bag suitable for merging into event properties.\n * Keys are prefixed with `$attribution_` to avoid collisions.\n */\nexport function getAttributionProperties(): Record<string, string> {\n const data = getAttribution();\n if (!data) return {};\n\n const props: Record<string, string> = {};\n\n if (data.click_id_param) props['$attribution_click_id_param'] = data.click_id_param;\n if (data.click_id_value) props['$attribution_click_id_value'] = data.click_id_value;\n if (data.utm_source) props['$attribution_utm_source'] = data.utm_source;\n if (data.utm_medium) props['$attribution_utm_medium'] = data.utm_medium;\n if (data.utm_campaign) props['$attribution_utm_campaign'] = data.utm_campaign;\n if (data.utm_content) props['$attribution_utm_content'] = data.utm_content;\n if (data.utm_term) props['$attribution_utm_term'] = data.utm_term;\n if (data.referrer_url) props['$attribution_referrer_url'] = data.referrer_url;\n\n return props;\n}\n\nfunction writeAttribution(data: AttributionData): void {\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(data));\n } catch {\n // localStorage full or unavailable — silently ignore\n }\n}\n","// CAPI (Conversions API) readiness utilities.\n// Captures Meta _fbp, TikTok _ttp cookies, page URL, and formats fbc from fbclid.\n\n/**\n * Read a named cookie from document.cookie.\n * Returns the cookie value or undefined if not found / unavailable.\n */\nfunction getCookie(name: string): string | undefined {\n if (typeof document === 'undefined' || !document.cookie) return undefined;\n\n // Cookies are separated by \"; \" — match the exact name to avoid\n // partial matches (e.g. \"x_fbp\" matching \"_fbp\").\n const prefix = `${name}=`;\n const cookies = document.cookie.split('; ');\n for (const cookie of cookies) {\n if (cookie.startsWith(prefix)) {\n return decodeURIComponent(cookie.slice(prefix.length));\n }\n }\n return undefined;\n}\n\n/**\n * Read Meta's _fbp cookie.\n * Format set by Meta Pixel: fb.1.{timestamp}.{random}\n */\nexport function getFbpCookie(): string | undefined {\n return getCookie('_fbp');\n}\n\n/**\n * Read TikTok's _ttp cookie.\n */\nexport function getTtpCookie(): string | undefined {\n return getCookie('_ttp');\n}\n\n/**\n * Capture the current page URL.\n * Required by Meta CAPI for every web event (event_source_url).\n */\nexport function getPageUrl(): string | undefined {\n if (typeof window === 'undefined') return undefined;\n try {\n return window.location.href;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Format an fbclid value into Meta's fbc cookie format.\n * Format: fb.1.{timestamp_ms}.{fbclid}\n *\n * @param fbclid - The raw fbclid parameter value from the URL\n * @param timestampMs - Optional timestamp in milliseconds (defaults to Date.now())\n */\nexport function formatFbc(fbclid: string, timestampMs?: number): string {\n const ts = timestampMs ?? Date.now();\n return `fb.1.${ts}.${fbclid}`;\n}\n\n/**\n * Get the fbc value. Checks in order:\n * 1. If an fbclid URL parameter is present, format it as fbc\n * 2. If an _fbc cookie exists, return it\n * 3. Return undefined\n */\nexport function getFbc(): string | undefined {\n // Check for fbclid in URL params\n if (typeof window !== 'undefined') {\n try {\n const params = new URLSearchParams(window.location.search);\n const fbclid = params.get('fbclid');\n if (fbclid) {\n return formatFbc(fbclid);\n }\n } catch {\n // URL parsing failed\n }\n }\n\n // Fall back to existing _fbc cookie\n return getCookie('_fbc');\n}\n\n/**\n * Build the full set of CAPI properties for an event.\n * All values are optional — only present keys are included.\n */\nexport function getCapiProperties(): Record<string, string> {\n const props: Record<string, string> = {};\n\n const fbp = getFbpCookie();\n if (fbp) props['$fbp'] = fbp;\n\n const ttp = getTtpCookie();\n if (ttp) props['$ttp'] = ttp;\n\n const pageUrl = getPageUrl();\n if (pageUrl) props['$page_url'] = pageUrl;\n\n const fbc = getFbc();\n if (fbc) props['$fbc'] = fbc;\n\n return props;\n}\n","// Lightweight DOM-based debug overlay for the Web SDK.\n// Shows: SDK version, queue depth, session ID, install ID, recent events.\n// Toggled via enableDebugOverlay() / disableDebugOverlay().\n\nexport interface DebugOverlayState {\n sdkVersion: string;\n queueDepth: number;\n sessionId: string;\n installId: string | null;\n appId: string;\n environment: string;\n isOnline: boolean;\n recentEvents: readonly string[];\n}\n\nexport type DebugOverlayStateProvider = () => DebugOverlayState;\n\nconst OVERLAY_ID = 'layers-debug-overlay';\nconst OVERLAY_STYLES = `\n position: fixed;\n bottom: 8px;\n right: 8px;\n width: 360px;\n max-height: 400px;\n background: rgba(0, 0, 0, 0.88);\n color: #e0e0e0;\n font-family: monospace;\n font-size: 11px;\n line-height: 1.5;\n padding: 12px;\n border-radius: 8px;\n z-index: 999999;\n overflow-y: auto;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);\n pointer-events: auto;\n`;\n\nlet overlayElement: HTMLDivElement | null = null;\nlet updateInterval: ReturnType<typeof setInterval> | null = null;\n\n/**\n * Create and display the debug overlay in the DOM.\n * Updates every second with the latest state from the provider.\n */\nexport function enableDebugOverlay(stateProvider: DebugOverlayStateProvider): void {\n if (typeof document === 'undefined') return;\n\n // Don't create a duplicate\n if (overlayElement) {\n disableDebugOverlay();\n }\n\n overlayElement = document.createElement('div');\n overlayElement.id = OVERLAY_ID;\n overlayElement.setAttribute('style', OVERLAY_STYLES);\n document.body.appendChild(overlayElement);\n\n const update = (): void => {\n if (!overlayElement) return;\n const state = stateProvider();\n overlayElement.innerHTML = renderOverlay(state);\n };\n\n update();\n updateInterval = setInterval(update, 1000);\n}\n\n/**\n * Remove the debug overlay from the DOM and stop updates.\n */\nexport function disableDebugOverlay(): void {\n if (updateInterval) {\n clearInterval(updateInterval);\n updateInterval = null;\n }\n if (overlayElement && overlayElement.parentNode) {\n overlayElement.parentNode.removeChild(overlayElement);\n }\n overlayElement = null;\n}\n\n/**\n * Returns whether the debug overlay is currently active.\n */\nexport function isDebugOverlayActive(): boolean {\n return overlayElement !== null;\n}\n\nfunction renderOverlay(state: DebugOverlayState): string {\n const statusDot = state.isOnline ? '&#x1F7E2;' : '&#x1F534;';\n const lines: string[] = [\n `<div style=\"font-size:13px;font-weight:bold;margin-bottom:8px;\">Layers Debug ${statusDot}</div>`,\n `<div><b>SDK:</b> ${escapeHtml(state.sdkVersion)}</div>`,\n `<div><b>App:</b> ${escapeHtml(state.appId)} (${escapeHtml(state.environment)})</div>`,\n `<div><b>Session:</b> ${escapeHtml(truncate(state.sessionId, 20))}</div>`,\n `<div><b>Install:</b> ${escapeHtml(truncate(state.installId ?? 'N/A', 20))}</div>`,\n `<div><b>Queue:</b> ${String(state.queueDepth)} events</div>`,\n `<div><b>Network:</b> ${state.isOnline ? 'Online' : 'Offline'}</div>`\n ];\n\n if (state.recentEvents.length > 0) {\n lines.push(\n `<div style=\"margin-top:8px;font-weight:bold;border-top:1px solid #444;padding-top:6px;\">Recent Events</div>`\n );\n for (const event of state.recentEvents.slice(0, 10)) {\n lines.push(`<div style=\"color:#aaa;\">${escapeHtml(event)}</div>`);\n }\n } else {\n lines.push(`<div style=\"margin-top:8px;color:#888;\">No events tracked yet</div>`);\n }\n\n return lines.join('');\n}\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n\nfunction truncate(str: string, maxLen: number): string {\n if (str.length <= maxLen) return str;\n return `${str.slice(0, maxLen)}...`;\n}\n","// Deep link handling and parsing for the Web SDK.\n// Parses URL parameters for deep link data (scheme, host, path, query params)\n// and extracts attribution parameters (fbclid, gclid, ttclid, etc.).\n\nexport interface DeepLinkData {\n url: string;\n scheme: string;\n host: string;\n path: string;\n queryParams: Record<string, string>;\n timestamp: number;\n}\n\n/**\n * Parse a URL string into structured DeepLinkData.\n * Returns null if the URL cannot be parsed.\n */\nexport function parseDeepLink(url: string): DeepLinkData | null {\n try {\n const parsed = new URL(url);\n const params: Record<string, string> = {};\n parsed.searchParams.forEach((value, key) => {\n params[key] = value;\n });\n return {\n url,\n scheme: parsed.protocol.replace(':', ''),\n host: parsed.hostname,\n path: parsed.pathname,\n queryParams: params,\n timestamp: Date.now()\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Set up a listener for URL changes in a web browser context.\n * Listens for `popstate` and `hashchange` events and calls the callback\n * with parsed deep link data on each URL change.\n *\n * Returns an unsubscribe function to remove the listeners.\n */\nexport function setupDeepLinkListener(onDeepLink: (data: DeepLinkData) => void): () => void {\n if (typeof window === 'undefined' || !window.location) return () => {};\n\n let lastUrl: string;\n try {\n lastUrl = window.location.href;\n } catch {\n return () => {};\n }\n\n const handleUrlChange = (): void => {\n const currentUrl = window.location.href;\n if (currentUrl === lastUrl) return;\n lastUrl = currentUrl;\n\n const parsed = parseDeepLink(currentUrl);\n if (parsed) onDeepLink(parsed);\n };\n\n window.addEventListener('popstate', handleUrlChange);\n window.addEventListener('hashchange', handleUrlChange);\n\n // Also check the initial URL\n const initialData = parseDeepLink(window.location.href);\n if (initialData && Object.keys(initialData.queryParams).length > 0) {\n onDeepLink(initialData);\n }\n\n return () => {\n window.removeEventListener('popstate', handleUrlChange);\n window.removeEventListener('hashchange', handleUrlChange);\n };\n}\n","// Standard event types for Layers Analytics.\n// These 18 events match the canonical Layers event taxonomy\n// and are consistent across all SDK platforms.\nimport type { EventProperties } from '@layers/core-wasm';\n\n/**\n * Predefined standard event name constants.\n *\n * Usage:\n * ```ts\n * import { StandardEvents } from '@layers/client';\n * client.track(StandardEvents.PURCHASE, { amount: 9.99, currency: 'USD' });\n * ```\n */\nexport const StandardEvents = {\n APP_OPEN: 'app_open',\n LOGIN: 'login',\n SIGN_UP: 'sign_up',\n REGISTER: 'register',\n PURCHASE: 'purchase_success',\n ADD_TO_CART: 'add_to_cart',\n ADD_TO_WISHLIST: 'add_to_wishlist',\n INITIATE_CHECKOUT: 'initiate_checkout',\n BEGIN_CHECKOUT: 'begin_checkout',\n START_TRIAL: 'start_trial',\n SUBSCRIBE: 'subscribe',\n LEVEL_START: 'level_start',\n LEVEL_COMPLETE: 'level_complete',\n TUTORIAL_COMPLETE: 'tutorial_complete',\n SEARCH: 'search',\n VIEW_ITEM: 'view_item',\n VIEW_CONTENT: 'view_content',\n SHARE: 'share',\n DEEP_LINK: 'deep_link_opened',\n SCREEN_VIEW: 'screen_view'\n} as const;\n\n/** Union type of all standard event name strings. */\nexport type StandardEventName = (typeof StandardEvents)[keyof typeof StandardEvents];\n\n// ---------------------------------------------------------------------------\n// Typed helper functions for building standard event payloads.\n// Each returns { event: string; properties: EventProperties } suitable for\n// destructuring into a track() call.\n// ---------------------------------------------------------------------------\n\nexport interface StandardEventPayload {\n event: StandardEventName;\n properties: EventProperties;\n}\n\n/** Build a login event. */\nexport function loginEvent(method?: string): StandardEventPayload {\n const properties: EventProperties = {};\n if (method !== undefined) properties.method = method;\n return { event: StandardEvents.LOGIN, properties };\n}\n\n/** Build a sign-up event. */\nexport function signUpEvent(method?: string): StandardEventPayload {\n const properties: EventProperties = {};\n if (method !== undefined) properties.method = method;\n return { event: StandardEvents.SIGN_UP, properties };\n}\n\n/** Build a register event. */\nexport function registerEvent(method?: string): StandardEventPayload {\n const properties: EventProperties = {};\n if (method !== undefined) properties.method = method;\n return { event: StandardEvents.REGISTER, properties };\n}\n\n/** Build a purchase event. */\nexport function purchaseEvent(\n amount: number,\n currency = 'USD',\n itemId?: string\n): StandardEventPayload {\n const properties: EventProperties = { amount, currency };\n if (itemId !== undefined) properties.item_id = itemId;\n return { event: StandardEvents.PURCHASE, properties };\n}\n\n/** Build an add-to-cart event. */\nexport function addToCartEvent(itemId: string, price: number, quantity = 1): StandardEventPayload {\n return {\n event: StandardEvents.ADD_TO_CART,\n properties: { item_id: itemId, price, quantity }\n };\n}\n\n/** Build an add-to-wishlist event. */\nexport function addToWishlistEvent(\n itemId: string,\n name?: string,\n price?: number\n): StandardEventPayload {\n const properties: EventProperties = { item_id: itemId };\n if (name !== undefined) properties.name = name;\n if (price !== undefined) properties.price = price;\n return { event: StandardEvents.ADD_TO_WISHLIST, properties };\n}\n\n/** Build an initiate-checkout event. */\nexport function initiateCheckoutEvent(\n value: number,\n currency = 'USD',\n itemCount?: number\n): StandardEventPayload {\n const properties: EventProperties = { value, currency };\n if (itemCount !== undefined) properties.item_count = itemCount;\n return { event: StandardEvents.INITIATE_CHECKOUT, properties };\n}\n\n/** Build a start-trial event. */\nexport function startTrialEvent(plan?: string, durationDays?: number): StandardEventPayload {\n const properties: EventProperties = {};\n if (plan !== undefined) properties.plan = plan;\n if (durationDays !== undefined) properties.duration_days = durationDays;\n return { event: StandardEvents.START_TRIAL, properties };\n}\n\n/** Build a subscribe event. */\nexport function subscribeEvent(\n plan: string,\n amount: number,\n currency = 'USD'\n): StandardEventPayload {\n return {\n event: StandardEvents.SUBSCRIBE,\n properties: { plan, amount, currency }\n };\n}\n\n/** Build a level-start event. */\nexport function levelStartEvent(level: string): StandardEventPayload {\n return { event: StandardEvents.LEVEL_START, properties: { level } };\n}\n\n/** Build a level-complete event. */\nexport function levelCompleteEvent(level: string, score?: number): StandardEventPayload {\n const properties: EventProperties = { level };\n if (score !== undefined) properties.score = score;\n return { event: StandardEvents.LEVEL_COMPLETE, properties };\n}\n\n/** Build a tutorial-complete event. */\nexport function tutorialCompleteEvent(name?: string): StandardEventPayload {\n const properties: EventProperties = {};\n if (name !== undefined) properties.name = name;\n return { event: StandardEvents.TUTORIAL_COMPLETE, properties };\n}\n\n/** Build a search event. */\nexport function searchEvent(query: string, resultCount?: number): StandardEventPayload {\n const properties: EventProperties = { query };\n if (resultCount !== undefined) properties.result_count = resultCount;\n return { event: StandardEvents.SEARCH, properties };\n}\n\n/** Build a view-item event. */\nexport function viewItemEvent(\n itemId: string,\n name?: string,\n category?: string\n): StandardEventPayload {\n const properties: EventProperties = { item_id: itemId };\n if (name !== undefined) properties.name = name;\n if (category !== undefined) properties.category = category;\n return { event: StandardEvents.VIEW_ITEM, properties };\n}\n\n/** Build a view-content event. */\nexport function viewContentEvent(\n contentId: string,\n contentType?: string,\n name?: string\n): StandardEventPayload {\n const properties: EventProperties = { content_id: contentId };\n if (contentType !== undefined) properties.content_type = contentType;\n if (name !== undefined) properties.name = name;\n return { event: StandardEvents.VIEW_CONTENT, properties };\n}\n\n/** Build a share event. */\nexport function shareEvent(\n contentType: string,\n method?: string,\n contentId?: string\n): StandardEventPayload {\n const properties: EventProperties = { content_type: contentType };\n if (method !== undefined) properties.method = method;\n if (contentId !== undefined) properties.content_id = contentId;\n return { event: StandardEvents.SHARE, properties };\n}\n\n/** Build a screen-view event. */\nexport function screenViewEvent(name: string, screenClass?: string): StandardEventPayload {\n const properties: EventProperties = { screen_name: name };\n if (screenClass !== undefined) properties.screen_class = screenClass;\n return { event: StandardEvents.SCREEN_VIEW, properties };\n}\n","// Commerce module for @layers/client (Web SDK).\n// Cross-platform purchase, subscription, cart, and refund tracking helpers.\n// Each function accepts a WebCommerceTracker instance and tracks the appropriate\n// event with standardized properties consistent with the iOS CommerceModule,\n// Android CommerceModule, and RN commerce module.\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Event properties bag accepted by commerce functions. */\nexport type EventProperties = Record<string, unknown>;\n\n/** Minimal Layers SDK interface required by the commerce module. */\nexport interface WebCommerceTracker {\n track(event: string, properties?: Record<string, any>): void;\n}\n\n/** Purchase details for trackPurchase. */\nexport interface PurchaseParams {\n productId: string;\n /** Unit price of the item. Revenue is computed as `price * quantity`. */\n price: number;\n currency: string;\n transactionId?: string;\n quantity?: number;\n isRestored?: boolean;\n store?: string;\n properties?: EventProperties;\n}\n\n/** Subscription details for trackSubscription. */\nexport interface SubscriptionParams {\n productId: string;\n /** Unit price of the subscription. */\n price: number;\n currency: string;\n period?: string;\n transactionId?: string;\n isRenewal?: boolean;\n isTrial?: boolean;\n subscriptionGroupId?: string;\n originalTransactionId?: string;\n properties?: EventProperties;\n}\n\n/** Cart item for order and checkout tracking. */\nexport interface CartItem {\n productId: string;\n name: string;\n price: number;\n quantity?: number;\n category?: string;\n}\n\n/** Order details for trackOrder. */\nexport interface OrderParams {\n orderId: string;\n items: CartItem[];\n subtotal: number;\n currency?: string;\n tax?: number;\n shipping?: number;\n discount?: number;\n couponCode?: string;\n properties?: EventProperties;\n}\n\n/** Refund details for trackRefund. */\nexport interface RefundParams {\n transactionId: string;\n amount: number;\n currency: string;\n reason?: string;\n properties?: EventProperties;\n}\n\n/** Purchase failure details for trackPurchaseFailed. */\nexport interface PurchaseFailedParams {\n productId: string;\n currency: string;\n errorCode: string | number;\n errorMessage?: string;\n properties?: EventProperties;\n}\n\n// ---------------------------------------------------------------------------\n// Purchase tracking\n// ---------------------------------------------------------------------------\n\n/**\n * Track a successful purchase.\n *\n * ```ts\n * import { trackPurchase } from '@layers/client';\n *\n * trackPurchase(layers, {\n * productId: 'premium_monthly',\n * price: 9.99,\n * currency: 'USD',\n * transactionId: 'txn_abc123',\n * });\n * ```\n */\nexport function trackPurchase(layers: WebCommerceTracker, params: PurchaseParams): void {\n const quantity = params.quantity ?? 1;\n const props: EventProperties = {\n product_id: params.productId,\n price: params.price,\n currency: params.currency,\n quantity,\n revenue: params.price * quantity,\n ...params.properties\n };\n if (params.transactionId !== undefined) props.transaction_id = params.transactionId;\n if (params.isRestored !== undefined) props.is_restored = params.isRestored;\n if (params.store !== undefined) props.store = params.store;\n\n layers.track('purchase_success', props);\n}\n\n/**\n * Track a failed purchase attempt.\n */\nexport function trackPurchaseFailed(\n layers: WebCommerceTracker,\n params: PurchaseFailedParams\n): void {\n const props: EventProperties = {\n product_id: params.productId,\n currency: params.currency,\n error_code: params.errorCode,\n ...params.properties\n };\n if (params.errorMessage !== undefined) props.error_message = params.errorMessage;\n\n layers.track('purchase_failed', props);\n}\n\n// ---------------------------------------------------------------------------\n// Subscription tracking\n// ---------------------------------------------------------------------------\n\n/**\n * Track a subscription purchase or renewal.\n *\n * ```ts\n * import { trackSubscription } from '@layers/client';\n *\n * trackSubscription(layers, {\n * productId: 'pro_annual',\n * price: 49.99,\n * currency: 'USD',\n * period: 'P1Y',\n * });\n * ```\n */\nexport function trackSubscription(layers: WebCommerceTracker, params: SubscriptionParams): void {\n const props: EventProperties = {\n product_id: params.productId,\n price: params.price,\n currency: params.currency,\n quantity: 1,\n revenue: params.price,\n ...params.properties\n };\n if (params.transactionId !== undefined) props.transaction_id = params.transactionId;\n if (params.period !== undefined) props.period = params.period;\n if (params.isRenewal !== undefined) props.is_renewal = params.isRenewal;\n if (params.isTrial !== undefined) props.is_trial = params.isTrial;\n if (params.subscriptionGroupId !== undefined)\n props.subscription_group_id = params.subscriptionGroupId;\n if (params.originalTransactionId !== undefined)\n props.original_transaction_id = params.originalTransactionId;\n\n layers.track('subscribe', props);\n}\n\n// ---------------------------------------------------------------------------\n// Order / cart tracking\n// ---------------------------------------------------------------------------\n\n/**\n * Track a completed order with multiple line items.\n */\nexport function trackOrder(layers: WebCommerceTracker, params: OrderParams): void {\n const currency = params.currency ?? 'USD';\n let total = params.subtotal;\n if (params.tax !== undefined) total += params.tax;\n if (params.shipping !== undefined) total += params.shipping;\n if (params.discount !== undefined) total -= params.discount;\n\n const props: EventProperties = {\n order_id: params.orderId,\n subtotal: params.subtotal,\n total,\n currency,\n item_count: params.items.length,\n revenue: total,\n product_ids: params.items.map((i) => i.productId).join(','),\n ...params.properties\n };\n if (params.tax !== undefined) props.tax = params.tax;\n if (params.shipping !== undefined) props.shipping = params.shipping;\n if (params.discount !== undefined) props.discount = params.discount;\n if (params.couponCode !== undefined) props.coupon_code = params.couponCode;\n\n layers.track('purchase_success', props);\n}\n\n/**\n * Track an item being added to the cart.\n */\nexport function trackAddToCart(\n layers: WebCommerceTracker,\n item: CartItem,\n properties?: EventProperties\n): void {\n const quantity = item.quantity ?? 1;\n const props: EventProperties = {\n product_id: item.productId,\n product_name: item.name,\n price: item.price,\n quantity,\n value: item.price * quantity,\n ...properties\n };\n if (item.category !== undefined) props.category = item.category;\n\n layers.track('add_to_cart', props);\n}\n\n/**\n * Track an item being removed from the cart.\n */\nexport function trackRemoveFromCart(\n layers: WebCommerceTracker,\n item: CartItem,\n properties?: EventProperties\n): void {\n const quantity = item.quantity ?? 1;\n const props: EventProperties = {\n product_id: item.productId,\n product_name: item.name,\n price: item.price,\n quantity,\n ...properties\n };\n if (item.category !== undefined) props.category = item.category;\n\n layers.track('remove_from_cart', props);\n}\n\n/**\n * Track beginning the checkout flow.\n */\nexport function trackBeginCheckout(\n layers: WebCommerceTracker,\n items: CartItem[],\n currency = 'USD',\n properties?: EventProperties\n): void {\n const total = items.reduce((sum, item) => sum + item.price * (item.quantity ?? 1), 0);\n const props: EventProperties = {\n item_count: items.length,\n value: total,\n currency,\n product_ids: items.map((i) => i.productId).join(','),\n ...properties\n };\n\n layers.track('begin_checkout', props);\n}\n\n// ---------------------------------------------------------------------------\n// Product view tracking\n// ---------------------------------------------------------------------------\n\n/**\n * Track viewing a product detail page.\n */\nexport function trackViewProduct(\n layers: WebCommerceTracker,\n productId: string,\n name: string,\n price: number,\n currency = 'USD',\n category?: string,\n properties?: EventProperties\n): void {\n const props: EventProperties = {\n product_id: productId,\n product_name: name,\n price,\n currency,\n ...properties\n };\n if (category !== undefined) props.category = category;\n\n layers.track('view_item', props);\n}\n\n// ---------------------------------------------------------------------------\n// Refund tracking\n// ---------------------------------------------------------------------------\n\n/**\n * Track a refund.\n */\nexport function trackRefund(layers: WebCommerceTracker, params: RefundParams): void {\n const props: EventProperties = {\n transaction_id: params.transactionId,\n amount: params.amount,\n currency: params.currency,\n ...params.properties\n };\n if (params.reason !== undefined) props.reason = params.reason;\n\n layers.track('refund', props);\n}\n","// @layers/client — Web browser SDK.\n// Thin wrapper over @layers/core-wasm. Owns only:\n// - localStorage persistence\n// - window online/offline detection\n// - navigator device info\n// - visibilitychange / beforeunload flush\n// - Install ID persistence (localStorage)\n// - Deep link handling/parsing (popstate, hashchange)\n// - setAttributionData API\n// - Clipboard attribution check\n// - Install event gating (24h window)\n// - Debug overlay (DOM-based)\n// - Init timing measurement\n// - Full lifecycle events (app_open, app_background, app_foreground)\n// - Queue depth gating\nimport {\n FetchHttpClient,\n LayersCore,\n LayersError,\n createDefaultPersistence\n} from '@layers/core-wasm';\nimport type {\n ConsentState,\n DeviceContext,\n Environment,\n EventProperties,\n UserProperties\n} from '@layers/core-wasm';\n\nimport { captureAttribution, getAttributionProperties } from './attribution.js';\nimport { getCapiProperties } from './capi.js';\nimport {\n disableDebugOverlay as hideDebugOverlay,\n enableDebugOverlay as showDebugOverlay\n} from './debug-overlay.js';\nimport { parseDeepLink, setupDeepLinkListener } from './deep-links.js';\nimport type { DeepLinkData } from './deep-links.js';\n\nexport type {\n ConsentState,\n DeviceContext,\n Environment,\n EventProperties,\n UserProperties\n} from '@layers/core-wasm';\n\nexport { LayersError } from '@layers/core-wasm';\n\nexport * from './api-types.js';\nexport type { AttributionData } from './attribution.js';\nexport { getAttribution, getAttributionProperties } from './attribution.js';\nexport {\n getCapiProperties,\n getFbpCookie,\n getTtpCookie,\n getPageUrl,\n getFbc,\n formatFbc\n} from './capi.js';\n\nexport { StandardEvents } from './standard-events.js';\nexport type { StandardEventName, StandardEventPayload } from './standard-events.js';\nexport {\n loginEvent,\n signUpEvent,\n registerEvent,\n purchaseEvent,\n addToCartEvent,\n addToWishlistEvent,\n initiateCheckoutEvent,\n startTrialEvent,\n subscribeEvent,\n levelStartEvent,\n levelCompleteEvent,\n tutorialCompleteEvent,\n searchEvent,\n viewItemEvent,\n viewContentEvent,\n shareEvent,\n screenViewEvent\n} from './standard-events.js';\n\nexport type { DeepLinkData } from './deep-links.js';\nexport { parseDeepLink, setupDeepLinkListener } from './deep-links.js';\nexport type { DebugOverlayState } from './debug-overlay.js';\n\nexport type {\n WebCommerceTracker,\n PurchaseParams,\n SubscriptionParams,\n CartItem,\n OrderParams,\n RefundParams,\n PurchaseFailedParams\n} from './commerce.js';\nexport {\n trackPurchase,\n trackPurchaseFailed,\n trackSubscription,\n trackOrder,\n trackAddToCart,\n trackRemoveFromCart,\n trackBeginCheckout,\n trackViewProduct,\n trackRefund\n} from './commerce.js';\n\nexport interface LayersConfig {\n /** Unique application identifier issued by the Layers dashboard. */\n appId: string;\n /** Deployment environment label. @default \"production\" */\n environment?: Environment;\n /** Optional user identifier to associate events with from the start. */\n appUserId?: string;\n /** Enable verbose debug logging to the console. @default false */\n enableDebug?: boolean;\n /** Base URL for the Layers ingest API. @default \"https://in.layers.com\" */\n baseUrl?: string;\n /** How often the event queue is flushed, in milliseconds. @default 30000 */\n flushIntervalMs?: number;\n /** Number of queued events that triggers an automatic flush. @default 10 */\n flushThreshold?: number;\n /** Maximum number of events to hold in the queue before dropping. @default 1000 */\n maxQueueSize?: number;\n /**\n * Whether to automatically fire an `app_open` event during init().\n * Set to `false` if you want to fire the event manually.\n * @default true\n */\n autoTrackAppOpen?: boolean;\n /**\n * Whether to automatically track `deep_link_opened` events when the URL\n * changes (popstate/hashchange). The tracked event includes the parsed\n * URL components and all query parameters.\n * @default true\n */\n autoTrackDeepLinks?: boolean;\n}\n\n/**\n * Attribution data that will be attached to all subsequent events.\n * Matches the RN SDK's setAttributionData pattern.\n */\nexport interface WebAttributionData {\n deeplinkId?: string | null;\n gclid?: string | null;\n fbclid?: string | null;\n ttclid?: string | null;\n msclkid?: string | null;\n}\n\nexport type ErrorListener = (error: Error) => void;\n\n/**\n * Listener for SDK initialization timing metrics.\n *\n * @param mainThreadDurationMs Time spent in the synchronous portion of init.\n * @param totalDurationMs Total wall-clock time of the `init()` call,\n * including all async work (remote config fetch, etc.).\n */\nexport type InitListener = (mainThreadDurationMs: number, totalDurationMs: number) => void;\n\n// --- localStorage keys ---\nconst INSTALL_ID_KEY = 'layers_install_id';\nconst FIRST_LAUNCH_TRACKED_KEY = 'layers_first_launch_tracked';\nconst FIRST_INSTALL_TIME_KEY = 'layers_first_install_time';\nconst CLIPBOARD_CHECKED_KEY = 'layers_clipboard_checked';\nconst ATTRIBUTION_DATA_KEY = 'layers_attribution_data';\n\n/** Maximum age of an installation for install event gating (24 hours). */\nconst INSTALL_EVENT_MAX_DIFF_MS = 24 * 60 * 60 * 1000;\n\nexport class LayersClient {\n private core: LayersCore;\n private appUserId: string | undefined;\n private isOnline = true;\n private readonly enableDebug: boolean;\n private readonly baseUrl: string;\n private readonly config: LayersConfig;\n\n // Stored listener references for cleanup on shutdown\n private onlineListener: (() => void) | null = null;\n private offlineListener: (() => void) | null = null;\n private visibilityListener: (() => void) | null = null;\n private beforeUnloadListener: (() => void) | null = null;\n private deepLinkUnsubscribe: (() => void) | null = null;\n\n // Error listeners\n private readonly errorListeners: Set<ErrorListener> = new Set();\n\n // Install ID\n private _installId: string | null = null;\n\n // Whether the SDK had prior state (install_id existed) at initialization time.\n private _hadPriorSdkState = false;\n\n // Attribution data stored for attachment to subsequent events.\n private _attributionData: WebAttributionData | null = null;\n\n // Recent events buffer for debug overlay (circular, last N events)\n private static readonly MAX_RECENT_EVENTS = 20;\n private _recentEvents: string[] = [];\n private _isInitialized = false;\n\n // Init timing listener\n private _initListener: InitListener | null = null;\n\n // Debug overlay state\n private _debugOverlayActive = false;\n\n constructor(config: LayersConfig) {\n this.enableDebug = config.enableDebug ?? false;\n this.baseUrl = (config.baseUrl ?? 'https://in.layers.com').replace(/\\/$/, '');\n this.appUserId = config.appUserId;\n this.config = config;\n\n const persistence = createDefaultPersistence(config.appId);\n const httpClient = new FetchHttpClient();\n\n this.core = LayersCore.init({\n config: {\n appId: config.appId,\n environment: config.environment ?? 'production',\n ...(config.baseUrl != null && { baseUrl: config.baseUrl }),\n ...(config.enableDebug != null && { enableDebug: config.enableDebug }),\n ...(config.flushIntervalMs != null && { flushIntervalMs: config.flushIntervalMs }),\n ...(config.flushThreshold != null && { flushThreshold: config.flushThreshold }),\n ...(config.maxQueueSize != null && { maxQueueSize: config.maxQueueSize }),\n sdkVersion: `client/${SDK_VERSION}`\n },\n httpClient,\n persistence\n });\n\n if (this.appUserId) {\n this.core.identify(this.appUserId);\n }\n }\n\n /** Initialize the client: detects device info, attaches lifecycle listeners, and fetches remote config. */\n async init(): Promise<void> {\n const initStartTime = typeof performance !== 'undefined' ? performance.now() : Date.now();\n\n // Synchronous init work\n this.initializeInstallId();\n this.initializeDeviceInfo();\n this.setupNetworkListener();\n this.setupLifecycleListeners();\n captureAttribution();\n this.restoreAttributionData();\n\n // Main thread init complete — record timing\n const mainThreadEndTime = typeof performance !== 'undefined' ? performance.now() : Date.now();\n const mainThreadDurationMs = Math.round(mainThreadEndTime - initStartTime);\n\n // Async init work\n await this.core.fetchRemoteConfig().catch(() => {\n // Remote config fetch is best-effort\n });\n\n // Clipboard attribution check (only on first init, gated by remote config)\n this.checkClipboardAttribution();\n\n // Install event gating + app_open\n if (this.config.autoTrackAppOpen !== false) {\n const appOpenProps: Record<string, unknown> = {};\n const isFirstLaunch = this.shouldTreatAsNewInstall();\n appOpenProps.is_first_launch = isFirstLaunch;\n\n if (isFirstLaunch) {\n this.storageSet(FIRST_LAUNCH_TRACKED_KEY, 'true');\n }\n\n this.track('app_open', appOpenProps);\n }\n\n // Auto-track deep link events\n if (this.config.autoTrackDeepLinks !== false) {\n this.setupDeepLinkAutoTracking();\n }\n\n this._isInitialized = true;\n\n // Measure total duration\n const totalEndTime = typeof performance !== 'undefined' ? performance.now() : Date.now();\n const totalDurationMs = Math.round(totalEndTime - initStartTime);\n\n // Track init timing event\n this.trackInitTiming(mainThreadDurationMs, totalDurationMs);\n\n if (this.enableDebug) {\n console.log(\n `[Layers] Init timing: mainThread=${String(mainThreadDurationMs)}ms, total=${String(totalDurationMs)}ms`\n );\n }\n\n // Notify init listener\n if (this._initListener) {\n try {\n this._initListener(mainThreadDurationMs, totalDurationMs);\n } catch (e) {\n if (this.enableDebug) {\n console.warn('[Layers] InitListener threw:', e);\n }\n }\n }\n }\n\n /**\n * Record a custom analytics event with an optional property bag.\n *\n * Events are batched and flushed automatically when the queue reaches\n * `flushThreshold` or the periodic flush timer fires.\n */\n track(eventName: string, properties?: EventProperties): void {\n if (this.enableDebug) {\n console.log(\n `[Layers] track(\"${eventName}\", ${Object.keys(properties ?? {}).length} properties)`\n );\n }\n try {\n const merged = this.mergeAllProperties(properties);\n const depthBefore = this.core.queueDepth();\n this.core.track(eventName, merged, this.appUserId);\n const depthAfter = this.core.queueDepth();\n\n // Queue depth gating: verify event was accepted by core\n if (depthAfter <= depthBefore && this.enableDebug) {\n console.warn(\n `[Layers] track(\"${eventName}\") — event may not have been queued ` +\n `(depth before=${String(depthBefore)}, after=${String(depthAfter)})`\n );\n }\n\n // Record for debug overlay\n this.recordRecentEvent(eventName, properties);\n } catch (e) {\n this.emitError(e);\n }\n }\n\n /**\n * Record a screen view event with an optional property bag.\n *\n * Events are batched and flushed automatically when the queue reaches\n * `flushThreshold` or the periodic flush timer fires.\n */\n screen(screenName: string, properties?: EventProperties): void {\n if (this.enableDebug) {\n console.log(\n `[Layers] screen(\"${screenName}\", ${Object.keys(properties ?? {}).length} properties)`\n );\n }\n try {\n const merged = this.mergeAllProperties(properties);\n const depthBefore = this.core.queueDepth();\n this.core.screen(screenName, merged, this.appUserId);\n const depthAfter = this.core.queueDepth();\n\n if (depthAfter <= depthBefore && this.enableDebug) {\n console.warn(\n `[Layers] screen(\"${screenName}\") — event may not have been queued ` +\n `(depth before=${String(depthBefore)}, after=${String(depthAfter)})`\n );\n }\n\n this.recordRecentEvent(`screen:${screenName}`, properties);\n } catch (e) {\n this.emitError(e);\n }\n }\n\n /** Set or update user-level properties that persist across sessions. */\n setUserProperties(properties: UserProperties): void {\n this.core.setUserProperties(properties);\n }\n\n /** Set user-level properties with \"set once\" semantics — only keys not previously set are applied. */\n setUserPropertiesOnce(properties: UserProperties): void {\n this.core.setUserPropertiesOnce(properties);\n }\n\n /** Update the user's consent state for analytics and advertising data collection. */\n setConsent(consent: ConsentState): void {\n this.core.setConsent(consent);\n }\n\n /**\n * Associate all subsequent events with a group (company, team, organization).\n * Pass `undefined` or empty string to clear the group association.\n */\n group(groupId: string | undefined, properties?: EventProperties): void {\n if (this.enableDebug) {\n console.log(\n `[Layers] group(${groupId ? `\"${groupId}\"` : 'undefined'}, ${Object.keys(properties ?? {}).length} properties)`\n );\n }\n try {\n this.core.group(groupId ?? '', properties);\n } catch (e) {\n this.emitError(e);\n }\n }\n\n /** Associate all subsequent events with the given user ID, or clear it with `undefined`. */\n setAppUserId(appUserId: string | undefined): void {\n if (this.enableDebug) {\n console.log(`[Layers] setAppUserId(${appUserId ? `\"${appUserId}\"` : 'undefined'})`);\n }\n this.appUserId = appUserId;\n if (appUserId) {\n this.core.identify(appUserId);\n } else {\n this.core.identify('');\n }\n }\n\n /** @deprecated Use setAppUserId instead */\n setUserId(userId: string): void {\n this.setAppUserId(userId);\n }\n\n /** Return the current app user ID, or `undefined` if not set. */\n getAppUserId(): string | undefined {\n return this.appUserId;\n }\n\n /** @deprecated Use getAppUserId instead */\n getUserId(): string | undefined {\n return this.appUserId;\n }\n\n /** Return the current anonymous session ID. */\n getSessionId(): string {\n return this.core.getSessionId();\n }\n\n /** Return the current consent state for analytics and advertising. */\n getConsentState(): ConsentState {\n return this.core.getConsentState();\n }\n\n /** Override device-level context fields (platform, OS, locale, etc.). */\n setDeviceInfo(deviceInfo: DeviceContext): void {\n this.core.setDeviceContext(deviceInfo);\n }\n\n /**\n * Return the install ID (persistent UUID stored in localStorage).\n * Generated on first init, persists across sessions.\n */\n getInstallId(): string | null {\n return this._installId;\n }\n\n /**\n * Return the number of events currently queued.\n */\n getQueueDepth(): number {\n return this.core.queueDepth();\n }\n\n /**\n * Store attribution data that will be attached to all subsequent events.\n *\n * The values are persisted in localStorage so they survive page reloads.\n * Pass `null` to clear a value.\n *\n * When set, `deeplinkId`, `gclid`, `fbclid`, `ttclid`, and/or `msclkid`\n * are included in every event's properties.\n */\n setAttributionData(data: WebAttributionData): void {\n this._attributionData = data;\n\n // Update the Rust core's DeviceContext with the deeplinkId so the\n // top-level event field is populated.\n try {\n const currentContext = this.core.getDeviceContext();\n const ctx: DeviceContext = { ...currentContext };\n if (data.deeplinkId) {\n ctx.deeplinkId = data.deeplinkId;\n } else {\n delete ctx.deeplinkId;\n }\n this.core.setDeviceContext(ctx);\n } catch {\n // DeviceContext update is best-effort\n }\n\n // Persist to localStorage\n try {\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem(ATTRIBUTION_DATA_KEY, JSON.stringify(data));\n }\n } catch {\n // localStorage write is best-effort\n }\n\n if (this.enableDebug) {\n console.log(`[Layers] setAttributionData(${JSON.stringify(data)})`);\n }\n }\n\n /**\n * Set a listener to receive SDK initialization timing metrics.\n * Must be called **before** `init()` to receive the callback.\n * Pass `null` to clear the listener.\n */\n setInitListener(listener: InitListener | null): void {\n this._initListener = listener;\n }\n\n /**\n * Enable the debug overlay — a lightweight DOM panel showing SDK state.\n * The overlay updates every second with queue depth, session info, and recent events.\n */\n enableDebugOverlay(): void {\n this._debugOverlayActive = true;\n showDebugOverlay(() => ({\n sdkVersion: SDK_VERSION,\n queueDepth: this.core.queueDepth(),\n sessionId: this.core.getSessionId(),\n installId: this._installId,\n appId: this.config.appId,\n environment: this.config.environment ?? 'production',\n isOnline: this.isOnline,\n recentEvents: this._recentEvents\n }));\n }\n\n /**\n * Disable and remove the debug overlay from the DOM.\n */\n disableDebugOverlay(): void {\n this._debugOverlayActive = false;\n hideDebugOverlay();\n }\n\n /** Whether the SDK has completed async initialization. */\n get isInitialized(): boolean {\n return this._isInitialized;\n }\n\n /** SDK version string. */\n getSdkVersion(): string {\n return SDK_VERSION;\n }\n\n /**\n * Returns the most recent tracked events (newest first).\n * Each entry is a formatted string like \"12:34:56 event_name (3 props)\".\n */\n getRecentEvents(): readonly string[] {\n return this._recentEvents;\n }\n\n /** Flush all queued events to the server. Falls back to synchronous persistence on failure. */\n async flush(): Promise<void> {\n try {\n await this.core.flushAsync();\n } catch (e) {\n this.emitError(e);\n this.core.flush();\n }\n }\n\n /** Immediately shut down the client, removing all event listeners. Queued events are persisted but not flushed. */\n shutdown(): void {\n this.cleanupListeners();\n if (this._debugOverlayActive) {\n this.disableDebugOverlay();\n }\n this._attributionData = null;\n this._initListener = null;\n this.core.shutdown();\n }\n\n /**\n * Async shutdown: flushes remaining events before shutting down.\n * @param timeoutMs Maximum time to wait for flush (default 3000ms).\n */\n async shutdownAsync(timeoutMs = 3000): Promise<void> {\n this.cleanupListeners();\n if (this._debugOverlayActive) {\n this.disableDebugOverlay();\n }\n this._attributionData = null;\n this._initListener = null;\n try {\n await Promise.race([\n this.core.flushAsync(),\n new Promise<void>((resolve) => setTimeout(resolve, timeoutMs))\n ]);\n } catch {\n // Best effort — proceed with shutdown\n }\n this.core.shutdown();\n }\n\n private cleanupListeners(): void {\n if (typeof window !== 'undefined') {\n if (this.onlineListener) {\n window.removeEventListener('online', this.onlineListener);\n this.onlineListener = null;\n }\n if (this.offlineListener) {\n window.removeEventListener('offline', this.offlineListener);\n this.offlineListener = null;\n }\n if (this.beforeUnloadListener) {\n window.removeEventListener('beforeunload', this.beforeUnloadListener);\n this.beforeUnloadListener = null;\n }\n }\n if (typeof document !== 'undefined' && this.visibilityListener) {\n document.removeEventListener('visibilitychange', this.visibilityListener);\n this.visibilityListener = null;\n }\n if (this.deepLinkUnsubscribe) {\n this.deepLinkUnsubscribe();\n this.deepLinkUnsubscribe = null;\n }\n }\n\n /** End the current session and start a new one with a fresh session ID. */\n startNewSession(): void {\n this.core.startNewSession();\n }\n\n /**\n * Register an error listener. Errors from track/screen/flush\n * that would otherwise be silently dropped are forwarded here.\n */\n on(event: 'error', listener: ErrorListener): this {\n if (event === 'error') this.errorListeners.add(listener);\n return this;\n }\n\n /**\n * Remove a previously registered error listener.\n */\n off(event: 'error', listener: ErrorListener): this {\n if (event === 'error') this.errorListeners.delete(listener);\n return this;\n }\n\n private emitError(error: unknown): void {\n const err = error instanceof Error ? error : new Error(String(error));\n for (const listener of this.errorListeners) {\n try {\n listener(err);\n } catch {}\n }\n if (this.enableDebug && this.errorListeners.size === 0) {\n console.warn('[Layers]', err.message);\n }\n }\n\n // --- Install ID persistence ---\n\n private initializeInstallId(): void {\n if (typeof localStorage === 'undefined') return;\n\n try {\n const existing = localStorage.getItem(INSTALL_ID_KEY);\n if (existing) {\n this._installId = existing;\n this._hadPriorSdkState = true;\n } else {\n const newId = generateUUID();\n localStorage.setItem(INSTALL_ID_KEY, newId);\n this._installId = newId;\n this._hadPriorSdkState = false;\n\n // Store first install timestamp for install event gating\n if (!localStorage.getItem(FIRST_INSTALL_TIME_KEY)) {\n localStorage.setItem(FIRST_INSTALL_TIME_KEY, String(Date.now()));\n }\n }\n } catch {\n // localStorage unavailable — generate an in-memory ID\n this._installId = generateUUID();\n }\n }\n\n // --- Install event gating (24h window) ---\n\n private shouldTreatAsNewInstall(): boolean {\n // If first_launch was already tracked, this is not a first launch\n const flag = this.storageGet(FIRST_LAUNCH_TRACKED_KEY);\n if (flag === 'true') return false;\n\n // If SDK had prior state (install_id existed), trust the flag\n if (this._hadPriorSdkState) return true;\n\n // SDK has no prior state — check whether the install is recent\n const installTimeStr = this.storageGet(FIRST_INSTALL_TIME_KEY);\n if (installTimeStr) {\n const installTime = Number(installTimeStr);\n if (Number.isFinite(installTime)) {\n const elapsed = Date.now() - installTime;\n const isRecentInstall = elapsed <= INSTALL_EVENT_MAX_DIFF_MS;\n\n if (!isRecentInstall && this.enableDebug) {\n console.log(\n `[Layers] Install event gated: installed ${Math.round(elapsed / 1000)}s ago ` +\n `(threshold=${INSTALL_EVENT_MAX_DIFF_MS / 1000}s), ` +\n 'no prior SDK state — suppressing is_first_launch'\n );\n }\n\n return isRecentInstall;\n }\n }\n\n // If we can't read the install time, default to trusting it as a new install\n return true;\n }\n\n // --- Attribution data ---\n\n private restoreAttributionData(): void {\n try {\n if (typeof localStorage === 'undefined') return;\n const raw = localStorage.getItem(ATTRIBUTION_DATA_KEY);\n if (!raw) return;\n const data: WebAttributionData = JSON.parse(raw);\n this._attributionData = data;\n\n // Sync restored deeplinkId to the Rust core's DeviceContext\n if (data.deeplinkId) {\n try {\n const currentContext = this.core.getDeviceContext();\n this.core.setDeviceContext({ ...currentContext, deeplinkId: data.deeplinkId });\n } catch {\n // best-effort\n }\n }\n\n if (this.enableDebug && data) {\n console.log(`[Layers] Restored attribution data: ${JSON.stringify(data)}`);\n }\n } catch {\n // Restoration is best-effort\n }\n }\n\n /**\n * Merge attribution properties into event properties.\n * deeplink_id flows through DeviceContext; other fields flow through properties.\n */\n private mergeAttributionProperties(properties?: EventProperties): EventProperties | undefined {\n const data = this._attributionData;\n if (!data) return properties;\n\n const merged: Record<string, unknown> = { ...(properties ?? {}) };\n if (data.gclid) merged.gclid = data.gclid;\n if (data.fbclid) merged.fbclid = data.fbclid;\n if (data.ttclid) merged.ttclid = data.ttclid;\n if (data.msclkid) merged.msclkid = data.msclkid;\n return merged as EventProperties;\n }\n\n /** Merge CAPI, attribution (url-based), setAttributionData, and user properties. */\n private mergeAllProperties(properties?: EventProperties): EventProperties {\n const capiProps = getCapiProperties();\n const urlAttrProps = getAttributionProperties();\n const attrProps = this.mergeAttributionProperties(undefined) ?? {};\n return { ...capiProps, ...urlAttrProps, ...attrProps, ...properties } as EventProperties;\n }\n\n // --- Clipboard attribution check ---\n\n private checkClipboardAttribution(): void {\n if (typeof navigator === 'undefined' || typeof localStorage === 'undefined') return;\n\n // Only check on first init\n try {\n if (localStorage.getItem(CLIPBOARD_CHECKED_KEY) === 'true') return;\n } catch {\n return;\n }\n\n // Gate behind remote config\n let clipboardEnabled = false;\n try {\n const configJson = this.core.getRemoteConfigJson?.();\n if (configJson) {\n const config = JSON.parse(configJson);\n clipboardEnabled = config?.clipboard_attribution_enabled === true;\n }\n } catch {}\n\n // Mark as checked regardless of outcome\n this.storageSet(CLIPBOARD_CHECKED_KEY, 'true');\n\n if (!clipboardEnabled) return;\n\n // Read clipboard (async, best-effort)\n if (navigator.clipboard?.readText) {\n void navigator.clipboard\n .readText()\n .then((text) => {\n if (!text) return;\n // Check for Layers click URL pattern\n const match = text.match(/https?:\\/\\/(in\\.layers\\.com|link\\.layers\\.com)\\/c\\/([^?\\s]+)/);\n if (match) {\n this.track('clipboard_attribution', {\n clipboard_url: text,\n clipboard_click_id: match[2]!\n });\n if (this.enableDebug) {\n console.log(`[Layers] Clipboard attribution found: ${match[2]!}`);\n }\n }\n })\n .catch(() => {\n // Clipboard read failed (permission denied) — silently ignore\n });\n }\n }\n\n // --- Deep link auto-tracking ---\n\n private _trackedDeepLinkUrls = new Set<string>();\n\n private setupDeepLinkAutoTracking(): void {\n this.deepLinkUnsubscribe = setupDeepLinkListener((data: DeepLinkData) => {\n // Deduplicate\n if (this._trackedDeepLinkUrls.has(data.url)) return;\n this._trackedDeepLinkUrls.add(data.url);\n // Clear after a short delay so the same URL can be tracked again later\n setTimeout(() => this._trackedDeepLinkUrls.delete(data.url), 2000);\n\n try {\n const properties: Record<string, unknown> = {\n ...data.queryParams,\n url: data.url,\n scheme: data.scheme,\n host: data.host,\n path: data.path\n };\n this.track('deep_link_opened', properties);\n if (this.enableDebug) {\n console.log(`[Layers] auto-tracked deep_link_opened: ${data.url}`);\n }\n } catch (e) {\n this.emitError(e);\n }\n });\n }\n\n // --- Init timing ---\n\n private trackInitTiming(mainThreadDurationMs: number, totalDurationMs: number): void {\n try {\n this.core.track(\n 'layers_init_timing',\n {\n duration_ms: totalDurationMs,\n main_thread_duration_ms: mainThreadDurationMs\n },\n this.appUserId\n );\n } catch {\n // Init timing is best-effort\n }\n }\n\n // --- Recent events buffer (for debug overlay) ---\n\n private recordRecentEvent(eventName: string, properties?: EventProperties): void {\n const now = new Date();\n const time = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`;\n const propCount = Object.keys(properties ?? {}).length;\n const entry = `${time} ${eventName}${propCount > 0 ? ` (${String(propCount)} props)` : ''}`;\n this._recentEvents.unshift(entry);\n if (this._recentEvents.length > LayersClient.MAX_RECENT_EVENTS) {\n this._recentEvents.pop();\n }\n }\n\n // --- Platform-specific: browser device info ---\n\n private initializeDeviceInfo(): void {\n const context: DeviceContext = {\n platform: 'web',\n osVersion: this.detectOS(),\n appVersion: SDK_VERSION,\n deviceModel: this.detectDeviceModel(),\n locale: this.detectLocale(),\n screenSize: this.detectScreenSize(),\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n ...(this._installId != null && { installId: this._installId })\n };\n this.core.setDeviceContext(context);\n }\n\n private detectOS(): string {\n if (typeof navigator === 'undefined') return 'unknown';\n const ua = navigator.userAgent;\n if (ua.includes('Windows')) return 'Windows';\n if (ua.includes('Mac OS')) return 'macOS';\n if (ua.includes('Linux')) return 'Linux';\n if (ua.includes('Android')) return 'Android';\n if (ua.includes('iPhone') || ua.includes('iPad')) return 'iOS';\n return 'unknown';\n }\n\n private detectDeviceModel(): string {\n if (typeof navigator === 'undefined') return 'unknown';\n if ('userAgentData' in navigator) {\n const uaData = (navigator as { userAgentData?: { platform?: string } }).userAgentData;\n if (uaData?.platform) return uaData.platform;\n }\n return navigator.platform ?? 'unknown';\n }\n\n private detectLocale(): string {\n if (typeof navigator === 'undefined') return 'en-US';\n return navigator.language ?? 'en-US';\n }\n\n private detectScreenSize(): string {\n if (typeof window === 'undefined' || typeof screen === 'undefined') return 'unknown';\n return `${screen.width}x${screen.height}`;\n }\n\n // --- Platform-specific: network listener ---\n\n private setupNetworkListener(): void {\n if (typeof window === 'undefined') return;\n\n this.isOnline = navigator?.onLine ?? true;\n\n this.onlineListener = () => {\n this.isOnline = true;\n void this.core.flushAsync().catch(() => {});\n };\n this.offlineListener = () => {\n this.isOnline = false;\n };\n\n window.addEventListener('online', this.onlineListener);\n window.addEventListener('offline', this.offlineListener);\n }\n\n // --- Platform-specific: lifecycle listeners ---\n\n private getBeaconUrl(): string {\n return `${this.baseUrl}/events`;\n }\n\n private setupLifecycleListeners(): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') return;\n\n // Track app_background / app_foreground on visibilitychange,\n // and flush on page hide using sendBeacon.\n this.visibilityListener = () => {\n if (document.visibilityState === 'hidden') {\n // Track app_background lifecycle event\n try {\n this.core.track('app_background', {}, this.appUserId);\n this.recordRecentEvent('app_background');\n } catch {\n // best-effort\n }\n\n if (\n typeof navigator !== 'undefined' &&\n navigator.sendBeacon &&\n this.core.queueDepth() > 0\n ) {\n try {\n const batch = this.core.createBeaconPayload();\n if (batch) {\n const sent = navigator.sendBeacon(this.getBeaconUrl(), batch);\n if (sent) {\n this.core.clearBeaconEvents();\n return;\n }\n // sendBeacon failed — requeue events before falling back to persistence\n this.core.requeueBeaconEvents();\n this.core.flush();\n return;\n }\n } catch {\n // sendBeacon threw — requeue any drained events before sync flush\n this.core.requeueBeaconEvents();\n }\n }\n this.core.flush();\n } else if (document.visibilityState === 'visible') {\n // Track app_foreground lifecycle event\n try {\n this.core.track('app_foreground', {}, this.appUserId);\n this.recordRecentEvent('app_foreground');\n } catch {\n // best-effort\n }\n\n // Flush queued events when returning to foreground\n if (this.isOnline) {\n void this.core.flushAsync().catch(() => {});\n }\n }\n };\n document.addEventListener('visibilitychange', this.visibilityListener);\n\n // Flush on beforeunload — synchronous persistence as last resort\n this.beforeUnloadListener = () => {\n this.core.flush();\n };\n window.addEventListener('beforeunload', this.beforeUnloadListener);\n }\n\n // --- localStorage helpers ---\n\n private storageGet(key: string): string | null {\n if (typeof localStorage === 'undefined') return null;\n try {\n return localStorage.getItem(key);\n } catch {\n return null;\n }\n }\n\n private storageSet(key: string, value: string): void {\n if (typeof localStorage === 'undefined') return;\n try {\n localStorage.setItem(key, value);\n } catch {\n // localStorage full or unavailable\n }\n }\n}\n\n// --- UUID generation ---\n\nfunction generateUUID(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = Math.floor(Math.random() * 16);\n const v = c === 'x' ? r : (r % 4) + 8;\n return v.toString(16);\n });\n}\n\n// SDK version injected at build time by tsdown define\ndeclare const __LAYERS_CLIENT_VERSION__: string;\nconst SDK_VERSION: string =\n typeof __LAYERS_CLIENT_VERSION__ !== 'undefined' ? __LAYERS_CLIENT_VERSION__ : '0.1.1-alpha.1';\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@layers/client",
3
- "version": "1.4.11",
3
+ "version": "2.0.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -33,7 +33,7 @@
33
33
  "dist"
34
34
  ],
35
35
  "dependencies": {
36
- "@layers/core-wasm": "1.4.11"
36
+ "@layers/core-wasm": "2.0.0"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "react": ">=18"
@@ -1,307 +0,0 @@
1
- //#region ../wasm/src/types.d.ts
2
- type Environment = 'development' | 'staging' | 'production';
3
- type Platform = 'ios' | 'android' | 'react-native' | 'web' | 'node' | 'flutter' | 'macos';
4
- interface DeviceContext {
5
- platform?: Platform;
6
- osVersion?: string;
7
- appVersion?: string;
8
- deviceModel?: string;
9
- locale?: string;
10
- buildNumber?: string;
11
- screenSize?: string;
12
- installId?: string;
13
- idfa?: string;
14
- idfv?: string;
15
- attStatus?: string;
16
- timezone?: string;
17
- }
18
- interface ConsentState {
19
- analytics?: boolean | null;
20
- advertising?: boolean | null;
21
- }
22
- /**
23
- * Event properties attached to track/screen calls.
24
- *
25
- * Supported value types:
26
- * - `string` — text values (max 64 KB per value)
27
- * - `number` — integers and floating-point numbers
28
- * - `boolean` — true/false flags
29
- * - `null` — explicit null to clear a property
30
- * - Nested `Record<string, unknown>` objects (one level deep recommended)
31
- *
32
- * Keys must be plain strings (max 128 chars). Prototype-pollution keys
33
- * (`__proto__`, `constructor`, `prototype`) are rejected. Maximum 100 keys
34
- * per call, 1 MB total payload.
35
- */
36
- interface EventProperties {
37
- [key: string]: unknown;
38
- }
39
- /**
40
- * User properties set via `setUserProperties()`.
41
- *
42
- * Supported value types:
43
- * - `string` — text values (max 64 KB per value)
44
- * - `number` — integers and floating-point numbers
45
- * - `boolean` — true/false flags
46
- * - `null` — explicit null to clear a property
47
- * - Nested `Record<string, unknown>` objects (one level deep recommended)
48
- *
49
- * Keys must be plain strings (max 128 chars). Prototype-pollution keys
50
- * (`__proto__`, `constructor`, `prototype`) are rejected. Maximum 100 keys
51
- * per call.
52
- */
53
- interface UserProperties {
54
- [key: string]: unknown;
55
- }
56
- type LayersErrorCode = 'NOT_INITIALIZED' | 'ALREADY_INITIALIZED' | 'SHUT_DOWN' | 'INVALID_CONFIG' | 'INVALID_ARGUMENT' | 'QUEUE_FULL' | 'SERIALIZATION' | 'NETWORK' | 'HTTP' | 'CIRCUIT_OPEN' | 'RATE_LIMITED' | 'CONSENT_NOT_GRANTED' | 'PERSISTENCE' | 'REMOTE_CONFIG' | 'EVENT_DENIED' | 'INTERNAL';
57
- declare class LayersError extends Error {
58
- readonly code: LayersErrorCode;
59
- readonly status?: number;
60
- constructor(code: LayersErrorCode, message: string, status?: number);
61
- }
62
- //#endregion
63
- //#region src/api-types.d.ts
64
- interface BaseEvent {
65
- event: string;
66
- timestamp: string;
67
- event_id?: string;
68
- app_id: string;
69
- app_user_id?: string;
70
- user_id?: string;
71
- anonymous_id?: string;
72
- session_id?: string;
73
- environment: 'development' | 'staging' | 'production';
74
- platform: 'ios' | 'android' | 'react-native' | 'web' | 'node' | 'flutter' | 'macos';
75
- os_version: string;
76
- app_version: string;
77
- build_number?: string;
78
- device_model: string;
79
- screen_size?: string;
80
- locale: string;
81
- install_id?: string;
82
- install_source?: string;
83
- campaign_hint?: string;
84
- referrer?: string;
85
- referrer_source?: string;
86
- utm_source?: string;
87
- utm_medium?: string;
88
- utm_campaign?: string;
89
- utm_content?: string;
90
- utm_term?: string;
91
- click_id_param?: string;
92
- idfa?: string;
93
- idfv?: string;
94
- att_status?: 'authorized' | 'denied' | 'restricted' | 'not_determined';
95
- country_code?: string;
96
- subdivision1_code?: string;
97
- properties?: Record<string, unknown>;
98
- [key: string]: unknown;
99
- }
100
- interface EventsBatchPayload {
101
- events: BaseEvent[];
102
- batch_id?: string;
103
- sent_at: string;
104
- }
105
- interface UserPropertiesPayload {
106
- app_user_id?: string;
107
- user_id?: string;
108
- app_id: string;
109
- properties: Record<string, unknown>;
110
- timestamp: string;
111
- }
112
- interface ConsentPayload {
113
- app_user_id?: string;
114
- user_id?: string;
115
- app_id: string;
116
- consent: {
117
- advertising?: boolean | null;
118
- analytics?: boolean | null;
119
- };
120
- att_status?: 'authorized' | 'denied' | 'restricted' | 'not_determined';
121
- timestamp: string;
122
- }
123
- interface RemoteConfigResponse {
124
- config: {
125
- att?: {
126
- strategy: 'immediate' | 'after_onboarding' | 'after_paywall_view' | 'manual';
127
- prompt_copy?: {
128
- title?: string;
129
- message?: string;
130
- };
131
- };
132
- skan?: {
133
- preset?: 'subscriptions' | 'engagement' | 'iap';
134
- rules?: Record<string, unknown>;
135
- lock_policy?: string;
136
- };
137
- events?: {
138
- allowlist?: string[];
139
- denylist?: string[];
140
- sampling?: Record<string, number>;
141
- sampling_rate?: number;
142
- rate_limit?: {
143
- per_minute?: number;
144
- per_hour?: number;
145
- per_event?: Record<string, {
146
- per_minute?: number;
147
- per_hour?: number;
148
- }>;
149
- };
150
- };
151
- connectors?: Record<string, {
152
- enabled?: boolean;
153
- app_id?: string;
154
- pixel_id?: string;
155
- test_mode?: boolean;
156
- }>;
157
- deeplinks?: {
158
- allowed_hosts?: string[];
159
- behavior?: string;
160
- };
161
- privacy?: {
162
- killswitches?: string[];
163
- analytics_enabled?: boolean;
164
- advertising_enabled?: boolean;
165
- };
166
- };
167
- version: string;
168
- cache_ttl: number;
169
- }
170
- interface SKANPostbackPayload {
171
- app_id: string;
172
- version: string;
173
- ad_network_id: string;
174
- campaign_id?: string;
175
- source_app_id?: string;
176
- conversion_value?: number;
177
- coarse_conversion_value?: string;
178
- lock_window?: boolean;
179
- postback_sequence_index?: number;
180
- did_win?: boolean;
181
- timestamp: string;
182
- }
183
- //#endregion
184
- //#region src/attribution.d.ts
185
- interface AttributionData {
186
- click_id_param?: string;
187
- click_id_value?: string;
188
- utm_source?: string;
189
- utm_medium?: string;
190
- utm_campaign?: string;
191
- utm_content?: string;
192
- utm_term?: string;
193
- referrer_url?: string;
194
- captured_at: number;
195
- }
196
- /**
197
- * Read stored attribution data, returning null if missing or expired.
198
- */
199
- declare function getAttribution(): AttributionData | null;
200
- /**
201
- * Return a flat property bag suitable for merging into event properties.
202
- * Keys are prefixed with `$attribution_` to avoid collisions.
203
- */
204
- declare function getAttributionProperties(): Record<string, string>;
205
- //#endregion
206
- //#region src/index.d.ts
207
- interface LayersConfig {
208
- /** Unique application identifier issued by the Layers dashboard. */
209
- appId: string;
210
- /** Deployment environment label. @default "production" */
211
- environment?: Environment;
212
- /** Optional user identifier to associate events with from the start. */
213
- appUserId?: string;
214
- /** Enable verbose debug logging to the console. @default false */
215
- enableDebug?: boolean;
216
- /** Base URL for the Layers ingest API. @default "https://in.layers.com" */
217
- baseUrl?: string;
218
- /** How often the event queue is flushed, in milliseconds. @default 30000 */
219
- flushIntervalMs?: number;
220
- /** Number of queued events that triggers an automatic flush. @default 10 */
221
- flushThreshold?: number;
222
- /** Maximum number of events to hold in the queue before dropping. @default 1000 */
223
- maxQueueSize?: number;
224
- }
225
- type ErrorListener = (error: Error) => void;
226
- declare class LayersClient {
227
- private core;
228
- private appUserId;
229
- private isOnline;
230
- private readonly enableDebug;
231
- private readonly baseUrl;
232
- private onlineListener;
233
- private offlineListener;
234
- private visibilityListener;
235
- private beforeUnloadListener;
236
- private readonly errorListeners;
237
- constructor(config: LayersConfig);
238
- /** Initialize the client: detects device info, attaches lifecycle listeners, and fetches remote config. */
239
- init(): Promise<void>;
240
- /**
241
- * Record a custom analytics event with an optional property bag.
242
- *
243
- * Events are batched and flushed automatically when the queue reaches
244
- * `flushThreshold` or the periodic flush timer fires.
245
- */
246
- track(eventName: string, properties?: EventProperties): void;
247
- /**
248
- * Record a screen view event with an optional property bag.
249
- *
250
- * Events are batched and flushed automatically when the queue reaches
251
- * `flushThreshold` or the periodic flush timer fires.
252
- */
253
- screen(screenName: string, properties?: EventProperties): void;
254
- /** Set or update user-level properties that persist across sessions. */
255
- setUserProperties(properties: UserProperties): void;
256
- /** Set user-level properties with "set once" semantics — only keys not previously set are applied. */
257
- setUserPropertiesOnce(properties: UserProperties): void;
258
- /** Update the user's consent state for analytics and advertising data collection. */
259
- setConsent(consent: ConsentState): void;
260
- /** Associate all subsequent events with the given user ID, or clear it with `undefined`. */
261
- setAppUserId(appUserId: string | undefined): void;
262
- /** @deprecated Use setAppUserId instead */
263
- setUserId(userId: string): void;
264
- /** Return the current app user ID, or `undefined` if not set. */
265
- getAppUserId(): string | undefined;
266
- /** @deprecated Use getAppUserId instead */
267
- getUserId(): string | undefined;
268
- /** Return the current anonymous session ID. */
269
- getSessionId(): string;
270
- /** Return the current consent state for analytics and advertising. */
271
- getConsentState(): ConsentState;
272
- /** Override device-level context fields (platform, OS, locale, etc.). */
273
- setDeviceInfo(deviceInfo: DeviceContext): void;
274
- /** Flush all queued events to the server. Falls back to synchronous persistence on failure. */
275
- flush(): Promise<void>;
276
- /** Immediately shut down the client, removing all event listeners. Queued events are persisted but not flushed. */
277
- shutdown(): void;
278
- /**
279
- * Async shutdown: flushes remaining events before shutting down.
280
- * @param timeoutMs Maximum time to wait for flush (default 3000ms).
281
- */
282
- shutdownAsync(timeoutMs?: number): Promise<void>;
283
- private cleanupListeners;
284
- /** End the current session and start a new one with a fresh session ID. */
285
- startNewSession(): void;
286
- /**
287
- * Register an error listener. Errors from track/screen/flush
288
- * that would otherwise be silently dropped are forwarded here.
289
- */
290
- on(event: 'error', listener: ErrorListener): this;
291
- /**
292
- * Remove a previously registered error listener.
293
- */
294
- off(event: 'error', listener: ErrorListener): this;
295
- private emitError;
296
- private initializeDeviceInfo;
297
- private detectOS;
298
- private detectDeviceModel;
299
- private detectLocale;
300
- private detectScreenSize;
301
- private setupNetworkListener;
302
- private getBeaconUrl;
303
- private setupLifecycleListeners;
304
- }
305
- //#endregion
306
- export { LayersError as _, getAttribution as a, ConsentPayload as c, SKANPostbackPayload as d, UserPropertiesPayload as f, EventProperties as g, Environment as h, AttributionData as i, EventsBatchPayload as l, DeviceContext as m, LayersClient as n, getAttributionProperties as o, ConsentState as p, LayersConfig as r, BaseEvent as s, ErrorListener as t, RemoteConfigResponse as u, UserProperties as v };
307
- //# sourceMappingURL=index-xkdOC7s3.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-xkdOC7s3.d.ts","names":[],"sources":["../../wasm/src/types.ts","../src/api-types.ts","../src/attribution.ts","../src/index.ts"],"sourcesContent":[],"mappings":";KAGY,WAAA;AAAA,KAEA,QAAA,GAFW,KAAA,GAAA,SAAA,GAAA,cAAA,GAAA,KAAA,GAAA,MAAA,GAAA,SAAA,GAAA,OAAA;UAgBN,aAAA;aACJ;;ECjBI,UAAA,CAAA,EAAS,MAAA;EAqCT,WAAA,CAAA,EAAA,MAAA;EAMA,MAAA,CAAA,EAAA,MAAA;EAQA,WAAA,CAAA,EAAA,MAAc;EAYd,UAAA,CAAA,EAAA,MAAA;EAQH,SAAA,CAAA,EAAA,MAAA;EAMG,IAAA,CAAA,EAAA,MAAA;EAKG,IAAA,CAAA,EAAA,MAAA;EAGH,SAAA,CAAA,EAAA,MAAA;EAAM,QAAA,CAAA,EAAA,MAAA;AAkBvB;UDxEiB,YAAA;;;AErBjB;AAkGA;AAsBA;;;;ACjGA;AAmBA;AAEA;;;;;;;AA0GsB,UH9GL,eAAA,CG8GK;EAsCD,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,OAAA;;;;;;;;;;;;;;;;UHlIJ,cAAA;;;KAsBL,eAAA;cAkBC,WAAA,SAAoB,KAAA;iBAChB;;oBAGG;;;;UChHH,SAAA;EDAL,KAAA,EAAA,MAAA;EAEA,SAAA,EAAQ,MAAA;EAcH,QAAA,CAAA,EAAA,MAAa;EAeb,MAAA,EAAA,MAAA;EAmBA,WAAA,CAAA,EAAA,MAAe;EAkBf,OAAA,CAAA,EAAA,MAAA;EAsBL,YAAA,CAAA,EAAA,MAAe;EAkBd,UAAA,CAAA,EAAA,MAAY;EACR,WAAA,EAAA,aAAA,GAAA,SAAA,GAAA,YAAA;EAGG,QAAA,EAAA,KAAA,GAAA,SAAA,GAAA,cAAA,GAAA,KAAA,GAAA,MAAA,GAAA,SAAA,GAAA,OAAA;EAJa,UAAA,EAAA,MAAA;EAAK,WAAA,EAAA,MAAA;;;;EC5GrB,MAAA,EAAA,MAAS;EAqCT,UAAA,CAAA,EAAA,MAAA;EAMA,cAAA,CAAA,EAAA,MAAA;EAQA,aAAA,CAAA,EAAA,MAAc;EAYd,QAAA,CAAA,EAAA,MAAA;EAQH,eAAA,CAAA,EAAA,MAAA;EAMG,UAAA,CAAA,EAAA,MAAA;EAKG,UAAA,CAAA,EAAA,MAAA;EAGH,YAAA,CAAA,EAAA,MAAA;EAAM,WAAA,CAAA,EAAA,MAAA;EAkBN,QAAA,CAAA,EAAA,MAAA;;;;EC7FA,UAAA,CAAA,EAAA,YAAe,GAAA,QAAA,GAAA,YAAA,GAAA,gBAAA;EAkGhB,YAAA,CAAA,EAAA,MAAc;EAsBd,iBAAA,CAAA,EAAA,MAAA;eDjGD;;;AEAE,UFIA,kBAAA,CEAD;EAeJ,MAAA,EFdF,SEce,EAAA;EAEZ,QAAA,CAAA,EAAA,MAAY;EAgBH,OAAA,EAAA,MAAA;;AA6CkB,UFxEvB,qBAAA,CEwEuB;EAoBE,WAAA,CAAA,EAAA,MAAA;EAeV,OAAA,CAAA,EAAA,MAAA;EAKI,MAAA,EAAA,MAAA;EAKd,UAAA,EFjHR,MEiHQ,CAAA,MAAA,EAAA,OAAA,CAAA;EAsCD,SAAA,EAAA,MAAA;;AAUJ,UF7JA,cAAA,CE6JA;EAmBwB,WAAA,CAAA,EAAA,MAAA;EA2CV,OAAA,CAAA,EAAA,MAAA;EAQC,MAAA,EAAA,MAAA;EAAa,OAAA,EAAA;;;;;;;UFvN5B,oBAAA;;;;;;;;;;;cAQH;;;;;;iBAMG;;;;;oBAKG;;;;;;iBAGH;;;;;;;;;;;;;;;;;;;UAkBA,mBAAA;;;;;;;;;;;;;;;UC7FA,eAAA;EFVL,cAAW,CAAA,EAAA,MAAA;EAEX,cAAQ,CAAA,EAAA,MAAA;EAcH,UAAA,CAAA,EAAA,MAAa;EAeb,UAAA,CAAA,EAAA,MAAY;EAmBZ,YAAA,CAAA,EAAA,MAAe;EAkBf,WAAA,CAAA,EAAA,MAAc;EAsBnB,QAAA,CAAA,EAAA,MAAA;EAkBC,YAAA,CAAA,EAAY,MAAA;EACR,WAAA,EAAA,MAAA;;ACxEjB;AAMA;AAQA;AAYiB,iBC6CD,cAAA,CAAA,CD7CqB,EC6CH,eD7CG,GAAA,IAAA;;;;;AAsBd,iBC6CP,wBAAA,CAAA,CD7CO,EC6CqB,MD7CrB,CAAA,MAAA,EAAA,MAAA,CAAA;;;ADjBN,UGnCA,YAAA,CHmCc;EAsBnB;EAkBC,KAAA,EAAA,MAAA;EACI;EAGG,WAAA,CAAA,EG3EJ,WH2EI;EAJa;EAAK,SAAA,CAAA,EAAA,MAAA;;;;EC5GrB,OAAA,CAAA,EAAA,MAAS;EAqCT;EAMA,eAAA,CAAA,EAAA,MAAqB;EAQrB;EAYA,cAAA,CAAA,EAAA,MAAoB;EAQvB;EAMG,YAAA,CAAA,EAAA,MAAA;;AAQA,KEjCL,aAAA,GFiCK,CAAA,KAAA,EEjCmB,KFiCnB,EAAA,GAAA,IAAA;AAAM,cE/BV,YAAA,CF+BU;EAkBN,QAAA,IAAA;;;;EC7FA,iBAAA,OAAe;EAkGhB,QAAA,cAAc;EAsBd,QAAA,eAAA;;;;ECjGC,WAAA,CAAA,MAAY,EAqCP,YAjCN;EAeJ;EAEC,IAAA,CAAA,CAAA,EA6CG,OA7CS,CAAA,IAAA,CAAA;EAgBH;;;;;;EA0FA,KAAA,CAAA,SAAA,EAAA,MAAA,EAAA,UAAA,CAAA,EA7CkB,eA6ClB,CAAA,EAAA,IAAA;EAsCD;;;;;;EAgFwB,MAAA,CAAA,UAAA,EAAA,MAAA,EAAA,UAAA,CAAA,EA/IH,eA+IG,CAAA,EAAA,IAAA;;gCAhIb;;oCAKI;;sBAKd;;;;;;;;;;;;qBAsCD;;4BAKO;;WAKX;;;;;;;qCAmBwB;;;;;;;;+BA2CV;;;;gCAQC"}