UNPKG

@calico/analytics

Version:

Analytics SDK for Calico Dashboard - Easy integration for tracking user interactions and configurations

1 lines 14.2 kB
{"version":3,"sources":["../src/index.ts"],"names":["CalicoAnalytics","config","existingId","newId","target","metadata","event","value","eventData","sync","events","payload","blob","response","error","userId","traits","analyticsInstance","init","track","identify","flush","reset","autoRegister","appName","appUrl","endpoint","registerEndpoint","data","index_default"],"mappings":"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","file":"index.mjs","sourcesContent":["export type AnalyticsEvent =\n | 'VIEW'\n | 'INTERACT'\n | 'CONFIGURE'\n | 'SHARE'\n | 'EXPORT'\n | 'AR_VIEW'\n | 'ADD_TO_CART'\n | 'PURCHASE'\n\nexport interface CalicoAnalyticsConfig {\n apiKey: string\n endpoint?: string\n debug?: boolean\n autoTrack?: boolean\n batchSize?: number\n flushInterval?: number\n}\n\nexport interface AnalyticsEventData {\n event: AnalyticsEvent\n metadata?: Record<string, any>\n value?: number\n sessionId?: string\n}\n\nclass CalicoAnalytics {\n private apiKey: string\n private endpoint: string\n private debug: boolean\n private sessionId: string\n private queue: AnalyticsEventData[] = []\n private batchSize: number\n private flushInterval: number\n private flushTimer?: NodeJS.Timeout\n private initialized: boolean = false\n\n constructor(config: CalicoAnalyticsConfig) {\n this.apiKey = config.apiKey\n this.endpoint = config.endpoint || 'http://localhost:3011/api/analytics/track'\n this.debug = config.debug || false\n this.batchSize = config.batchSize || 10\n this.flushInterval = config.flushInterval || 30000 // 30 seconds\n this.sessionId = this.generateSessionId()\n\n if (config.autoTrack !== false) {\n this.setupAutoTracking()\n }\n\n this.startFlushTimer()\n this.initialized = true\n\n if (this.debug) {\n console.log('[Calico Analytics] Initialized')\n }\n }\n\n private generateSessionId(): string {\n // Check if we have a session ID in sessionStorage\n if (typeof window !== 'undefined' && window.sessionStorage) {\n const existingId = window.sessionStorage.getItem('calico_session_id')\n if (existingId) return existingId\n\n const newId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`\n window.sessionStorage.setItem('calico_session_id', newId)\n return newId\n }\n\n return `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`\n }\n\n private setupAutoTracking(): void {\n if (typeof window === 'undefined') return\n\n // Track page views\n this.track('VIEW')\n\n // Track interactions\n document.addEventListener('click', (e) => {\n const target = e.target as HTMLElement\n\n // Track specific interactions\n if (target.matches('button, a, [role=\"button\"]')) {\n const metadata: Record<string, any> = {\n element: target.tagName.toLowerCase(),\n text: target.textContent?.slice(0, 50)\n }\n\n if (target.id) metadata.id = target.id\n if (target.className) metadata.className = target.className\n\n this.track('INTERACT', metadata)\n }\n })\n\n // Track page visibility changes\n document.addEventListener('visibilitychange', () => {\n if (document.hidden) {\n this.flush() // Flush events when page is hidden\n }\n })\n\n // Flush on page unload\n window.addEventListener('beforeunload', () => {\n this.flush(true) // Force sync flush on unload\n })\n }\n\n private startFlushTimer(): void {\n if (this.flushTimer) clearInterval(this.flushTimer)\n\n this.flushTimer = setInterval(() => {\n if (this.queue.length > 0) {\n this.flush()\n }\n }, this.flushInterval)\n }\n\n public track(event: AnalyticsEvent, metadata?: Record<string, any>, value?: number): void {\n if (!this.initialized) {\n // Fail silently if not initialized\n return\n }\n\n const eventData: AnalyticsEventData = {\n event,\n metadata,\n value,\n sessionId: this.sessionId\n }\n\n this.queue.push(eventData)\n\n // Flush if batch size is reached\n if (this.queue.length >= this.batchSize) {\n this.flush()\n }\n }\n\n public async flush(sync: boolean = false): Promise<void> {\n if (this.queue.length === 0) return\n\n const events = [...this.queue]\n this.queue = []\n\n const payload = {\n apiKey: this.apiKey,\n events,\n timestamp: new Date().toISOString()\n }\n\n // Remove verbose flushing logs\n\n try {\n if (sync && typeof navigator !== 'undefined' && navigator.sendBeacon) {\n // Use sendBeacon for sync flush (page unload)\n const blob = new Blob([JSON.stringify(payload)], { type: 'application/json' })\n navigator.sendBeacon(this.endpoint, blob)\n } else {\n // Use fetch for async flush\n const response = await fetch(this.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey\n },\n body: JSON.stringify(payload)\n })\n\n if (!response.ok) {\n throw new Error(`Failed to send analytics: ${response.statusText}`)\n }\n\n // Success - no need to log unless debugging\n }\n } catch (error) {\n // Fail silently - analytics shouldn't break the app\n if (this.debug) {\n console.warn('[Calico Analytics] Failed to send events:', error)\n }\n // Re-queue events on failure\n this.queue.unshift(...events)\n }\n }\n\n public identify(userId: string, traits?: Record<string, any>): void {\n // Store user identification\n if (typeof window !== 'undefined' && window.localStorage) {\n window.localStorage.setItem('calico_user_id', userId)\n if (traits) {\n window.localStorage.setItem('calico_user_traits', JSON.stringify(traits))\n }\n }\n\n // Track identification event\n this.track('INTERACT', {\n action: 'identify',\n userId,\n ...traits\n })\n }\n\n public reset(): void {\n // Clear stored data\n if (typeof window !== 'undefined') {\n window.sessionStorage.removeItem('calico_session_id')\n window.localStorage.removeItem('calico_user_id')\n window.localStorage.removeItem('calico_user_traits')\n }\n\n // Generate new session\n this.sessionId = this.generateSessionId()\n this.queue = []\n }\n\n public destroy(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer)\n }\n this.flush(true)\n this.initialized = false\n }\n}\n\n// Singleton instance\nlet analyticsInstance: CalicoAnalytics | null = null\n\nexport function init(config: CalicoAnalyticsConfig): CalicoAnalytics {\n if (analyticsInstance) {\n console.warn('[Calico Analytics] Already initialized. Destroying previous instance.')\n analyticsInstance.destroy()\n }\n\n analyticsInstance = new CalicoAnalytics(config)\n return analyticsInstance\n}\n\nexport function track(event: AnalyticsEvent, metadata?: Record<string, any>, value?: number): void {\n if (!analyticsInstance) {\n // Fail silently if not initialized\n return\n }\n\n analyticsInstance.track(event, metadata, value)\n}\n\nexport function identify(userId: string, traits?: Record<string, any>): void {\n if (!analyticsInstance) {\n // Fail silently if not initialized\n return\n }\n\n analyticsInstance.identify(userId, traits)\n}\n\nexport function flush(): Promise<void> {\n if (!analyticsInstance) {\n // Fail silently if not initialized\n return Promise.resolve()\n }\n\n return analyticsInstance.flush()\n}\n\nexport function reset(): void {\n if (!analyticsInstance) {\n // Fail silently if not initialized\n return\n }\n\n analyticsInstance.reset()\n}\n\n// Auto-register function for self-registration\nexport async function autoRegister(appName: string, appUrl: string, endpoint?: string): Promise<string> {\n const registerEndpoint = endpoint?.replace('/track', '/register') || 'https://dashboard.calico.media/api/analytics/register'\n\n try {\n const response = await fetch(registerEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n name: appName,\n url: appUrl\n })\n })\n\n if (!response.ok) {\n throw new Error(`Registration failed: ${response.statusText}`)\n }\n\n const data = await response.json()\n\n if (data.apiKey) {\n console.log(`[Calico Analytics] Registered successfully. API Key: ${data.apiKey}`)\n return data.apiKey\n } else {\n throw new Error('No API key received from registration')\n }\n } catch (error) {\n console.error('[Calico Analytics] Auto-registration failed:', error)\n throw error\n }\n}\n\nexport default {\n init,\n track,\n identify,\n flush,\n reset,\n autoRegister\n}"]}