@elevateab/sdk 1.4.4 → 1.4.5

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/geo.ts","../src/utils/navigation.ts","../src/handlers/splitUrl.ts","../src/utils/shopify.ts","../src/utils/content.ts","../src/utils/manualTracking.ts","../src/components/Experiment.tsx","../src/handlers/registry.ts","../src/handlers/pricePlus.ts","../src/handlers/content.ts","../src/handlers/customCode.ts","../src/handlers/index.ts","../src/components/ElevateProvider.tsx","../src/utils/customCode.ts","../src/index.ts"],"sourcesContent":["import {\n setCookie,\n setSessionItem,\n getJsonCookie,\n getJsonSessionItem,\n} from \"./storage\";\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\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\nexport function trackViews(testId: string): void {\n trackUniqueView(testId);\n trackSessionView(testId);\n}\n\nexport function hasSessionView(testId: string): boolean {\n const addedViews = getJsonSessionItem<Record<string, boolean>>(\"ABAV\") || {};\n return !!addedViews[testId];\n}\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 * 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 * SPA Navigation Listener Utility\n *\n * Detects ALL client-side navigation in SPAs including:\n * - history.pushState() — link clicks handled by SPA routers\n * - history.replaceState() — in-place URL changes\n * - popstate — browser back/forward buttons\n *\n * The native `popstate` event only fires on back/forward navigation.\n * SPA routers (React Router, Remix, Next.js, etc.) navigate via\n * history.pushState(), which does NOT fire `popstate`.\n *\n * This utility monkey-patches pushState/replaceState to dispatch\n * a custom `eab:navigate` event, giving us a single event to\n * listen for ALL navigation types.\n */\n\nconst NAVIGATE_EVENT = \"eab:navigate\";\n\nlet patched = false;\nlet patchRefCount = 0;\nlet originalPushState: typeof history.pushState | null = null;\nlet originalReplaceState: typeof history.replaceState | null = null;\n\n/**\n * Patch history.pushState and history.replaceState to dispatch\n * a custom navigation event. Uses reference counting so multiple\n * callers can install/uninstall independently.\n */\nfunction patchHistory(): void {\n if (typeof window === \"undefined\") return;\n\n patchRefCount++;\n if (patched) return;\n patched = true;\n\n originalPushState = history.pushState.bind(history);\n originalReplaceState = history.replaceState.bind(history);\n\n history.pushState = function (...args: Parameters<typeof history.pushState>) {\n originalPushState!(...args);\n window.dispatchEvent(new Event(NAVIGATE_EVENT));\n };\n\n history.replaceState = function (\n ...args: Parameters<typeof history.replaceState>\n ) {\n originalReplaceState!(...args);\n window.dispatchEvent(new Event(NAVIGATE_EVENT));\n };\n}\n\n/**\n * Restore original history methods when no more listeners need them.\n */\nfunction unpatchHistory(): void {\n if (typeof window === \"undefined\") return;\n\n patchRefCount--;\n if (patchRefCount > 0 || !patched) return;\n\n if (originalPushState) {\n history.pushState = originalPushState;\n originalPushState = null;\n }\n if (originalReplaceState) {\n history.replaceState = originalReplaceState;\n originalReplaceState = null;\n }\n patched = false;\n}\n\n/**\n * Subscribe to ALL SPA navigation events (pushState, replaceState, popstate).\n *\n * The callback receives no arguments — the caller should read\n * `window.location` to determine the new URL.\n *\n * Returns a cleanup function that removes all listeners.\n *\n * @example\n * ```ts\n * const cleanup = onNavigate(() => {\n * console.log(\"Navigated to\", window.location.pathname);\n * });\n * // Later:\n * cleanup();\n * ```\n */\nexport function onNavigate(callback: () => void): () => void {\n if (typeof window === \"undefined\") return () => {};\n\n // Keep track of the last URL to avoid duplicate calls\n // (e.g. replaceState to the same URL)\n let lastUrl = window.location.href;\n\n const handler = () => {\n const currentUrl = window.location.href;\n if (currentUrl === lastUrl) return;\n lastUrl = currentUrl;\n callback();\n };\n\n patchHistory();\n\n window.addEventListener(NAVIGATE_EVENT, handler);\n window.addEventListener(\"popstate\", handler);\n\n return () => {\n window.removeEventListener(NAVIGATE_EVENT, handler);\n window.removeEventListener(\"popstate\", handler);\n unpatchHistory();\n };\n}\n","import type { Test, Variation, ElevateConfig } from \"../types\";\nimport type { PreviewState } from \"../utils/preview\";\nimport { getAssignedVariant } from \"../utils/assignment\";\nimport { getCookie, setCookie, getJsonCookie } from \"../utils/storage\";\nimport { onNavigate } from \"../utils/navigation\";\n\nexport interface SplitUrlScriptOptions {\n configUrl: string;\n timeout?: number;\n}\n\nexport function getSplitUrlBlockingScript(\n options: SplitUrlScriptOptions\n): string {\n const { configUrl, timeout = 2000 } = options;\n\n return `(function(){\n if(new URLSearchParams(location.search).get('abtr')==='true')return;\n \n var style=document.createElement('style');\n style.id='eab-split-hide';\n style.textContent='body{opacity:0!important;visibility:hidden!important}';\n document.head.appendChild(style);\n \n window.__eab_reveal=function(){\n var s=document.getElementById('eab-split-hide');\n if(s)s.remove();\n window.__eab_reveal=null;\n };\n \n setTimeout(function(){\n if(window.__eab_reveal)window.__eab_reveal();\n },${timeout});\n \n if(window.eab_data){\n return;\n }\n \n var script=document.createElement('script');\n script.src='${configUrl}';\n script.onerror=function(){\n if(window.__eab_reveal)window.__eab_reveal();\n };\n document.head.appendChild(script);\n})();`;\n}\n\nfunction normalizeUrl(url: string): string {\n if (!url?.trim()) return \"\";\n\n try {\n const withoutProtocol = url.trim().replace(/^https?:\\/\\//, \"\");\n const withProtocol = `https://${withoutProtocol}`;\n const urlObj = new URL(withProtocol.replace(/\\/+$/, \"\"));\n\n if (urlObj.hostname.startsWith(\"www.\")) {\n urlObj.hostname = urlObj.hostname.substring(4);\n }\n\n return urlObj.toString().replace(/\\/+$/, \"\");\n } catch {\n return url.trim().replace(/\\/+$/, \"\");\n }\n}\n\nfunction urlMatches(currentUrl: string, urlList: string[]): boolean {\n const normalizedCurrent = normalizeUrl(currentUrl.split(\"?\")[0]);\n\n for (const url of urlList) {\n const normalizedTarget = normalizeUrl(url.split(\"?\")[0]);\n if (normalizedCurrent === normalizedTarget) {\n return true;\n }\n // Also check pathname match for same-origin URLs\n try {\n const currentPath = new URL(currentUrl).pathname;\n const targetPath = new URL(url, currentUrl).pathname;\n if (currentPath === targetPath) {\n return true;\n }\n } catch {\n // Ignore URL parse errors\n }\n }\n return false;\n}\n\ntype RedirectBehavior =\n | \"redirectAll\"\n | \"redirectOnlyControlUrl\"\n | \"redirectOnce\"\n | \"redirectOnceControlUrlOnly\";\n\nfunction getRedirectBehavior(test: Test): RedirectBehavior {\n return (\n ((test as any).data?.redirectBehavior as RedirectBehavior) || \"redirectAll\"\n );\n}\n\nfunction hasBeenRedirected(testId: string): boolean {\n const redirectedTests =\n getJsonCookie<Record<string, boolean>>(\"eabRedirectedTests\") || {};\n return !!redirectedTests[testId];\n}\n\nfunction markAsRedirected(testId: string): void {\n const redirectedTests =\n getJsonCookie<Record<string, boolean>>(\"eabRedirectedTests\") || {};\n redirectedTests[testId] = true;\n setCookie(\"eabRedirectedTests\", JSON.stringify(redirectedTests));\n}\n\n/**\n * Find SPLIT_URL test that matches current URL\n */\nfunction findMatchingSplitUrlTest(\n config: ElevateConfig,\n currentUrl: string\n): { test: Test; matchedVariation: Variation; isControlUrl: boolean } | null {\n for (const test of config.tests) {\n if (test.type !== \"SPLIT_URL\" || !test.enabled) continue;\n\n for (const variation of test.variations) {\n const urls = (variation as any).splitUrlTestLinks || [];\n if (urls.length === 0) continue;\n\n if (urlMatches(currentUrl, urls)) {\n return {\n test,\n matchedVariation: variation,\n isControlUrl: !!variation.isControl,\n };\n }\n }\n }\n return null;\n}\n\n/**\n * Perform redirect using appropriate method for SPA frameworks\n */\nfunction performRedirect(targetUrl: string): void {\n if (typeof window === \"undefined\") return;\n\n // Add abtr=true param to prevent redirect loops\n const url = new URL(targetUrl, window.location.origin);\n url.searchParams.set(\"abtr\", \"true\");\n const finalUrl = url.toString();\n\n // Try SPA navigation first (Remix, Next.js, Nuxt)\n const navigate =\n (window as any).__remixRouter?.navigate ??\n (window as any).next?.router?.push ??\n (window as any).$nuxt?.$router?.push ??\n (window as any).__navigate;\n\n if (navigate && typeof navigate === \"function\") {\n try {\n const pathname = url.pathname + url.search;\n navigate(pathname);\n return;\n } catch {\n // Fall through to hard redirect\n }\n }\n\n // Hard redirect as fallback\n window.location.href = finalUrl;\n}\n\n/**\n * Process Split URL tests and redirect if needed\n * This is called automatically from ElevateProvider on mount.\n *\n * @returns true if a redirect was triggered, false otherwise\n */\nexport function processSplitUrlTests(\n config: ElevateConfig,\n previewState?: PreviewState | null | undefined\n): boolean {\n if (typeof window === \"undefined\") return false;\n\n const currentUrl = window.location.href;\n\n // Check if already redirected (abtr param present)\n const urlParams = new URLSearchParams(window.location.search);\n if (urlParams.get(\"abtr\") === \"true\") {\n return false;\n }\n\n // Find matching Split URL test\n const match = findMatchingSplitUrlTest(config, currentUrl);\n if (!match) return false;\n\n const { test, matchedVariation, isControlUrl } = match;\n const testId = test.testId;\n const redirectBehavior = getRedirectBehavior(test);\n\n const assignedVariationId = getAssignedVariant(testId);\n if (!assignedVariationId) return false;\n\n const assignedVariation = test.variations.find(\n (v) => v.id === assignedVariationId\n );\n if (!assignedVariation) return false;\n\n // Get target URLs for assigned variation\n const targetUrls = (assignedVariation as any).splitUrlTestLinks || [];\n if (targetUrls.length === 0) return false;\n\n // Check if already on assigned variation's URL\n if (urlMatches(currentUrl, targetUrls)) {\n return false; // Already on correct URL\n }\n\n // Apply redirect behavior rules\n switch (redirectBehavior) {\n case \"redirectOnlyControlUrl\":\n // Only redirect when on control URL\n if (!isControlUrl) return false;\n if (assignedVariation.isControl) return false; // Assigned to control, no redirect needed\n break;\n\n case \"redirectOnce\":\n // Redirect once from any URL\n if (hasBeenRedirected(testId)) return false;\n markAsRedirected(testId);\n break;\n\n case \"redirectOnceControlUrlOnly\":\n // Redirect once, but only from control URL\n if (!isControlUrl) return false;\n if (hasBeenRedirected(testId)) return false;\n if (assignedVariation.isControl) return false;\n markAsRedirected(testId);\n break;\n\n case \"redirectAll\":\n default:\n // Always redirect to assigned variation URL\n break;\n }\n\n // Perform the redirect\n const targetUrl = targetUrls[0];\n performRedirect(targetUrl);\n return true;\n}\n\n/**\n * Check if there are any active split URL tests\n */\nexport function hasSplitUrlTests(config: ElevateConfig): boolean {\n return config.tests.some((t) => t.type === \"SPLIT_URL\" && t.enabled);\n}\n\n/**\n * Setup event listeners for split URL test re-processing.\n *\n * Listens for ALL SPA navigation (pushState, replaceState, popstate)\n * and re-checks whether a redirect is needed on the new URL.\n */\nexport function setupSplitUrlTestListeners(\n config: ElevateConfig,\n previewState?: PreviewState | null\n): () => void {\n if (typeof window === \"undefined\") return () => {};\n\n // Handle ALL SPA navigation (pushState, replaceState, popstate)\n const cleanupNavigation = onNavigate(() => {\n processSplitUrlTests(config, previewState);\n });\n\n return () => {\n cleanupNavigation();\n };\n}\n\nexport const splitUrlHandler = {\n type: \"SPLIT_URL\",\n\n shouldActivate(test: Test, context: { currentUrl: string }): boolean {\n for (const variation of test.variations) {\n const urls = (variation as any).splitUrlTestLinks || [];\n if (urlMatches(context.currentUrl, urls)) {\n return true;\n }\n }\n return false;\n },\n\n getVariantData() {\n return { handlerActivated: true };\n },\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 * Content Test Utilities\n *\n * Handles DOM manipulation for Content Tests (Visual Editor Experiments).\n * Automatically applies changes, injects elements, and runs custom code\n * based on the assigned variant's content configuration.\n */\n\nimport type {\n ContentChange,\n ContentElement,\n ContentCustomCode,\n ContentTestData,\n ElevateConfig,\n Test,\n Variation,\n} from \"../types\";\nimport type { PreviewState } from \"./preview\";\nimport { getAssignedVariant } from \"./assignment\";\nimport { onNavigate } from \"./navigation\";\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst ELEMENT_ID_PREFIX = \"elv-el-\";\nconst NODE_TYPES = {\n ELEMENT_NODE: 1,\n TEXT_NODE: 3,\n};\n\n// ============================================================================\n// STATE MANAGEMENT\n// ============================================================================\n\n/** Tracks original elements for restoration on responsive changes */\nconst originalElements: Map<string, HTMLElement> = new Map();\n\n/** Tracks injected style elements for cleanup */\nconst injectedStyles: Map<string, HTMLStyleElement> = new Map();\n\n/** Tracks injected script elements for cleanup */\nconst injectedScripts: Map<string, HTMLScriptElement> = new Map();\n\n/** Tracks injected content elements for cleanup */\nconst injectedElements: Map<string, HTMLElement> = new Map();\n\n/** Current device type (for responsive changes) */\nlet isMobile = false;\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\n/**\n * Check if a pathname matches a wildcard pattern\n * Supports: \"*\" (all), \"*\\/products\\/*\" (contains), \"*.json\" (ends with)\n */\nexport function matchesWildcardPattern(\n pathname: string,\n pattern: string\n): boolean {\n // Match all\n if (pattern === \"*\") return true;\n\n // Contains pattern: *something*\n if (pattern.startsWith(\"*\") && pattern.endsWith(\"*\")) {\n const trimmedPattern = pattern.slice(1, -1);\n return pathname.includes(trimmedPattern);\n }\n\n // Ends with pattern: *something\n if (pattern.startsWith(\"*\")) {\n const suffix = pattern.slice(1);\n return pathname.endsWith(suffix);\n }\n\n // Exact match\n return pattern === pathname;\n}\n\n/**\n * Check if current device is mobile (viewport width < 768px)\n */\nfunction checkIsMobile(): boolean {\n if (typeof window === \"undefined\") return false;\n return window.innerWidth < 768;\n}\n\n/**\n * Convert camelCase to kebab-case for CSS properties\n */\nfunction convertToKebabCase(text: string): string {\n let result = \"\";\n for (let i = 0; i < text.length; i++) {\n const char = text[i];\n if (char >= \"A\" && char <= \"Z\") {\n if (i !== 0) result += \"-\";\n result += char.toLowerCase();\n } else {\n result += char;\n }\n }\n return result;\n}\n\n/**\n * Check if a node is a text node\n */\nfunction isTextNode(node: Node): boolean {\n return node.nodeType === NODE_TYPES.TEXT_NODE;\n}\n\n/**\n * Check if a node is an element node\n */\nfunction isElementNode(node: Node): node is HTMLElement {\n return node.nodeType === NODE_TYPES.ELEMENT_NODE;\n}\n\n/**\n * Store original element for later restoration\n */\nfunction storeOriginalElement(selector: string, element: HTMLElement): void {\n if (!originalElements.has(selector)) {\n originalElements.set(selector, element.cloneNode(true) as HTMLElement);\n }\n}\n\n/**\n * Decode escaped characters in code strings\n */\nfunction decodeCodeString(code: string): string {\n let decoded = code;\n\n // Replace HTML entities (using replace with regex for ES2020 compatibility)\n decoded = decoded.replace(/&#96;/g, \"`\").replace(/&#36;/g, \"$\");\n\n // Try JSON decode for escaped characters\n try {\n decoded = JSON.parse('\"' + decoded + '\"');\n } catch {\n // If JSON.parse fails, use the partially decoded version\n }\n\n return decoded;\n}\n\n// ============================================================================\n// CONTENT CHANGES (Text, Styles, Attributes)\n// ============================================================================\n\n/**\n * Apply content changes to existing elements\n */\nexport function applyContentChanges(changes: ContentChange[]): void {\n if (typeof window === \"undefined\" || changes.length === 0) return;\n\n const currentPathname = window.location.pathname;\n\n // Filter changes that match current pathname\n const matchingChanges = changes.filter((change) => {\n if (!change.pathnames?.length) return true;\n return change.pathnames.some((pathname) =>\n matchesWildcardPattern(currentPathname, pathname)\n );\n });\n\n // Detect mobile/desktop\n isMobile = checkIsMobile();\n\n // Process each change with responsive support\n const processedChanges = matchingChanges.map((change) => ({\n ...change,\n // Merge responsive styles (lg + sm if mobile)\n style: change.style\n ? { ...change.style.lg, ...(isMobile && change.style.sm) }\n : undefined,\n // Select responsive content\n content:\n change.content && isMobile && change.content.sm\n ? change.content.sm\n : change.content?.lg,\n }));\n\n // Apply text content changes\n processedChanges\n .filter((change) => change.content !== undefined)\n .forEach((change) => {\n const elements = change.applyAll\n ? Array.from(document.querySelectorAll(change.selector))\n : [document.querySelector(change.selector)].filter(Boolean);\n\n elements.forEach((element, index) => {\n if (!element) return;\n\n const uniqueSelector = change.applyAll\n ? `${change.selector}-${index}-${\n element.id || Math.random().toString(36).substr(2, 9)\n }`\n : change.selector;\n\n storeOriginalElement(uniqueSelector, element as HTMLElement);\n\n // Apply content\n if (isTextNode(element)) {\n (element as unknown as Text).nodeValue = change.content!;\n } else if (isElementNode(element)) {\n element.innerHTML = change.content!;\n }\n });\n });\n\n // Apply style changes\n processedChanges\n .filter((change) => change.style !== undefined)\n .forEach((change) => {\n const element = document.querySelector(change.selector) as HTMLElement;\n if (!element || !isElementNode(element)) return;\n\n storeOriginalElement(change.selector, element);\n\n Object.entries(change.style!).forEach(([prop, value]) => {\n const kebabProp = convertToKebabCase(prop);\n const hasImportant = value.toString().includes(\"!important\");\n const cleanValue = value.toString().replace(/\\s*!important\\s*$/, \"\");\n\n element.style.setProperty(\n kebabProp,\n cleanValue,\n hasImportant ? \"important\" : undefined\n );\n });\n });\n\n // Apply attribute changes\n processedChanges\n .filter((change) => change.attributes !== undefined)\n .forEach((change) => {\n const element = document.querySelector(change.selector) as HTMLElement;\n if (!element || !isElementNode(element)) return;\n\n storeOriginalElement(change.selector, element);\n\n Object.entries(change.attributes!).forEach(([key, value]) => {\n element.setAttribute(key, value);\n });\n });\n}\n\n// ============================================================================\n// CONTENT ELEMENTS (HTML Injection)\n// ============================================================================\n\n/**\n * Apply content elements (inject new HTML elements)\n */\nexport function applyContentElements(elements: ContentElement[]): void {\n if (typeof window === \"undefined\" || elements.length === 0) return;\n\n const currentPathname = window.location.pathname;\n\n // Filter elements that match current pathname\n const matchingElements = elements.filter((element) => {\n if (!element.selector?.pathnames?.length) return true;\n return element.selector.pathnames.some((pathname) =>\n matchesWildcardPattern(currentPathname, pathname)\n );\n });\n\n matchingElements.forEach((element) => {\n const elementId = `${ELEMENT_ID_PREFIX}${element.id}`;\n\n // Skip if already injected\n if (injectedElements.has(elementId)) return;\n\n const target = document.querySelector(element.selector.target);\n if (!target) return;\n\n // Check if element already exists in DOM\n if (target.parentElement?.querySelector(`#${elementId}`)) return;\n\n // Create the DOM element\n const domElement = createDOMElement(element);\n\n // Insert based on placement\n if (element.selector.placement === \"after\") {\n target.after(domElement);\n } else if (element.selector.placement === \"before\") {\n target.before(domElement);\n }\n\n // Track for cleanup\n injectedElements.set(elementId, domElement);\n });\n}\n\n/**\n * Create a DOM element from ContentElement definition\n */\nfunction createDOMElement(element: ContentElement): HTMLElement {\n const domElement = document.createElement(element.tagName);\n\n domElement.id = `${ELEMENT_ID_PREFIX}${element.id}`;\n\n // Apply styles\n if (element.style) {\n Object.assign(domElement.style, element.style);\n }\n\n // Apply attributes\n if (element.attributes) {\n Object.entries(element.attributes).forEach(([key, value]) => {\n domElement.setAttribute(key, value);\n });\n }\n\n // Set content for text elements\n if (element.kind === \"text\" && element.content) {\n domElement.innerText = element.content;\n }\n\n // Recursively render children for containers\n if (element.kind === \"container\" && element.childrens) {\n element.childrens.forEach((child) => {\n const childElement = createDOMElement(child);\n domElement.appendChild(childElement);\n });\n }\n\n return domElement;\n}\n\n// ============================================================================\n// CUSTOM CODE (CSS/JS Injection)\n// ============================================================================\n\n/**\n * Apply custom code (CSS and JS injection)\n */\nexport function applyCustomCode(\n customCodes: ContentCustomCode[],\n nonce?: string\n): void {\n if (typeof window === \"undefined\" || customCodes.length === 0) return;\n\n const currentPathname = window.location.pathname;\n\n // Filter codes that match current pathname\n const matchingCodes = customCodes.filter((code) => {\n // If no pathnames specified, run on all pages\n if (!code.pathnames?.length) return true;\n\n // Check excludes first\n if (code.excludePathnames?.length) {\n const isExcluded = code.excludePathnames.some((pathname) =>\n matchesWildcardPattern(currentPathname, pathname)\n );\n if (isExcluded) return false;\n }\n\n // Check includes\n return code.pathnames.some((pathname) =>\n matchesWildcardPattern(currentPathname, pathname)\n );\n });\n\n matchingCodes.forEach((code) => {\n if (!code.id) return;\n\n // Inject CSS\n if (code.css) {\n injectCSS(code.id, code.css);\n }\n\n // Inject JS\n if (code.js) {\n injectJS(code.id, code.js, nonce);\n }\n });\n}\n\n/**\n * Inject CSS into the page\n */\nfunction injectCSS(id: string, css: string): void {\n const styleId = `css-${id}`;\n\n // Skip if already injected\n if (injectedStyles.has(styleId) || document.getElementById(styleId)) return;\n\n const styleElement = document.createElement(\"style\");\n styleElement.id = styleId;\n styleElement.textContent = decodeCodeString(css);\n\n document.head.appendChild(styleElement);\n injectedStyles.set(styleId, styleElement);\n}\n\n/**\n * Inject JavaScript into the page\n */\nfunction injectJS(id: string, js: string, nonce?: string): void {\n const scriptId = `js-${id}`;\n\n // Skip if already injected\n if (injectedScripts.has(scriptId) || document.getElementById(scriptId))\n return;\n\n const scriptElement = document.createElement(\"script\");\n scriptElement.id = scriptId;\n\n // Add nonce for CSP compliance\n if (nonce) {\n scriptElement.setAttribute(\"nonce\", nonce);\n }\n\n // Wrap in try-catch for safety\n const decodedJs = decodeCodeString(js);\n scriptElement.textContent =\n \"(function(){try{\" +\n decodedJs +\n \"}catch(e){console.error('[ElevateAB] Custom script error:', e)}})();\";\n\n document.head.appendChild(scriptElement);\n injectedScripts.set(scriptId, scriptElement);\n}\n\n// ============================================================================\n// CLEANUP FUNCTIONS\n// ============================================================================\n\n/**\n * Restore original elements (for responsive changes or cleanup)\n */\nexport function restoreOriginalElements(): void {\n originalElements.forEach((originalElement, selector) => {\n // Try to find the element by selector\n const target = document.querySelector(selector);\n if (target) {\n target.replaceWith(originalElement.cloneNode(true));\n }\n });\n}\n\n/**\n * Clean up all injected content\n */\nexport function cleanupContentTests(): void {\n // Remove injected styles\n injectedStyles.forEach((style) => style.remove());\n injectedStyles.clear();\n\n // Remove injected scripts\n injectedScripts.forEach((script) => script.remove());\n injectedScripts.clear();\n\n // Remove injected elements\n injectedElements.forEach((element) => element.remove());\n injectedElements.clear();\n\n // Clear original elements cache\n originalElements.clear();\n}\n\n// ============================================================================\n// MAIN PROCESSOR\n// ============================================================================\n\n/**\n * Collect all content test data from assigned variants\n */\nfunction collectContentTestData(\n config: ElevateConfig,\n previewState?: PreviewState | null\n): ContentTestData {\n const allChanges: ContentChange[] = [];\n const allElements: ContentElement[] = [];\n const allCustomCodes: ContentCustomCode[] = [];\n\n // Find all CONTENT type tests\n const contentTests = config.tests.filter(\n (test) => test.type === \"CONTENT\" && test.enabled\n );\n\n contentTests.forEach((test) => {\n const assignedVariantId = getAssignedVariant(test.testId);\n if (!assignedVariantId) return;\n\n const variation = test.variations.find((v) => v.id === assignedVariantId);\n if (!variation?.content) return;\n\n // Collect content data\n if (variation.content.changes?.length) {\n allChanges.push(...variation.content.changes);\n }\n if (variation.content.elements?.length) {\n allElements.push(...variation.content.elements);\n }\n if (variation.content.customCodes?.length) {\n allCustomCodes.push(...variation.content.customCodes);\n }\n });\n\n return {\n changes: allChanges,\n elements: allElements,\n blocks: [], // Blocks deferred to v2\n customCodes: allCustomCodes,\n };\n}\n\n/**\n * Check if there are any active content tests\n */\nexport function hasContentTests(config: ElevateConfig): boolean {\n return config.tests.some((test) => test.type === \"CONTENT\" && test.enabled);\n}\n\n/**\n * Process and apply all content tests\n * This is the main entry point called by ElevateProvider\n */\nexport function processContentTests(\n config: ElevateConfig,\n previewState?: PreviewState | null,\n nonce?: string\n): void {\n if (typeof window === \"undefined\") return;\n\n // Check if there are any content tests\n if (!hasContentTests(config)) return;\n\n // Collect all content data from assigned variants\n const contentData = collectContentTestData(config, previewState);\n\n // Check if there's anything to apply\n const hasContent =\n contentData.changes.length > 0 ||\n contentData.elements.length > 0 ||\n contentData.customCodes.length > 0;\n\n if (!hasContent) return;\n\n // Apply content changes\n applyContentChanges(contentData.changes);\n applyContentElements(contentData.elements);\n applyCustomCode(contentData.customCodes, nonce);\n}\n\n/**\n * Re-process content tests (called on pathname change or resize)\n */\nexport function reprocessContentTests(\n config: ElevateConfig,\n previewState?: PreviewState | null,\n nonce?: string\n): void {\n if (typeof window === \"undefined\") return;\n\n // Check for responsive change\n const currentIsMobile = checkIsMobile();\n if (currentIsMobile !== isMobile) {\n // Responsive change - restore and reapply\n restoreOriginalElements();\n isMobile = currentIsMobile;\n }\n\n // Re-process content tests\n processContentTests(config, previewState, nonce);\n}\n\n/**\n * Setup event listeners for content test re-processing.\n *\n * Listens for ALL SPA navigation (pushState, replaceState, popstate)\n * via the shared navigation utility, plus resize for responsive changes.\n */\nexport function setupContentTestListeners(\n config: ElevateConfig,\n previewState?: PreviewState | null,\n nonce?: string,\n listenToNavigation = true\n): () => void {\n if (typeof window === \"undefined\") return () => {};\n\n // Handle resize for responsive changes\n const handleResize = () => {\n const currentIsMobile = checkIsMobile();\n if (currentIsMobile !== isMobile) {\n reprocessContentTests(config, previewState, nonce);\n }\n };\n\n window.addEventListener(\"resize\", handleResize);\n\n let cleanupNavigation = () => {};\n if (listenToNavigation) {\n // Handle ALL SPA navigation (pushState, replaceState, popstate)\n cleanupNavigation = onNavigate(() => {\n reprocessContentTests(config, previewState, nonce);\n });\n }\n\n // Return cleanup function\n return () => {\n window.removeEventListener(\"resize\", handleResize);\n cleanupNavigation();\n cleanupContentTests();\n };\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 { getAssignedVariant } from \"../utils/assignment\";\nimport { trackViews } from \"../utils/tracking\";\nimport { getPreviewState } from \"../utils/preview\";\nimport { getHandler } from \"../handlers/registry\";\nimport type { HandlerContext } from \"../handlers/types\";\n\n// Import handlers to trigger auto-registration\nimport \"../handlers\";\n\nexport interface UseExperimentResult {\n /** The full variant object with handler data (null if not assigned) */\n variant: (Variation & Record<string, unknown>) | 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 * Build handler context from current environment\n */\nfunction buildHandlerContext(selectors?: unknown): HandlerContext {\n if (typeof window === \"undefined\") {\n return { currentUrl: \"\", selectors };\n }\n\n const url = window.location.href;\n const pathname = window.location.pathname;\n\n // Extract product info from URL\n let productHandle: string | undefined;\n const productMatch = pathname.match(/\\/products\\/([^\\/\\?]+)/);\n if (productMatch) {\n productHandle = productMatch[1];\n }\n\n // Try to detect variant ID from URL params\n let variantId: string | undefined;\n const urlParams = new URLSearchParams(window.location.search);\n const variantParam = urlParams.get(\"variant\");\n if (variantParam) {\n variantId = variantParam;\n }\n\n return {\n currentUrl: url,\n productHandle,\n variantId,\n selectors,\n currencyCode: \"USD\", // Default, could be detected from page\n };\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 {\n config,\n previewState: contextPreviewState,\n selectors,\n } = useElevateConfig();\n const [variant, setVariant] = React.useState<\n (Variation & Record<string, unknown>) | null\n >(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 // Get preview state - use context or get fresh\n const previewState = React.useMemo(() => {\n return contextPreviewState || getPreviewState();\n }, [contextPreviewState]);\n\n // Track if we've already warned about this test (prevent duplicate warnings)\n const hasWarnedRef = React.useRef<string | null>(null);\n // Track if effect has been applied (prevent duplicate DOM updates)\n const effectAppliedRef = React.useRef<string | null>(null);\n\n React.useEffect(() => {\n let isMounted = true;\n\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 if (isMounted) setIsLoading(false);\n return;\n }\n\n // Test exists but is disabled (but allow preview mode to override)\n if (!testConfig.enabled && !previewState?.isPreview) {\n if (hasWarnedRef.current !== `${testId}-disabled`) {\n hasWarnedRef.current = `${testId}-disabled`;\n console.warn(`[ElevateAB] Test disabled: ${testId}`);\n }\n if (isMounted) setIsLoading(false);\n return;\n }\n\n const assignedVariantId = getAssignedVariant(testId);\n if (!assignedVariantId) {\n if (isMounted) setIsLoading(false);\n return;\n }\n\n const assigned = testConfig.variations.find(\n (v) => v.id === assignedVariantId\n );\n if (!assigned) {\n if (isMounted) setIsLoading(false);\n return;\n }\n\n const handler = getHandler(testConfig.type);\n const context = buildHandlerContext(selectors);\n\n let enrichedVariant: Variation & Record<string, unknown> = {\n ...assigned,\n };\n\n if (handler?.shouldActivate(testConfig, context)) {\n const effectKey = `${testId}-${assigned.id}`;\n if (handler.applyEffect && effectAppliedRef.current !== effectKey) {\n effectAppliedRef.current = effectKey;\n handler.applyEffect(testConfig, assigned, context);\n }\n\n const handlerData = handler.getVariantData(testConfig, assigned, context);\n enrichedVariant = { ...enrichedVariant, ...handlerData };\n }\n\n if (isMounted) setVariant(enrichedVariant);\n\n if (!previewState?.isPreview) {\n trackViews(testId);\n }\n\n if (isMounted) setIsLoading(false);\n\n return () => {\n isMounted = false;\n };\n }, [config, testConfig, testId, previewState, selectors]);\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 type { TestTypeHandler } from \"./types\";\n\nconst handlers: Map<string, TestTypeHandler> = new Map();\n\nexport function registerHandler(handler: TestTypeHandler): void {\n handlers.set(handler.type, handler);\n}\n\nexport function getHandler(type: string): TestTypeHandler | undefined {\n return handlers.get(type);\n}\n\nexport function getAllHandlers(): TestTypeHandler[] {\n return Array.from(handlers.values());\n}\n\nexport function hasHandler(type: string): boolean {\n return handlers.has(type);\n}\n","\nimport type { Test, Variation } from \"../types\";\nimport type {\n TestTypeHandler,\n HandlerContext,\n HandlerVariantData,\n} from \"./types\";\n\nfunction extractProductHandleFromUrl(url: string): string | null {\n try {\n const urlObj = new URL(url, \"https://example.com\");\n const pathname = urlObj.pathname;\n\n // Match /products/{handle}\n const match = pathname.match(/\\/products\\/([^\\/\\?]+)/);\n return match ? match[1] : null;\n } catch {\n return null;\n }\n}\n\nfunction updatePriceElements(\n variation: Variation,\n variantId: string | undefined,\n currencyCode: string,\n selectors: unknown,\n): void {\n if (typeof window === \"undefined\") return;\n if (!variation.prices || !variantId) return;\n\n const variantPrices = variation.prices[variantId];\n if (!variantPrices) return;\n\n const priceAmount = variantPrices.price?.[currencyCode];\n const compareAmount = variantPrices.compare?.[currencyCode];\n\n // Get selectors configuration\n const selectorsConfig = selectors as {\n selectorsV2?: Array<{\n price?: string;\n compareAt?: string;\n saving?: Array<{ selector: string; isPercentage?: boolean }>;\n }>;\n };\n\n if (!selectorsConfig?.selectorsV2?.length) return;\n\n // Update each selector set\n for (const selectorSet of selectorsConfig.selectorsV2) {\n // Update price elements\n if (selectorSet.price && priceAmount) {\n const priceSelector = selectorSet.price.replace(\n \"{{variant}}\",\n variantId,\n );\n const priceElements = document.querySelectorAll(priceSelector);\n priceElements.forEach((el) => {\n // Format price with currency\n const formattedPrice = new Intl.NumberFormat(\"en-US\", {\n style: \"currency\",\n currency: currencyCode,\n }).format(parseFloat(priceAmount));\n el.textContent = formattedPrice;\n });\n }\n\n // Update compare-at price elements\n if (selectorSet.compareAt && compareAmount) {\n const compareSelector = selectorSet.compareAt.replace(\n \"{{variant}}\",\n variantId,\n );\n const compareElements = document.querySelectorAll(compareSelector);\n compareElements.forEach((el) => {\n const formattedPrice = new Intl.NumberFormat(\"en-US\", {\n style: \"currency\",\n currency: currencyCode,\n }).format(parseFloat(compareAmount));\n el.textContent = formattedPrice;\n });\n }\n\n // Update saving elements\n if (selectorSet.saving && priceAmount && compareAmount) {\n for (const savingConfig of selectorSet.saving) {\n const savingSelector = savingConfig.selector.replace(\n \"{{variant}}\",\n variantId,\n );\n const savingElements = document.querySelectorAll(savingSelector);\n savingElements.forEach((el) => {\n const price = parseFloat(priceAmount);\n const compare = parseFloat(compareAmount);\n const saving = compare - price;\n\n if (savingConfig.isPercentage) {\n const percentage = Math.round((saving / compare) * 100);\n el.textContent = `${percentage}%`;\n } else {\n const formattedSaving = new Intl.NumberFormat(\"en-US\", {\n style: \"currency\",\n currency: currencyCode,\n }).format(saving);\n el.textContent = formattedSaving;\n }\n });\n }\n }\n }\n}\n\nexport const pricePlusHandler: TestTypeHandler = {\n type: \"PRICE_PLUS\",\n\n shouldActivate(test: Test, context: HandlerContext): boolean {\n const testData = (test as any).data;\n if (!testData) return false;\n\n // Get product identifiers from context\n const { productId, productHandle } = context;\n\n // Auto-detect from URL if not provided\n const urlHandle =\n productHandle || extractProductHandleFromUrl(context.currentUrl);\n\n // Check if this product matches the test\n const matchesProductId =\n productId && testData.productIds?.includes(productId);\n const matchesHandle = urlHandle && testData.handles?.includes(urlHandle);\n\n return matchesProductId || matchesHandle;\n },\n\n applyEffect(\n test: Test,\n variation: Variation,\n context: HandlerContext,\n ): void {\n // Update DOM elements using selectors\n updatePriceElements(\n variation,\n context.variantId,\n context.currencyCode || \"USD\",\n context.selectors,\n );\n },\n\n getVariantData(\n test: Test,\n variation: Variation,\n context: HandlerContext,\n ): HandlerVariantData {\n const testData = (test as any).data;\n\n // Return prices for manual rendering\n return {\n prices: variation.prices,\n matchedProductId: context.productId,\n productIds: testData?.productIds,\n handles: testData?.handles,\n handlerActivated: true,\n };\n },\n};\n","/**\n * Content Test Handler\n *\n * Handles CONTENT type tests (Visual Editor Experiments).\n * These tests modify page content without redirects - they can:\n * - Remove/hide elements\n * - Modify text/headings/descriptions\n * - Change CSS styles (colors, fonts, sizes)\n * - Add new HTML elements\n * - Modify attributes (href, src, etc.)\n * - Inject custom CSS/JS\n */\n\nimport type { Test, Variation } from \"../types\";\nimport type {\n TestTypeHandler,\n HandlerContext,\n HandlerVariantData,\n} from \"./types\";\nimport { matchesWildcardPattern } from \"../utils/content\";\n\n/**\n * Check if current URL matches any of the test's pathnames\n */\nfunction matchesTestPathnames(test: Test, currentUrl: string): boolean {\n const testData = (test as any).data;\n const pathnames: string[] = testData?.pathnames || [];\n\n // If no pathnames specified, match all pages\n if (!pathnames.length) return true;\n\n try {\n const url = new URL(currentUrl, \"https://example.com\");\n const pathname = url.pathname;\n\n // Check if any pathname pattern matches\n return pathnames.some((pattern) =>\n matchesWildcardPattern(pathname, pattern)\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Content test handler\n *\n * Note: Content test effects are applied by processContentTests in ElevateProvider,\n * not through the handler's applyEffect. This ensures all content tests are\n * processed together after variant assignment for better coordination.\n */\nexport const contentHandler: TestTypeHandler = {\n type: \"CONTENT\",\n\n shouldActivate(test: Test, context: HandlerContext): boolean {\n return matchesTestPathnames(test, context.currentUrl);\n },\n\n getVariantData(\n test: Test,\n variation: Variation,\n _context: HandlerContext\n ): HandlerVariantData {\n const testData = (test as any).data;\n\n return {\n content: variation.content,\n pathnames: testData?.pathnames,\n handlerActivated: true,\n };\n },\n};\n","/**\n * Custom Code Test Handler\n *\n * Handles CUSTOM_CODE type tests.\n * These tests inject custom CSS/JS into the page based on pathname matching.\n * Both control and non-control variations can have custom code.\n */\n\nimport type { Test, Variation } from \"../types\";\nimport type {\n TestTypeHandler,\n HandlerContext,\n HandlerVariantData,\n} from \"./types\";\nimport { matchesWildcardPattern } from \"../utils/content\";\n\n/**\n * Check if current URL matches any of the test's pathnames\n */\nfunction matchesTestPathnames(test: Test, currentUrl: string): boolean {\n const testData = (test as any).data;\n const pathnames: string[] = testData?.pathnames || [];\n\n // If no pathnames specified, match all pages\n if (!pathnames.length) return true;\n\n try {\n const url = new URL(currentUrl, \"https://example.com\");\n const pathname = url.pathname;\n\n // Check if any pathname pattern matches\n return pathnames.some((pattern) =>\n matchesWildcardPattern(pathname, pattern)\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Custom code test handler\n *\n * Note: Custom code effects are applied by processCustomCodeTests in ElevateProvider,\n * not through the handler's applyEffect. This ensures all custom code tests are\n * processed together after variant assignment for better coordination.\n */\nexport const customCodeHandler: TestTypeHandler = {\n type: \"CUSTOM_CODE\",\n\n shouldActivate(test: Test, context: HandlerContext): boolean {\n return matchesTestPathnames(test, context.currentUrl);\n },\n\n getVariantData(\n test: Test,\n variation: Variation,\n _context: HandlerContext\n ): HandlerVariantData {\n const testData = (test as any).data;\n\n return {\n customCode: variation.customCode,\n pathnames: testData?.pathnames,\n excludePathnames: testData?.excludePathnames,\n handlerActivated: true,\n };\n },\n};\n","export type {\n TestTypeHandler,\n HandlerContext,\n HandlerVariantData,\n} from \"./types\";\n\nexport {\n registerHandler,\n getHandler,\n getAllHandlers,\n hasHandler,\n} from \"./registry\";\n\nexport { pricePlusHandler } from \"./pricePlus\";\nexport { splitUrlHandler, processSplitUrlTests } from \"./splitUrl\";\nexport { contentHandler } from \"./content\";\nexport { customCodeHandler } from \"./customCode\";\n\nimport { registerHandler } from \"./registry\";\nimport { pricePlusHandler } from \"./pricePlus\";\nimport { splitUrlHandler } from \"./splitUrl\";\nimport { contentHandler } from \"./content\";\nimport { customCodeHandler } from \"./customCode\";\n\nregisterHandler(pricePlusHandler);\nregisterHandler(splitUrlHandler);\nregisterHandler(contentHandler);\nregisterHandler(customCodeHandler);","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 {\n processSplitUrlTests,\n getSplitUrlBlockingScript,\n hasSplitUrlTests,\n} from \"../handlers/splitUrl\";\nimport {\n processContentTests,\n setupContentTestListeners,\n hasContentTests,\n} from \"../utils/content\";\nimport {\n processCustomCodeTests,\n hasCustomCodeTests,\n} from \"../utils/customCode\";\nimport { assignAllTests } from \"../utils/assignment\";\nimport { onNavigate } from \"../utils/navigation\";\n\nconst CDN_BASE_URL = \"https://ds0wlyksfn0sb.cloudfront.net/headless\";\n\nfunction getStoreName(storeId: string): string {\n return storeId.replace(/^https?:\\/\\//, \"\").replace(\".myshopify.com\", \"\");\n}\n\nfunction getConfigUrl(storeId: string): string {\n return `${CDN_BASE_URL}/${getStoreName(storeId)}.js`;\n}\n\ninterface ElevateProviderSimpleProps {\n storeId: string;\n storefrontAccessToken?: string;\n preventFlickering?: boolean;\n flickerTimeout?: number;\n nonce?: string;\n children: React.ReactNode;\n}\n\nconst useIsomorphicLayoutEffect =\n typeof window !== \"undefined\" ? React.useLayoutEffect : React.useEffect;\n\nexport function ElevateProvider({\n storeId,\n storefrontAccessToken,\n preventFlickering = false,\n flickerTimeout = 3000,\n nonce,\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\n React.useEffect(() => {\n if (typeof window === \"undefined\") return;\n\n initAnalytics({\n storeId,\n storefrontAccessToken,\n });\n\n const preview = getPreviewState();\n setPreviewState(preview);\n\n const country = getCountryCode();\n setCountryCode(country);\n }, [storeId, storefrontAccessToken]);\n\n const revealContent = useCallback(() => {\n if (typeof window !== \"undefined\" && (window as any).__eab_reveal) {\n (window as any).__eab_reveal();\n }\n }, []);\n\n React.useEffect(() => {\n async function fetchConfig() {\n try {\n const currentPreviewState =\n typeof window !== \"undefined\" ? getPreviewState() : null;\n\n let backendData: BackendConfig | null = null;\n\n if (typeof window !== \"undefined\" && (window as any).eab_data) {\n backendData = (window as any).eab_data;\n } else {\n const url = getConfigUrl(storeId);\n\n backendData = await new Promise((resolve, reject) => {\n const script = document.createElement(\"script\");\n script.src = url;\n script.async = true;\n\n script.onload = () => {\n const data = (window as any).eab_data;\n script.remove();\n\n if (!data) {\n reject(\n new Error(\n \"Failed to load config: window.eab_data is undefined\",\n ),\n );\n } else {\n resolve(data);\n }\n };\n\n script.onerror = () => {\n script.remove();\n resolve({\n allTests: {},\n selectors: { selectorsV2: [] },\n } as BackendConfig);\n };\n\n document.head.appendChild(script);\n });\n }\n\n if (\n !backendData ||\n !backendData.allTests ||\n Object.keys(backendData.allTests).length === 0\n ) {\n setConfig({ tests: [], selectors: undefined });\n revealContent();\n return;\n }\n\n if (backendData.subscriptionPaused) {\n console.error(\n `[ElevateAB] Subscription is paused or stopped for store: ${storeId}. ` +\n (backendData.subscriptionMessage ||\n \"A/B tests will not run. Please reactivate your subscription.\"),\n );\n setConfig({ tests: [], selectors: undefined });\n revealContent();\n return;\n }\n\n const hasActiveTests =\n Object.keys(backendData.allTests || {}).length > 0;\n if (!hasActiveTests) {\n setConfig({ tests: [], selectors: undefined });\n revealContent();\n return;\n }\n\n const parsedConfig = parseBackendConfig(backendData);\n\n assignAllTests(parsedConfig, currentPreviewState);\n\n if (parsedConfig.tests.some((t) => t.type === \"SPLIT_URL\")) {\n const redirected = processSplitUrlTests(\n parsedConfig,\n currentPreviewState,\n );\n if (redirected) return;\n }\n\n setConfig(parsedConfig);\n revealContent();\n } catch (err) {\n console.error(\"[ElevateAB] Failed to load config:\", err);\n setConfig({ tests: [], selectors: undefined });\n revealContent();\n }\n }\n\n fetchConfig();\n }, [storeId, revealContent]);\n\n useIsomorphicLayoutEffect(() => {\n if (!config || typeof window === \"undefined\") return;\n\n const applyAllTests = () => {\n // Split URL tests run first because they can redirect.\n if (hasSplitUrlTests(config)) {\n const redirected = processSplitUrlTests(config, previewState);\n if (redirected) return;\n }\n\n if (hasContentTests(config)) {\n processContentTests(config, previewState, nonce);\n }\n\n if (hasCustomCodeTests(config)) {\n processCustomCodeTests(config, previewState, nonce);\n }\n };\n\n // Apply on initial load with parsed config.\n applyAllTests();\n\n // Re-apply all tests on SPA navigation.\n const cleanupNavigation = onNavigate(() => {\n applyAllTests();\n });\n\n // Keep content resize handling, but avoid duplicate navigation listeners.\n const cleanupContent = hasContentTests(config)\n ? setupContentTestListeners(config, previewState, nonce, false)\n : () => {};\n\n return () => {\n cleanupNavigation();\n cleanupContent();\n };\n }, [config, previewState, nonce]);\n\n const value = React.useMemo(\n () => ({\n config,\n storeId,\n storefrontAccessToken,\n isPreviewMode: previewState?.isPreview ?? false,\n previewTestId: previewState?.previewTestId ?? null,\n previewState,\n selectors: config?.selectors,\n countryCode,\n }),\n [config, storeId, storefrontAccessToken, previewState, countryCode],\n );\n\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. Orders won't be attributed to test variants.\",\n );\n }\n }, [storefrontAccessToken]);\n\n const configUrl = React.useMemo(() => getConfigUrl(storeId), [storeId]);\n\n const blockingScript = React.useMemo(() => {\n const timeout = preventFlickering ? flickerTimeout : 2000;\n return getSplitUrlBlockingScript({ configUrl, timeout });\n }, [configUrl, preventFlickering, flickerTimeout]);\n\n return (\n <ElevateContext.Provider value={value}>\n <script\n nonce={nonce}\n dangerouslySetInnerHTML={{ __html: blockingScript }}\n />\n {children}\n </ElevateContext.Provider>\n );\n}\n","/**\n * Custom Code Test Utilities\n *\n * Handles processing and execution of CUSTOM_CODE type tests.\n * Custom code tests inject CSS/JS into the page based on pathname matching.\n * Both control and non-control variations can have custom code.\n */\n\nimport type { ContentCustomCode, ElevateConfig } from \"../types\";\nimport type { PreviewState } from \"./preview\";\nimport { getAssignedVariant } from \"./assignment\";\nimport { applyCustomCode } from \"./content\";\nimport { onNavigate } from \"./navigation\";\n\n/**\n * Check if there are any active custom code tests\n */\nexport function hasCustomCodeTests(config: ElevateConfig): boolean {\n return config.tests.some(\n (test) => test.type === \"CUSTOM_CODE\" && test.enabled\n );\n}\n\n/**\n * Process and apply all custom code tests\n * This is the main entry point called by ElevateProvider\n */\nexport function processCustomCodeTests(\n config: ElevateConfig,\n previewState?: PreviewState | null,\n nonce?: string\n): void {\n if (typeof window === \"undefined\") return;\n\n console.log(\"[ElevateAB Custom Code] All tests:\", config.tests.map(t => ({ id: t.testId, type: t.type, enabled: t.enabled })));\n\n // Check if there are any custom code tests\n if (!hasCustomCodeTests(config)) {\n console.log(\"[ElevateAB Custom Code] No CUSTOM_CODE tests found\");\n return;\n }\n\n // Find all CUSTOM_CODE type tests\n const customCodeTests = config.tests.filter(\n (test) => test.type === \"CUSTOM_CODE\" && test.enabled\n );\n\n console.log(\"[ElevateAB Custom Code] Found\", customCodeTests.length, \"CUSTOM_CODE tests\");\n\n // Collect custom code from assigned variants\n const customCodes: ContentCustomCode[] = [];\n\n customCodeTests.forEach((test) => {\n const assignedVariantId = getAssignedVariant(test.testId);\n console.log(\"[ElevateAB Custom Code] Test\", test.testId, \"assigned variant:\", assignedVariantId);\n \n if (!assignedVariantId) return;\n\n const variation = test.variations.find((v) => v.id === assignedVariantId);\n console.log(\"[ElevateAB Custom Code] Variation data:\", variation);\n \n if (!variation?.customCode) {\n console.log(\"[ElevateAB Custom Code] No customCode on variation\");\n return;\n }\n\n // Check if there's any code to inject\n if (variation.customCode.js || variation.customCode.css) {\n console.log(\"[ElevateAB Custom Code] Adding custom code:\", variation.customCode);\n customCodes.push(variation.customCode);\n }\n });\n\n // Apply custom code if we have any\n if (customCodes.length > 0) {\n console.log(\"[ElevateAB Custom Code] Applying\", customCodes.length, \"custom code blocks\");\n applyCustomCode(customCodes, nonce);\n } else {\n console.log(\"[ElevateAB Custom Code] No custom code to apply\");\n }\n}\n\n/**\n * Re-process custom code tests (called on pathname change)\n */\nexport function reprocessCustomCodeTests(\n config: ElevateConfig,\n previewState?: PreviewState | null,\n nonce?: string\n): void {\n if (typeof window === \"undefined\") return;\n\n // Re-process custom code tests\n processCustomCodeTests(config, previewState, nonce);\n}\n\n/**\n * Setup event listeners for custom code test re-processing.\n *\n * Listens for ALL SPA navigation (pushState, replaceState, popstate)\n * via the shared navigation utility.\n */\nexport function setupCustomCodeTestListeners(\n config: ElevateConfig,\n previewState?: PreviewState | null,\n nonce?: string\n): () => void {\n if (typeof window === \"undefined\") return () => {};\n\n // Handle ALL SPA navigation (pushState, replaceState, popstate)\n const cleanupNavigation = onNavigate(() => {\n reprocessCustomCodeTests(config, previewState, nonce);\n });\n\n // Return cleanup function\n return () => {\n cleanupNavigation();\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 // Content test types\n ContentChange,\n ContentElement,\n ContentBlock,\n ContentCustomCode,\n ContentTestData,\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// Internal utilities (exported for advanced use cases)\nexport { hashString } 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 canAssignToTest,\n assignAllTests,\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// Split URL utilities\nexport {\n getSplitUrlBlockingScript,\n processSplitUrlTests,\n} from \"./handlers/splitUrl\";\nexport type { SplitUrlScriptOptions } from \"./handlers/splitUrl\";\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// Content test utilities\nexport {\n matchesWildcardPattern,\n applyContentChanges,\n applyContentElements,\n applyCustomCode,\n processContentTests,\n reprocessContentTests,\n setupContentTestListeners,\n hasContentTests,\n restoreOriginalElements,\n cleanupContentTests,\n} from \"./utils/content\";\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\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 { ElevateAnalytics } from \"./components/ElevateAnalytics\";\nexport type {\n UseAnalyticsHook,\n ElevateHydrogenAnalyticsProps,\n} from \"./components/ElevateAnalytics\";\n\nexport { useElevateConfig } from \"./contexts/ElevateContext\";\n\nexport type {\n TestTypeHandler,\n HandlerContext,\n HandlerVariantData,\n} from \"./handlers/types\";\nexport {\n registerHandler,\n getHandler,\n getAllHandlers,\n hasHandler,\n} from \"./handlers/registry\";\n\n// Handlers\nexport { contentHandler } from \"./handlers/content\";\n\nexport const VERSION = \"1.1.2\";\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","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","NAVIGATE_EVENT","patched","patchRefCount","originalPushState","originalReplaceState","patchHistory","args","unpatchHistory","onNavigate","callback","lastUrl","handler","currentUrl","getSplitUrlBlockingScript","options","configUrl","timeout","normalizeUrl","url","withProtocol","urlObj","urlMatches","currentUrl","urlList","normalizedCurrent","normalizedTarget","currentPath","targetPath","getRedirectBehavior","test","hasBeenRedirected","testId","getJsonCookie","markAsRedirected","redirectedTests","setCookie","findMatchingSplitUrlTest","config","variation","urls","performRedirect","targetUrl","finalUrl","navigate","pathname","processSplitUrlTests","previewState","match","matchedVariation","isControlUrl","redirectBehavior","assignedVariationId","getAssignedVariant","assignedVariation","v","targetUrls","hasSplitUrlTests","t","splitUrlHandler","test","context","variation","urls","urlMatches","extractShopifyId","gid","extractShopifyType","isShopifyGid","value","detectShopifyCurrency","shopify","ELEMENT_ID_PREFIX","NODE_TYPES","originalElements","injectedStyles","injectedScripts","injectedElements","isMobile","matchesWildcardPattern","pathname","pattern","trimmedPattern","suffix","checkIsMobile","convertToKebabCase","text","result","i","char","isTextNode","node","isElementNode","storeOriginalElement","selector","element","decodeCodeString","code","decoded","applyContentChanges","changes","currentPathname","matchingChanges","change","processedChanges","index","uniqueSelector","prop","value","kebabProp","hasImportant","cleanValue","key","applyContentElements","elements","elementId","target","domElement","createDOMElement","child","childElement","applyCustomCode","customCodes","nonce","injectCSS","injectJS","id","css","styleId","styleElement","js","scriptId","scriptElement","decodedJs","restoreOriginalElements","originalElement","cleanupContentTests","style","script","collectContentTestData","config","previewState","allChanges","allElements","allCustomCodes","test","assignedVariantId","getAssignedVariant","variation","v","hasContentTests","processContentTests","contentData","reprocessContentTests","currentIsMobile","setupContentTestListeners","listenToNavigation","handleResize","cleanupNavigation","onNavigate","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","handlers","registerHandler","handler","getHandler","type","getAllHandlers","hasHandler","extractProductHandleFromUrl","url","match","updatePriceElements","variation","variantId","currencyCode","selectors","variantPrices","priceAmount","compareAmount","selectorsConfig","selectorSet","priceSelector","el","formattedPrice","compareSelector","savingConfig","savingSelector","price","compare","saving","percentage","formattedSaving","pricePlusHandler","test","context","testData","productId","productHandle","urlHandle","matchesProductId","matchesHandle","matchesTestPathnames","test","currentUrl","pathnames","pathname","pattern","matchesWildcardPattern","contentHandler","context","variation","_context","testData","matchesTestPathnames","test","currentUrl","pathnames","pathname","pattern","matchesWildcardPattern","customCodeHandler","context","variation","_context","testData","registerHandler","pricePlusHandler","splitUrlHandler","contentHandler","customCodeHandler","buildHandlerContext","selectors","url","pathname","productHandle","productMatch","variantId","variantParam","useExperiment","testId","config","contextPreviewState","useElevateConfig","variant","setVariant","React","isLoading","setIsLoading","testConfig","test","previewState","getPreviewState","hasWarnedRef","effectAppliedRef","isMounted","assignedVariantId","getAssignedVariant","assigned","v","handler","getHandler","context","enrichedVariant","effectKey","handlerData","trackViews","React","useRef","useCallback","hasCustomCodeTests","config","test","processCustomCodeTests","previewState","nonce","t","customCodeTests","customCodes","assignedVariantId","getAssignedVariant","variation","v","applyCustomCode","jsx","jsxs","CDN_BASE_URL","getStoreName","storeId","getConfigUrl","useIsomorphicLayoutEffect","React","ElevateProvider","storefrontAccessToken","preventFlickering","flickerTimeout","nonce","children","config","setConfig","previewState","setPreviewState","countryCode","setCountryCode","initAnalytics","preview","getPreviewState","country","getCountryCode","revealContent","useCallback","fetchConfig","currentPreviewState","backendData","url","resolve","reject","script","data","parsedConfig","parseBackendConfig","assignAllTests","t","processSplitUrlTests","err","applyAllTests","hasSplitUrlTests","hasContentTests","processContentTests","hasCustomCodeTests","processCustomCodeTests","cleanupNavigation","onNavigate","cleanupContent","setupContentTestListeners","value","hasWarnedRef","useRef","configUrl","blockingScript","getSplitUrlBlockingScript","ElevateContext","VERSION"]}
1
+ {"version":3,"sources":["../src/utils/tracking.ts","../src/utils/conditions.ts","../src/utils/geo.ts","../src/utils/navigation.ts","../src/handlers/splitUrl.ts","../src/utils/shopify.ts","../src/utils/content.ts","../src/utils/manualTracking.ts","../src/components/Experiment.tsx","../src/handlers/registry.ts","../src/handlers/pricePlus.ts","../src/handlers/content.ts","../src/handlers/customCode.ts","../src/handlers/index.ts","../src/components/ElevateProvider.tsx","../src/utils/customCode.ts","../src/index.ts"],"sourcesContent":["import {\n setCookie,\n setSessionItem,\n getJsonCookie,\n getJsonSessionItem,\n} from \"./storage\";\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\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\nexport function trackViews(testId: string): void {\n trackUniqueView(testId);\n trackSessionView(testId);\n}\n\nexport function hasSessionView(testId: string): boolean {\n const addedViews = getJsonSessionItem<Record<string, boolean>>(\"ABAV\") || {};\n return !!addedViews[testId];\n}\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 * 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 * SPA Navigation Listener Utility\n *\n * Detects ALL client-side navigation in SPAs including:\n * - history.pushState() — link clicks handled by SPA routers\n * - history.replaceState() — in-place URL changes\n * - popstate — browser back/forward buttons\n *\n * The native `popstate` event only fires on back/forward navigation.\n * SPA routers (React Router, Remix, Next.js, etc.) navigate via\n * history.pushState(), which does NOT fire `popstate`.\n *\n * This utility monkey-patches pushState/replaceState to dispatch\n * a custom `eab:navigate` event, giving us a single event to\n * listen for ALL navigation types.\n */\n\nconst NAVIGATE_EVENT = \"eab:navigate\";\n\nlet patched = false;\nlet patchRefCount = 0;\nlet originalPushState: typeof history.pushState | null = null;\nlet originalReplaceState: typeof history.replaceState | null = null;\n\n/**\n * Patch history.pushState and history.replaceState to dispatch\n * a custom navigation event. Uses reference counting so multiple\n * callers can install/uninstall independently.\n */\nfunction patchHistory(): void {\n if (typeof window === \"undefined\") return;\n\n patchRefCount++;\n if (patched) return;\n patched = true;\n\n originalPushState = history.pushState.bind(history);\n originalReplaceState = history.replaceState.bind(history);\n\n history.pushState = function (...args: Parameters<typeof history.pushState>) {\n originalPushState!(...args);\n window.dispatchEvent(new Event(NAVIGATE_EVENT));\n };\n\n history.replaceState = function (\n ...args: Parameters<typeof history.replaceState>\n ) {\n originalReplaceState!(...args);\n window.dispatchEvent(new Event(NAVIGATE_EVENT));\n };\n}\n\n/**\n * Restore original history methods when no more listeners need them.\n */\nfunction unpatchHistory(): void {\n if (typeof window === \"undefined\") return;\n\n patchRefCount--;\n if (patchRefCount > 0 || !patched) return;\n\n if (originalPushState) {\n history.pushState = originalPushState;\n originalPushState = null;\n }\n if (originalReplaceState) {\n history.replaceState = originalReplaceState;\n originalReplaceState = null;\n }\n patched = false;\n}\n\n/**\n * Subscribe to ALL SPA navigation events (pushState, replaceState, popstate).\n *\n * The callback receives no arguments — the caller should read\n * `window.location` to determine the new URL.\n *\n * Returns a cleanup function that removes all listeners.\n *\n * @example\n * ```ts\n * const cleanup = onNavigate(() => {\n * // handle route change\n * });\n * // Later:\n * cleanup();\n * ```\n */\nexport function onNavigate(callback: () => void): () => void {\n if (typeof window === \"undefined\") return () => {};\n\n // Keep track of the last URL to avoid duplicate calls\n // (e.g. replaceState to the same URL)\n let lastUrl = window.location.href;\n\n const handler = () => {\n const currentUrl = window.location.href;\n if (currentUrl === lastUrl) return;\n lastUrl = currentUrl;\n callback();\n };\n\n patchHistory();\n\n window.addEventListener(NAVIGATE_EVENT, handler);\n window.addEventListener(\"popstate\", handler);\n\n return () => {\n window.removeEventListener(NAVIGATE_EVENT, handler);\n window.removeEventListener(\"popstate\", handler);\n unpatchHistory();\n };\n}\n","import type { Test, Variation, ElevateConfig } from \"../types\";\nimport type { PreviewState } from \"../utils/preview\";\nimport { getAssignedVariant } from \"../utils/assignment\";\nimport { getCookie, setCookie, getJsonCookie } from \"../utils/storage\";\nimport { onNavigate } from \"../utils/navigation\";\n\nexport interface SplitUrlScriptOptions {\n configUrl: string;\n timeout?: number;\n}\n\nexport function getSplitUrlBlockingScript(\n options: SplitUrlScriptOptions\n): string {\n const { configUrl, timeout = 2000 } = options;\n\n return `(function(){\n if(new URLSearchParams(location.search).get('abtr')==='true')return;\n \n var style=document.createElement('style');\n style.id='eab-split-hide';\n style.textContent='body{opacity:0!important;visibility:hidden!important}';\n document.head.appendChild(style);\n \n window.__eab_reveal=function(){\n var s=document.getElementById('eab-split-hide');\n if(s)s.remove();\n window.__eab_reveal=null;\n };\n \n setTimeout(function(){\n if(window.__eab_reveal)window.__eab_reveal();\n },${timeout});\n \n if(window.eab_data){\n return;\n }\n \n var script=document.createElement('script');\n script.src='${configUrl}';\n script.onerror=function(){\n if(window.__eab_reveal)window.__eab_reveal();\n };\n document.head.appendChild(script);\n})();`;\n}\n\nfunction normalizeUrl(url: string): string {\n if (!url?.trim()) return \"\";\n\n try {\n const withoutProtocol = url.trim().replace(/^https?:\\/\\//, \"\");\n const withProtocol = `https://${withoutProtocol}`;\n const urlObj = new URL(withProtocol.replace(/\\/+$/, \"\"));\n\n if (urlObj.hostname.startsWith(\"www.\")) {\n urlObj.hostname = urlObj.hostname.substring(4);\n }\n\n return urlObj.toString().replace(/\\/+$/, \"\");\n } catch {\n return url.trim().replace(/\\/+$/, \"\");\n }\n}\n\nfunction urlMatches(currentUrl: string, urlList: string[]): boolean {\n const normalizedCurrent = normalizeUrl(currentUrl.split(\"?\")[0]);\n\n for (const url of urlList) {\n const normalizedTarget = normalizeUrl(url.split(\"?\")[0]);\n if (normalizedCurrent === normalizedTarget) {\n return true;\n }\n // Also check pathname match for same-origin URLs\n try {\n const currentPath = new URL(currentUrl).pathname;\n const targetPath = new URL(url, currentUrl).pathname;\n if (currentPath === targetPath) {\n return true;\n }\n } catch {\n // Ignore URL parse errors\n }\n }\n return false;\n}\n\ntype RedirectBehavior =\n | \"redirectAll\"\n | \"redirectOnlyControlUrl\"\n | \"redirectOnce\"\n | \"redirectOnceControlUrlOnly\";\n\nfunction getRedirectBehavior(test: Test): RedirectBehavior {\n return (\n ((test as any).data?.redirectBehavior as RedirectBehavior) || \"redirectAll\"\n );\n}\n\nfunction hasBeenRedirected(testId: string): boolean {\n const redirectedTests =\n getJsonCookie<Record<string, boolean>>(\"eabRedirectedTests\") || {};\n return !!redirectedTests[testId];\n}\n\nfunction markAsRedirected(testId: string): void {\n const redirectedTests =\n getJsonCookie<Record<string, boolean>>(\"eabRedirectedTests\") || {};\n redirectedTests[testId] = true;\n setCookie(\"eabRedirectedTests\", JSON.stringify(redirectedTests));\n}\n\n/**\n * Find SPLIT_URL test that matches current URL\n */\nfunction findMatchingSplitUrlTest(\n config: ElevateConfig,\n currentUrl: string\n): { test: Test; matchedVariation: Variation; isControlUrl: boolean } | null {\n for (const test of config.tests) {\n if (test.type !== \"SPLIT_URL\" || !test.enabled) continue;\n\n for (const variation of test.variations) {\n const urls = (variation as any).splitUrlTestLinks || [];\n if (urls.length === 0) continue;\n\n if (urlMatches(currentUrl, urls)) {\n return {\n test,\n matchedVariation: variation,\n isControlUrl: !!variation.isControl,\n };\n }\n }\n }\n return null;\n}\n\n/**\n * Perform redirect using appropriate method for SPA frameworks\n */\nfunction performRedirect(targetUrl: string): void {\n if (typeof window === \"undefined\") return;\n\n // Add abtr=true param to prevent redirect loops\n const url = new URL(targetUrl, window.location.origin);\n url.searchParams.set(\"abtr\", \"true\");\n const finalUrl = url.toString();\n\n // Try SPA navigation first (Remix, Next.js, Nuxt)\n const navigate =\n (window as any).__remixRouter?.navigate ??\n (window as any).next?.router?.push ??\n (window as any).$nuxt?.$router?.push ??\n (window as any).__navigate;\n\n if (navigate && typeof navigate === \"function\") {\n try {\n const pathname = url.pathname + url.search;\n navigate(pathname);\n return;\n } catch {\n // Fall through to hard redirect\n }\n }\n\n // Hard redirect as fallback\n window.location.href = finalUrl;\n}\n\n/**\n * Process Split URL tests and redirect if needed\n * This is called automatically from ElevateProvider on mount.\n *\n * @returns true if a redirect was triggered, false otherwise\n */\nexport function processSplitUrlTests(\n config: ElevateConfig,\n previewState?: PreviewState | null | undefined\n): boolean {\n if (typeof window === \"undefined\") return false;\n\n const currentUrl = window.location.href;\n\n // Check if already redirected (abtr param present)\n const urlParams = new URLSearchParams(window.location.search);\n if (urlParams.get(\"abtr\") === \"true\") {\n return false;\n }\n\n // Find matching Split URL test\n const match = findMatchingSplitUrlTest(config, currentUrl);\n if (!match) return false;\n\n const { test, matchedVariation, isControlUrl } = match;\n const testId = test.testId;\n const redirectBehavior = getRedirectBehavior(test);\n\n const assignedVariationId = getAssignedVariant(testId);\n if (!assignedVariationId) return false;\n\n const assignedVariation = test.variations.find(\n (v) => v.id === assignedVariationId\n );\n if (!assignedVariation) return false;\n\n // Get target URLs for assigned variation\n const targetUrls = (assignedVariation as any).splitUrlTestLinks || [];\n if (targetUrls.length === 0) return false;\n\n // Check if already on assigned variation's URL\n if (urlMatches(currentUrl, targetUrls)) {\n return false; // Already on correct URL\n }\n\n // Apply redirect behavior rules\n switch (redirectBehavior) {\n case \"redirectOnlyControlUrl\":\n // Only redirect when on control URL\n if (!isControlUrl) return false;\n if (assignedVariation.isControl) return false; // Assigned to control, no redirect needed\n break;\n\n case \"redirectOnce\":\n // Redirect once from any URL\n if (hasBeenRedirected(testId)) return false;\n markAsRedirected(testId);\n break;\n\n case \"redirectOnceControlUrlOnly\":\n // Redirect once, but only from control URL\n if (!isControlUrl) return false;\n if (hasBeenRedirected(testId)) return false;\n if (assignedVariation.isControl) return false;\n markAsRedirected(testId);\n break;\n\n case \"redirectAll\":\n default:\n // Always redirect to assigned variation URL\n break;\n }\n\n // Perform the redirect\n const targetUrl = targetUrls[0];\n performRedirect(targetUrl);\n return true;\n}\n\n/**\n * Check if there are any active split URL tests\n */\nexport function hasSplitUrlTests(config: ElevateConfig): boolean {\n return config.tests.some((t) => t.type === \"SPLIT_URL\" && t.enabled);\n}\n\n/**\n * Setup event listeners for split URL test re-processing.\n *\n * Listens for ALL SPA navigation (pushState, replaceState, popstate)\n * and re-checks whether a redirect is needed on the new URL.\n */\nexport function setupSplitUrlTestListeners(\n config: ElevateConfig,\n previewState?: PreviewState | null\n): () => void {\n if (typeof window === \"undefined\") return () => {};\n\n // Handle ALL SPA navigation (pushState, replaceState, popstate)\n const cleanupNavigation = onNavigate(() => {\n processSplitUrlTests(config, previewState);\n });\n\n return () => {\n cleanupNavigation();\n };\n}\n\nexport const splitUrlHandler = {\n type: \"SPLIT_URL\",\n\n shouldActivate(test: Test, context: { currentUrl: string }): boolean {\n for (const variation of test.variations) {\n const urls = (variation as any).splitUrlTestLinks || [];\n if (urlMatches(context.currentUrl, urls)) {\n return true;\n }\n }\n return false;\n },\n\n getVariantData() {\n return { handlerActivated: true };\n },\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 * Content Test Utilities\n *\n * Handles DOM manipulation for Content Tests (Visual Editor Experiments).\n * Automatically applies changes, injects elements, and runs custom code\n * based on the assigned variant's content configuration.\n */\n\nimport type {\n ContentChange,\n ContentElement,\n ContentCustomCode,\n ContentTestData,\n ElevateConfig,\n Test,\n Variation,\n} from \"../types\";\nimport type { PreviewState } from \"./preview\";\nimport { getAssignedVariant } from \"./assignment\";\nimport { onNavigate } from \"./navigation\";\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst ELEMENT_ID_PREFIX = \"elv-el-\";\nconst NODE_TYPES = {\n ELEMENT_NODE: 1,\n TEXT_NODE: 3,\n};\n\n// ============================================================================\n// STATE MANAGEMENT\n// ============================================================================\n\n/** Tracks original elements for restoration on responsive changes */\nconst originalElements: Map<string, HTMLElement> = new Map();\n\n/** Tracks injected style elements for cleanup */\nconst injectedStyles: Map<string, HTMLStyleElement> = new Map();\n\n/** Tracks injected script elements for cleanup */\nconst injectedScripts: Map<string, HTMLScriptElement> = new Map();\n\n/** Tracks injected content elements for cleanup */\nconst injectedElements: Map<string, HTMLElement> = new Map();\n\n/** Current device type (for responsive changes) */\nlet isMobile = false;\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\n/**\n * Check if a pathname matches a wildcard pattern\n * Supports: \"*\" (all), \"*\\/products\\/*\" (contains), \"*.json\" (ends with)\n */\nexport function matchesWildcardPattern(\n pathname: string,\n pattern: string\n): boolean {\n // Match all\n if (pattern === \"*\") return true;\n\n // Contains pattern: *something*\n if (pattern.startsWith(\"*\") && pattern.endsWith(\"*\")) {\n const trimmedPattern = pattern.slice(1, -1);\n return pathname.includes(trimmedPattern);\n }\n\n // Ends with pattern: *something\n if (pattern.startsWith(\"*\")) {\n const suffix = pattern.slice(1);\n return pathname.endsWith(suffix);\n }\n\n // Exact match\n return pattern === pathname;\n}\n\n/**\n * Check if current device is mobile (viewport width < 768px)\n */\nfunction checkIsMobile(): boolean {\n if (typeof window === \"undefined\") return false;\n return window.innerWidth < 768;\n}\n\n/**\n * Convert camelCase to kebab-case for CSS properties\n */\nfunction convertToKebabCase(text: string): string {\n let result = \"\";\n for (let i = 0; i < text.length; i++) {\n const char = text[i];\n if (char >= \"A\" && char <= \"Z\") {\n if (i !== 0) result += \"-\";\n result += char.toLowerCase();\n } else {\n result += char;\n }\n }\n return result;\n}\n\n/**\n * Check if a node is a text node\n */\nfunction isTextNode(node: Node): boolean {\n return node.nodeType === NODE_TYPES.TEXT_NODE;\n}\n\n/**\n * Check if a node is an element node\n */\nfunction isElementNode(node: Node): node is HTMLElement {\n return node.nodeType === NODE_TYPES.ELEMENT_NODE;\n}\n\n/**\n * Store original element for later restoration\n */\nfunction storeOriginalElement(selector: string, element: HTMLElement): void {\n if (!originalElements.has(selector)) {\n originalElements.set(selector, element.cloneNode(true) as HTMLElement);\n }\n}\n\n/**\n * Decode escaped characters in code strings\n */\nfunction decodeCodeString(code: string): string {\n let decoded = code;\n\n // Replace HTML entities (using replace with regex for ES2020 compatibility)\n decoded = decoded.replace(/&#96;/g, \"`\").replace(/&#36;/g, \"$\");\n\n // Try JSON decode for escaped characters\n try {\n decoded = JSON.parse('\"' + decoded + '\"');\n } catch {\n // If JSON.parse fails, use the partially decoded version\n }\n\n return decoded;\n}\n\n// ============================================================================\n// CONTENT CHANGES (Text, Styles, Attributes)\n// ============================================================================\n\n/**\n * Apply content changes to existing elements\n */\nexport function applyContentChanges(changes: ContentChange[]): void {\n if (typeof window === \"undefined\" || changes.length === 0) return;\n\n const currentPathname = window.location.pathname;\n\n // Filter changes that match current pathname\n const matchingChanges = changes.filter((change) => {\n if (!change.pathnames?.length) return true;\n return change.pathnames.some((pathname) =>\n matchesWildcardPattern(currentPathname, pathname)\n );\n });\n\n // Detect mobile/desktop\n isMobile = checkIsMobile();\n\n // Process each change with responsive support\n const processedChanges = matchingChanges.map((change) => ({\n ...change,\n // Merge responsive styles (lg + sm if mobile)\n style: change.style\n ? { ...change.style.lg, ...(isMobile && change.style.sm) }\n : undefined,\n // Select responsive content\n content:\n change.content && isMobile && change.content.sm\n ? change.content.sm\n : change.content?.lg,\n }));\n\n // Apply text content changes\n processedChanges\n .filter((change) => change.content !== undefined)\n .forEach((change) => {\n const elements = change.applyAll\n ? Array.from(document.querySelectorAll(change.selector))\n : [document.querySelector(change.selector)].filter(Boolean);\n\n elements.forEach((element, index) => {\n if (!element) return;\n\n const uniqueSelector = change.applyAll\n ? `${change.selector}-${index}-${\n element.id || Math.random().toString(36).substr(2, 9)\n }`\n : change.selector;\n\n storeOriginalElement(uniqueSelector, element as HTMLElement);\n\n // Apply content\n if (isTextNode(element)) {\n (element as unknown as Text).nodeValue = change.content!;\n } else if (isElementNode(element)) {\n element.innerHTML = change.content!;\n }\n });\n });\n\n // Apply style changes\n processedChanges\n .filter((change) => change.style !== undefined)\n .forEach((change) => {\n const element = document.querySelector(change.selector) as HTMLElement;\n if (!element || !isElementNode(element)) return;\n\n storeOriginalElement(change.selector, element);\n\n Object.entries(change.style!).forEach(([prop, value]) => {\n const kebabProp = convertToKebabCase(prop);\n const hasImportant = value.toString().includes(\"!important\");\n const cleanValue = value.toString().replace(/\\s*!important\\s*$/, \"\");\n\n element.style.setProperty(\n kebabProp,\n cleanValue,\n hasImportant ? \"important\" : undefined\n );\n });\n });\n\n // Apply attribute changes\n processedChanges\n .filter((change) => change.attributes !== undefined)\n .forEach((change) => {\n const element = document.querySelector(change.selector) as HTMLElement;\n if (!element || !isElementNode(element)) return;\n\n storeOriginalElement(change.selector, element);\n\n Object.entries(change.attributes!).forEach(([key, value]) => {\n element.setAttribute(key, value);\n });\n });\n}\n\n// ============================================================================\n// CONTENT ELEMENTS (HTML Injection)\n// ============================================================================\n\n/**\n * Apply content elements (inject new HTML elements)\n */\nexport function applyContentElements(elements: ContentElement[]): void {\n if (typeof window === \"undefined\" || elements.length === 0) return;\n\n const currentPathname = window.location.pathname;\n\n // Filter elements that match current pathname\n const matchingElements = elements.filter((element) => {\n if (!element.selector?.pathnames?.length) return true;\n return element.selector.pathnames.some((pathname) =>\n matchesWildcardPattern(currentPathname, pathname)\n );\n });\n\n matchingElements.forEach((element) => {\n const elementId = `${ELEMENT_ID_PREFIX}${element.id}`;\n\n // Skip if already injected\n if (injectedElements.has(elementId)) return;\n\n const target = document.querySelector(element.selector.target);\n if (!target) return;\n\n // Check if element already exists in DOM\n if (target.parentElement?.querySelector(`#${elementId}`)) return;\n\n // Create the DOM element\n const domElement = createDOMElement(element);\n\n // Insert based on placement\n if (element.selector.placement === \"after\") {\n target.after(domElement);\n } else if (element.selector.placement === \"before\") {\n target.before(domElement);\n }\n\n // Track for cleanup\n injectedElements.set(elementId, domElement);\n });\n}\n\n/**\n * Create a DOM element from ContentElement definition\n */\nfunction createDOMElement(element: ContentElement): HTMLElement {\n const domElement = document.createElement(element.tagName);\n\n domElement.id = `${ELEMENT_ID_PREFIX}${element.id}`;\n\n // Apply styles\n if (element.style) {\n Object.assign(domElement.style, element.style);\n }\n\n // Apply attributes\n if (element.attributes) {\n Object.entries(element.attributes).forEach(([key, value]) => {\n domElement.setAttribute(key, value);\n });\n }\n\n // Set content for text elements\n if (element.kind === \"text\" && element.content) {\n domElement.innerText = element.content;\n }\n\n // Recursively render children for containers\n if (element.kind === \"container\" && element.childrens) {\n element.childrens.forEach((child) => {\n const childElement = createDOMElement(child);\n domElement.appendChild(childElement);\n });\n }\n\n return domElement;\n}\n\n// ============================================================================\n// CUSTOM CODE (CSS/JS Injection)\n// ============================================================================\n\n/**\n * Apply custom code (CSS and JS injection)\n */\nexport function applyCustomCode(\n customCodes: ContentCustomCode[],\n nonce?: string\n): void {\n if (typeof window === \"undefined\" || customCodes.length === 0) return;\n\n const currentPathname = window.location.pathname;\n\n // Filter codes that match current pathname\n const matchingCodes = customCodes.filter((code) => {\n // If no pathnames specified, run on all pages\n if (!code.pathnames?.length) return true;\n\n // Check excludes first\n if (code.excludePathnames?.length) {\n const isExcluded = code.excludePathnames.some((pathname) =>\n matchesWildcardPattern(currentPathname, pathname)\n );\n if (isExcluded) return false;\n }\n\n // Check includes\n return code.pathnames.some((pathname) =>\n matchesWildcardPattern(currentPathname, pathname)\n );\n });\n\n matchingCodes.forEach((code) => {\n if (!code.id) return;\n\n // Inject CSS\n if (code.css) {\n injectCSS(code.id, code.css);\n }\n\n // Inject JS\n if (code.js) {\n injectJS(code.id, code.js, nonce);\n }\n });\n}\n\n/**\n * Inject CSS into the page\n */\nfunction injectCSS(id: string, css: string): void {\n const styleId = `css-${id}`;\n\n // Skip if already injected\n if (injectedStyles.has(styleId) || document.getElementById(styleId)) return;\n\n const styleElement = document.createElement(\"style\");\n styleElement.id = styleId;\n styleElement.textContent = decodeCodeString(css);\n\n document.head.appendChild(styleElement);\n injectedStyles.set(styleId, styleElement);\n}\n\n/**\n * Inject JavaScript into the page\n */\nfunction injectJS(id: string, js: string, nonce?: string): void {\n const scriptId = `js-${id}`;\n\n // Skip if already injected\n if (injectedScripts.has(scriptId) || document.getElementById(scriptId))\n return;\n\n const scriptElement = document.createElement(\"script\");\n scriptElement.id = scriptId;\n\n // Add nonce for CSP compliance\n if (nonce) {\n scriptElement.setAttribute(\"nonce\", nonce);\n }\n\n // Wrap in try-catch for safety\n const decodedJs = decodeCodeString(js);\n scriptElement.textContent =\n \"(function(){try{\" +\n decodedJs +\n \"}catch(e){console.error('[ElevateAB] Custom script error:', e)}})();\";\n\n document.head.appendChild(scriptElement);\n injectedScripts.set(scriptId, scriptElement);\n}\n\n// ============================================================================\n// CLEANUP FUNCTIONS\n// ============================================================================\n\n/**\n * Restore original elements (for responsive changes or cleanup)\n */\nexport function restoreOriginalElements(): void {\n originalElements.forEach((originalElement, selector) => {\n // Try to find the element by selector\n const target = document.querySelector(selector);\n if (target) {\n target.replaceWith(originalElement.cloneNode(true));\n }\n });\n}\n\n/**\n * Clean up all injected content\n */\nexport function cleanupContentTests(): void {\n // Remove injected styles\n injectedStyles.forEach((style) => style.remove());\n injectedStyles.clear();\n\n // Remove injected scripts\n injectedScripts.forEach((script) => script.remove());\n injectedScripts.clear();\n\n // Remove injected elements\n injectedElements.forEach((element) => element.remove());\n injectedElements.clear();\n\n // Clear original elements cache\n originalElements.clear();\n}\n\n// ============================================================================\n// MAIN PROCESSOR\n// ============================================================================\n\n/**\n * Collect all content test data from assigned variants\n */\nfunction collectContentTestData(\n config: ElevateConfig,\n previewState?: PreviewState | null\n): ContentTestData {\n const allChanges: ContentChange[] = [];\n const allElements: ContentElement[] = [];\n const allCustomCodes: ContentCustomCode[] = [];\n\n // Find all CONTENT type tests\n const contentTests = config.tests.filter(\n (test) => test.type === \"CONTENT\" && test.enabled\n );\n\n contentTests.forEach((test) => {\n const assignedVariantId = getAssignedVariant(test.testId);\n if (!assignedVariantId) return;\n\n const variation = test.variations.find((v) => v.id === assignedVariantId);\n if (!variation?.content) return;\n\n // Collect content data\n if (variation.content.changes?.length) {\n allChanges.push(...variation.content.changes);\n }\n if (variation.content.elements?.length) {\n allElements.push(...variation.content.elements);\n }\n if (variation.content.customCodes?.length) {\n allCustomCodes.push(...variation.content.customCodes);\n }\n });\n\n return {\n changes: allChanges,\n elements: allElements,\n blocks: [], // Blocks deferred to v2\n customCodes: allCustomCodes,\n };\n}\n\n/**\n * Check if there are any active content tests\n */\nexport function hasContentTests(config: ElevateConfig): boolean {\n return config.tests.some((test) => test.type === \"CONTENT\" && test.enabled);\n}\n\n/**\n * Process and apply all content tests\n * This is the main entry point called by ElevateProvider\n */\nexport function processContentTests(\n config: ElevateConfig,\n previewState?: PreviewState | null,\n nonce?: string\n): void {\n if (typeof window === \"undefined\") return;\n\n // Check if there are any content tests\n if (!hasContentTests(config)) return;\n\n // Collect all content data from assigned variants\n const contentData = collectContentTestData(config, previewState);\n\n // Check if there's anything to apply\n const hasContent =\n contentData.changes.length > 0 ||\n contentData.elements.length > 0 ||\n contentData.customCodes.length > 0;\n\n if (!hasContent) return;\n\n // Apply content changes\n applyContentChanges(contentData.changes);\n applyContentElements(contentData.elements);\n applyCustomCode(contentData.customCodes, nonce);\n}\n\n/**\n * Re-process content tests (called on pathname change or resize)\n */\nexport function reprocessContentTests(\n config: ElevateConfig,\n previewState?: PreviewState | null,\n nonce?: string\n): void {\n if (typeof window === \"undefined\") return;\n\n // Check for responsive change\n const currentIsMobile = checkIsMobile();\n if (currentIsMobile !== isMobile) {\n // Responsive change - restore and reapply\n restoreOriginalElements();\n isMobile = currentIsMobile;\n }\n\n // Re-process content tests\n processContentTests(config, previewState, nonce);\n}\n\n/**\n * Setup event listeners for content test re-processing.\n *\n * Listens for ALL SPA navigation (pushState, replaceState, popstate)\n * via the shared navigation utility, plus resize for responsive changes.\n */\nexport function setupContentTestListeners(\n config: ElevateConfig,\n previewState?: PreviewState | null,\n nonce?: string,\n listenToNavigation = true\n): () => void {\n if (typeof window === \"undefined\") return () => {};\n\n // Handle resize for responsive changes\n const handleResize = () => {\n const currentIsMobile = checkIsMobile();\n if (currentIsMobile !== isMobile) {\n reprocessContentTests(config, previewState, nonce);\n }\n };\n\n window.addEventListener(\"resize\", handleResize);\n\n let cleanupNavigation = () => {};\n if (listenToNavigation) {\n // Handle ALL SPA navigation (pushState, replaceState, popstate)\n cleanupNavigation = onNavigate(() => {\n reprocessContentTests(config, previewState, nonce);\n });\n }\n\n // Return cleanup function\n return () => {\n window.removeEventListener(\"resize\", handleResize);\n cleanupNavigation();\n cleanupContentTests();\n };\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 { getAssignedVariant } from \"../utils/assignment\";\nimport { trackViews } from \"../utils/tracking\";\nimport { getPreviewState } from \"../utils/preview\";\nimport { getHandler } from \"../handlers/registry\";\nimport type { HandlerContext } from \"../handlers/types\";\n\n// Import handlers to trigger auto-registration\nimport \"../handlers\";\n\nexport interface UseExperimentResult {\n /** The full variant object with handler data (null if not assigned) */\n variant: (Variation & Record<string, unknown>) | 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 * Build handler context from current environment\n */\nfunction buildHandlerContext(selectors?: unknown): HandlerContext {\n if (typeof window === \"undefined\") {\n return { currentUrl: \"\", selectors };\n }\n\n const url = window.location.href;\n const pathname = window.location.pathname;\n\n // Extract product info from URL\n let productHandle: string | undefined;\n const productMatch = pathname.match(/\\/products\\/([^\\/\\?]+)/);\n if (productMatch) {\n productHandle = productMatch[1];\n }\n\n // Try to detect variant ID from URL params\n let variantId: string | undefined;\n const urlParams = new URLSearchParams(window.location.search);\n const variantParam = urlParams.get(\"variant\");\n if (variantParam) {\n variantId = variantParam;\n }\n\n return {\n currentUrl: url,\n productHandle,\n variantId,\n selectors,\n currencyCode: \"USD\", // Default, could be detected from page\n };\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 {\n config,\n previewState: contextPreviewState,\n selectors,\n } = useElevateConfig();\n const [variant, setVariant] = React.useState<\n (Variation & Record<string, unknown>) | null\n >(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 // Get preview state - use context or get fresh\n const previewState = React.useMemo(() => {\n return contextPreviewState || getPreviewState();\n }, [contextPreviewState]);\n\n // Track if we've already warned about this test (prevent duplicate warnings)\n const hasWarnedRef = React.useRef<string | null>(null);\n // Track if effect has been applied (prevent duplicate DOM updates)\n const effectAppliedRef = React.useRef<string | null>(null);\n\n React.useEffect(() => {\n let isMounted = true;\n\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 if (isMounted) setIsLoading(false);\n return;\n }\n\n // Test exists but is disabled (but allow preview mode to override)\n if (!testConfig.enabled && !previewState?.isPreview) {\n if (hasWarnedRef.current !== `${testId}-disabled`) {\n hasWarnedRef.current = `${testId}-disabled`;\n console.warn(`[ElevateAB] Test disabled: ${testId}`);\n }\n if (isMounted) setIsLoading(false);\n return;\n }\n\n const assignedVariantId = getAssignedVariant(testId);\n if (!assignedVariantId) {\n if (isMounted) setIsLoading(false);\n return;\n }\n\n const assigned = testConfig.variations.find(\n (v) => v.id === assignedVariantId\n );\n if (!assigned) {\n if (isMounted) setIsLoading(false);\n return;\n }\n\n const handler = getHandler(testConfig.type);\n const context = buildHandlerContext(selectors);\n\n let enrichedVariant: Variation & Record<string, unknown> = {\n ...assigned,\n };\n\n if (handler?.shouldActivate(testConfig, context)) {\n const effectKey = `${testId}-${assigned.id}`;\n if (handler.applyEffect && effectAppliedRef.current !== effectKey) {\n effectAppliedRef.current = effectKey;\n handler.applyEffect(testConfig, assigned, context);\n }\n\n const handlerData = handler.getVariantData(testConfig, assigned, context);\n enrichedVariant = { ...enrichedVariant, ...handlerData };\n }\n\n if (isMounted) setVariant(enrichedVariant);\n\n if (!previewState?.isPreview) {\n trackViews(testId);\n }\n\n if (isMounted) setIsLoading(false);\n\n return () => {\n isMounted = false;\n };\n }, [config, testConfig, testId, previewState, selectors]);\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 type { TestTypeHandler } from \"./types\";\n\nconst handlers: Map<string, TestTypeHandler> = new Map();\n\nexport function registerHandler(handler: TestTypeHandler): void {\n handlers.set(handler.type, handler);\n}\n\nexport function getHandler(type: string): TestTypeHandler | undefined {\n return handlers.get(type);\n}\n\nexport function getAllHandlers(): TestTypeHandler[] {\n return Array.from(handlers.values());\n}\n\nexport function hasHandler(type: string): boolean {\n return handlers.has(type);\n}\n","\nimport type { Test, Variation } from \"../types\";\nimport type {\n TestTypeHandler,\n HandlerContext,\n HandlerVariantData,\n} from \"./types\";\n\nfunction extractProductHandleFromUrl(url: string): string | null {\n try {\n const urlObj = new URL(url, \"https://example.com\");\n const pathname = urlObj.pathname;\n\n // Match /products/{handle}\n const match = pathname.match(/\\/products\\/([^\\/\\?]+)/);\n return match ? match[1] : null;\n } catch {\n return null;\n }\n}\n\nfunction updatePriceElements(\n variation: Variation,\n variantId: string | undefined,\n currencyCode: string,\n selectors: unknown,\n): void {\n if (typeof window === \"undefined\") return;\n if (!variation.prices || !variantId) return;\n\n const variantPrices = variation.prices[variantId];\n if (!variantPrices) return;\n\n const priceAmount = variantPrices.price?.[currencyCode];\n const compareAmount = variantPrices.compare?.[currencyCode];\n\n // Get selectors configuration\n const selectorsConfig = selectors as {\n selectorsV2?: Array<{\n price?: string;\n compareAt?: string;\n saving?: Array<{ selector: string; isPercentage?: boolean }>;\n }>;\n };\n\n if (!selectorsConfig?.selectorsV2?.length) return;\n\n // Update each selector set\n for (const selectorSet of selectorsConfig.selectorsV2) {\n // Update price elements\n if (selectorSet.price && priceAmount) {\n const priceSelector = selectorSet.price.replace(\n \"{{variant}}\",\n variantId,\n );\n const priceElements = document.querySelectorAll(priceSelector);\n priceElements.forEach((el) => {\n // Format price with currency\n const formattedPrice = new Intl.NumberFormat(\"en-US\", {\n style: \"currency\",\n currency: currencyCode,\n }).format(parseFloat(priceAmount));\n el.textContent = formattedPrice;\n });\n }\n\n // Update compare-at price elements\n if (selectorSet.compareAt && compareAmount) {\n const compareSelector = selectorSet.compareAt.replace(\n \"{{variant}}\",\n variantId,\n );\n const compareElements = document.querySelectorAll(compareSelector);\n compareElements.forEach((el) => {\n const formattedPrice = new Intl.NumberFormat(\"en-US\", {\n style: \"currency\",\n currency: currencyCode,\n }).format(parseFloat(compareAmount));\n el.textContent = formattedPrice;\n });\n }\n\n // Update saving elements\n if (selectorSet.saving && priceAmount && compareAmount) {\n for (const savingConfig of selectorSet.saving) {\n const savingSelector = savingConfig.selector.replace(\n \"{{variant}}\",\n variantId,\n );\n const savingElements = document.querySelectorAll(savingSelector);\n savingElements.forEach((el) => {\n const price = parseFloat(priceAmount);\n const compare = parseFloat(compareAmount);\n const saving = compare - price;\n\n if (savingConfig.isPercentage) {\n const percentage = Math.round((saving / compare) * 100);\n el.textContent = `${percentage}%`;\n } else {\n const formattedSaving = new Intl.NumberFormat(\"en-US\", {\n style: \"currency\",\n currency: currencyCode,\n }).format(saving);\n el.textContent = formattedSaving;\n }\n });\n }\n }\n }\n}\n\nexport const pricePlusHandler: TestTypeHandler = {\n type: \"PRICE_PLUS\",\n\n shouldActivate(test: Test, context: HandlerContext): boolean {\n const testData = (test as any).data;\n if (!testData) return false;\n\n // Get product identifiers from context\n const { productId, productHandle } = context;\n\n // Auto-detect from URL if not provided\n const urlHandle =\n productHandle || extractProductHandleFromUrl(context.currentUrl);\n\n // Check if this product matches the test\n const matchesProductId =\n productId && testData.productIds?.includes(productId);\n const matchesHandle = urlHandle && testData.handles?.includes(urlHandle);\n\n return matchesProductId || matchesHandle;\n },\n\n applyEffect(\n test: Test,\n variation: Variation,\n context: HandlerContext,\n ): void {\n // Update DOM elements using selectors\n updatePriceElements(\n variation,\n context.variantId,\n context.currencyCode || \"USD\",\n context.selectors,\n );\n },\n\n getVariantData(\n test: Test,\n variation: Variation,\n context: HandlerContext,\n ): HandlerVariantData {\n const testData = (test as any).data;\n\n // Return prices for manual rendering\n return {\n prices: variation.prices,\n matchedProductId: context.productId,\n productIds: testData?.productIds,\n handles: testData?.handles,\n handlerActivated: true,\n };\n },\n};\n","/**\n * Content Test Handler\n *\n * Handles CONTENT type tests (Visual Editor Experiments).\n * These tests modify page content without redirects - they can:\n * - Remove/hide elements\n * - Modify text/headings/descriptions\n * - Change CSS styles (colors, fonts, sizes)\n * - Add new HTML elements\n * - Modify attributes (href, src, etc.)\n * - Inject custom CSS/JS\n */\n\nimport type { Test, Variation } from \"../types\";\nimport type {\n TestTypeHandler,\n HandlerContext,\n HandlerVariantData,\n} from \"./types\";\nimport { matchesWildcardPattern } from \"../utils/content\";\n\n/**\n * Check if current URL matches any of the test's pathnames\n */\nfunction matchesTestPathnames(test: Test, currentUrl: string): boolean {\n const testData = (test as any).data;\n const pathnames: string[] = testData?.pathnames || [];\n\n // If no pathnames specified, match all pages\n if (!pathnames.length) return true;\n\n try {\n const url = new URL(currentUrl, \"https://example.com\");\n const pathname = url.pathname;\n\n // Check if any pathname pattern matches\n return pathnames.some((pattern) =>\n matchesWildcardPattern(pathname, pattern)\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Content test handler\n *\n * Note: Content test effects are applied by processContentTests in ElevateProvider,\n * not through the handler's applyEffect. This ensures all content tests are\n * processed together after variant assignment for better coordination.\n */\nexport const contentHandler: TestTypeHandler = {\n type: \"CONTENT\",\n\n shouldActivate(test: Test, context: HandlerContext): boolean {\n return matchesTestPathnames(test, context.currentUrl);\n },\n\n getVariantData(\n test: Test,\n variation: Variation,\n _context: HandlerContext\n ): HandlerVariantData {\n const testData = (test as any).data;\n\n return {\n content: variation.content,\n pathnames: testData?.pathnames,\n handlerActivated: true,\n };\n },\n};\n","/**\n * Custom Code Test Handler\n *\n * Handles CUSTOM_CODE type tests.\n * These tests inject custom CSS/JS into the page based on pathname matching.\n * Both control and non-control variations can have custom code.\n */\n\nimport type { Test, Variation } from \"../types\";\nimport type {\n TestTypeHandler,\n HandlerContext,\n HandlerVariantData,\n} from \"./types\";\nimport { matchesWildcardPattern } from \"../utils/content\";\n\n/**\n * Check if current URL matches any of the test's pathnames\n */\nfunction matchesTestPathnames(test: Test, currentUrl: string): boolean {\n const testData = (test as any).data;\n const pathnames: string[] = testData?.pathnames || [];\n\n // If no pathnames specified, match all pages\n if (!pathnames.length) return true;\n\n try {\n const url = new URL(currentUrl, \"https://example.com\");\n const pathname = url.pathname;\n\n // Check if any pathname pattern matches\n return pathnames.some((pattern) =>\n matchesWildcardPattern(pathname, pattern)\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Custom code test handler\n *\n * Note: Custom code effects are applied by processCustomCodeTests in ElevateProvider,\n * not through the handler's applyEffect. This ensures all custom code tests are\n * processed together after variant assignment for better coordination.\n */\nexport const customCodeHandler: TestTypeHandler = {\n type: \"CUSTOM_CODE\",\n\n shouldActivate(test: Test, context: HandlerContext): boolean {\n return matchesTestPathnames(test, context.currentUrl);\n },\n\n getVariantData(\n test: Test,\n variation: Variation,\n _context: HandlerContext\n ): HandlerVariantData {\n const testData = (test as any).data;\n\n return {\n customCode: variation.customCode,\n pathnames: testData?.pathnames,\n excludePathnames: testData?.excludePathnames,\n handlerActivated: true,\n };\n },\n};\n","export type {\n TestTypeHandler,\n HandlerContext,\n HandlerVariantData,\n} from \"./types\";\n\nexport {\n registerHandler,\n getHandler,\n getAllHandlers,\n hasHandler,\n} from \"./registry\";\n\nexport { pricePlusHandler } from \"./pricePlus\";\nexport { splitUrlHandler, processSplitUrlTests } from \"./splitUrl\";\nexport { contentHandler } from \"./content\";\nexport { customCodeHandler } from \"./customCode\";\n\nimport { registerHandler } from \"./registry\";\nimport { pricePlusHandler } from \"./pricePlus\";\nimport { splitUrlHandler } from \"./splitUrl\";\nimport { contentHandler } from \"./content\";\nimport { customCodeHandler } from \"./customCode\";\n\nregisterHandler(pricePlusHandler);\nregisterHandler(splitUrlHandler);\nregisterHandler(contentHandler);\nregisterHandler(customCodeHandler);","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 {\n processSplitUrlTests,\n getSplitUrlBlockingScript,\n hasSplitUrlTests,\n} from \"../handlers/splitUrl\";\nimport {\n processContentTests,\n setupContentTestListeners,\n hasContentTests,\n} from \"../utils/content\";\nimport {\n processCustomCodeTests,\n hasCustomCodeTests,\n} from \"../utils/customCode\";\nimport { assignAllTests } from \"../utils/assignment\";\nimport { onNavigate } from \"../utils/navigation\";\n\nconst CDN_BASE_URL = \"https://ds0wlyksfn0sb.cloudfront.net/headless\";\n\nfunction getStoreName(storeId: string): string {\n return storeId.replace(/^https?:\\/\\//, \"\").replace(\".myshopify.com\", \"\");\n}\n\nfunction getConfigUrl(storeId: string): string {\n return `${CDN_BASE_URL}/${getStoreName(storeId)}.js`;\n}\n\ninterface ElevateProviderSimpleProps {\n storeId: string;\n storefrontAccessToken?: string;\n preventFlickering?: boolean;\n flickerTimeout?: number;\n nonce?: string;\n children: React.ReactNode;\n}\n\nconst useIsomorphicLayoutEffect =\n typeof window !== \"undefined\" ? React.useLayoutEffect : React.useEffect;\n\nexport function ElevateProvider({\n storeId,\n storefrontAccessToken,\n preventFlickering = false,\n flickerTimeout = 3000,\n nonce,\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\n React.useEffect(() => {\n if (typeof window === \"undefined\") return;\n\n initAnalytics({\n storeId,\n storefrontAccessToken,\n });\n\n const preview = getPreviewState();\n setPreviewState(preview);\n\n const country = getCountryCode();\n setCountryCode(country);\n }, [storeId, storefrontAccessToken]);\n\n const revealContent = useCallback(() => {\n if (typeof window !== \"undefined\" && (window as any).__eab_reveal) {\n (window as any).__eab_reveal();\n }\n }, []);\n\n React.useEffect(() => {\n async function fetchConfig() {\n try {\n const currentPreviewState =\n typeof window !== \"undefined\" ? getPreviewState() : null;\n\n let backendData: BackendConfig | null = null;\n\n if (typeof window !== \"undefined\" && (window as any).eab_data) {\n backendData = (window as any).eab_data;\n } else {\n const url = getConfigUrl(storeId);\n\n backendData = await new Promise((resolve, reject) => {\n const script = document.createElement(\"script\");\n script.src = url;\n script.async = true;\n\n script.onload = () => {\n const data = (window as any).eab_data;\n script.remove();\n\n if (!data) {\n reject(\n new Error(\n \"Failed to load config: window.eab_data is undefined\",\n ),\n );\n } else {\n resolve(data);\n }\n };\n\n script.onerror = () => {\n script.remove();\n resolve({\n allTests: {},\n selectors: { selectorsV2: [] },\n } as BackendConfig);\n };\n\n document.head.appendChild(script);\n });\n }\n\n if (\n !backendData ||\n !backendData.allTests ||\n Object.keys(backendData.allTests).length === 0\n ) {\n setConfig({ tests: [], selectors: undefined });\n revealContent();\n return;\n }\n\n if (backendData.subscriptionPaused) {\n console.error(\n `[ElevateAB] Subscription is paused or stopped for store: ${storeId}. ` +\n (backendData.subscriptionMessage ||\n \"A/B tests will not run. Please reactivate your subscription.\"),\n );\n setConfig({ tests: [], selectors: undefined });\n revealContent();\n return;\n }\n\n const hasActiveTests =\n Object.keys(backendData.allTests || {}).length > 0;\n if (!hasActiveTests) {\n setConfig({ tests: [], selectors: undefined });\n revealContent();\n return;\n }\n\n const parsedConfig = parseBackendConfig(backendData);\n\n assignAllTests(parsedConfig, currentPreviewState);\n\n if (parsedConfig.tests.some((t) => t.type === \"SPLIT_URL\")) {\n const redirected = processSplitUrlTests(\n parsedConfig,\n currentPreviewState,\n );\n if (redirected) return;\n }\n\n setConfig(parsedConfig);\n revealContent();\n } catch (err) {\n console.error(\"[ElevateAB] Failed to load config:\", err);\n setConfig({ tests: [], selectors: undefined });\n revealContent();\n }\n }\n\n fetchConfig();\n }, [storeId, revealContent]);\n\n useIsomorphicLayoutEffect(() => {\n if (!config || typeof window === \"undefined\") return;\n\n const applySplitUrlTests = (): boolean => {\n if (!hasSplitUrlTests(config)) return false;\n return processSplitUrlTests(config, previewState);\n };\n\n const applyNonRedirectTests = () => {\n if (hasContentTests(config)) {\n try {\n processContentTests(config, previewState, nonce);\n } catch (err) {\n console.error(\"[ElevateAB] Failed to apply CONTENT tests:\", err);\n }\n }\n\n if (hasCustomCodeTests(config)) {\n try {\n processCustomCodeTests(config, previewState, nonce);\n } catch (err) {\n console.error(\"[ElevateAB] Failed to apply CUSTOM_CODE tests:\", err);\n }\n }\n };\n\n // Apply on initial load with parsed config.\n if (!applySplitUrlTests()) {\n applyNonRedirectTests();\n }\n\n let navigationRafId: number | null = null;\n\n // Re-apply all tests on SPA navigation.\n const cleanupNavigation = onNavigate(() => {\n // Run split URL checks immediately on navigation.\n if (applySplitUrlTests()) return;\n\n // Try immediately, then retry after the next paint frame so\n // content tests can target elements rendered by the new route.\n applyNonRedirectTests();\n if (navigationRafId !== null) {\n window.cancelAnimationFrame(navigationRafId);\n }\n navigationRafId = window.requestAnimationFrame(() => {\n applyNonRedirectTests();\n navigationRafId = null;\n });\n });\n\n // Keep content resize handling, but avoid duplicate navigation listeners.\n const cleanupContent = hasContentTests(config)\n ? setupContentTestListeners(config, previewState, nonce, false)\n : () => {};\n\n return () => {\n if (navigationRafId !== null) {\n window.cancelAnimationFrame(navigationRafId);\n }\n cleanupNavigation();\n cleanupContent();\n };\n }, [config, previewState, nonce]);\n\n const value = React.useMemo(\n () => ({\n config,\n storeId,\n storefrontAccessToken,\n isPreviewMode: previewState?.isPreview ?? false,\n previewTestId: previewState?.previewTestId ?? null,\n previewState,\n selectors: config?.selectors,\n countryCode,\n }),\n [config, storeId, storefrontAccessToken, previewState, countryCode],\n );\n\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. Orders won't be attributed to test variants.\",\n );\n }\n }, [storefrontAccessToken]);\n\n const configUrl = React.useMemo(() => getConfigUrl(storeId), [storeId]);\n\n const blockingScript = React.useMemo(() => {\n const timeout = preventFlickering ? flickerTimeout : 2000;\n return getSplitUrlBlockingScript({ configUrl, timeout });\n }, [configUrl, preventFlickering, flickerTimeout]);\n\n return (\n <ElevateContext.Provider value={value}>\n <script\n nonce={nonce}\n dangerouslySetInnerHTML={{ __html: blockingScript }}\n />\n {children}\n </ElevateContext.Provider>\n );\n}\n","/**\n * Custom Code Test Utilities\n *\n * Handles processing and execution of CUSTOM_CODE type tests.\n * Custom code tests inject CSS/JS into the page based on pathname matching.\n * Both control and non-control variations can have custom code.\n */\n\nimport type { ContentCustomCode, ElevateConfig } from \"../types\";\nimport type { PreviewState } from \"./preview\";\nimport { getAssignedVariant } from \"./assignment\";\nimport { applyCustomCode } from \"./content\";\nimport { onNavigate } from \"./navigation\";\n\n/**\n * Check if there are any active custom code tests\n */\nexport function hasCustomCodeTests(config: ElevateConfig): boolean {\n return config.tests.some(\n (test) => test.type === \"CUSTOM_CODE\" && test.enabled\n );\n}\n\n/**\n * Process and apply all custom code tests\n * This is the main entry point called by ElevateProvider\n */\nexport function processCustomCodeTests(\n config: ElevateConfig,\n previewState?: PreviewState | null,\n nonce?: string\n): void {\n if (typeof window === \"undefined\") return;\n\n // Check if there are any custom code tests\n if (!hasCustomCodeTests(config)) return;\n\n // Find all CUSTOM_CODE type tests\n const customCodeTests = config.tests.filter(\n (test) => test.type === \"CUSTOM_CODE\" && test.enabled\n );\n\n // Collect custom code from assigned variants\n const customCodes: ContentCustomCode[] = [];\n\n customCodeTests.forEach((test) => {\n const assignedVariantId = getAssignedVariant(test.testId);\n if (!assignedVariantId) return;\n\n const variation = test.variations.find((v) => v.id === assignedVariantId);\n if (!variation?.customCode) return;\n\n // Check if there's any code to inject\n if (variation.customCode.js || variation.customCode.css) {\n customCodes.push(variation.customCode);\n }\n });\n\n // Apply custom code if we have any\n if (customCodes.length > 0) {\n applyCustomCode(customCodes, nonce);\n }\n}\n\n/**\n * Re-process custom code tests (called on pathname change)\n */\nexport function reprocessCustomCodeTests(\n config: ElevateConfig,\n previewState?: PreviewState | null,\n nonce?: string\n): void {\n if (typeof window === \"undefined\") return;\n\n // Re-process custom code tests\n processCustomCodeTests(config, previewState, nonce);\n}\n\n/**\n * Setup event listeners for custom code test re-processing.\n *\n * Listens for ALL SPA navigation (pushState, replaceState, popstate)\n * via the shared navigation utility.\n */\nexport function setupCustomCodeTestListeners(\n config: ElevateConfig,\n previewState?: PreviewState | null,\n nonce?: string\n): () => void {\n if (typeof window === \"undefined\") return () => {};\n\n // Handle ALL SPA navigation (pushState, replaceState, popstate)\n const cleanupNavigation = onNavigate(() => {\n reprocessCustomCodeTests(config, previewState, nonce);\n });\n\n // Return cleanup function\n return () => {\n cleanupNavigation();\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 // Content test types\n ContentChange,\n ContentElement,\n ContentBlock,\n ContentCustomCode,\n ContentTestData,\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// Internal utilities (exported for advanced use cases)\nexport { hashString } 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 canAssignToTest,\n assignAllTests,\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// Split URL utilities\nexport {\n getSplitUrlBlockingScript,\n processSplitUrlTests,\n} from \"./handlers/splitUrl\";\nexport type { SplitUrlScriptOptions } from \"./handlers/splitUrl\";\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// Content test utilities\nexport {\n matchesWildcardPattern,\n applyContentChanges,\n applyContentElements,\n applyCustomCode,\n processContentTests,\n reprocessContentTests,\n setupContentTestListeners,\n hasContentTests,\n restoreOriginalElements,\n cleanupContentTests,\n} from \"./utils/content\";\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\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 { ElevateAnalytics } from \"./components/ElevateAnalytics\";\nexport type {\n UseAnalyticsHook,\n ElevateHydrogenAnalyticsProps,\n} from \"./components/ElevateAnalytics\";\n\nexport { useElevateConfig } from \"./contexts/ElevateContext\";\n\nexport type {\n TestTypeHandler,\n HandlerContext,\n HandlerVariantData,\n} from \"./handlers/types\";\nexport {\n registerHandler,\n getHandler,\n getAllHandlers,\n hasHandler,\n} from \"./handlers/registry\";\n\n// Handlers\nexport { contentHandler } from \"./handlers/content\";\n\nexport const VERSION = \"1.1.2\";\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","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","NAVIGATE_EVENT","patched","patchRefCount","originalPushState","originalReplaceState","patchHistory","args","unpatchHistory","onNavigate","callback","lastUrl","handler","currentUrl","getSplitUrlBlockingScript","options","configUrl","timeout","normalizeUrl","url","withProtocol","urlObj","urlMatches","currentUrl","urlList","normalizedCurrent","normalizedTarget","currentPath","targetPath","getRedirectBehavior","test","hasBeenRedirected","testId","getJsonCookie","markAsRedirected","redirectedTests","setCookie","findMatchingSplitUrlTest","config","variation","urls","performRedirect","targetUrl","finalUrl","navigate","pathname","processSplitUrlTests","previewState","match","matchedVariation","isControlUrl","redirectBehavior","assignedVariationId","getAssignedVariant","assignedVariation","v","targetUrls","hasSplitUrlTests","t","splitUrlHandler","test","context","variation","urls","urlMatches","extractShopifyId","gid","extractShopifyType","isShopifyGid","value","detectShopifyCurrency","shopify","ELEMENT_ID_PREFIX","NODE_TYPES","originalElements","injectedStyles","injectedScripts","injectedElements","isMobile","matchesWildcardPattern","pathname","pattern","trimmedPattern","suffix","checkIsMobile","convertToKebabCase","text","result","i","char","isTextNode","node","isElementNode","storeOriginalElement","selector","element","decodeCodeString","code","decoded","applyContentChanges","changes","currentPathname","matchingChanges","change","processedChanges","index","uniqueSelector","prop","value","kebabProp","hasImportant","cleanValue","key","applyContentElements","elements","elementId","target","domElement","createDOMElement","child","childElement","applyCustomCode","customCodes","nonce","injectCSS","injectJS","id","css","styleId","styleElement","js","scriptId","scriptElement","decodedJs","restoreOriginalElements","originalElement","cleanupContentTests","style","script","collectContentTestData","config","previewState","allChanges","allElements","allCustomCodes","test","assignedVariantId","getAssignedVariant","variation","v","hasContentTests","processContentTests","contentData","reprocessContentTests","currentIsMobile","setupContentTestListeners","listenToNavigation","handleResize","cleanupNavigation","onNavigate","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","handlers","registerHandler","handler","getHandler","type","getAllHandlers","hasHandler","extractProductHandleFromUrl","url","match","updatePriceElements","variation","variantId","currencyCode","selectors","variantPrices","priceAmount","compareAmount","selectorsConfig","selectorSet","priceSelector","el","formattedPrice","compareSelector","savingConfig","savingSelector","price","compare","saving","percentage","formattedSaving","pricePlusHandler","test","context","testData","productId","productHandle","urlHandle","matchesProductId","matchesHandle","matchesTestPathnames","test","currentUrl","pathnames","pathname","pattern","matchesWildcardPattern","contentHandler","context","variation","_context","testData","matchesTestPathnames","test","currentUrl","pathnames","pathname","pattern","matchesWildcardPattern","customCodeHandler","context","variation","_context","testData","registerHandler","pricePlusHandler","splitUrlHandler","contentHandler","customCodeHandler","buildHandlerContext","selectors","url","pathname","productHandle","productMatch","variantId","variantParam","useExperiment","testId","config","contextPreviewState","useElevateConfig","variant","setVariant","React","isLoading","setIsLoading","testConfig","test","previewState","getPreviewState","hasWarnedRef","effectAppliedRef","isMounted","assignedVariantId","getAssignedVariant","assigned","v","handler","getHandler","context","enrichedVariant","effectKey","handlerData","trackViews","React","useRef","useCallback","hasCustomCodeTests","config","test","processCustomCodeTests","previewState","nonce","customCodeTests","customCodes","assignedVariantId","getAssignedVariant","variation","v","applyCustomCode","jsx","jsxs","CDN_BASE_URL","getStoreName","storeId","getConfigUrl","useIsomorphicLayoutEffect","React","ElevateProvider","storefrontAccessToken","preventFlickering","flickerTimeout","nonce","children","config","setConfig","previewState","setPreviewState","countryCode","setCountryCode","initAnalytics","preview","getPreviewState","country","getCountryCode","revealContent","useCallback","fetchConfig","currentPreviewState","backendData","url","resolve","reject","script","data","parsedConfig","parseBackendConfig","assignAllTests","t","processSplitUrlTests","err","applySplitUrlTests","hasSplitUrlTests","applyNonRedirectTests","hasContentTests","processContentTests","hasCustomCodeTests","processCustomCodeTests","navigationRafId","cleanupNavigation","onNavigate","cleanupContent","setupContentTestListeners","value","hasWarnedRef","useRef","configUrl","blockingScript","getSplitUrlBlockingScript","ElevateContext","VERSION"]}