@deriv-com/analytics
Version:
Comprehensive analytics package for Deriv applications. Provides unified event tracking, A/B testing, and user analytics through RudderStack, PostHog and GrowthBook integrations with built-in caching and offline support.
1 lines • 11 kB
Source Map (JSON)
{"version":3,"sources":["../src/utils/urls.ts","../src/utils/helpers.ts"],"names":["cloudflareTrace","growthbookApi","rudderstackDataplane","posthogApiHost","posthogUiHost","getPosthogApiHost","hostname","allowedDomains","internalEmailDomains","createLogger","prefix","isDebugEnabled","args","isInternalEmail","email","domain","isUUID","str","countryPromise","getCountry","cookieCountry","Cookies","response","text","v","cleanObject","obj","out","i","cleaned","hasKeys","key","_flattenInto","target","value","flattenObject"],"mappings":"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","file":"chunk-TE3DYZVQ.mjs","sourcesContent":["export const cloudflareTrace = 'https://deriv.com/cdn-cgi/trace'\nexport const growthbookApi = 'https://cdn.growthbook.io'\nexport const rudderstackDataplane = 'https://deriv-dataplane.rudderstack.com'\nexport const posthogApiHost = 'https://ph.deriv.com'\nexport const posthogUiHost = 'https://us.posthog.com'\n\nexport const getPosthogApiHost = (): string => {\n if (typeof window === 'undefined') return posthogApiHost\n const hostname = window.location.hostname\n if (hostname.includes('.deriv.me')) return 'https://ph.deriv.me'\n if (hostname.includes('.deriv.be')) return 'https://ph.deriv.be'\n if (hostname.includes('.deriv.ae')) return 'https://ph.deriv.ae'\n return posthogApiHost\n}\n\nexport const allowedDomains = ['deriv.com', 'deriv.be', 'deriv.me', 'deriv.team', 'deriv.ae'] as const\n\nexport const internalEmailDomains = [\n 'deriv.com',\n 'derivcrypto.com',\n 'besquare.my',\n 'besquare.com.my',\n 'ewallet.exchange',\n 'champion-fx.com',\n 'opalstraits.com',\n 'binary.com',\n 'binary.marketing',\n 'championgbs.com',\n '4x.my',\n 're-work.dev',\n 'regentmarkets.com',\n '4x.com',\n 'binary.me',\n 'deriv.team',\n 'firstsource.io',\n 'firstsource.tech',\n 'deriv.hr',\n 'vmgbpo.net',\n 'mailisk.net',\n 'mailosaur.net',\n 'mobileapps.mailisk.net',\n 'w3e180zd.mailosaur.net',\n] as const\n\nexport const getAllowedDomain = (): string => {\n if (typeof window === 'undefined') return '.deriv.com'\n const hostname = window.location.hostname\n\n if (hostname === 'localhost') return ''\n\n const matched = allowedDomains.find(d => hostname.includes(d))\n return matched ? `.${matched}` : '.deriv.com'\n}\n","import Cookies from 'js-cookie'\nimport { cloudflareTrace, internalEmailDomains } from './urls'\n\n/**\n * Creates a prefixed logger that only outputs when debug mode is enabled.\n * Pass a getter function so the logger always reads the latest debug state.\n *\n * @param prefix - Optional provider name appended after [ANALYTIC], e.g. '[RudderStack]'\n * @param isDebugEnabled - A function that returns the current debug flag value\n * @returns A log function with the same signature as console.log\n *\n * @example\n * // In a class\n * private log = createLogger('[RudderStack]', () => this.debug)\n *\n * // In a closure\n * const log = createLogger('', () => _debug)\n */\nexport const createLogger =\n (prefix: string, isDebugEnabled: () => boolean) =>\n (...args: any[]): void => {\n if (isDebugEnabled()) console.log(`[ANALYTIC]${prefix}`, ...args)\n }\n\nexport const isInternalEmail = (email: string): boolean => {\n const domain = email.split('@')[1]?.toLowerCase()\n if (!domain) return false\n return (internalEmailDomains as readonly string[]).includes(domain)\n}\n\nexport const isUUID = (str: string): boolean => {\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i\n return uuidRegex.test(str)\n}\n\ntype TraceData = {\n loc?: string\n}\n\nlet countryPromise: Promise<string> | null = null\n\n/**\n * Fetches the country information based on Cloudflare's trace data or a fallback from cookies.\n * This function attempts to retrieve the country location by first fetching trace data from Cloudflare\n * and then falling back to the location stored in the cookies if the fetch fails.\n *\n * @returns {Promise<string>} A Promise that resolves to a string representing the country code in lowercase.\n * Returns an empty string if no country data is available or if an error occurs.\n *\n * @example\n * // Returns the country code in lowercase based on Cloudflare's trace data or cookies.\n * getCountry().then(country => console.log(country));\n */\nexport const getCountry = async (): Promise<string> => {\n if (countryPromise) return countryPromise\n\n const cookieCountry = JSON.parse(Cookies.get('website_status') || '{}')?.clients_country\n\n countryPromise = (async () => {\n try {\n const response = await fetch(cloudflareTrace).catch(() => null)\n if (!response) return cookieCountry || ''\n\n const text = await response.text().catch(() => '')\n if (!text) return cookieCountry || ''\n\n const data: TraceData = Object.fromEntries(text.split('\\n').map(v => v.split('=', 2)))\n return data.loc?.toLowerCase() || cookieCountry || ''\n } catch {\n return cookieCountry || ''\n }\n })()\n\n return countryPromise\n}\n\n/**\n * Recursively cleans an object by removing undefined, null, empty strings, empty objects, and empty arrays\n * Used to sanitize event properties before sending to analytics providers\n *\n * @param obj - The object to clean\n * @returns The cleaned object, or undefined if the result would be empty\n */\nexport const cleanObject = (obj: any): any => {\n if (obj == null || typeof obj !== 'object') return obj\n\n if (Array.isArray(obj)) {\n const out: any[] = []\n for (let i = 0; i < obj.length; i++) {\n const v = cleanObject(obj[i])\n if (v !== undefined && v !== null) out.push(v)\n }\n return out.length ? out : undefined\n }\n\n const cleaned: Record<string, any> = {}\n let hasKeys = false\n for (const key in obj) {\n if (!Object.prototype.hasOwnProperty.call(obj, key)) continue\n const v = cleanObject(obj[key])\n if (\n v === undefined ||\n v === null ||\n v === '' ||\n (typeof v === 'object' && !Array.isArray(v) && Object.keys(v).length === 0) ||\n (Array.isArray(v) && v.length === 0)\n )\n continue\n cleaned[key] = v\n hasKeys = true\n }\n return hasKeys ? cleaned : undefined\n}\n\n/**\n * Flattens a nested object structure into a single-level object\n * Lifts all nested properties to the top level without prefixing\n *\n * @param obj - The object to flatten\n * @returns A flattened object with all nested properties at the top level\n *\n * @example\n * flattenObject({ action: 'click', event_metadata: { version: 2, user_language: 'en' } })\n * // Returns: { action: 'click', version: 2, user_language: 'en' }\n *\n * flattenObject({ form_name: 'signup', cta_information: { cta_name: 'signup', section_name: 'header' } })\n * // Returns: { form_name: 'signup', cta_name: 'signup', section_name: 'header' }\n */\nconst _flattenInto = (obj: Record<string, any>, target: Record<string, any>): void => {\n for (const key in obj) {\n if (!Object.prototype.hasOwnProperty.call(obj, key)) continue\n const value = obj[key]\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n _flattenInto(value, target)\n } else {\n target[key] = value\n }\n }\n}\n\nexport const flattenObject = (obj: any): Record<string, any> => {\n if (obj == null || typeof obj !== 'object' || Array.isArray(obj)) {\n return obj\n }\n const target: Record<string, any> = {}\n _flattenInto(obj, target)\n return target\n}\n"]}