@elevateab/sdk 1.2.2 → 1.2.4

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.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/tracking.ts","../src/utils/conditions.ts","../src/utils/preview.ts","../src/utils/geo.ts","../src/utils/antiFlicker.ts","../src/utils/shopify.ts","../src/utils/manualTracking.ts","../src/components/Experiment.tsx","../src/components/ElevateProvider.tsx","../src/index.ts"],"sourcesContent":["/**\n * Tracking utilities for views and participation\n */\n\nimport {\n setCookie,\n setSessionItem,\n getJsonCookie,\n getJsonSessionItem,\n} from \"./storage\";\n\n/**\n * Track unique view for a test (ABAU cookie)\n */\nexport function trackUniqueView(testId: string): void {\n const addedUniqueViews = getJsonCookie<Record<string, boolean>>(\"ABAU\") || {};\n\n if (!addedUniqueViews[testId]) {\n addedUniqueViews[testId] = true;\n setCookie(\"ABAU\", JSON.stringify(addedUniqueViews));\n }\n}\n\n/**\n * Track session view for a test (ABAV session storage)\n */\nexport function trackSessionView(testId: string): void {\n const addedViews = getJsonSessionItem<Record<string, boolean>>(\"ABAV\") || {};\n\n if (!addedViews[testId]) {\n addedViews[testId] = true;\n setSessionItem(\"ABAV\", addedViews);\n }\n}\n\n/**\n * Track both unique and session views\n */\nexport function trackViews(testId: string): void {\n trackUniqueView(testId);\n trackSessionView(testId);\n}\n\n/**\n * Check if test has been viewed in this session\n */\nexport function hasSessionView(testId: string): boolean {\n const addedViews = getJsonSessionItem<Record<string, boolean>>(\"ABAV\") || {};\n return !!addedViews[testId];\n}\n\n/**\n * Check if test has a unique view\n */\nexport function hasUniqueView(testId: string): boolean {\n const addedUniqueViews = getJsonCookie<Record<string, boolean>>(\"ABAU\") || {};\n return !!addedUniqueViews[testId];\n}\n","/**\n * Condition checking utilities for targeting and filtering\n */\n\n/**\n * Detect device type\n */\nexport function getDeviceType(): \"desktop\" | \"tablet\" | \"mobile\" {\n if (typeof navigator === \"undefined\") return \"desktop\";\n\n const ua =\n navigator.userAgent || navigator.vendor || (window as any).opera || \"\";\n const lower = ua.toLowerCase();\n\n if (\n /windows nt.*tablet pc|pixelbook/i.test(ua) ||\n (/(?:macintosh|windows nt|win(?:95|98)|linux x86_64)/i.test(ua) &&\n !/android|mobile|tablet|ipad|iphone|ipod/i.test(lower))\n )\n return \"desktop\";\n\n const isTablet =\n /ipad|playbook|tablet|kindle|nexus (7|10)|xoom|sm-t\\d+|galaxy tab|tb-\\d+|tb\\d+[a-z]+|lenovo.*tb|dtab|ideatab|mediapad|matepad|honor.*pad|pad\\s+\\d+|mi\\s+pad|redmi.*pad|xiaomi.*pad|oppo.*pad|oneplus.*pad|opd\\d+|tcl.*tab|9\\d{3}[a-z]|kobo|archos.*5(?!\\d)|ereader|droipad/i.test(\n ua\n ) ||\n (/android(?!.*mobile)/i.test(lower) &&\n !/dalvik.*miui|pocophone|mi\\s+mix|edge\\s+\\d+|sm-g9\\d+|pixel\\s+\\d|pixel\\s+fold|advan\\s+\\d{4}/i.test(\n ua\n ));\n\n if (isTablet) return \"tablet\";\n\n const isMobile =\n /android.+mobile|iphone|ipod|windows phone|blackberry|mobile|phone|kaios/i.test(\n lower\n ) ||\n /^mobile;/i.test(ua) ||\n /dalvik.*miui|pocophone|mi\\s+mix|pixel\\s+\\d|pixel\\s+fold/i.test(ua);\n\n if (isMobile) return \"mobile\";\n\n return \"desktop\";\n}\n\n/**\n * Check if Facebook in-app browser\n */\nexport function checkFacebookBrowser(): boolean {\n if (typeof navigator === \"undefined\") return false;\n\n const userAgent =\n navigator.userAgent || navigator.vendor || (window as any).opera;\n const facebookBrowserRegex =\n /((?:fban\\/fbios|fb_iab\\/fb4a)(?!.+fbav)|;fbav\\/([\\w\\.]+);)/i;\n return facebookBrowserRegex.test(userAgent);\n}\n\n/**\n * Check if Instagram in-app browser\n */\nexport function checkInstagramBrowser(): boolean {\n if (typeof navigator === \"undefined\") return false;\n\n const userAgent =\n navigator.userAgent || navigator.vendor || (window as any).opera;\n const instagramBrowserRegex = /(instagram)[\\/ ]([-\\w\\.]+)/i;\n return instagramBrowserRegex.test(userAgent);\n}\n\n/**\n * Check if TikTok in-app browser\n */\nexport function checkTikTokBrowser(): boolean {\n if (typeof navigator === \"undefined\") return false;\n\n const userAgent =\n navigator.userAgent || navigator.vendor || (window as any).opera;\n const tikTokBrowserRegex = /musical_ly/i;\n return tikTokBrowserRegex.test(userAgent);\n}\n\n/**\n * Check if Pinterest in-app browser\n */\nexport function checkPinterestBrowser(): boolean {\n if (typeof navigator === \"undefined\") return false;\n\n const userAgent =\n navigator.userAgent || navigator.vendor || (window as any).opera;\n const pinterestBrowserRegex = /(pinterest)[\\/ ]([-\\w\\.]+)/i;\n return pinterestBrowserRegex.test(userAgent);\n}\n\n/**\n * Get traffic source\n */\nexport function getTrafficSource(): string {\n if (typeof window === \"undefined\") return \"direct\";\n\n const referrer = (document.referrer || \"\").toLowerCase();\n const referrerDomain = referrer\n ? new URL(referrer).hostname.replace(\"www.\", \"\")\n : \"\";\n const hostname = window.location.hostname;\n\n const isFacebookBrowser = checkFacebookBrowser();\n const isInstagramBrowser = checkInstagramBrowser();\n const isTikTokBrowser = checkTikTokBrowser();\n const isPinterestBrowser = checkPinterestBrowser();\n\n if (\n isFacebookBrowser ||\n [\n \"facebook.com\",\n \"fb.com\",\n \"fb.me\",\n \"m.facebook.com\",\n \"l.facebook.com\",\n \"lm.facebook.com\",\n ].includes(referrerDomain)\n ) {\n return \"facebook\";\n } else if (isInstagramBrowser || referrerDomain.includes(\"instagram\")) {\n return \"instagram\";\n } else if (\n isTikTokBrowser ||\n [\"tiktok.com\", \"pangleglobal.com\", \"ads.tiktok.com\"].includes(\n referrerDomain\n )\n ) {\n return \"tiktok\";\n } else if (\n isPinterestBrowser ||\n referrerDomain.includes(\"pinterest\") ||\n referrerDomain === \"pin.it\"\n ) {\n return \"pinterest\";\n } else if (referrerDomain.includes(\"google\")) {\n return \"google\";\n } else if (!referrer || hostname === referrerDomain) {\n return \"direct\";\n } else {\n return \"other\";\n }\n}\n","/**\n * Preview Mode Utilities\n *\n * Enables QA/preview functionality for A/B tests via URL parameters.\n *\n * URL Format:\n * ?eabUserPreview=true&abtid=<test_id>&eab_tests=<shortid>_<variation>_<seen>\n *\n * Example:\n * https://store.com/?eabUserPreview=true&abtid=b6e2ecb8-e35a-450e-ae49-bdc560cf0fd7&eab_tests=f0fd7_29913_1\n */\n\nimport {\n getCookie,\n setCookie,\n getSessionItem,\n setSessionItem,\n} from \"./storage\";\n\n/**\n * Preview mode state\n */\nexport interface PreviewState {\n /** Whether preview mode is active */\n isPreview: boolean;\n /** The test ID being previewed (full UUID or short ID) */\n previewTestId: string | null;\n /** Forced test assignments from URL params */\n forcedAssignments: Record<string, string>;\n /** Tests that have been marked as \"seen\" in preview */\n previewedViews: Record<string, boolean>;\n}\n\n/**\n * Parse eab_tests parameter format: shortid_variation_seen~shortid2_variation2_seen2\n * Example: \"f0fd7_29913_1\" or \"f0fd7_29913_1~a1b2c_12345_0\"\n */\nfunction parseEabTestsParam(param: string): {\n assignments: Record<string, string>;\n views: Record<string, boolean>;\n} {\n const assignments: Record<string, string> = {};\n const views: Record<string, boolean> = {};\n\n if (!param) return { assignments, views };\n\n const tests = param.split(\"~\");\n for (const test of tests) {\n const parts = test.split(\"_\");\n if (parts.length >= 2) {\n const shortTestId = parts[0];\n const variationId = parts[1];\n const hasSeen = parts[2] === \"1\";\n\n assignments[shortTestId] = variationId;\n views[shortTestId] = hasSeen;\n }\n }\n\n return { assignments, views };\n}\n\n/**\n * Get preview state from URL parameters and cookies\n */\nexport function getPreviewState(): PreviewState {\n if (typeof window === \"undefined\") {\n return {\n isPreview: false,\n previewTestId: null,\n forcedAssignments: {},\n previewedViews: {},\n };\n }\n\n const urlParams = new URLSearchParams(window.location.search);\n\n // Check URL param first, then cookie\n const eabUserPreview =\n urlParams.get(\"eabUserPreview\") || getCookie(\"eabUserPreview\");\n const isPreview = eabUserPreview === \"true\";\n\n // Get test ID from URL param or sessionStorage\n let previewTestId = urlParams.get(\"abtid\");\n if (!previewTestId) {\n previewTestId = getSessionItem(\"eabPreviewTestId\");\n }\n\n // Parse forced assignments from eab_tests param\n const eabTestsParam = urlParams.get(\"eab_tests\") || \"\";\n const { assignments, views } = parseEabTestsParam(eabTestsParam);\n\n // If URL has preview params, persist to storage\n if (urlParams.has(\"eabUserPreview\")) {\n setCookie(\"eabUserPreview\", \"true\");\n }\n if (urlParams.has(\"abtid\") && previewTestId) {\n setSessionItem(\"eabPreviewTestId\", previewTestId);\n }\n\n return {\n isPreview,\n previewTestId,\n forcedAssignments: assignments,\n previewedViews: views,\n };\n}\n\n/**\n * Check if a test matches the preview test ID\n * Supports both full UUID and short ID (last 5 chars) matching\n */\nexport function isPreviewTest(\n testId: string,\n previewTestId: string | null,\n): boolean {\n if (!previewTestId) return false;\n\n // Exact match\n if (testId === previewTestId) return true;\n\n // Short ID match (last 5 characters)\n const shortTestId = testId.slice(-5);\n if (shortTestId === previewTestId || previewTestId.endsWith(shortTestId)) {\n return true;\n }\n\n // Preview ID might be short, check if test ends with it\n if (testId.endsWith(previewTestId)) return true;\n\n return false;\n}\n\n/**\n * Get forced variation for a test in preview mode\n * Returns null if no forced assignment exists\n */\nexport function getPreviewVariation(\n testId: string,\n previewState: PreviewState,\n): string | null {\n if (!previewState.isPreview) return null;\n\n // Check by full test ID\n if (previewState.forcedAssignments[testId]) {\n return previewState.forcedAssignments[testId];\n }\n\n // Check by short test ID\n const shortTestId = testId.slice(-5);\n if (previewState.forcedAssignments[shortTestId]) {\n return previewState.forcedAssignments[shortTestId];\n }\n\n return null;\n}\n\n/**\n * Build eab_tests URL parameter string from test assignments\n * Used for sharing preview URLs with Facebook/Instagram traffic\n */\nexport function buildEabTestsParam(\n testAssignments: Record<string, string>,\n viewedTests: Record<string, boolean>,\n): string {\n return Object.entries(testAssignments)\n .map(([testId, variationId]) => {\n const shortTestId = testId.slice(-5);\n const hasSeen = viewedTests[testId] ? \"1\" : \"0\";\n return `${shortTestId}_${variationId}_${hasSeen}`;\n })\n .join(\"~\");\n}\n\n/**\n * Update URL with test parameters for social media browsers (FB/IG/TikTok)\n * These browsers often lose cookies, so we persist test assignments in URL\n */\nexport function updateUrlWithTestParams(\n testAssignments: Record<string, string>,\n viewedTests: Record<string, boolean>,\n visitorId?: string,\n): void {\n if (typeof window === \"undefined\") return;\n\n const url = new URL(window.location.href);\n const eabTestsValue = buildEabTestsParam(testAssignments, viewedTests);\n\n if (eabTestsValue) {\n url.searchParams.set(\"eab_tests\", eabTestsValue);\n }\n\n if (visitorId) {\n url.searchParams.set(\"eabUserId\", visitorId);\n }\n\n // Update URL without reload\n window.history.replaceState({}, \"\", url.toString());\n}\n\n/**\n * Clear preview mode\n */\nexport function clearPreviewMode(): void {\n if (typeof document === \"undefined\") return;\n\n // Clear cookie\n document.cookie =\n \"eabUserPreview=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;\";\n\n // Clear session storage\n if (typeof sessionStorage !== \"undefined\") {\n sessionStorage.removeItem(\"eabPreviewTestId\");\n }\n\n // Remove preview params from URL\n if (typeof window !== \"undefined\") {\n const url = new URL(window.location.href);\n url.searchParams.delete(\"eabUserPreview\");\n url.searchParams.delete(\"abtid\");\n url.searchParams.delete(\"eab_tests\");\n window.history.replaceState({}, \"\", url.toString());\n }\n}\n\n/**\n * Check if current session is in preview mode\n */\nexport function isInPreviewMode(): boolean {\n return getPreviewState().isPreview;\n}\n","/**\n * Geo-targeting Utilities\n *\n * Provides country detection and geo-based targeting for A/B tests.\n */\n\nimport { getCookie, setCookie } from \"./storage\";\n\n/**\n * Geo-location data\n */\nexport interface GeoLocation {\n /** ISO 3166-1 alpha-2 country code (e.g., \"US\", \"GB\", \"CA\") */\n country: string | null;\n /** Region/state code (if available) */\n region?: string | null;\n /** City name (if available) */\n city?: string | null;\n}\n\n/**\n * Cookie names for geo data\n */\nconst GEO_COOKIE_NAMES = {\n /** Shopify's localization cookie */\n SHOPIFY_LOCALIZATION: \"localization\",\n /** Our cached country code */\n EAB_COUNTRY: \"eabCountryCode\",\n /** Full geo data (JSON) */\n EAB_GEO: \"eabGeoLocation\",\n} as const;\n\n/**\n * Get the user's country code from various sources\n * Priority: URL param > Shopify localization > our cookie > localStorage\n */\nexport function getCountryCode(): string | null {\n if (typeof window === \"undefined\") return null;\n\n // 1. Check URL param (for explicit override)\n const urlParams = new URLSearchParams(window.location.search);\n const urlCountry = urlParams.get(\"country\");\n if (urlCountry) {\n // Cache it\n setCookie(GEO_COOKIE_NAMES.EAB_COUNTRY, urlCountry.toUpperCase());\n return urlCountry.toUpperCase();\n }\n\n // 2. Check Shopify's localization cookie (most common for Shopify stores)\n const shopifyCountry = getCookie(GEO_COOKIE_NAMES.SHOPIFY_LOCALIZATION);\n if (shopifyCountry) {\n return shopifyCountry.toUpperCase();\n }\n\n // 3. Check our cached cookie\n const cachedCountry = getCookie(GEO_COOKIE_NAMES.EAB_COUNTRY);\n if (cachedCountry) {\n return cachedCountry.toUpperCase();\n }\n\n // 4. Check localStorage (fallback)\n if (typeof localStorage !== \"undefined\") {\n const storedCountry = localStorage.getItem(GEO_COOKIE_NAMES.EAB_COUNTRY);\n if (storedCountry) {\n return storedCountry.toUpperCase();\n }\n }\n\n return null;\n}\n\n/**\n * Get full geo-location data\n */\nexport function getGeoLocation(): GeoLocation {\n const country = getCountryCode();\n\n // Try to get extended geo data from cookie\n const geoData = getCookie(GEO_COOKIE_NAMES.EAB_GEO);\n if (geoData) {\n try {\n const parsed = JSON.parse(geoData) as GeoLocation;\n return {\n country: parsed.country || country,\n region: parsed.region,\n city: parsed.city,\n };\n } catch {\n // Invalid JSON, fall through\n }\n }\n\n return { country };\n}\n\n/**\n * Set the user's country code (for manual override or after geo detection)\n */\nexport function setCountryCode(countryCode: string): void {\n const normalized = countryCode.toUpperCase();\n setCookie(GEO_COOKIE_NAMES.EAB_COUNTRY, normalized);\n\n // Also store in localStorage for extra persistence\n if (typeof localStorage !== \"undefined\") {\n localStorage.setItem(GEO_COOKIE_NAMES.EAB_COUNTRY, normalized);\n }\n}\n\n/**\n * Set full geo-location data\n */\nexport function setGeoLocation(geo: GeoLocation): void {\n if (geo.country) {\n setCountryCode(geo.country);\n }\n\n // Store full geo data\n setCookie(GEO_COOKIE_NAMES.EAB_GEO, JSON.stringify(geo));\n}\n\n/**\n * Check if user's country matches a list of allowed countries\n */\nexport function isCountryIncluded(\n allowedCountries: string[],\n userCountry?: string | null,\n): boolean {\n const country = userCountry ?? getCountryCode();\n if (!country) return false;\n\n const normalizedAllowed = allowedCountries.map((c) => c.toUpperCase());\n return normalizedAllowed.includes(country.toUpperCase());\n}\n\n/**\n * Check if user's country is in an excluded list\n */\nexport function isCountryExcluded(\n excludedCountries: string[],\n userCountry?: string | null,\n): boolean {\n const country = userCountry ?? getCountryCode();\n if (!country) return false;\n\n const normalizedExcluded = excludedCountries.map((c) => c.toUpperCase());\n return normalizedExcluded.includes(country.toUpperCase());\n}\n\n/**\n * Check if user matches geo-targeting rules\n * Returns true if user should be included in the test\n */\nexport function matchesGeoTargeting(rules: {\n includeCountries?: string[];\n excludeCountries?: string[];\n}): boolean {\n const country = getCountryCode();\n\n // If no rules, include everyone\n if (!rules.includeCountries?.length && !rules.excludeCountries?.length) {\n return true;\n }\n\n // If no country detected and rules exist, exclude (can't target unknown)\n if (!country) {\n return false;\n }\n\n // Check exclusions first (takes precedence)\n if (rules.excludeCountries?.length) {\n if (isCountryExcluded(rules.excludeCountries, country)) {\n return false;\n }\n }\n\n // If include list exists, user must be in it\n if (rules.includeCountries?.length) {\n return isCountryIncluded(rules.includeCountries, country);\n }\n\n // No include list, and not excluded = included\n return true;\n}\n\n/**\n * Common country code constants for convenience\n */\nexport const COUNTRIES = {\n US: \"US\",\n CA: \"CA\",\n GB: \"GB\",\n AU: \"AU\",\n DE: \"DE\",\n FR: \"FR\",\n JP: \"JP\",\n MX: \"MX\",\n BR: \"BR\",\n IN: \"IN\",\n // Add more as needed\n} as const;\n\n/**\n * Region groupings for common use cases\n */\nexport const REGIONS = {\n NORTH_AMERICA: [\"US\", \"CA\", \"MX\"],\n EUROPE: [\n \"GB\",\n \"DE\",\n \"FR\",\n \"IT\",\n \"ES\",\n \"NL\",\n \"BE\",\n \"AT\",\n \"CH\",\n \"PL\",\n \"SE\",\n \"NO\",\n \"DK\",\n \"FI\",\n \"IE\",\n \"PT\",\n ],\n APAC: [\n \"AU\",\n \"NZ\",\n \"JP\",\n \"KR\",\n \"SG\",\n \"HK\",\n \"TW\",\n \"TH\",\n \"MY\",\n \"PH\",\n \"ID\",\n \"VN\",\n \"IN\",\n ],\n LATAM: [\"MX\", \"BR\", \"AR\", \"CL\", \"CO\", \"PE\"],\n} as const;\n","/**\n * Anti-Flicker Utilities\n *\n * Prevents visual flickering during A/B test assignment by hiding content\n * until the variant is determined. Uses a CSS-based approach for performance.\n *\n * Note: Called \"preventFlickering\" in our API to differentiate from competitors.\n */\n\n/**\n * CSS class used to hide content during flicker prevention\n */\nconst FLICKER_CLASS = \"eab-prevent-flicker\";\n\n/**\n * Inline style ID for flicker prevention\n */\nconst STYLE_ID = \"eab-flicker-styles\";\n\n/**\n * Default timeout (ms) before showing content anyway (failsafe)\n */\nconst DEFAULT_TIMEOUT = 3000;\n\n/**\n * Inject the flicker prevention CSS styles\n * This should be called as early as possible (ideally in <head>)\n */\nexport function injectFlickerStyles(timeout = DEFAULT_TIMEOUT): void {\n if (typeof document === \"undefined\") return;\n\n // Don't inject twice\n if (document.getElementById(STYLE_ID)) return;\n\n const style = document.createElement(\"style\");\n style.id = STYLE_ID;\n style.textContent = `\n /* Elevate AB - Flicker Prevention */\n .${FLICKER_CLASS} {\n opacity: 0 !important;\n pointer-events: none !important;\n }\n \n /* Failsafe: ensure content shows after timeout via animation */\n @keyframes eab-reveal {\n to { opacity: 1; pointer-events: auto; }\n }\n \n .${FLICKER_CLASS} {\n animation: eab-reveal 0s ${timeout}ms forwards;\n }\n `;\n\n // Insert at the beginning of <head> for highest priority\n const head = document.head || document.getElementsByTagName(\"head\")[0];\n head.insertBefore(style, head.firstChild);\n}\n\n/**\n * Hide the page/element to prevent flicker\n * Call this before test assignment begins\n */\nexport function hideForFlicker(selector: string = \"body\"): void {\n if (typeof document === \"undefined\") return;\n\n const element = document.querySelector(selector);\n if (element) {\n element.classList.add(FLICKER_CLASS);\n }\n}\n\n/**\n * Show the page/element after test assignment is complete\n * Call this after variant is determined\n */\nexport function revealAfterFlicker(selector: string = \"body\"): void {\n if (typeof document === \"undefined\") return;\n\n const element = document.querySelector(selector);\n if (element) {\n element.classList.remove(FLICKER_CLASS);\n }\n}\n\n/**\n * Setup flicker prevention with automatic reveal\n * Returns a function to call when assignment is complete\n *\n * @param selector - CSS selector for element to hide (default: \"body\")\n * @param timeout - Maximum time to wait before revealing (default: 3000ms)\n * @returns Function to call when test assignment is complete\n *\n * @example\n * ```tsx\n * // In your app initialization\n * const reveal = setupFlickerPrevention();\n *\n * // After test assignment\n * reveal();\n * ```\n */\nexport function setupFlickerPrevention(\n selector: string = \"body\",\n timeout: number = DEFAULT_TIMEOUT,\n): () => void {\n if (typeof document === \"undefined\") {\n return () => {}; // No-op for SSR\n }\n\n // Inject styles\n injectFlickerStyles(timeout);\n\n // Hide content\n hideForFlicker(selector);\n\n // Setup timeout failsafe\n let hasRevealed = false;\n const timeoutId = setTimeout(() => {\n if (!hasRevealed) {\n console.warn(\n `[ElevateAB] Flicker prevention timeout (${timeout}ms) reached. ` +\n \"Revealing content. Check if test assignment is completing.\",\n );\n revealAfterFlicker(selector);\n hasRevealed = true;\n }\n }, timeout);\n\n // Return reveal function\n return () => {\n if (!hasRevealed) {\n clearTimeout(timeoutId);\n revealAfterFlicker(selector);\n hasRevealed = true;\n }\n };\n}\n\n/**\n * Remove all flicker prevention styles and classes\n * Useful for cleanup or when disabling the feature\n */\nexport function cleanupFlickerPrevention(): void {\n if (typeof document === \"undefined\") return;\n\n // Remove style tag\n const style = document.getElementById(STYLE_ID);\n if (style) {\n style.remove();\n }\n\n // Remove class from all elements\n const elements = document.querySelectorAll(`.${FLICKER_CLASS}`);\n elements.forEach((el) => el.classList.remove(FLICKER_CLASS));\n}\n\n/**\n * Check if flicker prevention is currently active\n */\nexport function isFlickerPreventionActive(): boolean {\n if (typeof document === \"undefined\") return false;\n\n return document.querySelector(`.${FLICKER_CLASS}`) !== null;\n}\n\n/**\n * Script snippet for inline injection in <head>\n * Use this when you need synchronous flicker prevention before React hydrates\n *\n * @example\n * ```tsx\n * // In your _document.tsx or layout.tsx\n * <script dangerouslySetInnerHTML={{ __html: getFlickerPreventionScript() }} />\n * ```\n */\nexport function getFlickerPreventionScript(timeout = DEFAULT_TIMEOUT): string {\n return `\n(function() {\n var style = document.createElement('style');\n style.id = '${STYLE_ID}';\n style.textContent = '.${FLICKER_CLASS}{opacity:0!important;pointer-events:none!important}@keyframes eab-reveal{to{opacity:1;pointer-events:auto}}.${FLICKER_CLASS}{animation:eab-reveal 0s ${timeout}ms forwards}';\n document.head.insertBefore(style, document.head.firstChild);\n document.body && document.body.classList.add('${FLICKER_CLASS}');\n})();\n`.trim();\n}\n","/**\n * Shopify-specific utilities\n */\n\n/**\n * Extract numeric ID from a Shopify Global ID (GID).\n *\n * Shopify uses GIDs like \"gid://shopify/Product/123456789\" or \"gid://shopify/ProductVariant/987654321\".\n * This utility extracts the numeric ID portion.\n *\n * @param gid - Shopify Global ID or numeric ID string\n * @returns The numeric ID portion (e.g., \"123456789\")\n *\n * @example\n * ```ts\n * extractShopifyId(\"gid://shopify/Product/123456789\"); // \"123456789\"\n * extractShopifyId(\"gid://shopify/ProductVariant/987654321\"); // \"987654321\"\n * extractShopifyId(\"gid://shopify/Cart/abc123\"); // \"abc123\"\n * extractShopifyId(\"123456789\"); // \"123456789\" (already numeric)\n * ```\n */\nexport function extractShopifyId(gid: string | null | undefined): string {\n if (!gid) return \"\";\n\n // If it's a GID, extract the last segment\n if (gid.includes(\"gid://\")) {\n return gid.split(\"/\").pop() || gid;\n }\n\n // Already a plain ID\n return gid;\n}\n\n/**\n * Extract the resource type from a Shopify Global ID.\n *\n * @param gid - Shopify Global ID\n * @returns The resource type (e.g., \"Product\", \"ProductVariant\", \"Cart\")\n *\n * @example\n * ```ts\n * extractShopifyType(\"gid://shopify/Product/123\"); // \"Product\"\n * extractShopifyType(\"gid://shopify/ProductVariant/456\"); // \"ProductVariant\"\n * extractShopifyType(\"123456789\"); // null (not a GID)\n * ```\n */\nexport function extractShopifyType(\n gid: string | null | undefined,\n): string | null {\n if (!gid || !gid.includes(\"gid://\")) return null;\n\n const parts = gid.split(\"/\");\n // gid://shopify/Product/123 -> parts = [\"gid:\", \"\", \"shopify\", \"Product\", \"123\"]\n return parts[3] || null;\n}\n\n/**\n * Check if a string is a Shopify Global ID.\n *\n * @param value - String to check\n * @returns True if it's a GID\n *\n * @example\n * ```ts\n * isShopifyGid(\"gid://shopify/Product/123\"); // true\n * isShopifyGid(\"123456789\"); // false\n * ```\n */\nexport function isShopifyGid(value: string | null | undefined): boolean {\n return !!value && value.startsWith(\"gid://shopify/\");\n}\n\n/**\n * Try to detect the shop currency from Shopify's global object.\n * Only works on Shopify theme stores, not headless.\n *\n * @returns Currency code (e.g., \"USD\") or undefined\n */\nexport function detectShopifyCurrency(): string | undefined {\n if (typeof window === \"undefined\") return undefined;\n\n // Try Shopify's global currency object (theme stores only)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const shopify = (window as any).Shopify;\n if (shopify?.currency?.active) {\n return shopify.currency.active;\n }\n\n return undefined;\n}\n","/**\n * Manual tracking functions for non-Hydrogen frameworks (Next.js, etc.)\n *\n * Initialize once with initAnalytics(), then call tracking functions without params:\n *\n * @example\n * ```tsx\n * // In your layout/provider (once)\n * initAnalytics({\n * storeId: 'your-store.myshopify.com',\n * storefrontAccessToken: 'your-token',\n * });\n *\n * // Then anywhere in your app (no params needed)\n * trackPageView();\n * trackAddToCart({ cartId, productId, ... });\n * ```\n */\n\nimport type {\n EventPayload,\n TestAssignment,\n TrackPageViewParams,\n TrackProductViewParams,\n TrackAddToCartParams,\n TrackRemoveFromCartParams,\n TrackCartViewParams,\n TrackSearchSubmittedParams,\n TrackCheckoutStartedParams,\n TrackCheckoutCompletedParams,\n} from \"../types\";\n\nimport { extractShopifyId } from \"./shopify\";\n\n// Re-import types for cleaner syntax below\nimport type { UsePageViewTrackingParams } from \"../types\";\nimport {\n parseAddViewData,\n extractCartToken,\n cleanCartToken,\n getPageTypeFromPathname,\n sanitizeString,\n roundToTwo,\n getUserAgentNoBrowser,\n} from \"./analytics\";\nimport {\n getVisitorId,\n getSessionId,\n getCookie,\n getJsonCookie,\n getReferrerData,\n uuidv4,\n} from \"./storage\";\n\nconst DEFAULT_WORKER_URL = \"https://bitter-river-9c62.support-67d.workers.dev\";\nconst DEFAULT_ORDERS_WORKER_URL =\n \"https://d339co84ntxcme.cloudfront.net/Prod/orders\";\n\n// ============================================================================\n// GLOBAL ANALYTICS CONFIG (initialized once, used by all tracking functions)\n// ============================================================================\n\ninterface AnalyticsConfig {\n storeId: string;\n storefrontAccessToken?: string;\n hasLocalizedPaths?: boolean;\n workerUrl?: string;\n ordersWorkerUrl?: string;\n}\n\nlet globalConfig: AnalyticsConfig | null = null;\n\n/**\n * Initialize analytics config once. Call this in your app's entry point or layout.\n * After init, tracking functions don't need storeId/token params.\n *\n * @example\n * ```tsx\n * // In _app.tsx or layout.tsx\n * initAnalytics({\n * storeId: 'your-store.myshopify.com',\n * storefrontAccessToken: process.env.NEXT_PUBLIC_STOREFRONT_TOKEN,\n * });\n * ```\n */\nexport function initAnalytics(config: AnalyticsConfig): void {\n globalConfig = config;\n}\n\n/**\n * Get the current analytics config (for internal use)\n */\nexport function getAnalyticsConfig(): AnalyticsConfig | null {\n return globalConfig;\n}\n\n/**\n * Check if analytics is initialized\n */\nexport function isAnalyticsInitialized(): boolean {\n return globalConfig !== null;\n}\n\n// Helper to get storeId from params or global config\nfunction getStoreId(params?: { storeId?: string }): string {\n const storeId = params?.storeId || globalConfig?.storeId;\n if (!storeId) {\n console.warn(\n \"[ElevateAB] No storeId provided. Call initAnalytics() first or pass storeId to tracking function.\",\n );\n return \"\";\n }\n return storeId;\n}\n\n// Transform test list format to event format\nfunction transformTestFormat(\n testObject: Record<string, string>,\n): TestAssignment[] {\n return Object.entries(testObject)\n .filter(([, variationId]) => typeof variationId === \"string\")\n .map(([testId, variationId]) => ({\n test_id: testId,\n variant_id: variationId,\n }));\n}\n\n// Transform viewed tests format\nfunction transformViewedTests(\n viewedObject: Record<string, boolean>,\n assignmentObject: Record<string, string>,\n): TestAssignment[] {\n return Object.entries(viewedObject)\n .filter(([testId]) => typeof assignmentObject[testId] === \"string\")\n .map(([testId]) => ({\n test_id: testId,\n variant_id: assignmentObject[testId],\n }));\n}\n\n// Check if we're in preview mode (should skip analytics)\nfunction isInPreviewMode(): boolean {\n if (typeof window === \"undefined\") return false;\n\n // Check cookie\n if (getCookie(\"eabUserPreview\") === \"true\") return true;\n\n // Check URL param\n const urlParams = new URLSearchParams(window.location.search);\n if (urlParams.get(\"eabUserPreview\") === \"true\") return true;\n\n return false;\n}\n\n// Create base event data (returns null if in preview mode)\nfunction createBaseEventData(\n storeId: string,\n eventType: string,\n hasLocalizedPaths?: boolean,\n): Partial<EventPayload> | null {\n if (typeof window === \"undefined\") return null;\n\n // Skip tracking in preview mode to avoid polluting analytics\n if (isInPreviewMode()) {\n return null;\n }\n\n const userAgent = navigator.userAgent;\n const pathname = window.location.pathname;\n const pageType = getPageTypeFromPathname(pathname, hasLocalizedPaths);\n\n const { referrer, entryPage } = getReferrerData();\n\n const parsedData = parseAddViewData({\n referrer,\n entryPage,\n userAgent,\n });\n\n const abtlObject = getJsonCookie<Record<string, string>>(\"ABTL\") || {};\n const abauObject = getJsonCookie<Record<string, boolean>>(\"ABAU\") || {};\n\n const testAssignments = transformTestFormat(abtlObject);\n const viewedTests = transformViewedTests(abauObject, abtlObject);\n\n const cartToken = extractCartToken(localStorage.getItem(\"shopifyCartId\"));\n const currentTimestamp = new Date().toISOString();\n\n // Get additional state\n const isFirstVisit = sessionStorage.getItem(\"eabIsFirstVisit\") === \"true\";\n const shopifyCountry = getCookie(\"localization\") || \"\";\n\n return {\n pixel_event_id: `sh-${uuidv4()}`,\n shop_name: storeId,\n timestamp: currentTimestamp,\n event_type: eventType,\n client_id: getCookie(\"_shopify_y\") || undefined,\n visitor_id: getVisitorId(),\n session_id: getSessionId(),\n cart_token: cleanCartToken(cartToken || getCookie(\"cart\")),\n\n page_url: window.location.href,\n page_pathname: pathname,\n page_search: window.location.search,\n referrer_url: referrer,\n referrer_source: parsedData?.referrer_source,\n previous_page: document.referrer,\n page_entry: entryPage,\n page_entry_path: parsedData?.page_entry_path,\n page_type: pageType,\n\n utm_medium: parsedData?.utm_medium,\n utm_source: parsedData?.utm_source,\n utm_campaign: parsedData?.utm_campaign,\n utm_content: parsedData?.utm_content,\n utm_term: parsedData?.utm_term,\n\n gclid: parsedData?.gclid,\n fbclid: parsedData?.fbclid,\n pins_campaign_id: parsedData?.pins_campaign_id,\n epik: parsedData?.epik,\n\n browser_info: parsedData?.browser_info,\n os_info: parsedData?.os_info,\n device_type: parsedData?.device_type,\n language: navigator.language,\n root_route: localStorage.getItem(\"eabRootRoute\") || \"\",\n user_agent: userAgent,\n user_agent_no_browser: getUserAgentNoBrowser(userAgent),\n\n is_first_visit: isFirstVisit,\n shopify_country: shopifyCountry,\n\n ab_test_assignments: testAssignments,\n ab_test_views: viewedTests,\n is_first_order: null,\n };\n}\n\n// Send event to CloudFlare Worker\nasync function sendEvent(\n data: Partial<EventPayload>,\n workerUrl: string = DEFAULT_WORKER_URL,\n): Promise<void> {\n try {\n const response = await fetch(workerUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(data),\n keepalive: true,\n });\n\n if (!response.ok) {\n throw new Error(`Worker error: ${response.status}`);\n }\n } catch (error) {\n console.error(\"[ElevateAB] Error sending analytics:\", error);\n }\n}\n\n/**\n * Track page view event\n * Call this on route changes or in useEffect\n *\n * @example After initAnalytics()\n * ```ts\n * await trackPageView(); // No params needed!\n * ```\n *\n * @example Without init (pass params)\n * ```ts\n * await trackPageView({ storeId: \"mystore.myshopify.com\" });\n * ```\n */\nexport async function trackPageView(\n params: Partial<TrackPageViewParams> = {},\n): Promise<void> {\n const storeId = getStoreId(params);\n if (!storeId) return;\n\n const hasLocalizedPaths =\n params.hasLocalizedPaths ?? globalConfig?.hasLocalizedPaths;\n const workerUrl = params.workerUrl ?? globalConfig?.workerUrl;\n\n const baseData = createBaseEventData(\n storeId,\n \"page_viewed\",\n hasLocalizedPaths,\n );\n if (!baseData) return; // Skip in preview mode\n\n const eventData: Partial<EventPayload> = {\n ...baseData,\n cart_currency: params.currency,\n };\n\n await sendEvent(eventData, workerUrl);\n}\n\n/**\n * Track product view event\n * Call this on product pages\n *\n * @example After initAnalytics()\n * ```ts\n * await trackProductView({ productId: \"123\", productPrice: 99.99 });\n * ```\n */\nexport async function trackProductView(\n params: Omit<TrackProductViewParams, \"storeId\"> & { storeId?: string },\n): Promise<void> {\n const storeId = getStoreId(params);\n if (!storeId) return;\n\n const { productId, productVendor, productPrice, productSku, currency } =\n params;\n const hasLocalizedPaths =\n params.hasLocalizedPaths ?? globalConfig?.hasLocalizedPaths;\n const workerUrl = params.workerUrl ?? globalConfig?.workerUrl;\n\n const baseData = createBaseEventData(\n storeId,\n \"product_viewed\",\n hasLocalizedPaths,\n );\n if (!baseData) return; // Skip in preview mode\n\n const eventData: Partial<EventPayload> = {\n ...baseData,\n cart_currency: currency,\n product_id: extractShopifyId(productId),\n product_vendor: sanitizeString(productVendor),\n product_price: roundToTwo(productPrice),\n product_sku: sanitizeString(productSku),\n };\n\n await sendEvent(eventData, workerUrl);\n}\n\n/**\n * Track add to cart event\n * Call this when user clicks \"Add to Cart\"\n *\n * Cart attributes are automatically updated for order attribution if\n * storefrontAccessToken was provided to initAnalytics() or passed here.\n *\n * @example After initAnalytics()\n * ```ts\n * await trackAddToCart({\n * cartId: \"gid://shopify/Cart/abc123\",\n * productId: \"123456789\",\n * variantId: \"987654321\",\n * productPrice: 99.99,\n * productQuantity: 1,\n * });\n * ```\n */\nexport async function trackAddToCart(\n params: Omit<TrackAddToCartParams, \"storeId\" | \"storefrontAccessToken\"> & {\n storeId?: string;\n storefrontAccessToken?: string;\n },\n): Promise<void> {\n const storeId = getStoreId(params);\n if (!storeId) return;\n\n const {\n productId,\n variantId,\n productVendor,\n productPrice,\n productQuantity,\n productSku,\n currency,\n cartId,\n } = params;\n\n const hasLocalizedPaths =\n params.hasLocalizedPaths ?? globalConfig?.hasLocalizedPaths;\n const workerUrl = params.workerUrl ?? globalConfig?.workerUrl;\n const storefrontAccessToken =\n params.storefrontAccessToken ?? globalConfig?.storefrontAccessToken;\n\n const baseData = createBaseEventData(\n storeId,\n \"product_added_to_cart\",\n hasLocalizedPaths,\n );\n if (!baseData) return; // Skip in preview mode\n\n const eventData: Partial<EventPayload> = {\n ...baseData,\n cart_currency: currency,\n product_id: extractShopifyId(productId),\n variant_id: extractShopifyId(variantId),\n product_vendor: sanitizeString(productVendor),\n product_price: roundToTwo(productPrice),\n product_quantity: productQuantity,\n product_sku: sanitizeString(productSku),\n };\n\n await sendEvent(eventData, workerUrl);\n\n // Auto-update cart attributes if cartId and token are available\n if (cartId && storefrontAccessToken) {\n try {\n const { updateCartAttributes } = await import(\"./cartAttributes\");\n await updateCartAttributes(cartId, { storefrontAccessToken });\n } catch (err) {\n console.error(\"[ElevateAB] Failed to update cart attributes:\", err);\n }\n }\n}\n\n/**\n * Track remove from cart event\n * Call this when user removes item from cart\n */\nexport async function trackRemoveFromCart(\n params: Omit<TrackRemoveFromCartParams, \"storeId\"> & { storeId?: string },\n): Promise<void> {\n const storeId = getStoreId(params);\n if (!storeId) return;\n\n const {\n productId,\n variantId,\n productVendor,\n productPrice,\n productQuantity,\n productSku,\n currency,\n } = params;\n\n const hasLocalizedPaths =\n params.hasLocalizedPaths ?? globalConfig?.hasLocalizedPaths;\n const workerUrl = params.workerUrl ?? globalConfig?.workerUrl;\n\n const baseData = createBaseEventData(\n storeId,\n \"product_removed_from_cart\",\n hasLocalizedPaths,\n );\n if (!baseData) return; // Skip in preview mode\n\n const eventData: Partial<EventPayload> = {\n ...baseData,\n cart_currency: currency,\n product_id: extractShopifyId(productId),\n variant_id: extractShopifyId(variantId),\n product_vendor: sanitizeString(productVendor),\n product_price: roundToTwo(productPrice),\n product_quantity: productQuantity,\n product_sku: sanitizeString(productSku),\n };\n\n await sendEvent(eventData, workerUrl);\n}\n\n/**\n * Track cart view event\n * Call this when user views cart page\n */\nexport async function trackCartView(\n params: Omit<TrackCartViewParams, \"storeId\"> & { storeId?: string },\n): Promise<void> {\n const storeId = getStoreId(params);\n if (!storeId) return;\n\n const { cartTotalPrice, cartTotalQuantity, cartItems, currency } = params;\n const hasLocalizedPaths =\n params.hasLocalizedPaths ?? globalConfig?.hasLocalizedPaths;\n const workerUrl = params.workerUrl ?? globalConfig?.workerUrl;\n\n const baseData = createBaseEventData(\n storeId,\n \"cart_viewed\",\n hasLocalizedPaths,\n );\n if (!baseData) return; // Skip in preview mode\n\n const eventData: Partial<EventPayload> = {\n ...baseData,\n cart_currency: currency,\n cart_total_price: roundToTwo(cartTotalPrice),\n cart_total_quantity: cartTotalQuantity,\n cart_items: cartItems?.map((item) => ({\n product_id: extractShopifyId(item.productId),\n variant_id: extractShopifyId(item.variantId),\n product_vendor: sanitizeString(item.productVendor) || \"\",\n product_price: roundToTwo(item.productPrice),\n product_quantity: item.productQuantity ?? null,\n product_sku: sanitizeString(item.productSku) || \"\",\n })),\n };\n\n await sendEvent(eventData, workerUrl);\n}\n\n/**\n * Track search submitted event\n * Call this when user submits a search\n *\n * @example After initAnalytics()\n * ```ts\n * await trackSearchSubmitted({ searchQuery: \"running shoes\" });\n * ```\n *\n * @example Without init (pass params)\n * ```ts\n * await trackSearchSubmitted({\n * storeId: \"mystore.myshopify.com\",\n * searchQuery: \"running shoes\",\n * currency: \"USD\"\n * });\n * ```\n */\nexport async function trackSearchSubmitted(\n params: Omit<TrackSearchSubmittedParams, \"storeId\"> & { storeId?: string },\n): Promise<void> {\n const storeId = getStoreId(params);\n if (!storeId) return;\n\n const { searchQuery, currency } = params;\n const hasLocalizedPaths =\n params.hasLocalizedPaths ?? globalConfig?.hasLocalizedPaths;\n const workerUrl = params.workerUrl ?? globalConfig?.workerUrl;\n\n const baseData = createBaseEventData(\n storeId,\n \"search_submitted\",\n hasLocalizedPaths,\n );\n if (!baseData) return; // Skip in preview mode\n\n const eventData: Partial<EventPayload> = {\n ...baseData,\n cart_currency: currency,\n search_query: sanitizeString(searchQuery),\n };\n\n await sendEvent(eventData, workerUrl);\n}\n\n/**\n * Track checkout started event\n * Call this when user starts checkout\n *\n * @example After initAnalytics()\n * ```ts\n * await trackCheckoutStarted({\n * cartTotalPrice: 109.98,\n * cartItems: [...],\n * });\n * ```\n */\nexport async function trackCheckoutStarted(\n params: Omit<TrackCheckoutStartedParams, \"storeId\"> & { storeId?: string },\n): Promise<void> {\n const storeId = getStoreId(params);\n if (!storeId) return;\n\n const {\n cartTotalPrice,\n cartSubtotalPrice,\n cartShippingPrice,\n cartTaxAmount,\n cartDiscountAmount,\n customerId,\n cartItems,\n currency,\n } = params;\n\n const hasLocalizedPaths =\n params.hasLocalizedPaths ?? globalConfig?.hasLocalizedPaths;\n const workerUrl = params.workerUrl ?? globalConfig?.workerUrl;\n\n const baseData = createBaseEventData(\n storeId,\n \"checkout_started\",\n hasLocalizedPaths,\n );\n if (!baseData) return; // Skip in preview mode\n\n let totalQuantity = 0;\n const items =\n cartItems?.map((item) => {\n const quantity = item.productQuantity ?? 0;\n totalQuantity += quantity;\n return {\n product_id: extractShopifyId(item.productId),\n variant_id: extractShopifyId(item.variantId),\n product_vendor: sanitizeString(item.productVendor) || \"\",\n vendor: sanitizeString(item.productVendor) || \"\",\n product_price: roundToTwo(item.productPrice),\n total_price: roundToTwo(item.productPrice),\n product_quantity: quantity,\n quantity: quantity,\n product_sku: sanitizeString(item.productSku) || \"\",\n sku: sanitizeString(item.productSku) || \"\",\n total_discount: roundToTwo(item.totalDiscount),\n };\n }) || [];\n\n const eventData: Partial<EventPayload> = {\n ...baseData,\n cart_currency: currency,\n cart_total_price: roundToTwo(cartTotalPrice),\n cart_subtotal_price: roundToTwo(cartSubtotalPrice),\n cart_total_quantity: totalQuantity,\n cart_shipping_price: roundToTwo(cartShippingPrice),\n cart_tax_amount: roundToTwo(cartTaxAmount),\n cart_discount_amount: roundToTwo(cartDiscountAmount),\n cart_items: items,\n customer_id: customerId,\n };\n\n await sendEvent(eventData, workerUrl);\n}\n\n/**\n * Track checkout completed event (order placed)\n * Call this when order is placed\n *\n * NOTE: This sends to a different endpoint (orders worker)\n *\n * @example After initAnalytics()\n * ```ts\n * await trackCheckoutCompleted({\n * orderId: \"order_123456\",\n * cartTotalPrice: 109.98,\n * cartItems: [...],\n * });\n * ```\n */\nexport async function trackCheckoutCompleted(\n params: Omit<TrackCheckoutCompletedParams, \"storeId\"> & { storeId?: string },\n): Promise<void> {\n const storeId = getStoreId(params);\n if (!storeId) return;\n\n const {\n orderId,\n cartTotalPrice,\n cartSubtotalPrice,\n cartShippingPrice,\n cartTaxAmount,\n cartDiscountAmount,\n customerId,\n isFirstOrder,\n noteAttributes,\n cartItems,\n currency,\n } = params;\n\n const hasLocalizedPaths =\n params.hasLocalizedPaths ?? globalConfig?.hasLocalizedPaths;\n const ordersWorkerUrl =\n params.ordersWorkerUrl ??\n globalConfig?.ordersWorkerUrl ??\n DEFAULT_ORDERS_WORKER_URL;\n\n const baseData = createBaseEventData(\n storeId,\n \"checkout_completed\",\n hasLocalizedPaths,\n );\n if (!baseData) return; // Skip in preview mode\n\n let totalQuantity = 0;\n const items =\n cartItems?.map((item) => {\n const quantity = item.productQuantity ?? 0;\n totalQuantity += quantity;\n return {\n product_id: extractShopifyId(item.productId),\n variant_id: extractShopifyId(item.variantId),\n product_vendor: sanitizeString(item.productVendor) || \"\",\n vendor: sanitizeString(item.productVendor) || \"\",\n product_price: roundToTwo(item.productPrice),\n total_price: roundToTwo(item.productPrice),\n product_quantity: quantity,\n quantity: quantity,\n product_sku: sanitizeString(item.productSku) || \"\",\n sku: sanitizeString(item.productSku) || \"\",\n total_discount: roundToTwo(item.totalDiscount),\n };\n }) || [];\n\n const eventData: Partial<EventPayload> = {\n ...baseData,\n cart_currency: currency,\n cart_total_price: roundToTwo(cartTotalPrice),\n cart_subtotal_price: roundToTwo(cartSubtotalPrice),\n cart_total_quantity: totalQuantity,\n cart_shipping_price: roundToTwo(cartShippingPrice),\n cart_tax_amount: roundToTwo(cartTaxAmount),\n cart_discount_amount: roundToTwo(cartDiscountAmount),\n cart_items: items,\n order_id: orderId,\n customer_id: customerId,\n is_first_order: isFirstOrder ?? null,\n note_attributes: noteAttributes,\n };\n\n // Send to orders worker (different endpoint!)\n await sendEvent(eventData, ordersWorkerUrl);\n}\n\nimport { useEffect, useRef, useState } from \"react\";\n\n/**\n * Hook for automatic page view tracking on route changes\n * Use in Next.js app router or any React app\n *\n * NOTE: For Next.js App Router, consider using this with `usePathname()`:\n * ```tsx\n * const pathname = usePathname();\n * usePageViewTracking({ pathname, enabled: true });\n * ```\n *\n * @example Basic usage (tracks on mount only)\n * ```tsx\n * function Layout({ children }) {\n * usePageViewTracking({ enabled: true });\n * return <>{children}</>;\n * }\n * ```\n *\n * @example With Next.js usePathname (tracks on route changes)\n * ```tsx\n * import { usePathname } from 'next/navigation';\n *\n * function Layout({ children }) {\n * const pathname = usePathname();\n * usePageViewTracking({ pathname, enabled: true });\n * return <>{children}</>;\n * }\n * ```\n */\nexport function usePageViewTracking(\n params: UsePageViewTrackingParams & { pathname?: string },\n): void {\n const {\n storeId,\n hasLocalizedPaths,\n currency,\n workerUrl,\n enabled = true,\n pathname: externalPathname,\n } = params;\n\n // Track the last pathname we've sent an event for\n const lastTrackedPathRef = useRef<string | null>(null);\n\n // Use external pathname if provided (e.g., from Next.js usePathname),\n // otherwise try to get it from window (works on mount)\n const [currentPath, setCurrentPath] = useState<string>(() => {\n if (externalPathname) return externalPathname;\n if (typeof window !== \"undefined\") return window.location.pathname;\n return \"\";\n });\n\n // Update currentPath when externalPathname changes\n useEffect(() => {\n if (externalPathname) {\n setCurrentPath(externalPathname);\n }\n }, [externalPathname]);\n\n // Track page view when path changes\n useEffect(() => {\n if (typeof window === \"undefined\" || !enabled) return;\n\n // Don't track if we've already tracked this path\n if (lastTrackedPathRef.current === currentPath) return;\n\n // Track the page view\n lastTrackedPathRef.current = currentPath;\n trackPageView({ storeId, hasLocalizedPaths, currency, workerUrl });\n }, [currentPath, storeId, hasLocalizedPaths, currency, workerUrl, enabled]);\n}\n","import React from \"react\";\nimport type { Variation } from \"../types\";\nimport { useElevateConfig } from \"../contexts/ElevateContext\";\nimport { getVisitorId } from \"../utils/storage\";\nimport { assignAndPersistVariant, shouldShowTest } from \"../utils/assignment\";\nimport { trackViews } from \"../utils/tracking\";\n\nexport interface UseExperimentResult {\n /** The full variant object (null if not assigned) */\n variant: Variation | null;\n /** True while loading config */\n isLoading: boolean;\n /** True if assigned to control group */\n isControl: boolean;\n /** True if assigned to variation A (first non-control) */\n isA: boolean;\n /** True if assigned to variation B (second non-control) */\n isB: boolean;\n /** True if assigned to variation C (third non-control) */\n isC: boolean;\n /** True if assigned to variation D (fourth non-control) */\n isD: boolean;\n}\n\n/**\n * Hook to get assigned variant for a test\n * \n * @example\n * ```tsx\n * const { isControl, isA, isB } = useExperiment('my-test');\n * \n * if (isControl) return <Original />;\n * if (isA) return <VariantA />;\n * if (isB) return <VariantB />;\n * ```\n */\nexport function useExperiment(testId: string): UseExperimentResult {\n const { config } = useElevateConfig();\n const [variant, setVariant] = React.useState<Variation | null>(null);\n const [isLoading, setIsLoading] = React.useState(true);\n\n const testConfig = React.useMemo(() => {\n if (!config) return null;\n return config.tests.find((test) => test.testId === testId) || null;\n }, [config, testId]);\n\n // Track if we've already warned about this test (prevent duplicate warnings)\n const hasWarnedRef = React.useRef<string | null>(null);\n\n React.useEffect(() => {\n // Wait for config to load before doing anything\n if (config === null) {\n return; // Still loading config\n }\n\n // Config loaded but test not found\n if (!testConfig) {\n if (hasWarnedRef.current !== testId) {\n hasWarnedRef.current = testId;\n console.warn(`[ElevateAB] Test not found: ${testId}`);\n }\n setIsLoading(false);\n return;\n }\n\n // Test exists but is disabled\n if (!testConfig.enabled) {\n if (hasWarnedRef.current !== `${testId}-disabled`) {\n hasWarnedRef.current = `${testId}-disabled`;\n console.warn(`[ElevateAB] Test disabled: ${testId}`);\n }\n setIsLoading(false);\n return;\n }\n\n // Check traffic percentage\n if (!shouldShowTest(testConfig)) {\n setIsLoading(false);\n return;\n }\n\n // Get or create visitor ID\n const userId = getVisitorId();\n\n // Assign variant (sets isControl, isA, isB, isC, isD)\n const assigned = assignAndPersistVariant(testId, testConfig, userId);\n\n if (assigned) {\n setVariant(assigned);\n trackViews(testId);\n }\n\n setIsLoading(false);\n }, [config, testConfig, testId]);\n\n return { \n variant, \n isLoading, \n isControl: variant?.isControl ?? false,\n isA: variant?.isA ?? false,\n isB: variant?.isB ?? false,\n isC: variant?.isC ?? false,\n isD: variant?.isD ?? false,\n };\n}\n","import React, { useRef, useCallback } from \"react\";\nimport type { ElevateConfig, BackendConfig } from \"../types\";\nimport { ElevateContext } from \"../contexts/ElevateContext\";\nimport { parseBackendConfig } from \"../utils\";\nimport { initAnalytics } from \"../utils/manualTracking\";\nimport { getPreviewState, type PreviewState } from \"../utils/preview\";\nimport { getCountryCode } from \"../utils/geo\";\nimport { setupFlickerPrevention } from \"../utils/antiFlicker\";\n\n/**\n * Set to false during local development to use fallback config.\n * In production, this should always be true.\n */\nconst USE_CDN_CONFIG = false; // Set to false for demo/development\n\n/**\n * Fallback config for local development/testing when CDN is not available.\n * Only used when USE_CDN_CONFIG = false.\n *\n * Supported test types: SPLIT_URL, PRICE_PLUS, CONTENT, CUSTOM_CODE\n */\nconst DEV_FALLBACK_CONFIG: BackendConfig = {\n allTests: {\n // Price Plus Test - Testing different price points\n \"price-test-001\": {\n \"8606\": {\n variationName: \"Control\",\n trafficPercentage: 50,\n isDone: false,\n isControl: true,\n },\n \"8607\": {\n variationName: \"Sale Price\",\n trafficPercentage: 50,\n isDone: false,\n prices: {\n \"41883969519701\": {\n main: \"USD\",\n price: { USD: \"599.95\" },\n compare: { USD: \"699.95\" },\n },\n },\n },\n data: {\n name: \"Snowboard Price Test\",\n isLive: true,\n settings: { afterDiscounts: true },\n type: \"PRICE_PLUS\",\n filters: [],\n isPersonalization: false,\n currencies: [\"USD\"],\n handles: [\"the-complete-snowboard\"],\n productIds: [\"7240161067093\"],\n },\n },\n\n // Content Test - Simple A/B/C headline test\n \"content-test-001\": {\n \"ctrl-001\": {\n variationName: \"Control\",\n trafficPercentage: 34,\n isDone: false,\n isControl: true,\n content: {\n headline: \"Welcome to our store\",\n subheadline: \"Shop the best products\",\n },\n },\n \"var-a-001\": {\n variationName: \"Urgency Copy\",\n trafficPercentage: 33,\n isDone: false,\n content: {\n headline: \"Limited Time Offer!\",\n subheadline: \"Don't miss out - sale ends soon\",\n },\n },\n \"var-b-001\": {\n variationName: \"Value Copy\",\n trafficPercentage: 33,\n isDone: false,\n content: {\n headline: \"Premium Quality, Great Value\",\n subheadline: \"Free shipping on orders over $50\",\n },\n },\n data: {\n name: \"Homepage Headline Test\",\n isLive: true,\n settings: { afterDiscounts: true },\n type: \"CONTENT\",\n filters: [],\n isPersonalization: false,\n pathnames: [\"/\"],\n },\n },\n\n // Custom Code Test - Injecting custom JS/CSS\n \"custom-code-test-001\": {\n \"10150\": {\n variationName: \"Control\",\n trafficPercentage: 50,\n isDone: false,\n customCode: {\n id: \"c11a582c-6b27-4263-941c-8ed123437c6b\",\n js: \"console.log('[Elevate] Control variant active');\",\n css: \"\",\n pathnames: [\"*\"],\n excludePathnames: [],\n },\n isControl: true,\n },\n \"10151\": {\n variationName: \"Enhanced UI\",\n trafficPercentage: 50,\n isDone: false,\n customCode: {\n id: \"df71546a-0a5f-4ad0-8d0f-a1bdeab05e84\",\n js: \"console.log('[Elevate] Enhanced UI variant'); document.body.style.borderTop = '3px solid #10b981';\",\n css: \".hero-section { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }\",\n pathnames: [\"*\"],\n excludePathnames: [],\n },\n },\n data: {\n name: \"Custom Code Enhancement\",\n isLive: true,\n settings: { afterDiscounts: true },\n type: \"CUSTOM_CODE\",\n filters: [],\n isPersonalization: false,\n pathnames: [\"*\"],\n excludePathnames: [],\n },\n },\n\n // Split URL Test - Redirect to different landing pages\n \"split-url-test-001\": {\n \"split-ctrl\": {\n variationName: \"Control\",\n trafficPercentage: 50,\n isDone: false,\n isControl: true,\n splitUrlTestLinks: {\n default: \"/collections/all\",\n },\n },\n \"split-var-a\": {\n variationName: \"New Landing Page\",\n trafficPercentage: 50,\n isDone: false,\n splitUrlTestLinks: {\n default: \"/collections/featured\",\n },\n },\n data: {\n name: \"Landing Page Split Test\",\n isLive: true,\n settings: { afterDiscounts: true },\n type: \"SPLIT_URL\",\n filters: [],\n isPersonalization: false,\n pathnames: [\"/promo\"],\n },\n },\n },\n selectors: { selectorsV2: [] },\n};\n\ninterface ElevateProviderSimpleProps {\n /** Shopify store domain (e.g., \"your-store.myshopify.com\") */\n storeId: string;\n /**\n * Storefront Access Token - Required for cart attribute tracking (order attribution)\n *\n * This is SAFE to use client-side - it's a public token with limited permissions.\n * Get it from: Shopify Admin → Settings → Apps → Develop apps\n *\n * Without this token, A/B tests work but orders won't be attributed to variants.\n */\n storefrontAccessToken?: string;\n /**\n * Enable flicker prevention - hides content until test assignment is complete\n * Prevents users from seeing content flash/change when variants load\n * @default false\n */\n preventFlickering?: boolean;\n /**\n * Timeout in milliseconds for flicker prevention failsafe\n * Content will show after this time even if assignment isn't complete\n * @default 3000\n */\n flickerTimeout?: number;\n children: React.ReactNode;\n}\n\n/**\n * ElevateProvider - Provides A/B test configuration to child components\n *\n * @example Next.js / Remix\n * ```tsx\n * <ElevateProvider\n * storeId=\"your-store.myshopify.com\"\n * storefrontAccessToken=\"your-public-token\"\n * >\n * <App />\n * </ElevateProvider>\n * ```\n *\n * @example Hydrogen (with Analytics.Provider)\n * ```tsx\n * <Analytics.Provider cart={cart} shop={shop} consent={consent}>\n * <ElevateProvider storeId=\"your-store.myshopify.com\" storefrontAccessToken=\"token\">\n * <ElevateAnalytics />\n * <App />\n * </ElevateProvider>\n * </Analytics.Provider>\n * ```\n */\nexport function ElevateProvider({\n storeId,\n storefrontAccessToken,\n preventFlickering = false,\n flickerTimeout = 3000,\n children,\n}: ElevateProviderSimpleProps) {\n const [config, setConfig] = React.useState<ElevateConfig | null>(null);\n const [previewState, setPreviewState] = React.useState<PreviewState | null>(\n null,\n );\n const [countryCode, setCountryCode] = React.useState<string | null>(null);\n const revealRef = useRef<(() => void) | null>(null);\n\n // Setup flicker prevention on mount (before config loads)\n React.useEffect(() => {\n if (preventFlickering && typeof window !== \"undefined\") {\n revealRef.current = setupFlickerPrevention(\"body\", flickerTimeout);\n }\n\n return () => {\n // Cleanup: ensure content is revealed on unmount\n if (revealRef.current) {\n revealRef.current();\n }\n };\n }, [preventFlickering, flickerTimeout]);\n\n // Initialize analytics config so tracking functions don't need storeId\n React.useEffect(() => {\n if (typeof window === \"undefined\") return;\n\n initAnalytics({\n storeId,\n storefrontAccessToken,\n });\n }, [storeId, storefrontAccessToken]);\n\n // Initialize preview state and geo on mount\n React.useEffect(() => {\n if (typeof window === \"undefined\") return;\n\n // Get preview state from URL/cookies\n const preview = getPreviewState();\n setPreviewState(preview);\n\n // Get country code\n const country = getCountryCode();\n setCountryCode(country);\n }, []);\n\n // Reveal callback - called when config is loaded or on error\n const revealContent = useCallback(() => {\n if (revealRef.current) {\n revealRef.current();\n revealRef.current = null;\n }\n }, []);\n\n React.useEffect(() => {\n async function fetchConfig() {\n try {\n // In dev mode, skip CDN fetch and use fallback config directly\n if (!USE_CDN_CONFIG) {\n const parsedConfig = parseBackendConfig(DEV_FALLBACK_CONFIG);\n setConfig(parsedConfig);\n revealContent();\n return;\n }\n\n // Production mode: fetch from CDN\n const url = `https://configs.elevateab.com/config/${storeId}.json`;\n const response = await fetch(url);\n\n if (response.status === 404) {\n // 404 means no config - could be new store or paused subscription\n // Just render children without A/B tests, no error log for this case\n setConfig({ tests: [], selectors: undefined });\n revealContent();\n return;\n }\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch config: ${response.status} ${response.statusText}`,\n );\n }\n\n const backendData: BackendConfig = await response.json();\n\n // Check if subscription is paused/stopped\n if (backendData.subscriptionPaused) {\n console.error(\n `[ElevateAB] Subscription is paused or stopped for store: ${storeId}. ` +\n (backendData.subscriptionMessage || \"A/B tests will not run. Please reactivate your subscription.\"),\n );\n // Graceful degradation - just render children, no A/B tests\n setConfig({ tests: [], selectors: undefined });\n revealContent();\n return;\n }\n\n // Check if config is empty (all tests might have been paused)\n const hasActiveTests = Object.keys(backendData.allTests || {}).length > 0;\n if (!hasActiveTests) {\n // Empty config - could be paused subscription or no active tests\n // Don't log error, just render children normally\n setConfig({ tests: [], selectors: undefined });\n revealContent();\n return;\n }\n\n const parsedConfig = parseBackendConfig(backendData);\n setConfig(parsedConfig);\n revealContent();\n } catch (err) {\n console.error(\"[ElevateAB] Failed to load config:\", err);\n // Graceful degradation - just render children, website doesn't break\n setConfig({ tests: [], selectors: undefined });\n revealContent();\n }\n }\n\n fetchConfig();\n }, [storeId, revealContent]);\n\n const value = React.useMemo(\n () => ({\n config,\n storeId,\n storefrontAccessToken,\n isPreviewMode: previewState?.isPreview ?? false,\n previewTestId: previewState?.previewTestId ?? null,\n countryCode,\n }),\n [config, storeId, storefrontAccessToken, previewState, countryCode],\n );\n\n // Initialize analytics so tracking functions don't need storeId/token each time\n React.useEffect(() => {\n initAnalytics({ storeId, storefrontAccessToken });\n }, [storeId, storefrontAccessToken]);\n\n // Warn once if storefrontAccessToken is missing (order attribution won't work)\n const hasWarnedRef = useRef(false);\n React.useEffect(() => {\n if (!storefrontAccessToken && !hasWarnedRef.current) {\n hasWarnedRef.current = true;\n console.warn(\n \"[ElevateAB] No storefrontAccessToken provided. \" +\n \"A/B tests will work, but orders won't be attributed to test variants. \" +\n \"Add storefrontAccessToken prop to enable order tracking.\",\n );\n }\n }, [storefrontAccessToken]);\n\n return (\n <ElevateContext.Provider value={value}>{children}</ElevateContext.Provider>\n );\n}\n","// Main entry point for the Elevate AB Testing NPM package\n\n// ============================================================================\n// TYPE EXPORTS\n// ============================================================================\n\n// Core SDK types\nexport type {\n Test,\n Variation,\n ElevateConfig,\n ElevateProviderProps,\n ElevateContextValue,\n ElevateAnalyticsProps,\n ExperimentStatus,\n BackendConfig,\n BackendTest,\n BackendVariation,\n} from \"./types\";\n\n// Event tracking types (for manual tracking)\nexport type {\n // Base config\n BaseTrackingConfig,\n // Individual event params\n TrackPageViewParams,\n TrackProductViewParams,\n TrackAddToCartParams,\n TrackRemoveFromCartParams,\n TrackCartViewParams,\n TrackSearchSubmittedParams,\n TrackCheckoutStartedParams,\n TrackCheckoutCompletedParams,\n UsePageViewTrackingParams,\n // Shared types\n CartItemInput,\n NoteAttribute,\n TestAssignment,\n // Internal payload (for advanced users)\n EventPayload,\n CartItemPayload,\n} from \"./types\";\n\n// Cart attribute types\nexport type { CartAttributesOptions, CartAttributeInput } from \"./types\";\n\n// Legacy types (deprecated)\nexport type { TrackingEvent } from \"./types\";\n\nexport {\n assignVariant,\n hashString,\n validateConfig,\n generateExperimentId,\n calculateRevenueLift,\n} from \"./utils\";\n\n// Storage utilities\nexport {\n setCookie,\n getCookie,\n getJsonCookie,\n deleteCookie,\n setSessionItem,\n getSessionItem,\n getJsonSessionItem,\n getVisitorId,\n initializeVisitorId,\n getSessionId,\n initializeSessionId,\n setReferrerData,\n getReferrerData,\n uuidv4,\n} from \"./utils/storage\";\n\n// Assignment utilities\nexport {\n getTestList,\n saveTestList,\n getAssignedVariant,\n assignAndPersistVariant,\n shouldShowTest,\n} from \"./utils/assignment\";\n\n// Tracking utilities\nexport {\n trackViews,\n trackUniqueView,\n trackSessionView,\n hasSessionView,\n hasUniqueView,\n} from \"./utils/tracking\";\n\n// Condition utilities\nexport {\n getDeviceType,\n getTrafficSource,\n checkFacebookBrowser,\n checkInstagramBrowser,\n checkTikTokBrowser,\n checkPinterestBrowser,\n} from \"./utils/conditions\";\n\n// Preview mode utilities\nexport {\n getPreviewState,\n isPreviewTest,\n getPreviewVariation,\n buildEabTestsParam,\n updateUrlWithTestParams,\n clearPreviewMode,\n isInPreviewMode,\n} from \"./utils/preview\";\nexport type { PreviewState } from \"./utils/preview\";\n\n// Geo-targeting utilities\nexport {\n getCountryCode,\n getGeoLocation,\n setCountryCode,\n setGeoLocation,\n isCountryIncluded,\n isCountryExcluded,\n matchesGeoTargeting,\n COUNTRIES,\n REGIONS,\n} from \"./utils/geo\";\nexport type { GeoLocation } from \"./utils/geo\";\n\n// Anti-flicker utilities\nexport {\n setupFlickerPrevention,\n injectFlickerStyles,\n hideForFlicker,\n revealAfterFlicker,\n cleanupFlickerPrevention,\n isFlickerPreventionActive,\n getFlickerPreventionScript,\n} from \"./utils/antiFlicker\";\n\n// Analytics utilities\nexport {\n parseAddViewData,\n extractProductId,\n extractProductVariantId,\n extractCartToken,\n cleanCartToken,\n getPageTypeFromPathname,\n sanitizeString,\n roundToTwo,\n checkFacebookInstagramBrowser,\n checkVisitorIdParams,\n getUserAgentNoBrowser,\n} from \"./utils/analytics\";\n\nexport type { ParsedViewData } from \"./utils/analytics\";\n\n// Shopify utilities\nexport {\n extractShopifyId,\n extractShopifyType,\n isShopifyGid,\n detectShopifyCurrency,\n} from \"./utils/shopify\";\n\n// Cart attribute utilities\nexport {\n updateCartAttributes,\n cleanupCartAttributes,\n getCartAttributesPayload,\n} from \"./utils/cartAttributes\";\n\n// Cart mutations\nexport { CART_ATTRIBUTES_UPDATE_MUTATION } from \"./mutations/cartAttributes.mutation\";\n\n// Manual tracking utilities (for Next.js and other frameworks)\n// Note: ElevateProvider auto-calls initAnalytics, so you usually don't need to call it manually\nexport {\n initAnalytics,\n trackPageView,\n trackProductView,\n trackAddToCart,\n trackRemoveFromCart,\n trackCartView,\n trackSearchSubmitted,\n trackCheckoutStarted,\n trackCheckoutCompleted,\n usePageViewTracking,\n} from \"./utils/manualTracking\";\n\n// Components\nexport { useExperiment } from \"./components/Experiment\";\nexport type { UseExperimentResult } from \"./components/Experiment\";\nexport { ElevateProvider } from \"./components/ElevateProvider\";\n\n// Analytics components\n// For Hydrogen stores: import from '@elevateab/sdk/hydrogen' for ElevateHydrogenAnalytics\n// For Next.js/Remix: use ElevateAnalytics with manual hook injection\nexport { ElevateAnalytics } from \"./components/ElevateAnalytics\";\nexport type {\n UseAnalyticsHook,\n ElevateHydrogenAnalyticsProps,\n} from \"./components/ElevateAnalytics\";\n\n// Note: ElevateHydrogenAnalytics is exported from '@elevateab/sdk/hydrogen' sub-path\n// to avoid bundling @shopify/hydrogen in non-Hydrogen projects\n\n// Contexts\nexport { useElevateConfig } from \"./contexts/ElevateContext\";\n\n// Package version\nexport const VERSION = \"1.1.2\";\n\n// Default configuration\nexport const DEFAULT_CONFIG = {\n enabled: true,\n trackingEndpoint: \"https://analytics.elevateab.com/track\",\n cacheDuration: 3600,\n};\n"],"mappings":"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","names":["trackUniqueView","testId","addedUniqueViews","getJsonCookie","setCookie","trackSessionView","addedViews","getJsonSessionItem","setSessionItem","trackViews","hasSessionView","hasUniqueView","getDeviceType","ua","lower","checkFacebookBrowser","userAgent","checkInstagramBrowser","checkTikTokBrowser","checkPinterestBrowser","getTrafficSource","referrer","referrerDomain","hostname","isFacebookBrowser","isInstagramBrowser","isTikTokBrowser","isPinterestBrowser","parseEabTestsParam","param","assignments","views","tests","test","parts","shortTestId","variationId","hasSeen","getPreviewState","urlParams","isPreview","getCookie","previewTestId","getSessionItem","eabTestsParam","setCookie","setSessionItem","isPreviewTest","testId","getPreviewVariation","previewState","buildEabTestsParam","testAssignments","viewedTests","updateUrlWithTestParams","visitorId","url","eabTestsValue","clearPreviewMode","isInPreviewMode","GEO_COOKIE_NAMES","getCountryCode","urlCountry","setCookie","shopifyCountry","getCookie","cachedCountry","storedCountry","getGeoLocation","country","geoData","parsed","setCountryCode","countryCode","normalized","setGeoLocation","geo","isCountryIncluded","allowedCountries","userCountry","c","isCountryExcluded","excludedCountries","matchesGeoTargeting","rules","COUNTRIES","REGIONS","FLICKER_CLASS","STYLE_ID","injectFlickerStyles","timeout","STYLE_ID","style","FLICKER_CLASS","head","hideForFlicker","selector","element","revealAfterFlicker","setupFlickerPrevention","hasRevealed","timeoutId","cleanupFlickerPrevention","el","isFlickerPreventionActive","getFlickerPreventionScript","extractShopifyId","gid","extractShopifyType","isShopifyGid","value","detectShopifyCurrency","shopify","useEffect","useRef","useState","DEFAULT_WORKER_URL","DEFAULT_ORDERS_WORKER_URL","globalConfig","initAnalytics","config","getStoreId","params","storeId","globalConfig","transformTestFormat","testObject","variationId","testId","transformViewedTests","viewedObject","assignmentObject","isInPreviewMode","getCookie","createBaseEventData","eventType","hasLocalizedPaths","userAgent","pathname","pageType","getPageTypeFromPathname","referrer","entryPage","getReferrerData","parsedData","parseAddViewData","abtlObject","getJsonCookie","abauObject","testAssignments","viewedTests","cartToken","extractCartToken","currentTimestamp","isFirstVisit","shopifyCountry","uuidv4","getVisitorId","getSessionId","cleanCartToken","getUserAgentNoBrowser","sendEvent","data","workerUrl","DEFAULT_WORKER_URL","response","error","trackPageView","baseData","eventData","trackProductView","productId","productVendor","productPrice","productSku","currency","extractShopifyId","sanitizeString","roundToTwo","trackAddToCart","variantId","productQuantity","cartId","storefrontAccessToken","updateCartAttributes","err","trackRemoveFromCart","trackCartView","cartTotalPrice","cartTotalQuantity","cartItems","item","trackSearchSubmitted","searchQuery","trackCheckoutStarted","cartSubtotalPrice","cartShippingPrice","cartTaxAmount","cartDiscountAmount","customerId","totalQuantity","items","quantity","trackCheckoutCompleted","orderId","isFirstOrder","noteAttributes","ordersWorkerUrl","DEFAULT_ORDERS_WORKER_URL","usePageViewTracking","enabled","externalPathname","lastTrackedPathRef","useRef","currentPath","setCurrentPath","useState","useEffect","React","useExperiment","testId","config","useElevateConfig","variant","setVariant","React","isLoading","setIsLoading","testConfig","test","hasWarnedRef","shouldShowTest","userId","getVisitorId","assigned","assignAndPersistVariant","trackViews","React","useRef","useCallback","USE_CDN_CONFIG","DEV_FALLBACK_CONFIG","ElevateProvider","storeId","storefrontAccessToken","preventFlickering","flickerTimeout","children","config","setConfig","React","previewState","setPreviewState","countryCode","setCountryCode","revealRef","useRef","setupFlickerPrevention","initAnalytics","preview","getPreviewState","country","getCountryCode","revealContent","useCallback","fetchConfig","parsedConfig","parseBackendConfig","url","response","backendData","err","value","hasWarnedRef","ElevateContext","VERSION","DEFAULT_CONFIG"]}
1
+ {"version":3,"sources":["../src/utils/tracking.ts","../src/utils/conditions.ts","../src/utils/preview.ts","../src/utils/geo.ts","../src/utils/antiFlicker.ts","../src/utils/shopify.ts","../src/utils/manualTracking.ts","../src/components/Experiment.tsx","../src/components/ElevateProvider.tsx","../src/components/AntiFlickerScript.tsx","../src/index.ts"],"sourcesContent":["/**\n * Tracking utilities for views and participation\n */\n\nimport {\n setCookie,\n setSessionItem,\n getJsonCookie,\n getJsonSessionItem,\n} from \"./storage\";\n\n/**\n * Track unique view for a test (ABAU cookie)\n */\nexport function trackUniqueView(testId: string): void {\n const addedUniqueViews = getJsonCookie<Record<string, boolean>>(\"ABAU\") || {};\n\n if (!addedUniqueViews[testId]) {\n addedUniqueViews[testId] = true;\n setCookie(\"ABAU\", JSON.stringify(addedUniqueViews));\n }\n}\n\n/**\n * Track session view for a test (ABAV session storage)\n */\nexport function trackSessionView(testId: string): void {\n const addedViews = getJsonSessionItem<Record<string, boolean>>(\"ABAV\") || {};\n\n if (!addedViews[testId]) {\n addedViews[testId] = true;\n setSessionItem(\"ABAV\", addedViews);\n }\n}\n\n/**\n * Track both unique and session views\n */\nexport function trackViews(testId: string): void {\n trackUniqueView(testId);\n trackSessionView(testId);\n}\n\n/**\n * Check if test has been viewed in this session\n */\nexport function hasSessionView(testId: string): boolean {\n const addedViews = getJsonSessionItem<Record<string, boolean>>(\"ABAV\") || {};\n return !!addedViews[testId];\n}\n\n/**\n * Check if test has a unique view\n */\nexport function hasUniqueView(testId: string): boolean {\n const addedUniqueViews = getJsonCookie<Record<string, boolean>>(\"ABAU\") || {};\n return !!addedUniqueViews[testId];\n}\n","/**\n * Condition checking utilities for targeting and filtering\n */\n\n/**\n * Detect device type\n */\nexport function getDeviceType(): \"desktop\" | \"tablet\" | \"mobile\" {\n if (typeof navigator === \"undefined\") return \"desktop\";\n\n const ua =\n navigator.userAgent || navigator.vendor || (window as any).opera || \"\";\n const lower = ua.toLowerCase();\n\n if (\n /windows nt.*tablet pc|pixelbook/i.test(ua) ||\n (/(?:macintosh|windows nt|win(?:95|98)|linux x86_64)/i.test(ua) &&\n !/android|mobile|tablet|ipad|iphone|ipod/i.test(lower))\n )\n return \"desktop\";\n\n const isTablet =\n /ipad|playbook|tablet|kindle|nexus (7|10)|xoom|sm-t\\d+|galaxy tab|tb-\\d+|tb\\d+[a-z]+|lenovo.*tb|dtab|ideatab|mediapad|matepad|honor.*pad|pad\\s+\\d+|mi\\s+pad|redmi.*pad|xiaomi.*pad|oppo.*pad|oneplus.*pad|opd\\d+|tcl.*tab|9\\d{3}[a-z]|kobo|archos.*5(?!\\d)|ereader|droipad/i.test(\n ua\n ) ||\n (/android(?!.*mobile)/i.test(lower) &&\n !/dalvik.*miui|pocophone|mi\\s+mix|edge\\s+\\d+|sm-g9\\d+|pixel\\s+\\d|pixel\\s+fold|advan\\s+\\d{4}/i.test(\n ua\n ));\n\n if (isTablet) return \"tablet\";\n\n const isMobile =\n /android.+mobile|iphone|ipod|windows phone|blackberry|mobile|phone|kaios/i.test(\n lower\n ) ||\n /^mobile;/i.test(ua) ||\n /dalvik.*miui|pocophone|mi\\s+mix|pixel\\s+\\d|pixel\\s+fold/i.test(ua);\n\n if (isMobile) return \"mobile\";\n\n return \"desktop\";\n}\n\n/**\n * Check if Facebook in-app browser\n */\nexport function checkFacebookBrowser(): boolean {\n if (typeof navigator === \"undefined\") return false;\n\n const userAgent =\n navigator.userAgent || navigator.vendor || (window as any).opera;\n const facebookBrowserRegex =\n /((?:fban\\/fbios|fb_iab\\/fb4a)(?!.+fbav)|;fbav\\/([\\w\\.]+);)/i;\n return facebookBrowserRegex.test(userAgent);\n}\n\n/**\n * Check if Instagram in-app browser\n */\nexport function checkInstagramBrowser(): boolean {\n if (typeof navigator === \"undefined\") return false;\n\n const userAgent =\n navigator.userAgent || navigator.vendor || (window as any).opera;\n const instagramBrowserRegex = /(instagram)[\\/ ]([-\\w\\.]+)/i;\n return instagramBrowserRegex.test(userAgent);\n}\n\n/**\n * Check if TikTok in-app browser\n */\nexport function checkTikTokBrowser(): boolean {\n if (typeof navigator === \"undefined\") return false;\n\n const userAgent =\n navigator.userAgent || navigator.vendor || (window as any).opera;\n const tikTokBrowserRegex = /musical_ly/i;\n return tikTokBrowserRegex.test(userAgent);\n}\n\n/**\n * Check if Pinterest in-app browser\n */\nexport function checkPinterestBrowser(): boolean {\n if (typeof navigator === \"undefined\") return false;\n\n const userAgent =\n navigator.userAgent || navigator.vendor || (window as any).opera;\n const pinterestBrowserRegex = /(pinterest)[\\/ ]([-\\w\\.]+)/i;\n return pinterestBrowserRegex.test(userAgent);\n}\n\n/**\n * Get traffic source\n */\nexport function getTrafficSource(): string {\n if (typeof window === \"undefined\") return \"direct\";\n\n const referrer = (document.referrer || \"\").toLowerCase();\n const referrerDomain = referrer\n ? new URL(referrer).hostname.replace(\"www.\", \"\")\n : \"\";\n const hostname = window.location.hostname;\n\n const isFacebookBrowser = checkFacebookBrowser();\n const isInstagramBrowser = checkInstagramBrowser();\n const isTikTokBrowser = checkTikTokBrowser();\n const isPinterestBrowser = checkPinterestBrowser();\n\n if (\n isFacebookBrowser ||\n [\n \"facebook.com\",\n \"fb.com\",\n \"fb.me\",\n \"m.facebook.com\",\n \"l.facebook.com\",\n \"lm.facebook.com\",\n ].includes(referrerDomain)\n ) {\n return \"facebook\";\n } else if (isInstagramBrowser || referrerDomain.includes(\"instagram\")) {\n return \"instagram\";\n } else if (\n isTikTokBrowser ||\n [\"tiktok.com\", \"pangleglobal.com\", \"ads.tiktok.com\"].includes(\n referrerDomain\n )\n ) {\n return \"tiktok\";\n } else if (\n isPinterestBrowser ||\n referrerDomain.includes(\"pinterest\") ||\n referrerDomain === \"pin.it\"\n ) {\n return \"pinterest\";\n } else if (referrerDomain.includes(\"google\")) {\n return \"google\";\n } else if (!referrer || hostname === referrerDomain) {\n return \"direct\";\n } else {\n return \"other\";\n }\n}\n","/**\n * Preview Mode Utilities\n *\n * Enables QA/preview functionality for A/B tests via URL parameters.\n *\n * URL Format:\n * ?eabUserPreview=true&abtid=<test_id>&eab_tests=<shortid>_<variation>_<seen>\n *\n * Example:\n * https://store.com/?eabUserPreview=true&abtid=b6e2ecb8-e35a-450e-ae49-bdc560cf0fd7&eab_tests=f0fd7_29913_1\n */\n\nimport {\n getCookie,\n setCookie,\n getSessionItem,\n setSessionItem,\n} from \"./storage\";\n\n/**\n * Preview mode state\n */\nexport interface PreviewState {\n /** Whether preview mode is active */\n isPreview: boolean;\n /** The test ID being previewed (full UUID or short ID) */\n previewTestId: string | null;\n /** Forced test assignments from URL params */\n forcedAssignments: Record<string, string>;\n /** Tests that have been marked as \"seen\" in preview */\n previewedViews: Record<string, boolean>;\n}\n\n/**\n * Parse eab_tests parameter format: shortid_variation_seen~shortid2_variation2_seen2\n * Example: \"f0fd7_29913_1\" or \"f0fd7_29913_1~a1b2c_12345_0\"\n */\nfunction parseEabTestsParam(param: string): {\n assignments: Record<string, string>;\n views: Record<string, boolean>;\n} {\n const assignments: Record<string, string> = {};\n const views: Record<string, boolean> = {};\n\n if (!param) return { assignments, views };\n\n const tests = param.split(\"~\");\n for (const test of tests) {\n const parts = test.split(\"_\");\n if (parts.length >= 2) {\n const shortTestId = parts[0];\n const variationId = parts[1];\n const hasSeen = parts[2] === \"1\";\n\n assignments[shortTestId] = variationId;\n views[shortTestId] = hasSeen;\n }\n }\n\n return { assignments, views };\n}\n\n/**\n * Get preview state from URL parameters and cookies\n */\nexport function getPreviewState(): PreviewState {\n if (typeof window === \"undefined\") {\n return {\n isPreview: false,\n previewTestId: null,\n forcedAssignments: {},\n previewedViews: {},\n };\n }\n\n const urlParams = new URLSearchParams(window.location.search);\n\n // Check URL param first, then cookie\n const eabUserPreview =\n urlParams.get(\"eabUserPreview\") || getCookie(\"eabUserPreview\");\n const isPreview = eabUserPreview === \"true\";\n\n // Get test ID from URL param or sessionStorage\n let previewTestId = urlParams.get(\"abtid\");\n if (!previewTestId) {\n previewTestId = getSessionItem(\"eabPreviewTestId\");\n }\n\n // Parse forced assignments from eab_tests param\n const eabTestsParam = urlParams.get(\"eab_tests\") || \"\";\n const { assignments, views } = parseEabTestsParam(eabTestsParam);\n\n // If URL has preview params, persist to storage\n if (urlParams.has(\"eabUserPreview\")) {\n setCookie(\"eabUserPreview\", \"true\");\n }\n if (urlParams.has(\"abtid\") && previewTestId) {\n setSessionItem(\"eabPreviewTestId\", previewTestId);\n }\n\n return {\n isPreview,\n previewTestId,\n forcedAssignments: assignments,\n previewedViews: views,\n };\n}\n\n/**\n * Check if a test matches the preview test ID\n * Supports both full UUID and short ID (last 5 chars) matching\n */\nexport function isPreviewTest(\n testId: string,\n previewTestId: string | null,\n): boolean {\n if (!previewTestId) return false;\n\n // Exact match\n if (testId === previewTestId) return true;\n\n // Short ID match (last 5 characters)\n const shortTestId = testId.slice(-5);\n if (shortTestId === previewTestId || previewTestId.endsWith(shortTestId)) {\n return true;\n }\n\n // Preview ID might be short, check if test ends with it\n if (testId.endsWith(previewTestId)) return true;\n\n return false;\n}\n\n/**\n * Get forced variation for a test in preview mode\n * Returns null if no forced assignment exists\n */\nexport function getPreviewVariation(\n testId: string,\n previewState: PreviewState,\n): string | null {\n if (!previewState.isPreview) return null;\n\n // Check by full test ID\n if (previewState.forcedAssignments[testId]) {\n return previewState.forcedAssignments[testId];\n }\n\n // Check by short test ID\n const shortTestId = testId.slice(-5);\n if (previewState.forcedAssignments[shortTestId]) {\n return previewState.forcedAssignments[shortTestId];\n }\n\n return null;\n}\n\n/**\n * Build eab_tests URL parameter string from test assignments\n * Used for sharing preview URLs with Facebook/Instagram traffic\n */\nexport function buildEabTestsParam(\n testAssignments: Record<string, string>,\n viewedTests: Record<string, boolean>,\n): string {\n return Object.entries(testAssignments)\n .map(([testId, variationId]) => {\n const shortTestId = testId.slice(-5);\n const hasSeen = viewedTests[testId] ? \"1\" : \"0\";\n return `${shortTestId}_${variationId}_${hasSeen}`;\n })\n .join(\"~\");\n}\n\n/**\n * Update URL with test parameters for social media browsers (FB/IG/TikTok)\n * These browsers often lose cookies, so we persist test assignments in URL\n */\nexport function updateUrlWithTestParams(\n testAssignments: Record<string, string>,\n viewedTests: Record<string, boolean>,\n visitorId?: string,\n): void {\n if (typeof window === \"undefined\") return;\n\n const url = new URL(window.location.href);\n const eabTestsValue = buildEabTestsParam(testAssignments, viewedTests);\n\n if (eabTestsValue) {\n url.searchParams.set(\"eab_tests\", eabTestsValue);\n }\n\n if (visitorId) {\n url.searchParams.set(\"eabUserId\", visitorId);\n }\n\n // Update URL without reload\n window.history.replaceState({}, \"\", url.toString());\n}\n\n/**\n * Clear preview mode\n */\nexport function clearPreviewMode(): void {\n if (typeof document === \"undefined\") return;\n\n // Clear cookie\n document.cookie =\n \"eabUserPreview=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;\";\n\n // Clear session storage\n if (typeof sessionStorage !== \"undefined\") {\n sessionStorage.removeItem(\"eabPreviewTestId\");\n }\n\n // Remove preview params from URL\n if (typeof window !== \"undefined\") {\n const url = new URL(window.location.href);\n url.searchParams.delete(\"eabUserPreview\");\n url.searchParams.delete(\"abtid\");\n url.searchParams.delete(\"eab_tests\");\n window.history.replaceState({}, \"\", url.toString());\n }\n}\n\n/**\n * Check if current session is in preview mode\n */\nexport function isInPreviewMode(): boolean {\n return getPreviewState().isPreview;\n}\n","/**\n * Geo-targeting Utilities\n *\n * Provides country detection and geo-based targeting for A/B tests.\n */\n\nimport { getCookie, setCookie } from \"./storage\";\n\n/**\n * Geo-location data\n */\nexport interface GeoLocation {\n /** ISO 3166-1 alpha-2 country code (e.g., \"US\", \"GB\", \"CA\") */\n country: string | null;\n /** Region/state code (if available) */\n region?: string | null;\n /** City name (if available) */\n city?: string | null;\n}\n\n/**\n * Cookie names for geo data\n */\nconst GEO_COOKIE_NAMES = {\n /** Shopify's localization cookie */\n SHOPIFY_LOCALIZATION: \"localization\",\n /** Our cached country code */\n EAB_COUNTRY: \"eabCountryCode\",\n /** Full geo data (JSON) */\n EAB_GEO: \"eabGeoLocation\",\n} as const;\n\n/**\n * Get the user's country code from various sources\n * Priority: URL param > Shopify localization > our cookie > localStorage\n */\nexport function getCountryCode(): string | null {\n if (typeof window === \"undefined\") return null;\n\n // 1. Check URL param (for explicit override)\n const urlParams = new URLSearchParams(window.location.search);\n const urlCountry = urlParams.get(\"country\");\n if (urlCountry) {\n // Cache it\n setCookie(GEO_COOKIE_NAMES.EAB_COUNTRY, urlCountry.toUpperCase());\n return urlCountry.toUpperCase();\n }\n\n // 2. Check Shopify's localization cookie (most common for Shopify stores)\n const shopifyCountry = getCookie(GEO_COOKIE_NAMES.SHOPIFY_LOCALIZATION);\n if (shopifyCountry) {\n return shopifyCountry.toUpperCase();\n }\n\n // 3. Check our cached cookie\n const cachedCountry = getCookie(GEO_COOKIE_NAMES.EAB_COUNTRY);\n if (cachedCountry) {\n return cachedCountry.toUpperCase();\n }\n\n // 4. Check localStorage (fallback)\n if (typeof localStorage !== \"undefined\") {\n const storedCountry = localStorage.getItem(GEO_COOKIE_NAMES.EAB_COUNTRY);\n if (storedCountry) {\n return storedCountry.toUpperCase();\n }\n }\n\n return null;\n}\n\n/**\n * Get full geo-location data\n */\nexport function getGeoLocation(): GeoLocation {\n const country = getCountryCode();\n\n // Try to get extended geo data from cookie\n const geoData = getCookie(GEO_COOKIE_NAMES.EAB_GEO);\n if (geoData) {\n try {\n const parsed = JSON.parse(geoData) as GeoLocation;\n return {\n country: parsed.country || country,\n region: parsed.region,\n city: parsed.city,\n };\n } catch {\n // Invalid JSON, fall through\n }\n }\n\n return { country };\n}\n\n/**\n * Set the user's country code (for manual override or after geo detection)\n */\nexport function setCountryCode(countryCode: string): void {\n const normalized = countryCode.toUpperCase();\n setCookie(GEO_COOKIE_NAMES.EAB_COUNTRY, normalized);\n\n // Also store in localStorage for extra persistence\n if (typeof localStorage !== \"undefined\") {\n localStorage.setItem(GEO_COOKIE_NAMES.EAB_COUNTRY, normalized);\n }\n}\n\n/**\n * Set full geo-location data\n */\nexport function setGeoLocation(geo: GeoLocation): void {\n if (geo.country) {\n setCountryCode(geo.country);\n }\n\n // Store full geo data\n setCookie(GEO_COOKIE_NAMES.EAB_GEO, JSON.stringify(geo));\n}\n\n/**\n * Check if user's country matches a list of allowed countries\n */\nexport function isCountryIncluded(\n allowedCountries: string[],\n userCountry?: string | null,\n): boolean {\n const country = userCountry ?? getCountryCode();\n if (!country) return false;\n\n const normalizedAllowed = allowedCountries.map((c) => c.toUpperCase());\n return normalizedAllowed.includes(country.toUpperCase());\n}\n\n/**\n * Check if user's country is in an excluded list\n */\nexport function isCountryExcluded(\n excludedCountries: string[],\n userCountry?: string | null,\n): boolean {\n const country = userCountry ?? getCountryCode();\n if (!country) return false;\n\n const normalizedExcluded = excludedCountries.map((c) => c.toUpperCase());\n return normalizedExcluded.includes(country.toUpperCase());\n}\n\n/**\n * Check if user matches geo-targeting rules\n * Returns true if user should be included in the test\n */\nexport function matchesGeoTargeting(rules: {\n includeCountries?: string[];\n excludeCountries?: string[];\n}): boolean {\n const country = getCountryCode();\n\n // If no rules, include everyone\n if (!rules.includeCountries?.length && !rules.excludeCountries?.length) {\n return true;\n }\n\n // If no country detected and rules exist, exclude (can't target unknown)\n if (!country) {\n return false;\n }\n\n // Check exclusions first (takes precedence)\n if (rules.excludeCountries?.length) {\n if (isCountryExcluded(rules.excludeCountries, country)) {\n return false;\n }\n }\n\n // If include list exists, user must be in it\n if (rules.includeCountries?.length) {\n return isCountryIncluded(rules.includeCountries, country);\n }\n\n // No include list, and not excluded = included\n return true;\n}\n\n/**\n * Common country code constants for convenience\n */\nexport const COUNTRIES = {\n US: \"US\",\n CA: \"CA\",\n GB: \"GB\",\n AU: \"AU\",\n DE: \"DE\",\n FR: \"FR\",\n JP: \"JP\",\n MX: \"MX\",\n BR: \"BR\",\n IN: \"IN\",\n // Add more as needed\n} as const;\n\n/**\n * Region groupings for common use cases\n */\nexport const REGIONS = {\n NORTH_AMERICA: [\"US\", \"CA\", \"MX\"],\n EUROPE: [\n \"GB\",\n \"DE\",\n \"FR\",\n \"IT\",\n \"ES\",\n \"NL\",\n \"BE\",\n \"AT\",\n \"CH\",\n \"PL\",\n \"SE\",\n \"NO\",\n \"DK\",\n \"FI\",\n \"IE\",\n \"PT\",\n ],\n APAC: [\n \"AU\",\n \"NZ\",\n \"JP\",\n \"KR\",\n \"SG\",\n \"HK\",\n \"TW\",\n \"TH\",\n \"MY\",\n \"PH\",\n \"ID\",\n \"VN\",\n \"IN\",\n ],\n LATAM: [\"MX\", \"BR\", \"AR\", \"CL\", \"CO\", \"PE\"],\n} as const;\n","/**\n * Anti-Flicker Utilities\n *\n * Prevents visual flickering during A/B test assignment by hiding content\n * until the variant is determined. Uses a CSS-based approach for performance.\n *\n * Note: Called \"preventFlickering\" in our API to differentiate from competitors.\n */\n\n/**\n * CSS class used to hide content during flicker prevention\n */\nconst FLICKER_CLASS = \"eab-prevent-flicker\";\n\n/**\n * Inline style ID for flicker prevention\n */\nconst STYLE_ID = \"eab-flicker-styles\";\n\n/**\n * Default timeout (ms) before showing content anyway (failsafe)\n */\nconst DEFAULT_TIMEOUT = 3000;\n\n/**\n * Inject the flicker prevention CSS styles\n * This should be called as early as possible (ideally in <head>)\n */\nexport function injectFlickerStyles(timeout = DEFAULT_TIMEOUT): void {\n if (typeof document === \"undefined\") return;\n\n // Don't inject twice\n if (document.getElementById(STYLE_ID)) return;\n\n const style = document.createElement(\"style\");\n style.id = STYLE_ID;\n style.textContent = `\n /* Elevate AB - Flicker Prevention */\n .${FLICKER_CLASS} {\n opacity: 0 !important;\n pointer-events: none !important;\n }\n \n /* Failsafe: ensure content shows after timeout via animation */\n @keyframes eab-reveal {\n to { opacity: 1; pointer-events: auto; }\n }\n \n .${FLICKER_CLASS} {\n animation: eab-reveal 0s ${timeout}ms forwards;\n }\n `;\n\n // Insert at the beginning of <head> for highest priority\n const head = document.head || document.getElementsByTagName(\"head\")[0];\n head.insertBefore(style, head.firstChild);\n}\n\n/**\n * Hide the page/element to prevent flicker\n * Call this before test assignment begins\n */\nexport function hideForFlicker(selector: string = \"body\"): void {\n if (typeof document === \"undefined\") return;\n\n const element = document.querySelector(selector);\n if (element) {\n element.classList.add(FLICKER_CLASS);\n }\n}\n\n/**\n * Show the page/element after test assignment is complete\n * Call this after variant is determined\n */\nexport function revealAfterFlicker(selector: string = \"body\"): void {\n if (typeof document === \"undefined\") return;\n\n const element = document.querySelector(selector);\n if (element) {\n element.classList.remove(FLICKER_CLASS);\n }\n}\n\n/**\n * Setup flicker prevention with automatic reveal\n * Returns a function to call when assignment is complete\n *\n * @param selector - CSS selector for element to hide (default: \"body\")\n * @param timeout - Maximum time to wait before revealing (default: 3000ms)\n * @returns Function to call when test assignment is complete\n *\n * @example\n * ```tsx\n * // In your app initialization\n * const reveal = setupFlickerPrevention();\n *\n * // After test assignment\n * reveal();\n * ```\n */\nexport function setupFlickerPrevention(\n selector: string = \"body\",\n timeout: number = DEFAULT_TIMEOUT,\n): () => void {\n if (typeof document === \"undefined\") {\n return () => {}; // No-op for SSR\n }\n\n // Inject styles\n injectFlickerStyles(timeout);\n\n // Hide content\n hideForFlicker(selector);\n\n // Setup timeout failsafe\n let hasRevealed = false;\n const timeoutId = setTimeout(() => {\n if (!hasRevealed) {\n console.warn(\n `[ElevateAB] Flicker prevention timeout (${timeout}ms) reached. ` +\n \"Revealing content. Check if test assignment is completing.\",\n );\n revealAfterFlicker(selector);\n hasRevealed = true;\n }\n }, timeout);\n\n // Return reveal function\n return () => {\n if (!hasRevealed) {\n clearTimeout(timeoutId);\n revealAfterFlicker(selector);\n hasRevealed = true;\n }\n };\n}\n\n/**\n * Remove all flicker prevention styles and classes\n * Useful for cleanup or when disabling the feature\n */\nexport function cleanupFlickerPrevention(): void {\n if (typeof document === \"undefined\") return;\n\n // Remove style tag\n const style = document.getElementById(STYLE_ID);\n if (style) {\n style.remove();\n }\n\n // Remove class from all elements\n const elements = document.querySelectorAll(`.${FLICKER_CLASS}`);\n elements.forEach((el) => el.classList.remove(FLICKER_CLASS));\n}\n\n/**\n * Check if flicker prevention is currently active\n */\nexport function isFlickerPreventionActive(): boolean {\n if (typeof document === \"undefined\") return false;\n\n return document.querySelector(`.${FLICKER_CLASS}`) !== null;\n}\n\n/**\n * Script snippet for inline injection in <head>\n * Use this when you need synchronous flicker prevention before React hydrates\n *\n * @example\n * ```tsx\n * // In your _document.tsx or layout.tsx\n * <script dangerouslySetInnerHTML={{ __html: getFlickerPreventionScript() }} />\n * ```\n */\nexport function getFlickerPreventionScript(timeout = DEFAULT_TIMEOUT): string {\n return `\n(function() {\n var style = document.createElement('style');\n style.id = '${STYLE_ID}';\n style.textContent = '.${FLICKER_CLASS}{opacity:0!important;pointer-events:none!important}@keyframes eab-reveal{to{opacity:1;pointer-events:auto}}.${FLICKER_CLASS}{animation:eab-reveal 0s ${timeout}ms forwards}';\n document.head.insertBefore(style, document.head.firstChild);\n document.body && document.body.classList.add('${FLICKER_CLASS}');\n})();\n`.trim();\n}\n","/**\n * Shopify-specific utilities\n */\n\n/**\n * Extract numeric ID from a Shopify Global ID (GID).\n *\n * Shopify uses GIDs like \"gid://shopify/Product/123456789\" or \"gid://shopify/ProductVariant/987654321\".\n * This utility extracts the numeric ID portion.\n *\n * @param gid - Shopify Global ID or numeric ID string\n * @returns The numeric ID portion (e.g., \"123456789\")\n *\n * @example\n * ```ts\n * extractShopifyId(\"gid://shopify/Product/123456789\"); // \"123456789\"\n * extractShopifyId(\"gid://shopify/ProductVariant/987654321\"); // \"987654321\"\n * extractShopifyId(\"gid://shopify/Cart/abc123\"); // \"abc123\"\n * extractShopifyId(\"123456789\"); // \"123456789\" (already numeric)\n * ```\n */\nexport function extractShopifyId(gid: string | null | undefined): string {\n if (!gid) return \"\";\n\n // If it's a GID, extract the last segment\n if (gid.includes(\"gid://\")) {\n return gid.split(\"/\").pop() || gid;\n }\n\n // Already a plain ID\n return gid;\n}\n\n/**\n * Extract the resource type from a Shopify Global ID.\n *\n * @param gid - Shopify Global ID\n * @returns The resource type (e.g., \"Product\", \"ProductVariant\", \"Cart\")\n *\n * @example\n * ```ts\n * extractShopifyType(\"gid://shopify/Product/123\"); // \"Product\"\n * extractShopifyType(\"gid://shopify/ProductVariant/456\"); // \"ProductVariant\"\n * extractShopifyType(\"123456789\"); // null (not a GID)\n * ```\n */\nexport function extractShopifyType(\n gid: string | null | undefined,\n): string | null {\n if (!gid || !gid.includes(\"gid://\")) return null;\n\n const parts = gid.split(\"/\");\n // gid://shopify/Product/123 -> parts = [\"gid:\", \"\", \"shopify\", \"Product\", \"123\"]\n return parts[3] || null;\n}\n\n/**\n * Check if a string is a Shopify Global ID.\n *\n * @param value - String to check\n * @returns True if it's a GID\n *\n * @example\n * ```ts\n * isShopifyGid(\"gid://shopify/Product/123\"); // true\n * isShopifyGid(\"123456789\"); // false\n * ```\n */\nexport function isShopifyGid(value: string | null | undefined): boolean {\n return !!value && value.startsWith(\"gid://shopify/\");\n}\n\n/**\n * Try to detect the shop currency from Shopify's global object.\n * Only works on Shopify theme stores, not headless.\n *\n * @returns Currency code (e.g., \"USD\") or undefined\n */\nexport function detectShopifyCurrency(): string | undefined {\n if (typeof window === \"undefined\") return undefined;\n\n // Try Shopify's global currency object (theme stores only)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const shopify = (window as any).Shopify;\n if (shopify?.currency?.active) {\n return shopify.currency.active;\n }\n\n return undefined;\n}\n","/**\n * Manual tracking functions for non-Hydrogen frameworks (Next.js, etc.)\n *\n * Initialize once with initAnalytics(), then call tracking functions without params:\n *\n * @example\n * ```tsx\n * // In your layout/provider (once)\n * initAnalytics({\n * storeId: 'your-store.myshopify.com',\n * storefrontAccessToken: 'your-token',\n * });\n *\n * // Then anywhere in your app (no params needed)\n * trackPageView();\n * trackAddToCart({ cartId, productId, ... });\n * ```\n */\n\nimport type {\n EventPayload,\n TestAssignment,\n TrackPageViewParams,\n TrackProductViewParams,\n TrackAddToCartParams,\n TrackRemoveFromCartParams,\n TrackCartViewParams,\n TrackSearchSubmittedParams,\n TrackCheckoutStartedParams,\n TrackCheckoutCompletedParams,\n} from \"../types\";\n\nimport { extractShopifyId } from \"./shopify\";\n\n// Re-import types for cleaner syntax below\nimport type { UsePageViewTrackingParams } from \"../types\";\nimport {\n parseAddViewData,\n extractCartToken,\n cleanCartToken,\n getPageTypeFromPathname,\n sanitizeString,\n roundToTwo,\n getUserAgentNoBrowser,\n} from \"./analytics\";\nimport {\n getVisitorId,\n getSessionId,\n getCookie,\n getJsonCookie,\n getReferrerData,\n uuidv4,\n} from \"./storage\";\n\nconst DEFAULT_WORKER_URL = \"https://bitter-river-9c62.support-67d.workers.dev\";\nconst DEFAULT_ORDERS_WORKER_URL =\n \"https://d339co84ntxcme.cloudfront.net/Prod/orders\";\n\n// ============================================================================\n// GLOBAL ANALYTICS CONFIG (initialized once, used by all tracking functions)\n// ============================================================================\n\ninterface AnalyticsConfig {\n storeId: string;\n storefrontAccessToken?: string;\n hasLocalizedPaths?: boolean;\n workerUrl?: string;\n ordersWorkerUrl?: string;\n}\n\nlet globalConfig: AnalyticsConfig | null = null;\n\n/**\n * Initialize analytics config once. Call this in your app's entry point or layout.\n * After init, tracking functions don't need storeId/token params.\n *\n * @example\n * ```tsx\n * // In _app.tsx or layout.tsx\n * initAnalytics({\n * storeId: 'your-store.myshopify.com',\n * storefrontAccessToken: process.env.NEXT_PUBLIC_STOREFRONT_TOKEN,\n * });\n * ```\n */\nexport function initAnalytics(config: AnalyticsConfig): void {\n globalConfig = config;\n}\n\n/**\n * Get the current analytics config (for internal use)\n */\nexport function getAnalyticsConfig(): AnalyticsConfig | null {\n return globalConfig;\n}\n\n/**\n * Check if analytics is initialized\n */\nexport function isAnalyticsInitialized(): boolean {\n return globalConfig !== null;\n}\n\n// Helper to get storeId from params or global config\nfunction getStoreId(params?: { storeId?: string }): string {\n const storeId = params?.storeId || globalConfig?.storeId;\n if (!storeId) {\n console.warn(\n \"[ElevateAB] No storeId provided. Call initAnalytics() first or pass storeId to tracking function.\",\n );\n return \"\";\n }\n return storeId;\n}\n\n// Transform test list format to event format\nfunction transformTestFormat(\n testObject: Record<string, string>,\n): TestAssignment[] {\n return Object.entries(testObject)\n .filter(([, variationId]) => typeof variationId === \"string\")\n .map(([testId, variationId]) => ({\n test_id: testId,\n variant_id: variationId,\n }));\n}\n\n// Transform viewed tests format\nfunction transformViewedTests(\n viewedObject: Record<string, boolean>,\n assignmentObject: Record<string, string>,\n): TestAssignment[] {\n return Object.entries(viewedObject)\n .filter(([testId]) => typeof assignmentObject[testId] === \"string\")\n .map(([testId]) => ({\n test_id: testId,\n variant_id: assignmentObject[testId],\n }));\n}\n\n// Check if we're in preview mode (should skip analytics)\nfunction isInPreviewMode(): boolean {\n if (typeof window === \"undefined\") return false;\n\n // Check cookie\n if (getCookie(\"eabUserPreview\") === \"true\") return true;\n\n // Check URL param\n const urlParams = new URLSearchParams(window.location.search);\n if (urlParams.get(\"eabUserPreview\") === \"true\") return true;\n\n return false;\n}\n\n// Create base event data (returns null if in preview mode)\nfunction createBaseEventData(\n storeId: string,\n eventType: string,\n hasLocalizedPaths?: boolean,\n): Partial<EventPayload> | null {\n if (typeof window === \"undefined\") return null;\n\n // Skip tracking in preview mode to avoid polluting analytics\n if (isInPreviewMode()) {\n return null;\n }\n\n const userAgent = navigator.userAgent;\n const pathname = window.location.pathname;\n const pageType = getPageTypeFromPathname(pathname, hasLocalizedPaths);\n\n const { referrer, entryPage } = getReferrerData();\n\n const parsedData = parseAddViewData({\n referrer,\n entryPage,\n userAgent,\n });\n\n const abtlObject = getJsonCookie<Record<string, string>>(\"ABTL\") || {};\n const abauObject = getJsonCookie<Record<string, boolean>>(\"ABAU\") || {};\n\n const testAssignments = transformTestFormat(abtlObject);\n const viewedTests = transformViewedTests(abauObject, abtlObject);\n\n const cartToken = extractCartToken(localStorage.getItem(\"shopifyCartId\"));\n const currentTimestamp = new Date().toISOString();\n\n // Get additional state\n const isFirstVisit = sessionStorage.getItem(\"eabIsFirstVisit\") === \"true\";\n const shopifyCountry = getCookie(\"localization\") || \"\";\n\n return {\n pixel_event_id: `sh-${uuidv4()}`,\n shop_name: storeId,\n timestamp: currentTimestamp,\n event_type: eventType,\n client_id: getCookie(\"_shopify_y\") || undefined,\n visitor_id: getVisitorId(),\n session_id: getSessionId(),\n cart_token: cleanCartToken(cartToken || getCookie(\"cart\")),\n\n page_url: window.location.href,\n page_pathname: pathname,\n page_search: window.location.search,\n referrer_url: referrer,\n referrer_source: parsedData?.referrer_source,\n previous_page: document.referrer,\n page_entry: entryPage,\n page_entry_path: parsedData?.page_entry_path,\n page_type: pageType,\n\n utm_medium: parsedData?.utm_medium,\n utm_source: parsedData?.utm_source,\n utm_campaign: parsedData?.utm_campaign,\n utm_content: parsedData?.utm_content,\n utm_term: parsedData?.utm_term,\n\n gclid: parsedData?.gclid,\n fbclid: parsedData?.fbclid,\n pins_campaign_id: parsedData?.pins_campaign_id,\n epik: parsedData?.epik,\n\n browser_info: parsedData?.browser_info,\n os_info: parsedData?.os_info,\n device_type: parsedData?.device_type,\n language: navigator.language,\n root_route: localStorage.getItem(\"eabRootRoute\") || \"\",\n user_agent: userAgent,\n user_agent_no_browser: getUserAgentNoBrowser(userAgent),\n\n is_first_visit: isFirstVisit,\n shopify_country: shopifyCountry,\n\n ab_test_assignments: testAssignments,\n ab_test_views: viewedTests,\n is_first_order: null,\n };\n}\n\n// Send event to CloudFlare Worker\nasync function sendEvent(\n data: Partial<EventPayload>,\n workerUrl: string = DEFAULT_WORKER_URL,\n): Promise<void> {\n try {\n const response = await fetch(workerUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(data),\n keepalive: true,\n });\n\n if (!response.ok) {\n throw new Error(`Worker error: ${response.status}`);\n }\n } catch (error) {\n console.error(\"[ElevateAB] Error sending analytics:\", error);\n }\n}\n\n/**\n * Track page view event\n * Call this on route changes or in useEffect\n *\n * @example After initAnalytics()\n * ```ts\n * await trackPageView(); // No params needed!\n * ```\n *\n * @example Without init (pass params)\n * ```ts\n * await trackPageView({ storeId: \"mystore.myshopify.com\" });\n * ```\n */\nexport async function trackPageView(\n params: Partial<TrackPageViewParams> = {},\n): Promise<void> {\n const storeId = getStoreId(params);\n if (!storeId) return;\n\n const hasLocalizedPaths =\n params.hasLocalizedPaths ?? globalConfig?.hasLocalizedPaths;\n const workerUrl = params.workerUrl ?? globalConfig?.workerUrl;\n\n const baseData = createBaseEventData(\n storeId,\n \"page_viewed\",\n hasLocalizedPaths,\n );\n if (!baseData) return; // Skip in preview mode\n\n const eventData: Partial<EventPayload> = {\n ...baseData,\n cart_currency: params.currency,\n };\n\n await sendEvent(eventData, workerUrl);\n}\n\n/**\n * Track product view event\n * Call this on product pages\n *\n * @example After initAnalytics()\n * ```ts\n * await trackProductView({ productId: \"123\", productPrice: 99.99 });\n * ```\n */\nexport async function trackProductView(\n params: Omit<TrackProductViewParams, \"storeId\"> & { storeId?: string },\n): Promise<void> {\n const storeId = getStoreId(params);\n if (!storeId) return;\n\n const { productId, productVendor, productPrice, productSku, currency } =\n params;\n const hasLocalizedPaths =\n params.hasLocalizedPaths ?? globalConfig?.hasLocalizedPaths;\n const workerUrl = params.workerUrl ?? globalConfig?.workerUrl;\n\n const baseData = createBaseEventData(\n storeId,\n \"product_viewed\",\n hasLocalizedPaths,\n );\n if (!baseData) return; // Skip in preview mode\n\n const eventData: Partial<EventPayload> = {\n ...baseData,\n cart_currency: currency,\n product_id: extractShopifyId(productId),\n product_vendor: sanitizeString(productVendor),\n product_price: roundToTwo(productPrice),\n product_sku: sanitizeString(productSku),\n };\n\n await sendEvent(eventData, workerUrl);\n}\n\n/**\n * Track add to cart event\n * Call this when user clicks \"Add to Cart\"\n *\n * Cart attributes are automatically updated for order attribution if\n * storefrontAccessToken was provided to initAnalytics() or passed here.\n *\n * @example After initAnalytics()\n * ```ts\n * await trackAddToCart({\n * cartId: \"gid://shopify/Cart/abc123\",\n * productId: \"123456789\",\n * variantId: \"987654321\",\n * productPrice: 99.99,\n * productQuantity: 1,\n * });\n * ```\n */\nexport async function trackAddToCart(\n params: Omit<TrackAddToCartParams, \"storeId\" | \"storefrontAccessToken\"> & {\n storeId?: string;\n storefrontAccessToken?: string;\n },\n): Promise<void> {\n const storeId = getStoreId(params);\n if (!storeId) return;\n\n const {\n productId,\n variantId,\n productVendor,\n productPrice,\n productQuantity,\n productSku,\n currency,\n cartId,\n } = params;\n\n const hasLocalizedPaths =\n params.hasLocalizedPaths ?? globalConfig?.hasLocalizedPaths;\n const workerUrl = params.workerUrl ?? globalConfig?.workerUrl;\n const storefrontAccessToken =\n params.storefrontAccessToken ?? globalConfig?.storefrontAccessToken;\n\n const baseData = createBaseEventData(\n storeId,\n \"product_added_to_cart\",\n hasLocalizedPaths,\n );\n if (!baseData) return; // Skip in preview mode\n\n const eventData: Partial<EventPayload> = {\n ...baseData,\n cart_currency: currency,\n product_id: extractShopifyId(productId),\n variant_id: extractShopifyId(variantId),\n product_vendor: sanitizeString(productVendor),\n product_price: roundToTwo(productPrice),\n product_quantity: productQuantity,\n product_sku: sanitizeString(productSku),\n };\n\n await sendEvent(eventData, workerUrl);\n\n // Auto-update cart attributes if cartId and token are available\n if (cartId && storefrontAccessToken) {\n try {\n const { updateCartAttributes } = await import(\"./cartAttributes\");\n await updateCartAttributes(cartId, { storefrontAccessToken });\n } catch (err) {\n console.error(\"[ElevateAB] Failed to update cart attributes:\", err);\n }\n }\n}\n\n/**\n * Track remove from cart event\n * Call this when user removes item from cart\n */\nexport async function trackRemoveFromCart(\n params: Omit<TrackRemoveFromCartParams, \"storeId\"> & { storeId?: string },\n): Promise<void> {\n const storeId = getStoreId(params);\n if (!storeId) return;\n\n const {\n productId,\n variantId,\n productVendor,\n productPrice,\n productQuantity,\n productSku,\n currency,\n } = params;\n\n const hasLocalizedPaths =\n params.hasLocalizedPaths ?? globalConfig?.hasLocalizedPaths;\n const workerUrl = params.workerUrl ?? globalConfig?.workerUrl;\n\n const baseData = createBaseEventData(\n storeId,\n \"product_removed_from_cart\",\n hasLocalizedPaths,\n );\n if (!baseData) return; // Skip in preview mode\n\n const eventData: Partial<EventPayload> = {\n ...baseData,\n cart_currency: currency,\n product_id: extractShopifyId(productId),\n variant_id: extractShopifyId(variantId),\n product_vendor: sanitizeString(productVendor),\n product_price: roundToTwo(productPrice),\n product_quantity: productQuantity,\n product_sku: sanitizeString(productSku),\n };\n\n await sendEvent(eventData, workerUrl);\n}\n\n/**\n * Track cart view event\n * Call this when user views cart page\n */\nexport async function trackCartView(\n params: Omit<TrackCartViewParams, \"storeId\"> & { storeId?: string },\n): Promise<void> {\n const storeId = getStoreId(params);\n if (!storeId) return;\n\n const { cartTotalPrice, cartTotalQuantity, cartItems, currency } = params;\n const hasLocalizedPaths =\n params.hasLocalizedPaths ?? globalConfig?.hasLocalizedPaths;\n const workerUrl = params.workerUrl ?? globalConfig?.workerUrl;\n\n const baseData = createBaseEventData(\n storeId,\n \"cart_viewed\",\n hasLocalizedPaths,\n );\n if (!baseData) return; // Skip in preview mode\n\n const eventData: Partial<EventPayload> = {\n ...baseData,\n cart_currency: currency,\n cart_total_price: roundToTwo(cartTotalPrice),\n cart_total_quantity: cartTotalQuantity,\n cart_items: cartItems?.map((item) => ({\n product_id: extractShopifyId(item.productId),\n variant_id: extractShopifyId(item.variantId),\n product_vendor: sanitizeString(item.productVendor) || \"\",\n product_price: roundToTwo(item.productPrice),\n product_quantity: item.productQuantity ?? null,\n product_sku: sanitizeString(item.productSku) || \"\",\n })),\n };\n\n await sendEvent(eventData, workerUrl);\n}\n\n/**\n * Track search submitted event\n * Call this when user submits a search\n *\n * @example After initAnalytics()\n * ```ts\n * await trackSearchSubmitted({ searchQuery: \"running shoes\" });\n * ```\n *\n * @example Without init (pass params)\n * ```ts\n * await trackSearchSubmitted({\n * storeId: \"mystore.myshopify.com\",\n * searchQuery: \"running shoes\",\n * currency: \"USD\"\n * });\n * ```\n */\nexport async function trackSearchSubmitted(\n params: Omit<TrackSearchSubmittedParams, \"storeId\"> & { storeId?: string },\n): Promise<void> {\n const storeId = getStoreId(params);\n if (!storeId) return;\n\n const { searchQuery, currency } = params;\n const hasLocalizedPaths =\n params.hasLocalizedPaths ?? globalConfig?.hasLocalizedPaths;\n const workerUrl = params.workerUrl ?? globalConfig?.workerUrl;\n\n const baseData = createBaseEventData(\n storeId,\n \"search_submitted\",\n hasLocalizedPaths,\n );\n if (!baseData) return; // Skip in preview mode\n\n const eventData: Partial<EventPayload> = {\n ...baseData,\n cart_currency: currency,\n search_query: sanitizeString(searchQuery),\n };\n\n await sendEvent(eventData, workerUrl);\n}\n\n/**\n * Track checkout started event\n * Call this when user starts checkout\n *\n * @example After initAnalytics()\n * ```ts\n * await trackCheckoutStarted({\n * cartTotalPrice: 109.98,\n * cartItems: [...],\n * });\n * ```\n */\nexport async function trackCheckoutStarted(\n params: Omit<TrackCheckoutStartedParams, \"storeId\"> & { storeId?: string },\n): Promise<void> {\n const storeId = getStoreId(params);\n if (!storeId) return;\n\n const {\n cartTotalPrice,\n cartSubtotalPrice,\n cartShippingPrice,\n cartTaxAmount,\n cartDiscountAmount,\n customerId,\n cartItems,\n currency,\n } = params;\n\n const hasLocalizedPaths =\n params.hasLocalizedPaths ?? globalConfig?.hasLocalizedPaths;\n const workerUrl = params.workerUrl ?? globalConfig?.workerUrl;\n\n const baseData = createBaseEventData(\n storeId,\n \"checkout_started\",\n hasLocalizedPaths,\n );\n if (!baseData) return; // Skip in preview mode\n\n let totalQuantity = 0;\n const items =\n cartItems?.map((item) => {\n const quantity = item.productQuantity ?? 0;\n totalQuantity += quantity;\n return {\n product_id: extractShopifyId(item.productId),\n variant_id: extractShopifyId(item.variantId),\n product_vendor: sanitizeString(item.productVendor) || \"\",\n vendor: sanitizeString(item.productVendor) || \"\",\n product_price: roundToTwo(item.productPrice),\n total_price: roundToTwo(item.productPrice),\n product_quantity: quantity,\n quantity: quantity,\n product_sku: sanitizeString(item.productSku) || \"\",\n sku: sanitizeString(item.productSku) || \"\",\n total_discount: roundToTwo(item.totalDiscount),\n };\n }) || [];\n\n const eventData: Partial<EventPayload> = {\n ...baseData,\n cart_currency: currency,\n cart_total_price: roundToTwo(cartTotalPrice),\n cart_subtotal_price: roundToTwo(cartSubtotalPrice),\n cart_total_quantity: totalQuantity,\n cart_shipping_price: roundToTwo(cartShippingPrice),\n cart_tax_amount: roundToTwo(cartTaxAmount),\n cart_discount_amount: roundToTwo(cartDiscountAmount),\n cart_items: items,\n customer_id: customerId,\n };\n\n await sendEvent(eventData, workerUrl);\n}\n\n/**\n * Track checkout completed event (order placed)\n * Call this when order is placed\n *\n * NOTE: This sends to a different endpoint (orders worker)\n *\n * @example After initAnalytics()\n * ```ts\n * await trackCheckoutCompleted({\n * orderId: \"order_123456\",\n * cartTotalPrice: 109.98,\n * cartItems: [...],\n * });\n * ```\n */\nexport async function trackCheckoutCompleted(\n params: Omit<TrackCheckoutCompletedParams, \"storeId\"> & { storeId?: string },\n): Promise<void> {\n const storeId = getStoreId(params);\n if (!storeId) return;\n\n const {\n orderId,\n cartTotalPrice,\n cartSubtotalPrice,\n cartShippingPrice,\n cartTaxAmount,\n cartDiscountAmount,\n customerId,\n isFirstOrder,\n noteAttributes,\n cartItems,\n currency,\n } = params;\n\n const hasLocalizedPaths =\n params.hasLocalizedPaths ?? globalConfig?.hasLocalizedPaths;\n const ordersWorkerUrl =\n params.ordersWorkerUrl ??\n globalConfig?.ordersWorkerUrl ??\n DEFAULT_ORDERS_WORKER_URL;\n\n const baseData = createBaseEventData(\n storeId,\n \"checkout_completed\",\n hasLocalizedPaths,\n );\n if (!baseData) return; // Skip in preview mode\n\n let totalQuantity = 0;\n const items =\n cartItems?.map((item) => {\n const quantity = item.productQuantity ?? 0;\n totalQuantity += quantity;\n return {\n product_id: extractShopifyId(item.productId),\n variant_id: extractShopifyId(item.variantId),\n product_vendor: sanitizeString(item.productVendor) || \"\",\n vendor: sanitizeString(item.productVendor) || \"\",\n product_price: roundToTwo(item.productPrice),\n total_price: roundToTwo(item.productPrice),\n product_quantity: quantity,\n quantity: quantity,\n product_sku: sanitizeString(item.productSku) || \"\",\n sku: sanitizeString(item.productSku) || \"\",\n total_discount: roundToTwo(item.totalDiscount),\n };\n }) || [];\n\n const eventData: Partial<EventPayload> = {\n ...baseData,\n cart_currency: currency,\n cart_total_price: roundToTwo(cartTotalPrice),\n cart_subtotal_price: roundToTwo(cartSubtotalPrice),\n cart_total_quantity: totalQuantity,\n cart_shipping_price: roundToTwo(cartShippingPrice),\n cart_tax_amount: roundToTwo(cartTaxAmount),\n cart_discount_amount: roundToTwo(cartDiscountAmount),\n cart_items: items,\n order_id: orderId,\n customer_id: customerId,\n is_first_order: isFirstOrder ?? null,\n note_attributes: noteAttributes,\n };\n\n // Send to orders worker (different endpoint!)\n await sendEvent(eventData, ordersWorkerUrl);\n}\n\nimport { useEffect, useRef, useState } from \"react\";\n\n/**\n * Hook for automatic page view tracking on route changes\n * Use in Next.js app router or any React app\n *\n * NOTE: For Next.js App Router, consider using this with `usePathname()`:\n * ```tsx\n * const pathname = usePathname();\n * usePageViewTracking({ pathname, enabled: true });\n * ```\n *\n * @example Basic usage (tracks on mount only)\n * ```tsx\n * function Layout({ children }) {\n * usePageViewTracking({ enabled: true });\n * return <>{children}</>;\n * }\n * ```\n *\n * @example With Next.js usePathname (tracks on route changes)\n * ```tsx\n * import { usePathname } from 'next/navigation';\n *\n * function Layout({ children }) {\n * const pathname = usePathname();\n * usePageViewTracking({ pathname, enabled: true });\n * return <>{children}</>;\n * }\n * ```\n */\nexport function usePageViewTracking(\n params: UsePageViewTrackingParams & { pathname?: string },\n): void {\n const {\n storeId,\n hasLocalizedPaths,\n currency,\n workerUrl,\n enabled = true,\n pathname: externalPathname,\n } = params;\n\n // Track the last pathname we've sent an event for\n const lastTrackedPathRef = useRef<string | null>(null);\n\n // Use external pathname if provided (e.g., from Next.js usePathname),\n // otherwise try to get it from window (works on mount)\n const [currentPath, setCurrentPath] = useState<string>(() => {\n if (externalPathname) return externalPathname;\n if (typeof window !== \"undefined\") return window.location.pathname;\n return \"\";\n });\n\n // Update currentPath when externalPathname changes\n useEffect(() => {\n if (externalPathname) {\n setCurrentPath(externalPathname);\n }\n }, [externalPathname]);\n\n // Track page view when path changes\n useEffect(() => {\n if (typeof window === \"undefined\" || !enabled) return;\n\n // Don't track if we've already tracked this path\n if (lastTrackedPathRef.current === currentPath) return;\n\n // Track the page view\n lastTrackedPathRef.current = currentPath;\n trackPageView({ storeId, hasLocalizedPaths, currency, workerUrl });\n }, [currentPath, storeId, hasLocalizedPaths, currency, workerUrl, enabled]);\n}\n","import React from \"react\";\nimport type { Variation } from \"../types\";\nimport { useElevateConfig } from \"../contexts/ElevateContext\";\nimport { getVisitorId } from \"../utils/storage\";\nimport { assignAndPersistVariant, shouldShowTest } from \"../utils/assignment\";\nimport { trackViews } from \"../utils/tracking\";\n\nexport interface UseExperimentResult {\n /** The full variant object (null if not assigned) */\n variant: Variation | null;\n /** True while loading config */\n isLoading: boolean;\n /** True if assigned to control group */\n isControl: boolean;\n /** True if assigned to variation A (first non-control) */\n isA: boolean;\n /** True if assigned to variation B (second non-control) */\n isB: boolean;\n /** True if assigned to variation C (third non-control) */\n isC: boolean;\n /** True if assigned to variation D (fourth non-control) */\n isD: boolean;\n}\n\n/**\n * Hook to get assigned variant for a test\n * \n * @example\n * ```tsx\n * const { isControl, isA, isB } = useExperiment('my-test');\n * \n * if (isControl) return <Original />;\n * if (isA) return <VariantA />;\n * if (isB) return <VariantB />;\n * ```\n */\nexport function useExperiment(testId: string): UseExperimentResult {\n const { config } = useElevateConfig();\n const [variant, setVariant] = React.useState<Variation | null>(null);\n const [isLoading, setIsLoading] = React.useState(true);\n\n const testConfig = React.useMemo(() => {\n if (!config) return null;\n return config.tests.find((test) => test.testId === testId) || null;\n }, [config, testId]);\n\n // Track if we've already warned about this test (prevent duplicate warnings)\n const hasWarnedRef = React.useRef<string | null>(null);\n\n React.useEffect(() => {\n // Wait for config to load before doing anything\n if (config === null) {\n return; // Still loading config\n }\n\n // Config loaded but test not found\n if (!testConfig) {\n if (hasWarnedRef.current !== testId) {\n hasWarnedRef.current = testId;\n console.warn(`[ElevateAB] Test not found: ${testId}`);\n }\n setIsLoading(false);\n return;\n }\n\n // Test exists but is disabled\n if (!testConfig.enabled) {\n if (hasWarnedRef.current !== `${testId}-disabled`) {\n hasWarnedRef.current = `${testId}-disabled`;\n console.warn(`[ElevateAB] Test disabled: ${testId}`);\n }\n setIsLoading(false);\n return;\n }\n\n // Check traffic percentage\n if (!shouldShowTest(testConfig)) {\n setIsLoading(false);\n return;\n }\n\n // Get or create visitor ID\n const userId = getVisitorId();\n\n // Assign variant (sets isControl, isA, isB, isC, isD)\n const assigned = assignAndPersistVariant(testId, testConfig, userId);\n\n if (assigned) {\n setVariant(assigned);\n trackViews(testId);\n }\n\n setIsLoading(false);\n }, [config, testConfig, testId]);\n\n return { \n variant, \n isLoading, \n isControl: variant?.isControl ?? false,\n isA: variant?.isA ?? false,\n isB: variant?.isB ?? false,\n isC: variant?.isC ?? false,\n isD: variant?.isD ?? false,\n };\n}\n","import React, { useRef, useCallback } from \"react\";\nimport type { ElevateConfig, BackendConfig } from \"../types\";\nimport { ElevateContext } from \"../contexts/ElevateContext\";\nimport { parseBackendConfig } from \"../utils\";\nimport { initAnalytics } from \"../utils/manualTracking\";\nimport { getPreviewState, type PreviewState } from \"../utils/preview\";\nimport { getCountryCode } from \"../utils/geo\";\nimport { setupFlickerPrevention } from \"../utils/antiFlicker\";\n\n/**\n * Set to false during local development to use fallback config.\n * In production, this should always be true.\n */\nconst USE_CDN_CONFIG = false; // Set to false for demo/development\n\n/**\n * Fallback config for local development/testing when CDN is not available.\n * Only used when USE_CDN_CONFIG = false.\n *\n * Supported test types: SPLIT_URL, PRICE_PLUS, CONTENT, CUSTOM_CODE\n */\nconst DEV_FALLBACK_CONFIG: BackendConfig = {\n allTests: {\n // Price Plus Test - Testing different price points\n \"price-test-001\": {\n \"8606\": {\n variationName: \"Control\",\n trafficPercentage: 50,\n isDone: false,\n isControl: true,\n },\n \"8607\": {\n variationName: \"Sale Price\",\n trafficPercentage: 50,\n isDone: false,\n prices: {\n \"41883969519701\": {\n main: \"USD\",\n price: { USD: \"599.95\" },\n compare: { USD: \"699.95\" },\n },\n },\n },\n data: {\n name: \"Snowboard Price Test\",\n isLive: true,\n settings: { afterDiscounts: true },\n type: \"PRICE_PLUS\",\n filters: [],\n isPersonalization: false,\n currencies: [\"USD\"],\n handles: [\"the-complete-snowboard\"],\n productIds: [\"7240161067093\"],\n },\n },\n\n // Content Test - Simple A/B/C headline test\n \"content-test-001\": {\n \"ctrl-001\": {\n variationName: \"Control\",\n trafficPercentage: 34,\n isDone: false,\n isControl: true,\n content: {\n headline: \"Welcome to our store\",\n subheadline: \"Shop the best products\",\n },\n },\n \"var-a-001\": {\n variationName: \"Urgency Copy\",\n trafficPercentage: 33,\n isDone: false,\n content: {\n headline: \"Limited Time Offer!\",\n subheadline: \"Don't miss out - sale ends soon\",\n },\n },\n \"var-b-001\": {\n variationName: \"Value Copy\",\n trafficPercentage: 33,\n isDone: false,\n content: {\n headline: \"Premium Quality, Great Value\",\n subheadline: \"Free shipping on orders over $50\",\n },\n },\n data: {\n name: \"Homepage Headline Test\",\n isLive: true,\n settings: { afterDiscounts: true },\n type: \"CONTENT\",\n filters: [],\n isPersonalization: false,\n pathnames: [\"/\"],\n },\n },\n\n // Custom Code Test - Injecting custom JS/CSS\n \"custom-code-test-001\": {\n \"10150\": {\n variationName: \"Control\",\n trafficPercentage: 50,\n isDone: false,\n customCode: {\n id: \"c11a582c-6b27-4263-941c-8ed123437c6b\",\n js: \"console.log('[Elevate] Control variant active');\",\n css: \"\",\n pathnames: [\"*\"],\n excludePathnames: [],\n },\n isControl: true,\n },\n \"10151\": {\n variationName: \"Enhanced UI\",\n trafficPercentage: 50,\n isDone: false,\n customCode: {\n id: \"df71546a-0a5f-4ad0-8d0f-a1bdeab05e84\",\n js: \"console.log('[Elevate] Enhanced UI variant'); document.body.style.borderTop = '3px solid #10b981';\",\n css: \".hero-section { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }\",\n pathnames: [\"*\"],\n excludePathnames: [],\n },\n },\n data: {\n name: \"Custom Code Enhancement\",\n isLive: true,\n settings: { afterDiscounts: true },\n type: \"CUSTOM_CODE\",\n filters: [],\n isPersonalization: false,\n pathnames: [\"*\"],\n excludePathnames: [],\n },\n },\n\n // Split URL Test - Redirect to different landing pages\n \"split-url-test-001\": {\n \"split-ctrl\": {\n variationName: \"Control\",\n trafficPercentage: 50,\n isDone: false,\n isControl: true,\n splitUrlTestLinks: {\n default: \"/collections/all\",\n },\n },\n \"split-var-a\": {\n variationName: \"New Landing Page\",\n trafficPercentage: 50,\n isDone: false,\n splitUrlTestLinks: {\n default: \"/collections/featured\",\n },\n },\n data: {\n name: \"Landing Page Split Test\",\n isLive: true,\n settings: { afterDiscounts: true },\n type: \"SPLIT_URL\",\n filters: [],\n isPersonalization: false,\n pathnames: [\"/promo\"],\n },\n },\n },\n selectors: { selectorsV2: [] },\n};\n\ninterface ElevateProviderSimpleProps {\n /** Shopify store domain (e.g., \"your-store.myshopify.com\") */\n storeId: string;\n /**\n * Storefront Access Token - Required for cart attribute tracking (order attribution)\n *\n * This is SAFE to use client-side - it's a public token with limited permissions.\n * Get it from: Shopify Admin → Settings → Apps → Develop apps\n *\n * Without this token, A/B tests work but orders won't be attributed to variants.\n */\n storefrontAccessToken?: string;\n /**\n * Enable flicker prevention - hides content until test assignment is complete\n * Prevents users from seeing content flash/change when variants load\n * @default false\n */\n preventFlickering?: boolean;\n /**\n * Timeout in milliseconds for flicker prevention failsafe\n * Content will show after this time even if assignment isn't complete\n * @default 3000\n */\n flickerTimeout?: number;\n children: React.ReactNode;\n}\n\n/**\n * ElevateProvider - Provides A/B test configuration to child components\n *\n * @example Next.js / Remix\n * ```tsx\n * <ElevateProvider\n * storeId=\"your-store.myshopify.com\"\n * storefrontAccessToken=\"your-public-token\"\n * >\n * <App />\n * </ElevateProvider>\n * ```\n *\n * @example Hydrogen (with Analytics.Provider)\n * ```tsx\n * <Analytics.Provider cart={cart} shop={shop} consent={consent}>\n * <ElevateProvider storeId=\"your-store.myshopify.com\" storefrontAccessToken=\"token\">\n * <ElevateAnalytics />\n * <App />\n * </ElevateProvider>\n * </Analytics.Provider>\n * ```\n */\nexport function ElevateProvider({\n storeId,\n storefrontAccessToken,\n preventFlickering = false,\n flickerTimeout = 3000,\n children,\n}: ElevateProviderSimpleProps) {\n const [config, setConfig] = React.useState<ElevateConfig | null>(null);\n const [previewState, setPreviewState] = React.useState<PreviewState | null>(\n null,\n );\n const [countryCode, setCountryCode] = React.useState<string | null>(null);\n const revealRef = useRef<(() => void) | null>(null);\n\n // Setup flicker prevention on mount (before config loads)\n React.useEffect(() => {\n if (preventFlickering && typeof window !== \"undefined\") {\n revealRef.current = setupFlickerPrevention(\"body\", flickerTimeout);\n }\n\n return () => {\n // Cleanup: ensure content is revealed on unmount\n if (revealRef.current) {\n revealRef.current();\n }\n };\n }, [preventFlickering, flickerTimeout]);\n\n // Initialize analytics config so tracking functions don't need storeId\n React.useEffect(() => {\n if (typeof window === \"undefined\") return;\n\n initAnalytics({\n storeId,\n storefrontAccessToken,\n });\n }, [storeId, storefrontAccessToken]);\n\n // Initialize preview state and geo on mount\n React.useEffect(() => {\n if (typeof window === \"undefined\") return;\n\n // Get preview state from URL/cookies\n const preview = getPreviewState();\n setPreviewState(preview);\n\n // Get country code\n const country = getCountryCode();\n setCountryCode(country);\n }, []);\n\n // Reveal callback - called when config is loaded or on error\n const revealContent = useCallback(() => {\n if (revealRef.current) {\n revealRef.current();\n revealRef.current = null;\n }\n }, []);\n\n React.useEffect(() => {\n async function fetchConfig() {\n try {\n // In dev mode, skip CDN fetch and use fallback config directly\n if (!USE_CDN_CONFIG) {\n const parsedConfig = parseBackendConfig(DEV_FALLBACK_CONFIG);\n setConfig(parsedConfig);\n revealContent();\n return;\n }\n\n // Production mode: fetch from CDN\n const url = `https://configs.elevateab.com/config/${storeId}.json`;\n const response = await fetch(url);\n\n if (response.status === 404) {\n // 404 means no config - could be new store or paused subscription\n // Just render children without A/B tests, no error log for this case\n setConfig({ tests: [], selectors: undefined });\n revealContent();\n return;\n }\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch config: ${response.status} ${response.statusText}`,\n );\n }\n\n const backendData: BackendConfig = await response.json();\n\n // Check if subscription is paused/stopped\n if (backendData.subscriptionPaused) {\n console.error(\n `[ElevateAB] Subscription is paused or stopped for store: ${storeId}. ` +\n (backendData.subscriptionMessage || \"A/B tests will not run. Please reactivate your subscription.\"),\n );\n // Graceful degradation - just render children, no A/B tests\n setConfig({ tests: [], selectors: undefined });\n revealContent();\n return;\n }\n\n // Check if config is empty (all tests might have been paused)\n const hasActiveTests = Object.keys(backendData.allTests || {}).length > 0;\n if (!hasActiveTests) {\n // Empty config - could be paused subscription or no active tests\n // Don't log error, just render children normally\n setConfig({ tests: [], selectors: undefined });\n revealContent();\n return;\n }\n\n const parsedConfig = parseBackendConfig(backendData);\n setConfig(parsedConfig);\n revealContent();\n } catch (err) {\n console.error(\"[ElevateAB] Failed to load config:\", err);\n // Graceful degradation - just render children, website doesn't break\n setConfig({ tests: [], selectors: undefined });\n revealContent();\n }\n }\n\n fetchConfig();\n }, [storeId, revealContent]);\n\n const value = React.useMemo(\n () => ({\n config,\n storeId,\n storefrontAccessToken,\n isPreviewMode: previewState?.isPreview ?? false,\n previewTestId: previewState?.previewTestId ?? null,\n countryCode,\n }),\n [config, storeId, storefrontAccessToken, previewState, countryCode],\n );\n\n // Initialize analytics so tracking functions don't need storeId/token each time\n React.useEffect(() => {\n initAnalytics({ storeId, storefrontAccessToken });\n }, [storeId, storefrontAccessToken]);\n\n // Warn once if storefrontAccessToken is missing (order attribution won't work)\n const hasWarnedRef = useRef(false);\n React.useEffect(() => {\n if (!storefrontAccessToken && !hasWarnedRef.current) {\n hasWarnedRef.current = true;\n console.warn(\n \"[ElevateAB] No storefrontAccessToken provided. \" +\n \"A/B tests will work, but orders won't be attributed to test variants. \" +\n \"Add storefrontAccessToken prop to enable order tracking.\",\n );\n }\n }, [storefrontAccessToken]);\n\n return (\n <ElevateContext.Provider value={value}>{children}</ElevateContext.Provider>\n );\n}\n","import React from \"react\";\nimport { getFlickerPreventionScript } from \"../utils/antiFlicker\";\n\nexport interface AntiFlickerScriptProps {\n /**\n * Maximum time (ms) to wait before showing content anyway (failsafe)\n * @default 3000\n */\n timeout?: number;\n}\n\n/**\n * Anti-flicker script component\n *\n * Renders a script tag that prevents content flash during A/B test loading.\n * Place this in your <head> element before any other scripts.\n *\n * @example Hydrogen (app/root.tsx)\n * ```tsx\n * import { AntiFlickerScript, ElevateProvider } from \"@elevateab/sdk\";\n *\n * export default function Root() {\n * return (\n * <html>\n * <head>\n * <AntiFlickerScript />\n * </head>\n * <body>\n * <ElevateProvider storeId=\"...\" preventFlickering={true}>\n * <Outlet />\n * </ElevateProvider>\n * </body>\n * </html>\n * );\n * }\n * ```\n *\n * @example Next.js (app/layout.tsx)\n * ```tsx\n * import { AntiFlickerScript } from \"@elevateab/sdk\";\n * import { ElevateNextProvider } from \"@elevateab/sdk/next\";\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html>\n * <head>\n * <AntiFlickerScript />\n * </head>\n * <body>\n * <ElevateNextProvider storeId=\"...\" preventFlickering={true}>\n * {children}\n * </ElevateNextProvider>\n * </body>\n * </html>\n * );\n * }\n * ```\n */\nexport function AntiFlickerScript({ timeout = 3000 }: AntiFlickerScriptProps) {\n return (\n <script\n dangerouslySetInnerHTML={{\n __html: getFlickerPreventionScript(timeout),\n }}\n />\n );\n}\n\nexport default AntiFlickerScript;\n","// Main entry point for the Elevate AB Testing NPM package\n\n// ============================================================================\n// TYPE EXPORTS\n// ============================================================================\n\n// Core SDK types\nexport type {\n Test,\n Variation,\n ElevateConfig,\n ElevateProviderProps,\n ElevateContextValue,\n ElevateAnalyticsProps,\n ExperimentStatus,\n BackendConfig,\n BackendTest,\n BackendVariation,\n} from \"./types\";\n\n// Event tracking types (for manual tracking)\nexport type {\n // Base config\n BaseTrackingConfig,\n // Individual event params\n TrackPageViewParams,\n TrackProductViewParams,\n TrackAddToCartParams,\n TrackRemoveFromCartParams,\n TrackCartViewParams,\n TrackSearchSubmittedParams,\n TrackCheckoutStartedParams,\n TrackCheckoutCompletedParams,\n UsePageViewTrackingParams,\n // Shared types\n CartItemInput,\n NoteAttribute,\n TestAssignment,\n // Internal payload (for advanced users)\n EventPayload,\n CartItemPayload,\n} from \"./types\";\n\n// Cart attribute types\nexport type { CartAttributesOptions, CartAttributeInput } from \"./types\";\n\n// Legacy types (deprecated)\nexport type { TrackingEvent } from \"./types\";\n\nexport {\n assignVariant,\n hashString,\n validateConfig,\n generateExperimentId,\n calculateRevenueLift,\n} from \"./utils\";\n\n// Storage utilities\nexport {\n setCookie,\n getCookie,\n getJsonCookie,\n deleteCookie,\n setSessionItem,\n getSessionItem,\n getJsonSessionItem,\n getVisitorId,\n initializeVisitorId,\n getSessionId,\n initializeSessionId,\n setReferrerData,\n getReferrerData,\n uuidv4,\n} from \"./utils/storage\";\n\n// Assignment utilities\nexport {\n getTestList,\n saveTestList,\n getAssignedVariant,\n assignAndPersistVariant,\n shouldShowTest,\n} from \"./utils/assignment\";\n\n// Tracking utilities\nexport {\n trackViews,\n trackUniqueView,\n trackSessionView,\n hasSessionView,\n hasUniqueView,\n} from \"./utils/tracking\";\n\n// Condition utilities\nexport {\n getDeviceType,\n getTrafficSource,\n checkFacebookBrowser,\n checkInstagramBrowser,\n checkTikTokBrowser,\n checkPinterestBrowser,\n} from \"./utils/conditions\";\n\n// Preview mode utilities\nexport {\n getPreviewState,\n isPreviewTest,\n getPreviewVariation,\n buildEabTestsParam,\n updateUrlWithTestParams,\n clearPreviewMode,\n isInPreviewMode,\n} from \"./utils/preview\";\nexport type { PreviewState } from \"./utils/preview\";\n\n// Geo-targeting utilities\nexport {\n getCountryCode,\n getGeoLocation,\n setCountryCode,\n setGeoLocation,\n isCountryIncluded,\n isCountryExcluded,\n matchesGeoTargeting,\n COUNTRIES,\n REGIONS,\n} from \"./utils/geo\";\nexport type { GeoLocation } from \"./utils/geo\";\n\n// Anti-flicker utilities\nexport {\n setupFlickerPrevention,\n injectFlickerStyles,\n hideForFlicker,\n revealAfterFlicker,\n cleanupFlickerPrevention,\n isFlickerPreventionActive,\n getFlickerPreventionScript,\n} from \"./utils/antiFlicker\";\n\n// Analytics utilities\nexport {\n parseAddViewData,\n extractProductId,\n extractProductVariantId,\n extractCartToken,\n cleanCartToken,\n getPageTypeFromPathname,\n sanitizeString,\n roundToTwo,\n checkFacebookInstagramBrowser,\n checkVisitorIdParams,\n getUserAgentNoBrowser,\n} from \"./utils/analytics\";\n\nexport type { ParsedViewData } from \"./utils/analytics\";\n\n// Shopify utilities\nexport {\n extractShopifyId,\n extractShopifyType,\n isShopifyGid,\n detectShopifyCurrency,\n} from \"./utils/shopify\";\n\n// Cart attribute utilities\nexport {\n updateCartAttributes,\n cleanupCartAttributes,\n getCartAttributesPayload,\n} from \"./utils/cartAttributes\";\n\n// Cart mutations\nexport { CART_ATTRIBUTES_UPDATE_MUTATION } from \"./mutations/cartAttributes.mutation\";\n\n// Manual tracking utilities (for Next.js and other frameworks)\n// Note: ElevateProvider auto-calls initAnalytics, so you usually don't need to call it manually\nexport {\n initAnalytics,\n trackPageView,\n trackProductView,\n trackAddToCart,\n trackRemoveFromCart,\n trackCartView,\n trackSearchSubmitted,\n trackCheckoutStarted,\n trackCheckoutCompleted,\n usePageViewTracking,\n} from \"./utils/manualTracking\";\n\n// Components\nexport { useExperiment } from \"./components/Experiment\";\nexport type { UseExperimentResult } from \"./components/Experiment\";\nexport { ElevateProvider } from \"./components/ElevateProvider\";\nexport { AntiFlickerScript } from \"./components/AntiFlickerScript\";\n\n// Analytics components\n// For Hydrogen stores: import from '@elevateab/sdk/hydrogen' for ElevateHydrogenAnalytics\n// For Next.js/Remix: use ElevateAnalytics with manual hook injection\nexport { ElevateAnalytics } from \"./components/ElevateAnalytics\";\nexport type {\n UseAnalyticsHook,\n ElevateHydrogenAnalyticsProps,\n} from \"./components/ElevateAnalytics\";\n\n// Note: ElevateHydrogenAnalytics is exported from '@elevateab/sdk/hydrogen' sub-path\n// to avoid bundling @shopify/hydrogen in non-Hydrogen projects\n\n// Contexts\nexport { useElevateConfig } from \"./contexts/ElevateContext\";\n\n// Package version\nexport const VERSION = \"1.1.2\";\n\n// Default configuration\nexport const DEFAULT_CONFIG = {\n enabled: true,\n trackingEndpoint: \"https://analytics.elevateab.com/track\",\n cacheDuration: 3600,\n};\n"],"mappings":"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","names":["trackUniqueView","testId","addedUniqueViews","getJsonCookie","setCookie","trackSessionView","addedViews","getJsonSessionItem","setSessionItem","trackViews","hasSessionView","hasUniqueView","getDeviceType","ua","lower","checkFacebookBrowser","userAgent","checkInstagramBrowser","checkTikTokBrowser","checkPinterestBrowser","getTrafficSource","referrer","referrerDomain","hostname","isFacebookBrowser","isInstagramBrowser","isTikTokBrowser","isPinterestBrowser","parseEabTestsParam","param","assignments","views","tests","test","parts","shortTestId","variationId","hasSeen","getPreviewState","urlParams","isPreview","getCookie","previewTestId","getSessionItem","eabTestsParam","setCookie","setSessionItem","isPreviewTest","testId","getPreviewVariation","previewState","buildEabTestsParam","testAssignments","viewedTests","updateUrlWithTestParams","visitorId","url","eabTestsValue","clearPreviewMode","isInPreviewMode","GEO_COOKIE_NAMES","getCountryCode","urlCountry","setCookie","shopifyCountry","getCookie","cachedCountry","storedCountry","getGeoLocation","country","geoData","parsed","setCountryCode","countryCode","normalized","setGeoLocation","geo","isCountryIncluded","allowedCountries","userCountry","c","isCountryExcluded","excludedCountries","matchesGeoTargeting","rules","COUNTRIES","REGIONS","FLICKER_CLASS","STYLE_ID","injectFlickerStyles","timeout","STYLE_ID","style","FLICKER_CLASS","head","hideForFlicker","selector","element","revealAfterFlicker","setupFlickerPrevention","hasRevealed","timeoutId","cleanupFlickerPrevention","el","isFlickerPreventionActive","getFlickerPreventionScript","extractShopifyId","gid","extractShopifyType","isShopifyGid","value","detectShopifyCurrency","shopify","useEffect","useRef","useState","DEFAULT_WORKER_URL","DEFAULT_ORDERS_WORKER_URL","globalConfig","initAnalytics","config","getStoreId","params","storeId","globalConfig","transformTestFormat","testObject","variationId","testId","transformViewedTests","viewedObject","assignmentObject","isInPreviewMode","getCookie","createBaseEventData","eventType","hasLocalizedPaths","userAgent","pathname","pageType","getPageTypeFromPathname","referrer","entryPage","getReferrerData","parsedData","parseAddViewData","abtlObject","getJsonCookie","abauObject","testAssignments","viewedTests","cartToken","extractCartToken","currentTimestamp","isFirstVisit","shopifyCountry","uuidv4","getVisitorId","getSessionId","cleanCartToken","getUserAgentNoBrowser","sendEvent","data","workerUrl","DEFAULT_WORKER_URL","response","error","trackPageView","baseData","eventData","trackProductView","productId","productVendor","productPrice","productSku","currency","extractShopifyId","sanitizeString","roundToTwo","trackAddToCart","variantId","productQuantity","cartId","storefrontAccessToken","updateCartAttributes","err","trackRemoveFromCart","trackCartView","cartTotalPrice","cartTotalQuantity","cartItems","item","trackSearchSubmitted","searchQuery","trackCheckoutStarted","cartSubtotalPrice","cartShippingPrice","cartTaxAmount","cartDiscountAmount","customerId","totalQuantity","items","quantity","trackCheckoutCompleted","orderId","isFirstOrder","noteAttributes","ordersWorkerUrl","DEFAULT_ORDERS_WORKER_URL","usePageViewTracking","enabled","externalPathname","lastTrackedPathRef","useRef","currentPath","setCurrentPath","useState","useEffect","React","useExperiment","testId","config","useElevateConfig","variant","setVariant","React","isLoading","setIsLoading","testConfig","test","hasWarnedRef","shouldShowTest","userId","getVisitorId","assigned","assignAndPersistVariant","trackViews","React","useRef","useCallback","USE_CDN_CONFIG","DEV_FALLBACK_CONFIG","ElevateProvider","storeId","storefrontAccessToken","preventFlickering","flickerTimeout","children","config","setConfig","React","previewState","setPreviewState","countryCode","setCountryCode","revealRef","useRef","setupFlickerPrevention","initAnalytics","preview","getPreviewState","country","getCountryCode","revealContent","useCallback","fetchConfig","parsedConfig","parseBackendConfig","url","response","backendData","err","value","hasWarnedRef","ElevateContext","React","AntiFlickerScript","timeout","React","getFlickerPreventionScript","VERSION","DEFAULT_CONFIG"]}