UNPKG

@tinytapanalytics/sdk

Version:

Behavioral psychology platform that detects visitor frustration, predicts abandonment, and helps you save at-risk conversions in real-time

183 lines (160 loc) 5.14 kB
/** * Minimal TinyTapAnalytics SDK - Core functionality only * Target: <15KB gzipped */ import { TinyTapAnalyticsConfig, EventData, TrackingOptions } from './types/index'; import packageJson from '../package.json'; interface MinimalConfig { apiKey?: string; websiteId?: string; endpoint?: string; debug?: boolean; } export class MinimalTinyTapAnalytics { private config: MinimalConfig; private sessionId: string; private userId?: string; constructor(config: MinimalConfig = {}) { // Use build-time injected API URL or config override const defaultEndpoint = process.env.API_URL || 'https://api.tinytapanalytics.com'; this.config = { endpoint: defaultEndpoint, debug: false, ...config }; this.sessionId = this.generateSessionId(); } /** * Set user identification */ public identify(userId: string): void { this.userId = userId; this.track('identify', { user_id: userId }); } /** * Track a custom event */ public async track(eventType: string, data?: Record<string, any>): Promise<void> { // Validate required configuration if (!this.config.websiteId) { const error = 'TinyTapAnalytics: websiteId is required but not configured'; if (this.config.debug) { console.error(error); } throw new Error(error); } const payload = { website_id: this.config.websiteId, event_type: eventType, user_id: this.userId, session_id: this.sessionId, timestamp: new Date().toISOString(), user_agent: navigator.userAgent, page_url: window.location.href, metadata: { ...data, viewport_width: window.innerWidth, viewport_height: window.innerHeight, sdk_version: `${packageJson.version}-minimal` } }; try { await this.sendEvent(payload); if (this.config.debug) { console.log('TinyTapAnalytics: Event tracked', payload); } } catch (error) { if (this.config.debug) { console.error('TinyTapAnalytics: Failed to track event', error); } throw error; } } /** * Track a conversion */ public async trackConversion(value: number, currency = 'USD', metadata?: Record<string, any>): Promise<void> { await this.track('conversion', { value, currency, ...metadata }); } /** * Track a page view */ public async trackPageView(): Promise<void> { await this.track('page_view', { url: window.location.href, title: document.title, referrer: document.referrer }); } /** * Track element click */ public async trackClick(selector: string, metadata?: Record<string, any>): Promise<void> { await this.track('click', { element: selector, page_url: window.location.href, ...metadata }); } private async sendEvent(payload: any): Promise<void> { const response = await fetch(`${this.config.endpoint}/api/v1/events`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.config.apiKey}` }, body: JSON.stringify(payload) }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } } private generateSessionId(): string { return 'ciq_' + Date.now().toString(36) + Math.random().toString(36).substring(2); } } // Auto-initialization for script tag usage only // Only run when loaded via <script> tag, not when imported as a module // Skip auto-initialization in test environment if (typeof window !== 'undefined' && process.env.NODE_ENV !== 'test') { const scriptTag = document.currentScript as HTMLScriptElement; const hasGlobalConfig = !!(window as any).__tinytapanalytics_config; // Only auto-initialize if loaded via script tag OR has global config if (scriptTag || hasGlobalConfig) { const config = (window as any).__tinytapanalytics_config || {}; if (scriptTag?.dataset.apiKey) { config.apiKey = scriptTag.dataset.apiKey; config.websiteId = scriptTag.dataset.websiteId || scriptTag.dataset.apiKey; } const sdk = new MinimalTinyTapAnalytics(config); (window as any).TinyTapAnalytics = sdk; // Log initialization info if (config.debug || !config.websiteId) { console.log('TinyTapAnalytics Minimal SDK initialized', { websiteId: config.websiteId || 'NOT SET - Events will fail!', apiKey: config.apiKey ? '***' : 'NOT SET', endpoint: config.endpoint, debug: config.debug }); } // Auto-track page view with error handling const trackInitialPageView = async () => { try { await sdk.trackPageView(); } catch (error) { console.error('TinyTapAnalytics: Failed to track initial page view', error); // Don't throw - allow the SDK to continue working } }; if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', trackInitialPageView); } else { trackInitialPageView(); } } } export default MinimalTinyTapAnalytics;