@grainql/analytics-web 2.2.0 → 2.3.0
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/attribution.d.ts +47 -0
- package/dist/attribution.d.ts.map +1 -0
- package/dist/attribution.js +228 -0
- package/dist/cjs/attribution.d.ts +47 -0
- package/dist/cjs/attribution.d.ts.map +1 -0
- package/dist/cjs/attribution.js +228 -0
- package/dist/cjs/attribution.js.map +1 -0
- package/dist/cjs/heartbeat.d.ts +1 -0
- package/dist/cjs/heartbeat.d.ts.map +1 -1
- package/dist/cjs/heartbeat.js +1 -1
- package/dist/cjs/heartbeat.js.map +1 -1
- package/dist/cjs/index.d.ts +25 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/page-tracking.d.ts +25 -0
- package/dist/cjs/page-tracking.d.ts.map +1 -1
- package/dist/cjs/page-tracking.js +158 -9
- package/dist/cjs/page-tracking.js.map +1 -1
- package/dist/esm/attribution.d.ts +47 -0
- package/dist/esm/attribution.d.ts.map +1 -0
- package/dist/esm/attribution.js +218 -0
- package/dist/esm/attribution.js.map +1 -0
- package/dist/esm/heartbeat.d.ts +1 -0
- package/dist/esm/heartbeat.d.ts.map +1 -1
- package/dist/esm/heartbeat.js +1 -1
- package/dist/esm/heartbeat.js.map +1 -1
- package/dist/esm/index.d.ts +25 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/page-tracking.d.ts +25 -0
- package/dist/esm/page-tracking.d.ts.map +1 -1
- package/dist/esm/page-tracking.js +158 -9
- package/dist/esm/page-tracking.js.map +1 -1
- package/dist/heartbeat.d.ts +1 -0
- package/dist/heartbeat.d.ts.map +1 -1
- package/dist/heartbeat.js +1 -1
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.global.dev.js +464 -11
- package/dist/index.global.dev.js.map +3 -3
- package/dist/index.global.js +2 -2
- package/dist/index.global.js.map +4 -4
- package/dist/index.js +157 -1
- package/dist/index.mjs +155 -0
- package/dist/page-tracking.d.ts +25 -0
- package/dist/page-tracking.d.ts.map +1 -1
- package/dist/page-tracking.js +158 -9
- package/dist/react/index.d.ts +44 -519
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +54 -1517
- package/dist/react/index.mjs +42 -1514
- package/package.json +1 -1
- package/dist/react/activity.d.ts +0 -59
- package/dist/react/activity.d.ts.map +0 -1
- package/dist/react/activity.js +0 -130
- package/dist/react/activity.mjs +0 -126
- package/dist/react/consent.d.ts +0 -72
- package/dist/react/consent.d.ts.map +0 -1
- package/dist/react/consent.js +0 -195
- package/dist/react/consent.mjs +0 -191
- package/dist/react/cookies.d.ts +0 -28
- package/dist/react/cookies.d.ts.map +0 -1
- package/dist/react/cookies.js +0 -94
- package/dist/react/cookies.mjs +0 -88
- package/dist/react/heartbeat.d.ts +0 -47
- package/dist/react/heartbeat.d.ts.map +0 -1
- package/dist/react/heartbeat.js +0 -119
- package/dist/react/heartbeat.mjs +0 -115
- package/dist/react/page-tracking.d.ts +0 -60
- package/dist/react/page-tracking.d.ts.map +0 -1
- package/dist/react/page-tracking.js +0 -179
- package/dist/react/page-tracking.mjs +0 -175
- package/dist/react/react/index.d.ts +0 -47
- package/dist/react/react/index.d.ts.map +0 -1
- package/dist/react/react/index.js +0 -58
- package/dist/react/react/index.mjs +0 -44
- /package/dist/react/{react/GrainProvider.d.ts → GrainProvider.d.ts} +0 -0
- /package/dist/react/{react/GrainProvider.d.ts.map → GrainProvider.d.ts.map} +0 -0
- /package/dist/react/{react/GrainProvider.js → GrainProvider.js} +0 -0
- /package/dist/react/{react/GrainProvider.mjs → GrainProvider.mjs} +0 -0
- /package/dist/react/{react/components → components}/ConsentBanner.d.ts +0 -0
- /package/dist/react/{react/components → components}/ConsentBanner.d.ts.map +0 -0
- /package/dist/react/{react/components → components}/ConsentBanner.js +0 -0
- /package/dist/react/{react/components → components}/ConsentBanner.mjs +0 -0
- /package/dist/react/{react/components → components}/CookieNotice.d.ts +0 -0
- /package/dist/react/{react/components → components}/CookieNotice.d.ts.map +0 -0
- /package/dist/react/{react/components → components}/CookieNotice.js +0 -0
- /package/dist/react/{react/components → components}/CookieNotice.mjs +0 -0
- /package/dist/react/{react/components → components}/PrivacyPreferenceCenter.d.ts +0 -0
- /package/dist/react/{react/components → components}/PrivacyPreferenceCenter.d.ts.map +0 -0
- /package/dist/react/{react/components → components}/PrivacyPreferenceCenter.js +0 -0
- /package/dist/react/{react/components → components}/PrivacyPreferenceCenter.mjs +0 -0
- /package/dist/react/{react/context.d.ts → context.d.ts} +0 -0
- /package/dist/react/{react/context.d.ts.map → context.d.ts.map} +0 -0
- /package/dist/react/{react/context.js → context.js} +0 -0
- /package/dist/react/{react/context.mjs → context.mjs} +0 -0
- /package/dist/react/{react/hooks → hooks}/useAllConfigs.d.ts +0 -0
- /package/dist/react/{react/hooks → hooks}/useAllConfigs.d.ts.map +0 -0
- /package/dist/react/{react/hooks → hooks}/useAllConfigs.js +0 -0
- /package/dist/react/{react/hooks → hooks}/useAllConfigs.mjs +0 -0
- /package/dist/react/{react/hooks → hooks}/useConfig.d.ts +0 -0
- /package/dist/react/{react/hooks → hooks}/useConfig.d.ts.map +0 -0
- /package/dist/react/{react/hooks → hooks}/useConfig.js +0 -0
- /package/dist/react/{react/hooks → hooks}/useConfig.mjs +0 -0
- /package/dist/react/{react/hooks → hooks}/useConsent.d.ts +0 -0
- /package/dist/react/{react/hooks → hooks}/useConsent.d.ts.map +0 -0
- /package/dist/react/{react/hooks → hooks}/useConsent.js +0 -0
- /package/dist/react/{react/hooks → hooks}/useConsent.mjs +0 -0
- /package/dist/react/{react/hooks → hooks}/useDataDeletion.d.ts +0 -0
- /package/dist/react/{react/hooks → hooks}/useDataDeletion.d.ts.map +0 -0
- /package/dist/react/{react/hooks → hooks}/useDataDeletion.js +0 -0
- /package/dist/react/{react/hooks → hooks}/useDataDeletion.mjs +0 -0
- /package/dist/react/{react/hooks → hooks}/useGrainAnalytics.d.ts +0 -0
- /package/dist/react/{react/hooks → hooks}/useGrainAnalytics.d.ts.map +0 -0
- /package/dist/react/{react/hooks → hooks}/useGrainAnalytics.js +0 -0
- /package/dist/react/{react/hooks → hooks}/useGrainAnalytics.mjs +0 -0
- /package/dist/react/{react/hooks → hooks}/usePrivacyPreferences.d.ts +0 -0
- /package/dist/react/{react/hooks → hooks}/usePrivacyPreferences.d.ts.map +0 -0
- /package/dist/react/{react/hooks → hooks}/usePrivacyPreferences.js +0 -0
- /package/dist/react/{react/hooks → hooks}/usePrivacyPreferences.mjs +0 -0
- /package/dist/react/{react/hooks → hooks}/useTrack.d.ts +0 -0
- /package/dist/react/{react/hooks → hooks}/useTrack.d.ts.map +0 -0
- /package/dist/react/{react/hooks → hooks}/useTrack.js +0 -0
- /package/dist/react/{react/hooks → hooks}/useTrack.mjs +0 -0
- /package/dist/react/{react/types.d.ts → types.d.ts} +0 -0
- /package/dist/react/{react/types.d.ts.map → types.d.ts.map} +0 -0
- /package/dist/react/{react/types.js → types.js} +0 -0
- /package/dist/react/{react/types.mjs → types.mjs} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/index.ts", "../src/consent.ts", "../src/cookies.ts", "../src/activity.ts", "../src/heartbeat.ts", "../src/page-tracking.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Grain Analytics Web SDK\n * A lightweight, dependency-free TypeScript SDK for sending analytics events to Grain's REST API\n */\n\nimport { ConsentManager, ConsentState, ConsentMode } from './consent';\nimport { setCookie, getCookie, deleteCookie, areCookiesEnabled, CookieConfig } from './cookies';\nimport { ActivityDetector } from './activity';\nimport { HeartbeatManager, type HeartbeatTracker } from './heartbeat';\nimport { PageTrackingManager, type PageTracker } from './page-tracking';\n\nexport interface GrainEvent {\n eventName: string;\n userId?: string;\n properties?: Record<string, unknown>;\n timestamp?: Date;\n}\n\nexport interface EventPayload {\n eventName: string;\n userId: string;\n properties: Record<string, unknown>;\n}\n\nexport type AuthStrategy = 'NONE' | 'SERVER_SIDE' | 'JWT';\n\nexport interface AuthProvider {\n getToken(): Promise<string> | string;\n}\n\n// Re-export privacy types\nexport type { ConsentState, ConsentMode, CookieConfig };\n\nexport interface GrainConfig {\n tenantId: string;\n apiUrl?: string;\n authStrategy?: AuthStrategy;\n secretKey?: string; // For SERVER_SIDE auth\n authProvider?: AuthProvider; // For JWT auth\n userId?: string; // Global user ID for all events\n batchSize?: number;\n flushInterval?: number; // milliseconds\n retryAttempts?: number;\n retryDelay?: number; // milliseconds\n maxEventsPerRequest?: number; // Maximum events to send in a single API request\n debug?: boolean;\n // Remote Config options\n defaultConfigurations?: Record<string, string>; // Default values for configurations\n configCacheKey?: string; // Custom cache key for configurations\n configRefreshInterval?: number; // Auto-refresh interval in milliseconds (default: 5 minutes)\n enableConfigCache?: boolean; // Enable/disable configuration caching (default: true)\n // Privacy & Consent options\n consentMode?: ConsentMode; // 'opt-in' | 'opt-out' | 'disabled' (default: 'opt-out')\n waitForConsent?: boolean; // Queue events until consent is granted (default: false)\n enableCookies?: boolean; // Use cookies for persistent user ID (default: false)\n cookieOptions?: CookieConfig; // Cookie configuration options\n anonymizeIP?: boolean; // Anonymize IP addresses (default: false)\n disableAutoProperties?: boolean; // Disable automatic property collection (default: false)\n allowedProperties?: string[]; // Whitelist of allowed event properties (default: all)\n // Automatic Tracking options\n enableHeartbeat?: boolean; // Enable heartbeat tracking (default: true)\n heartbeatActiveInterval?: number; // Active interval in ms (default: 120000 - 2 min)\n heartbeatInactiveInterval?: number; // Inactive interval in ms (default: 300000 - 5 min)\n enableAutoPageView?: boolean; // Enable automatic page view tracking (default: true)\n stripQueryParams?: boolean; // Strip query params from URLs (default: true)\n}\n\nexport interface SendEventOptions {\n flush?: boolean; // Force immediate send\n}\n\nexport interface SetPropertyOptions {\n userId?: string; // Override global userId\n}\n\nexport interface LoginOptions {\n userId?: string; // User ID to set\n authToken?: string; // Auth token to set for JWT auth\n authStrategy?: AuthStrategy; // Override auth strategy\n}\n\nexport interface PropertyPayload {\n userId: string;\n [key: string]: string; // All property values must be strings\n}\n\n// Remote Config interfaces\nexport interface RemoteConfigRequest {\n userId: string;\n immediateKeys: string[];\n properties?: Record<string, string>;\n}\n\nexport interface RemoteConfigResponse {\n userId: string;\n snapshotId: string;\n configurations: Record<string, string>;\n isFinal: boolean;\n qualifiedSegments: string[];\n qualifiedRuleSets: string[];\n timestamp: string;\n isFromCache: boolean;\n}\n\nexport interface RemoteConfigOptions {\n immediateKeys?: string[];\n properties?: Record<string, string>;\n userId?: string; // Override global userId\n forceRefresh?: boolean; // Force fetch from API, bypass cache\n}\n\nexport interface RemoteConfigCache {\n configurations: Record<string, string>;\n snapshotId: string;\n timestamp: string;\n userId: string;\n}\n\nexport type ConfigChangeListener = (configurations: Record<string, string>) => void;\n\n// Template event interfaces\nexport interface LoginEventProperties extends Record<string, unknown> {\n method?: string; // 'email', 'google', 'facebook', etc.\n success?: boolean;\n errorMessage?: string;\n loginAttempt?: number;\n rememberMe?: boolean;\n twoFactorEnabled?: boolean;\n}\n\nexport interface SignupEventProperties extends Record<string, unknown> {\n method?: string; // 'email', 'google', 'facebook', etc.\n source?: string; // 'landing_page', 'referral', 'ad', etc.\n plan?: string; // 'free', 'pro', 'enterprise', etc.\n success?: boolean;\n errorMessage?: string;\n}\n\nexport interface CheckoutEventProperties extends Record<string, unknown> {\n orderId?: string;\n total?: number;\n currency?: string;\n items?: Array<{\n id: string;\n name: string;\n price: number;\n quantity: number;\n }>;\n paymentMethod?: string; // 'credit_card', 'paypal', 'stripe', etc.\n success?: boolean;\n errorMessage?: string;\n couponCode?: string;\n discount?: number;\n}\n\nexport interface PageViewEventProperties extends Record<string, unknown> {\n page?: string;\n title?: string;\n referrer?: string;\n url?: string;\n userAgent?: string;\n screenResolution?: string;\n viewportSize?: string;\n}\n\nexport interface PurchaseEventProperties extends Record<string, unknown> {\n orderId?: string;\n total?: number;\n currency?: string;\n items?: Array<{\n id: string;\n name: string;\n price: number;\n quantity: number;\n category?: string;\n }>;\n paymentMethod?: string;\n shippingMethod?: string;\n tax?: number;\n shipping?: number;\n discount?: number;\n couponCode?: string;\n}\n\nexport interface SearchEventProperties extends Record<string, unknown> {\n query?: string;\n results?: number;\n filters?: Record<string, unknown>;\n sortBy?: string;\n category?: string;\n success?: boolean;\n}\n\nexport interface AddToCartEventProperties extends Record<string, unknown> {\n itemId?: string;\n itemName?: string;\n price?: number;\n quantity?: number;\n currency?: string;\n category?: string;\n variant?: string;\n}\n\nexport interface RemoveFromCartEventProperties extends Record<string, unknown> {\n itemId?: string;\n itemName?: string;\n price?: number;\n quantity?: number;\n currency?: string;\n category?: string;\n variant?: string;\n}\n\n// Error handling interfaces\nexport interface ErrorDigest {\n eventCount: number;\n totalProperties: number;\n totalSize: number;\n eventNames: string[];\n userIds: string[];\n}\n\nexport interface FormattedError {\n code: string;\n message: string;\n digest: ErrorDigest;\n timestamp: string;\n context: string;\n originalError?: unknown;\n}\n\n/**\n * Main Grain Analytics client\n * \n * Features:\n * - Automatic UUIDv4 generation for anonymous users\n * - Login/logout functionality for dynamic auth token injection\n * - Persistent anonymous user ID across sessions (when consent is granted)\n * - Support for multiple auth strategies (NONE, SERVER_SIDE, JWT)\n * - GDPR-compliant consent management with strict opt-in mode\n * - Optional cookie support for cross-session tracking\n * \n * GDPR Compliance (Opt-in Mode):\n * - Without consent: Only ephemeral session IDs (memory-only) are used\n * - No cookies or localStorage identifiers until consent is granted\n * - Exceptions: User explicitly identified via identify()/login() or JWT auth\n * - Remote config cache and consent preferences use localStorage (functional/necessary)\n */\ntype RequiredConfig = Required<Omit<GrainConfig, 'secretKey' | 'authProvider' | 'userId' | 'cookieOptions' | 'allowedProperties'>> & {\n secretKey?: string;\n authProvider?: AuthProvider;\n userId?: string;\n cookieOptions?: CookieConfig;\n allowedProperties?: string[];\n};\n\nexport class GrainAnalytics implements HeartbeatTracker, PageTracker {\n private config: RequiredConfig;\n private eventQueue: EventPayload[] = [];\n private waitingForConsentQueue: EventPayload[] = [];\n private flushTimer: number | null = null;\n private isDestroyed = false;\n private globalUserId: string | null = null;\n private persistentAnonymousUserId: string | null = null;\n // Remote Config properties\n private configCache: RemoteConfigCache | null = null;\n private configRefreshTimer: number | null = null;\n private configChangeListeners: ConfigChangeListener[] = [];\n private configFetchPromise: Promise<RemoteConfigResponse> | null = null;\n // Privacy & Consent properties\n private consentManager: ConsentManager;\n private cookiesEnabled: boolean = false;\n // Automatic Tracking properties\n private activityDetector: ActivityDetector | null = null;\n private heartbeatManager: HeartbeatManager | null = null;\n private pageTrackingManager: PageTrackingManager | null = null;\n private ephemeralSessionId: string | null = null;\n private eventCountSinceLastHeartbeat: number = 0;\n\n constructor(config: GrainConfig) {\n this.config = {\n apiUrl: 'https://api.grainql.com',\n authStrategy: 'NONE',\n batchSize: 50,\n flushInterval: 5000, // 5 seconds\n retryAttempts: 3,\n retryDelay: 1000, // 1 second\n maxEventsPerRequest: 160, // Maximum events per API request\n debug: false,\n // Remote Config defaults\n defaultConfigurations: {},\n configCacheKey: 'grain_config',\n configRefreshInterval: 300000, // 5 minutes\n enableConfigCache: true,\n // Privacy defaults\n consentMode: 'opt-out',\n waitForConsent: false,\n enableCookies: false,\n anonymizeIP: false,\n disableAutoProperties: false,\n // Automatic Tracking defaults\n enableHeartbeat: true,\n heartbeatActiveInterval: 120000, // 2 minutes\n heartbeatInactiveInterval: 300000, // 5 minutes\n enableAutoPageView: true,\n stripQueryParams: true,\n ...config,\n tenantId: config.tenantId,\n };\n\n // Initialize consent manager\n this.consentManager = new ConsentManager(this.config.tenantId, this.config.consentMode);\n\n // Check if cookies are enabled\n if (this.config.enableCookies) {\n this.cookiesEnabled = areCookiesEnabled();\n if (!this.cookiesEnabled && this.config.debug) {\n console.warn('[Grain Analytics] Cookies are not available, falling back to localStorage');\n }\n }\n\n // Set global userId if provided in config\n if (config.userId) {\n this.globalUserId = config.userId;\n }\n\n this.validateConfig();\n this.initializePersistentAnonymousUserId();\n this.setupBeforeUnload();\n this.startFlushTimer();\n this.initializeConfigCache();\n\n // Initialize ephemeral session ID (memory-only, not persisted)\n this.ephemeralSessionId = this.generateUUID();\n\n // Initialize automatic tracking (browser only)\n if (typeof window !== 'undefined') {\n this.initializeAutomaticTracking();\n }\n\n // Set up consent change listener to flush waiting events and handle consent upgrade\n this.consentManager.addListener((state) => {\n if (state.granted) {\n this.handleConsentGranted();\n }\n });\n }\n\n private validateConfig(): void {\n if (!this.config.tenantId) {\n throw new Error('Grain Analytics: tenantId is required');\n }\n\n if (this.config.authStrategy === 'SERVER_SIDE' && !this.config.secretKey) {\n throw new Error('Grain Analytics: secretKey is required for SERVER_SIDE auth strategy');\n }\n\n if (this.config.authStrategy === 'JWT' && !this.config.authProvider) {\n throw new Error('Grain Analytics: authProvider is required for JWT auth strategy');\n }\n }\n\n /**\n * Generate a UUID v4 string\n */\n private generateUUID(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n \n // Fallback for environments without crypto.randomUUID\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n const r = Math.random() * 16 | 0;\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n }\n\n /**\n * Check if we should allow persistent storage (GDPR compliance)\n * \n * Returns true if:\n * - Consent has been granted, OR\n * - Not in opt-in mode, OR\n * - User has been explicitly identified by the site (login/identify), OR\n * - Using JWT auth strategy (functional/essential purpose)\n */\n private shouldAllowPersistentStorage(): boolean {\n const hasConsent = this.consentManager.hasConsent('analytics');\n const isOptInMode = this.config.consentMode === 'opt-in';\n const userExplicitlyIdentified = !!this.globalUserId;\n const isJWTAuth = this.config.authStrategy === 'JWT';\n \n // Allow persistent storage if any of these conditions are met\n return hasConsent || !isOptInMode || userExplicitlyIdentified || isJWTAuth;\n }\n\n /**\n * Generate a proper UUIDv4 identifier for anonymous user ID\n */\n private generateAnonymousUserId(): string {\n return this.generateUUID();\n }\n\n /**\n * Initialize persistent anonymous user ID from cookies or localStorage\n * Priority: Cookie \u2192 localStorage \u2192 generate new\n * \n * GDPR Compliance: In opt-in mode without consent, we skip loading/saving\n * persistent IDs unless the user has been explicitly identified by the site\n * or when using JWT auth (functional/essential purpose)\n */\n private initializePersistentAnonymousUserId(): void {\n if (typeof window === 'undefined') return;\n\n // Check if we should avoid persistent storage (GDPR compliance)\n if (!this.shouldAllowPersistentStorage()) {\n this.log('Opt-in mode without consent: skipping persistent ID initialization (GDPR compliance)');\n return; // Don't load or create persistent ID\n }\n\n const storageKey = `grain_anonymous_user_id_${this.config.tenantId}`;\n const cookieName = '_grain_uid';\n \n try {\n // Try to load from cookie first if enabled\n if (this.cookiesEnabled) {\n const cookieValue = getCookie(cookieName);\n if (cookieValue) {\n this.persistentAnonymousUserId = cookieValue;\n this.log('Loaded persistent anonymous user ID from cookie:', this.persistentAnonymousUserId);\n return;\n }\n }\n\n // Fallback to localStorage\n const stored = localStorage.getItem(storageKey);\n if (stored) {\n this.persistentAnonymousUserId = stored;\n this.log('Loaded persistent anonymous user ID from localStorage:', this.persistentAnonymousUserId);\n \n // Migrate to cookie if enabled\n if (this.cookiesEnabled) {\n this.savePersistentAnonymousUserId(stored);\n }\n } else {\n // Generate new UUIDv4 anonymous user ID\n this.persistentAnonymousUserId = this.generateAnonymousUserId();\n this.savePersistentAnonymousUserId(this.persistentAnonymousUserId);\n this.log('Generated new persistent anonymous user ID:', this.persistentAnonymousUserId);\n }\n } catch (error) {\n this.log('Failed to initialize persistent anonymous user ID:', error);\n // Fallback: generate temporary ID without persistence\n this.persistentAnonymousUserId = this.generateAnonymousUserId();\n }\n }\n\n /**\n * Save persistent anonymous user ID to cookie and/or localStorage\n * \n * GDPR Compliance: In opt-in mode without consent, we don't persist IDs\n * unless the user has been explicitly identified or using JWT auth\n */\n private savePersistentAnonymousUserId(userId: string): void {\n if (typeof window === 'undefined') return;\n\n // Check if we should avoid persistent storage (GDPR compliance)\n if (!this.shouldAllowPersistentStorage()) {\n this.log('Opt-in mode without consent: skipping persistent ID save (GDPR compliance)');\n return; // Don't save persistent ID\n }\n\n const storageKey = `grain_anonymous_user_id_${this.config.tenantId}`;\n const cookieName = '_grain_uid';\n\n try {\n // Save to cookie if enabled\n if (this.cookiesEnabled) {\n const cookieOptions: CookieConfig = {\n maxAge: 365 * 24 * 60 * 60, // 365 days\n sameSite: 'lax',\n secure: typeof window !== 'undefined' && window.location.protocol === 'https:',\n ...this.config.cookieOptions,\n };\n setCookie(cookieName, userId, cookieOptions);\n }\n\n // Always save to localStorage as fallback\n localStorage.setItem(storageKey, userId);\n } catch (error) {\n this.log('Failed to save persistent anonymous user ID:', error);\n }\n }\n\n /**\n * Get the effective user ID (global userId or persistent anonymous ID)\n * \n * GDPR Compliance: In opt-in mode without consent and no explicit user identification,\n * this should not be called. Use getEphemeralSessionId() instead.\n */\n private getEffectiveUserIdInternal(): string {\n if (this.globalUserId) {\n return this.globalUserId;\n }\n \n if (this.persistentAnonymousUserId) {\n return this.persistentAnonymousUserId;\n }\n \n // Generate a new UUIDv4 identifier as fallback\n this.persistentAnonymousUserId = this.generateAnonymousUserId();\n \n // Try to persist it (will be skipped in opt-in mode without consent)\n this.savePersistentAnonymousUserId(this.persistentAnonymousUserId);\n \n return this.persistentAnonymousUserId;\n }\n\n private log(...args: unknown[]): void {\n if (this.config.debug) {\n console.log('[Grain Analytics]', ...args);\n }\n }\n\n /**\n * Create error digest from events\n */\n private createErrorDigest(events: EventPayload[]): ErrorDigest {\n const eventNames = [...new Set(events.map(e => e.eventName))];\n const userIds = [...new Set(events.map(e => e.userId))];\n \n let totalProperties = 0;\n let totalSize = 0;\n \n events.forEach(event => {\n const properties = event.properties || {};\n totalProperties += Object.keys(properties).length;\n totalSize += JSON.stringify(event).length;\n });\n\n return {\n eventCount: events.length,\n totalProperties,\n totalSize,\n eventNames,\n userIds,\n };\n }\n\n /**\n * Format error with beautiful structure\n */\n private formatError(\n error: unknown,\n context: string,\n events?: EventPayload[]\n ): FormattedError {\n const digest = events ? this.createErrorDigest(events) : {\n eventCount: 0,\n totalProperties: 0,\n totalSize: 0,\n eventNames: [],\n userIds: [],\n };\n\n let code = 'UNKNOWN_ERROR';\n let message = 'An unknown error occurred';\n\n if (error instanceof Error) {\n message = error.message;\n \n // Determine error code based on error type and message\n if (message.includes('fetch failed') || message.includes('network error')) {\n code = 'NETWORK_ERROR';\n } else if (message.includes('timeout')) {\n code = 'TIMEOUT_ERROR';\n } else if (message.includes('HTTP 4')) {\n code = 'CLIENT_ERROR';\n } else if (message.includes('HTTP 5')) {\n code = 'SERVER_ERROR';\n } else if (message.includes('JSON')) {\n code = 'PARSE_ERROR';\n } else if (message.includes('auth') || message.includes('unauthorized')) {\n code = 'AUTH_ERROR';\n } else if (message.includes('rate limit') || message.includes('429')) {\n code = 'RATE_LIMIT_ERROR';\n } else {\n code = 'GENERAL_ERROR';\n }\n } else if (typeof error === 'string') {\n message = error;\n code = 'STRING_ERROR';\n } else if (error && typeof error === 'object' && 'status' in error) {\n const status = (error as { status: number }).status;\n code = `HTTP_${status}`;\n message = `HTTP ${status} error`;\n }\n\n return {\n code,\n message,\n digest,\n timestamp: new Date().toISOString(),\n context,\n originalError: error,\n };\n }\n\n /**\n * Log formatted error gracefully\n */\n private logError(formattedError: FormattedError): void {\n const { code, message, digest, timestamp, context } = formattedError;\n \n const errorOutput = {\n '\uD83D\uDEA8 Grain Analytics Error': {\n 'Error Code': code,\n 'Message': message,\n 'Context': context,\n 'Timestamp': timestamp,\n 'Event Digest': {\n 'Events': digest.eventCount,\n 'Properties': digest.totalProperties,\n 'Size (bytes)': digest.totalSize,\n 'Event Names': digest.eventNames.length > 0 ? digest.eventNames.join(', ') : 'None',\n 'User IDs': digest.userIds.length > 0 ? digest.userIds.slice(0, 3).join(', ') + (digest.userIds.length > 3 ? '...' : '') : 'None',\n }\n }\n };\n\n console.error('\uD83D\uDEA8 Grain Analytics Error:', errorOutput);\n \n // Also log in a more compact format for debugging\n if (this.config.debug) {\n console.error(`[Grain Analytics] ${code}: ${message} (${context}) - Events: ${digest.eventCount}, Props: ${digest.totalProperties}, Size: ${digest.totalSize}B`);\n }\n }\n\n /**\n * Safely execute a function with error handling\n */\n private async safeExecute<T>(\n fn: () => Promise<T>,\n context: string,\n events?: EventPayload[]\n ): Promise<T | null> {\n try {\n return await fn();\n } catch (error) {\n const formattedError = this.formatError(error, context, events);\n this.logError(formattedError);\n return null;\n }\n }\n\n private formatEvent(event: GrainEvent): EventPayload {\n return {\n eventName: event.eventName,\n userId: event.userId || this.getEffectiveUserIdInternal(),\n properties: event.properties || {},\n };\n }\n\n private async getAuthHeaders(): Promise<Record<string, string>> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n switch (this.config.authStrategy) {\n case 'NONE':\n break;\n case 'SERVER_SIDE':\n headers['Authorization'] = `Chase ${this.config.secretKey}`;\n break;\n case 'JWT':\n if (this.config.authProvider) {\n const token = await this.config.authProvider.getToken();\n headers['Authorization'] = `Bearer ${token}`;\n }\n break;\n }\n\n return headers;\n }\n\n private async delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n\n private isRetriableError(error: unknown): boolean {\n if (error instanceof Error) {\n // Check for specific network or fetch errors\n const message = error.message.toLowerCase();\n if (message.includes('fetch failed')) return true;\n if (message === 'network error') return true; // Exact match to avoid \"Non-network error\"\n if (message.includes('timeout')) return true;\n if (message.includes('connection')) return true;\n }\n \n // Check for HTTP status codes that are retriable\n if (typeof error === 'object' && error !== null && 'status' in error) {\n const status = (error as { status: number }).status;\n return status >= 500 || status === 429; // Server errors or rate limiting\n }\n \n return false;\n }\n\n private async sendEvents(events: EventPayload[]): Promise<void> {\n if (events.length === 0) return;\n\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= this.config.retryAttempts; attempt++) {\n try {\n const headers = await this.getAuthHeaders();\n const url = `${this.config.apiUrl}/v1/events/${encodeURIComponent(this.config.tenantId)}/multi`;\n\n this.log(`Sending ${events.length} events to ${url} (attempt ${attempt + 1})`);\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(events),\n });\n\n if (!response.ok) {\n let errorMessage = `HTTP ${response.status}`;\n try {\n const errorBody = await response.json();\n if (errorBody?.message) {\n errorMessage = errorBody.message;\n }\n } catch {\n const errorText = await response.text();\n if (errorText) {\n errorMessage = errorText;\n }\n }\n \n const error = new Error(`Failed to send events: ${errorMessage}`) as Error & { status?: number };\n error.status = response.status;\n throw error;\n }\n\n this.log(`Successfully sent ${events.length} events`);\n return; // Success, exit retry loop\n \n } catch (error) {\n lastError = error;\n \n if (attempt === this.config.retryAttempts) {\n // Last attempt, don't retry - log error gracefully\n const formattedError = this.formatError(error, `sendEvents (attempt ${attempt + 1}/${this.config.retryAttempts + 1})`, events);\n this.logError(formattedError);\n return; // Don't throw, just return gracefully\n }\n \n if (!this.isRetriableError(error)) {\n // Non-retriable error, don't retry - log error gracefully\n const formattedError = this.formatError(error, `sendEvents (non-retriable error)`, events);\n this.logError(formattedError);\n return; // Don't throw, just return gracefully\n }\n \n const delayMs = this.config.retryDelay * Math.pow(2, attempt); // Exponential backoff\n this.log(`Retrying in ${delayMs}ms after error:`, error);\n await this.delay(delayMs);\n }\n }\n }\n\n private async sendEventsWithBeacon(events: EventPayload[]): Promise<void> {\n if (events.length === 0) return;\n\n try {\n const headers = await this.getAuthHeaders();\n const url = `${this.config.apiUrl}/v1/events/${encodeURIComponent(this.config.tenantId)}/multi`;\n\n const body = JSON.stringify({ events });\n\n // Try beacon API first (more reliable for page unload)\n if (typeof navigator !== 'undefined' && 'sendBeacon' in navigator) {\n const blob = new Blob([body], { type: 'application/json' });\n const success = navigator.sendBeacon(url, blob);\n \n if (success) {\n this.log(`Successfully sent ${events.length} events via beacon`);\n return;\n }\n }\n\n // Fallback to fetch with keepalive\n await fetch(url, {\n method: 'POST',\n headers,\n body,\n keepalive: true,\n });\n\n this.log(`Successfully sent ${events.length} events via fetch (keepalive)`);\n } catch (error) {\n // Log error gracefully for beacon failures (page unload scenarios)\n const formattedError = this.formatError(error, 'sendEventsWithBeacon', events);\n this.logError(formattedError);\n }\n }\n\n private startFlushTimer(): void {\n if (typeof window === 'undefined') return;\n \n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n }\n\n this.flushTimer = window.setInterval(() => {\n if (this.eventQueue.length > 0) {\n this.flush().catch((error) => {\n const formattedError = this.formatError(error, 'auto-flush');\n this.logError(formattedError);\n });\n }\n }, this.config.flushInterval);\n }\n\n private setupBeforeUnload(): void {\n if (typeof window === 'undefined') return;\n\n const handleBeforeUnload = () => {\n if (this.eventQueue.length > 0) {\n // Use beacon API for reliable delivery during page unload\n const eventsToSend = [...this.eventQueue];\n this.eventQueue = [];\n \n const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);\n \n // Send first chunk with beacon (most important for page unload)\n if (chunks.length > 0) {\n this.sendEventsWithBeacon(chunks[0]).catch(() => {\n // Silently fail - page is unloading\n });\n }\n }\n };\n\n // Handle page unload\n window.addEventListener('beforeunload', handleBeforeUnload);\n window.addEventListener('pagehide', handleBeforeUnload);\n \n // Handle visibility change (page hidden)\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden' && this.eventQueue.length > 0) {\n const eventsToSend = [...this.eventQueue];\n this.eventQueue = [];\n \n const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);\n \n // Send first chunk with beacon (most important for page hidden)\n if (chunks.length > 0) {\n this.sendEventsWithBeacon(chunks[0]).catch(() => {\n // Silently fail\n });\n }\n }\n });\n }\n\n /**\n * Initialize automatic tracking (heartbeat and page views)\n */\n private initializeAutomaticTracking(): void {\n if (this.config.enableHeartbeat) {\n try {\n this.activityDetector = new ActivityDetector();\n this.heartbeatManager = new HeartbeatManager(\n this,\n this.activityDetector,\n {\n activeInterval: this.config.heartbeatActiveInterval,\n inactiveInterval: this.config.heartbeatInactiveInterval,\n debug: this.config.debug,\n }\n );\n this.log('Heartbeat tracking initialized');\n } catch (error) {\n this.log('Failed to initialize heartbeat tracking:', error);\n }\n }\n\n if (this.config.enableAutoPageView) {\n try {\n this.pageTrackingManager = new PageTrackingManager(\n this,\n {\n stripQueryParams: this.config.stripQueryParams,\n debug: this.config.debug,\n }\n );\n this.log('Auto page view tracking initialized');\n } catch (error) {\n this.log('Failed to initialize page view tracking:', error);\n }\n }\n }\n\n /**\n * Handle consent granted - upgrade ephemeral session to persistent user\n */\n private handleConsentGranted(): void {\n this.flushWaitingForConsentQueue();\n\n // Initialize persistent ID now that consent is granted (if not already initialized)\n if (!this.persistentAnonymousUserId) {\n this.initializePersistentAnonymousUserId();\n this.log('Initialized persistent ID after consent grant');\n }\n\n // Track consent granted event with mapping from ephemeral to persistent ID\n if (this.ephemeralSessionId) {\n this.trackSystemEvent('_grain_consent_granted', {\n previous_session_id: this.ephemeralSessionId,\n new_user_id: this.getEffectiveUserId(),\n timestamp: Date.now(),\n });\n }\n }\n\n /**\n * Track system events that bypass consent checks (for necessary/functional tracking)\n */\n trackSystemEvent(eventName: string, properties: Record<string, unknown>): void {\n if (this.isDestroyed) return;\n\n const hasConsent = this.consentManager.hasConsent('analytics');\n\n // Create event with appropriate user ID\n const event: EventPayload = {\n eventName,\n userId: hasConsent ? this.getEffectiveUserId() : this.getEphemeralSessionId(),\n properties: {\n ...properties,\n _minimal: !hasConsent, // Flag to indicate minimal tracking\n _consent_status: hasConsent ? 'granted' : 'pending',\n },\n };\n\n // Bypass consent check for necessary system events\n this.eventQueue.push(event);\n this.eventCountSinceLastHeartbeat++;\n\n this.log(`Queued system event: ${eventName}`, properties);\n\n // Consider flushing\n if (this.eventQueue.length >= this.config.batchSize) {\n this.flush().catch((error) => {\n const formattedError = this.formatError(error, 'flush system event');\n this.logError(formattedError);\n });\n }\n }\n\n /**\n * Get ephemeral session ID (memory-only, not persisted)\n */\n getEphemeralSessionId(): string {\n if (!this.ephemeralSessionId) {\n this.ephemeralSessionId = this.generateUUID();\n }\n return this.ephemeralSessionId;\n }\n\n /**\n * Get the current page path from page tracker\n */\n getCurrentPage(): string | null {\n return this.pageTrackingManager?.getCurrentPage() || null;\n }\n\n /**\n * Get event count since last heartbeat\n */\n getEventCountSinceLastHeartbeat(): number {\n return this.eventCountSinceLastHeartbeat;\n }\n\n /**\n * Reset event count since last heartbeat\n */\n resetEventCountSinceLastHeartbeat(): void {\n this.eventCountSinceLastHeartbeat = 0;\n }\n\n /**\n * Get the effective user ID (public method)\n */\n getEffectiveUserId(): string {\n return this.getEffectiveUserIdInternal();\n }\n\n /**\n * Get the session ID (ephemeral or persistent based on consent)\n */\n getSessionId(): string {\n const hasConsent = this.consentManager.hasConsent('analytics');\n return hasConsent ? this.getEffectiveUserId() : this.getEphemeralSessionId();\n }\n\n /**\n * Track an analytics event\n */\n async track(eventName: string, properties?: Record<string, unknown>, options?: SendEventOptions): Promise<void>;\n async track(event: GrainEvent, options?: SendEventOptions): Promise<void>;\n async track(\n eventOrName: string | GrainEvent,\n propertiesOrOptions?: Record<string, unknown> | SendEventOptions,\n options?: SendEventOptions\n ): Promise<void> {\n try {\n if (this.isDestroyed) {\n const error = new Error('Grain Analytics: Client has been destroyed');\n const formattedError = this.formatError(error, 'track (client destroyed)');\n this.logError(formattedError);\n return;\n }\n\n let event: GrainEvent;\n let opts: SendEventOptions = {};\n\n if (typeof eventOrName === 'string') {\n event = {\n eventName: eventOrName,\n properties: propertiesOrOptions as Record<string, unknown>,\n };\n opts = options || {};\n } else {\n event = eventOrName;\n opts = propertiesOrOptions as SendEventOptions || {};\n }\n\n // Filter properties if whitelist is enabled\n if (this.config.allowedProperties && event.properties) {\n const filtered: Record<string, unknown> = {};\n for (const key of this.config.allowedProperties) {\n if (key in event.properties) {\n filtered[key] = event.properties[key];\n }\n }\n event.properties = filtered;\n }\n\n const formattedEvent = this.formatEvent(event);\n\n // Check consent before tracking\n if (this.consentManager.shouldWaitForConsent() && this.config.waitForConsent) {\n // Queue event until consent is granted\n this.waitingForConsentQueue.push(formattedEvent);\n this.log(`Event waiting for consent: ${event.eventName}`, event.properties);\n return;\n }\n\n if (!this.consentManager.hasConsent('analytics')) {\n this.log(`Event blocked by consent: ${event.eventName}`);\n return;\n }\n\n this.eventQueue.push(formattedEvent);\n this.eventCountSinceLastHeartbeat++;\n this.log(`Queued event: ${event.eventName}`, event.properties);\n\n // Check if we should flush immediately\n if (opts.flush || this.eventQueue.length >= this.config.batchSize) {\n await this.flush();\n }\n } catch (error) {\n const formattedError = this.formatError(error, 'track');\n this.logError(formattedError);\n }\n }\n\n /**\n * Flush events that were waiting for consent\n */\n private flushWaitingForConsentQueue(): void {\n if (this.waitingForConsentQueue.length === 0) return;\n\n this.log(`Flushing ${this.waitingForConsentQueue.length} events waiting for consent`);\n \n // Move waiting events to main queue\n this.eventQueue.push(...this.waitingForConsentQueue);\n this.waitingForConsentQueue = [];\n\n // Flush immediately\n this.flush().catch((error) => {\n const formattedError = this.formatError(error, 'flush waiting for consent queue');\n this.logError(formattedError);\n });\n }\n\n /**\n * Identify a user (sets userId for subsequent events)\n */\n identify(userId: string): void {\n this.log(`Identified user: ${userId}`);\n this.globalUserId = userId;\n // Clear persistent anonymous user ID since we now have a real user ID\n this.persistentAnonymousUserId = null;\n }\n\n /**\n * Set global user ID for all subsequent events\n */\n setUserId(userId: string | null): void {\n this.log(`Set global user ID: ${userId}`);\n this.globalUserId = userId;\n \n if (userId) {\n // Clear persistent anonymous user ID if setting a real user ID\n this.persistentAnonymousUserId = null;\n } else {\n // If clearing user ID, ensure we have an identifier\n if (!this.persistentAnonymousUserId) {\n this.persistentAnonymousUserId = this.generateAnonymousUserId();\n \n // Try to persist the new anonymous ID (respects GDPR opt-in mode)\n this.savePersistentAnonymousUserId(this.persistentAnonymousUserId);\n }\n }\n }\n\n /**\n * Get current global user ID\n */\n getUserId(): string | null {\n return this.globalUserId;\n }\n\n /**\n * Get current effective user ID (global userId or persistent anonymous ID)\n */\n getEffectiveUserIdPublic(): string {\n return this.getEffectiveUserIdInternal();\n }\n\n /**\n * Login with auth token or userId on the fly\n * \n * @example\n * // Login with userId only\n * client.login({ userId: 'user123' });\n * \n * // Login with auth token (automatically sets authStrategy to JWT)\n * client.login({ authToken: 'jwt-token-here' });\n * \n * // Login with both userId and auth token\n * client.login({ userId: 'user123', authToken: 'jwt-token-here' });\n * \n * // Override auth strategy\n * client.login({ userId: 'user123', authStrategy: 'SERVER_SIDE' });\n */\n login(options: LoginOptions): void {\n try {\n if (this.isDestroyed) {\n const error = new Error('Grain Analytics: Client has been destroyed');\n const formattedError = this.formatError(error, 'login (client destroyed)');\n this.logError(formattedError);\n return;\n }\n\n // Set userId if provided\n if (options.userId) {\n this.log(`Login: Setting user ID to ${options.userId}`);\n this.globalUserId = options.userId;\n // Clear persistent anonymous user ID since we now have a real user ID\n this.persistentAnonymousUserId = null;\n }\n\n // Handle auth token if provided\n if (options.authToken) {\n this.log('Login: Setting auth token');\n // Update auth strategy to JWT if not already set\n if (this.config.authStrategy === 'NONE') {\n this.config.authStrategy = 'JWT';\n }\n \n // Create a simple auth provider that returns the provided token\n this.config.authProvider = {\n getToken: () => options.authToken!\n };\n }\n\n // Override auth strategy if provided\n if (options.authStrategy) {\n this.log(`Login: Setting auth strategy to ${options.authStrategy}`);\n this.config.authStrategy = options.authStrategy;\n }\n\n this.log(`Login successful. Effective user ID: ${this.getEffectiveUserIdInternal()}`);\n } catch (error) {\n const formattedError = this.formatError(error, 'login');\n this.logError(formattedError);\n }\n }\n\n /**\n * Logout and clear user session, fall back to UUIDv4 identifier\n * \n * @example\n * // Logout user and return to anonymous mode\n * client.logout();\n * \n * // After logout, events will use the persistent UUIDv4 identifier\n * client.track('page_view', { page: 'home' });\n */\n logout(): void {\n try {\n if (this.isDestroyed) {\n const error = new Error('Grain Analytics: Client has been destroyed');\n const formattedError = this.formatError(error, 'logout (client destroyed)');\n this.logError(formattedError);\n return;\n }\n\n this.log('Logout: Clearing user session');\n \n // Clear global user ID\n this.globalUserId = null;\n \n // Reset auth strategy to NONE\n this.config.authStrategy = 'NONE';\n this.config.authProvider = undefined;\n \n // Generate new UUIDv4 identifier if we don't have one\n if (!this.persistentAnonymousUserId) {\n this.persistentAnonymousUserId = this.generateAnonymousUserId();\n \n // Try to persist the new anonymous ID\n if (typeof window !== 'undefined') {\n try {\n const storageKey = `grain_anonymous_user_id_${this.config.tenantId}`;\n localStorage.setItem(storageKey, this.persistentAnonymousUserId);\n } catch (error) {\n this.log('Failed to persist new anonymous user ID after logout:', error);\n }\n }\n }\n \n this.log(`Logout successful. Effective user ID: ${this.getEffectiveUserIdInternal()}`);\n } catch (error) {\n const formattedError = this.formatError(error, 'logout');\n this.logError(formattedError);\n }\n }\n\n /**\n * Set user properties\n */\n async setProperty(properties: Record<string, unknown>, options?: SetPropertyOptions): Promise<void> {\n try {\n if (this.isDestroyed) {\n const error = new Error('Grain Analytics: Client has been destroyed');\n const formattedError = this.formatError(error, 'setProperty (client destroyed)');\n this.logError(formattedError);\n return;\n }\n\n const userId = options?.userId || this.getEffectiveUserIdInternal();\n \n // Validate property count (max 4 properties)\n const propertyKeys = Object.keys(properties);\n if (propertyKeys.length > 4) {\n const error = new Error('Grain Analytics: Maximum 4 properties allowed per request');\n const formattedError = this.formatError(error, 'setProperty (validation)');\n this.logError(formattedError);\n return;\n }\n\n if (propertyKeys.length === 0) {\n const error = new Error('Grain Analytics: At least one property is required');\n const formattedError = this.formatError(error, 'setProperty (validation)');\n this.logError(formattedError);\n return;\n }\n\n // Serialize all values to strings\n const serializedProperties: Record<string, string> = {};\n for (const [key, value] of Object.entries(properties)) {\n if (value === null || value === undefined) {\n serializedProperties[key] = '';\n } else if (typeof value === 'string') {\n serializedProperties[key] = value;\n } else {\n serializedProperties[key] = JSON.stringify(value);\n }\n }\n\n const payload: PropertyPayload = {\n userId,\n ...serializedProperties,\n };\n\n await this.sendProperties(payload);\n } catch (error) {\n const formattedError = this.formatError(error, 'setProperty');\n this.logError(formattedError);\n }\n }\n\n /**\n * Send properties to the API\n */\n private async sendProperties(payload: PropertyPayload): Promise<void> {\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= this.config.retryAttempts; attempt++) {\n try {\n const headers = await this.getAuthHeaders();\n const url = `${this.config.apiUrl}/v1/events/${encodeURIComponent(this.config.tenantId)}/properties`;\n\n this.log(`Setting properties for user ${payload.userId} (attempt ${attempt + 1})`);\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n let errorMessage = `HTTP ${response.status}`;\n try {\n const errorBody = await response.json();\n if (errorBody?.message) {\n errorMessage = errorBody.message;\n }\n } catch {\n const errorText = await response.text();\n if (errorText) {\n errorMessage = errorText;\n }\n }\n \n const error = new Error(`Failed to set properties: ${errorMessage}`) as Error & { status?: number };\n error.status = response.status;\n throw error;\n }\n\n this.log(`Successfully set properties for user ${payload.userId}`);\n return; // Success, exit retry loop\n \n } catch (error) {\n lastError = error;\n \n if (attempt === this.config.retryAttempts) {\n // Last attempt, don't retry - log error gracefully\n const formattedError = this.formatError(error, `sendProperties (attempt ${attempt + 1}/${this.config.retryAttempts + 1})`);\n this.logError(formattedError);\n return; // Don't throw, just return gracefully\n }\n \n if (!this.isRetriableError(error)) {\n // Non-retriable error, don't retry - log error gracefully\n const formattedError = this.formatError(error, 'sendProperties (non-retriable error)');\n this.logError(formattedError);\n return; // Don't throw, just return gracefully\n }\n \n const delayMs = this.config.retryDelay * Math.pow(2, attempt); // Exponential backoff\n this.log(`Retrying in ${delayMs}ms after error:`, error);\n await this.delay(delayMs);\n }\n }\n }\n\n // Template event methods\n\n /**\n * Track user login event\n */\n async trackLogin(properties?: LoginEventProperties, options?: SendEventOptions): Promise<void> {\n try {\n return await this.track('login', properties, options);\n } catch (error) {\n const formattedError = this.formatError(error, 'trackLogin');\n this.logError(formattedError);\n }\n }\n\n /**\n * Track user signup event\n */\n async trackSignup(properties?: SignupEventProperties, options?: SendEventOptions): Promise<void> {\n try {\n return await this.track('signup', properties, options);\n } catch (error) {\n const formattedError = this.formatError(error, 'trackSignup');\n this.logError(formattedError);\n }\n }\n\n /**\n * Track checkout event\n */\n async trackCheckout(properties?: CheckoutEventProperties, options?: SendEventOptions): Promise<void> {\n try {\n return await this.track('checkout', properties, options);\n } catch (error) {\n const formattedError = this.formatError(error, 'trackCheckout');\n this.logError(formattedError);\n }\n }\n\n /**\n * Track page view event\n */\n async trackPageView(properties?: PageViewEventProperties, options?: SendEventOptions): Promise<void> {\n try {\n return await this.track('page_view', properties, options);\n } catch (error) {\n const formattedError = this.formatError(error, 'trackPageView');\n this.logError(formattedError);\n }\n }\n\n /**\n * Track purchase event\n */\n async trackPurchase(properties?: PurchaseEventProperties, options?: SendEventOptions): Promise<void> {\n try {\n return await this.track('purchase', properties, options);\n } catch (error) {\n const formattedError = this.formatError(error, 'trackPurchase');\n this.logError(formattedError);\n }\n }\n\n /**\n * Track search event\n */\n async trackSearch(properties?: SearchEventProperties, options?: SendEventOptions): Promise<void> {\n try {\n return await this.track('search', properties, options);\n } catch (error) {\n const formattedError = this.formatError(error, 'trackSearch');\n this.logError(formattedError);\n }\n }\n\n /**\n * Track add to cart event\n */\n async trackAddToCart(properties?: AddToCartEventProperties, options?: SendEventOptions): Promise<void> {\n try {\n return await this.track('add_to_cart', properties, options);\n } catch (error) {\n const formattedError = this.formatError(error, 'trackAddToCart');\n this.logError(formattedError);\n }\n }\n\n /**\n * Track remove from cart event\n */\n async trackRemoveFromCart(properties?: RemoveFromCartEventProperties, options?: SendEventOptions): Promise<void> {\n try {\n return await this.track('remove_from_cart', properties, options);\n } catch (error) {\n const formattedError = this.formatError(error, 'trackRemoveFromCart');\n this.logError(formattedError);\n }\n }\n\n /**\n * Manually flush all queued events\n */\n async flush(): Promise<void> {\n try {\n if (this.eventQueue.length === 0) return;\n\n const eventsToSend = [...this.eventQueue];\n this.eventQueue = [];\n\n // Split events into chunks to respect maxEventsPerRequest limit\n const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);\n \n // Send all chunks sequentially to maintain order\n for (const chunk of chunks) {\n await this.sendEvents(chunk);\n }\n } catch (error) {\n const formattedError = this.formatError(error, 'flush');\n this.logError(formattedError);\n }\n }\n\n // Remote Config Methods\n\n /**\n * Initialize configuration cache from localStorage\n */\n private initializeConfigCache(): void {\n if (!this.config.enableConfigCache || typeof window === 'undefined') return;\n\n try {\n const cached = localStorage.getItem(this.config.configCacheKey);\n if (cached) {\n this.configCache = JSON.parse(cached);\n this.log('Loaded configuration from cache:', this.configCache);\n }\n } catch (error) {\n this.log('Failed to load configuration cache:', error);\n }\n }\n\n /**\n * Save configuration cache to localStorage\n */\n private saveConfigCache(cache: RemoteConfigCache): void {\n if (!this.config.enableConfigCache || typeof window === 'undefined') return;\n\n try {\n localStorage.setItem(this.config.configCacheKey, JSON.stringify(cache));\n this.log('Saved configuration to cache:', cache);\n } catch (error) {\n this.log('Failed to save configuration cache:', error);\n }\n }\n\n /**\n * Get configuration value with fallback to defaults\n */\n getConfig(key: string): string | undefined {\n // First check cache\n if (this.configCache?.configurations?.[key]) {\n return this.configCache.configurations[key];\n }\n\n // Then check defaults\n if (this.config.defaultConfigurations?.[key]) {\n return this.config.defaultConfigurations[key];\n }\n\n return undefined;\n }\n\n /**\n * Get all configurations with fallback to defaults\n */\n getAllConfigs(): Record<string, string> {\n const configs = { ...this.config.defaultConfigurations };\n \n if (this.configCache?.configurations) {\n Object.assign(configs, this.configCache.configurations);\n }\n\n return configs;\n }\n\n /**\n * Fetch configurations from API\n */\n async fetchConfig(options: RemoteConfigOptions = {}): Promise<RemoteConfigResponse | null> {\n try {\n if (this.isDestroyed) {\n const error = new Error('Grain Analytics: Client has been destroyed');\n const formattedError = this.formatError(error, 'fetchConfig (client destroyed)');\n this.logError(formattedError);\n return null;\n }\n\n const userId = options.userId || this.getEffectiveUserIdInternal();\n const immediateKeys = options.immediateKeys || [];\n const properties = options.properties || {};\n\n const request: RemoteConfigRequest = {\n userId,\n immediateKeys,\n properties,\n };\n\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= this.config.retryAttempts; attempt++) {\n try {\n const headers = await this.getAuthHeaders();\n const url = `${this.config.apiUrl}/v1/client/${encodeURIComponent(this.config.tenantId)}/config/configurations`;\n\n this.log(`Fetching configurations for user ${userId} (attempt ${attempt + 1})`);\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n let errorMessage = `HTTP ${response.status}`;\n try {\n const errorBody = await response.json();\n if (errorBody?.message) {\n errorMessage = errorBody.message;\n }\n } catch {\n const errorText = await response.text();\n if (errorText) {\n errorMessage = errorText;\n }\n }\n \n const error = new Error(`Failed to fetch configurations: ${errorMessage}`) as Error & { status?: number };\n error.status = response.status;\n throw error;\n }\n\n const configResponse: RemoteConfigResponse = await response.json();\n \n // Update cache if successful\n if (configResponse.configurations) {\n this.updateConfigCache(configResponse, userId);\n }\n\n this.log(`Successfully fetched configurations for user ${userId}:`, configResponse);\n return configResponse;\n \n } catch (error) {\n lastError = error;\n \n if (attempt === this.config.retryAttempts) {\n // Last attempt, don't retry - log error gracefully\n const formattedError = this.formatError(error, `fetchConfig (attempt ${attempt + 1}/${this.config.retryAttempts + 1})`);\n this.logError(formattedError);\n return null;\n }\n \n if (!this.isRetriableError(error)) {\n // Non-retriable error, don't retry - log error gracefully\n const formattedError = this.formatError(error, 'fetchConfig (non-retriable error)');\n this.logError(formattedError);\n return null;\n }\n \n const delayMs = this.config.retryDelay * Math.pow(2, attempt);\n this.log(`Retrying config fetch in ${delayMs}ms after error:`, error);\n await this.delay(delayMs);\n }\n }\n\n return null;\n } catch (error) {\n const formattedError = this.formatError(error, 'fetchConfig');\n this.logError(formattedError);\n return null;\n }\n }\n\n /**\n * Get configuration asynchronously (cache-first with fallback to API)\n */\n async getConfigAsync(key: string, options: RemoteConfigOptions = {}): Promise<string | undefined> {\n try {\n // Return immediately if we have it in cache and not forcing refresh\n if (!options.forceRefresh && this.configCache?.configurations?.[key]) {\n return this.configCache.configurations[key];\n }\n\n // Return default if available and not forcing refresh\n if (!options.forceRefresh && this.config.defaultConfigurations?.[key]) {\n return this.config.defaultConfigurations[key];\n }\n\n // Fetch from API\n const response = await this.fetchConfig(options);\n if (response) {\n return response.configurations[key];\n }\n \n // Return default as fallback\n return this.config.defaultConfigurations?.[key];\n } catch (error) {\n const formattedError = this.formatError(error, 'getConfigAsync');\n this.logError(formattedError);\n // Return default as fallback\n return this.config.defaultConfigurations?.[key];\n }\n }\n\n /**\n * Get all configurations asynchronously (cache-first with fallback to API)\n */\n async getAllConfigsAsync(options: RemoteConfigOptions = {}): Promise<Record<string, string>> {\n try {\n // Return cache if available and not forcing refresh\n if (!options.forceRefresh && this.configCache?.configurations) {\n return { ...this.config.defaultConfigurations, ...this.configCache.configurations };\n }\n\n // Fetch from API\n const response = await this.fetchConfig(options);\n if (response) {\n return { ...this.config.defaultConfigurations, ...response.configurations };\n }\n \n // Return defaults as fallback\n return { ...this.config.defaultConfigurations };\n } catch (error) {\n const formattedError = this.formatError(error, 'getAllConfigsAsync');\n this.logError(formattedError);\n // Return defaults as fallback\n return { ...this.config.defaultConfigurations };\n }\n }\n\n /**\n * Update configuration cache and notify listeners\n */\n private updateConfigCache(response: RemoteConfigResponse, userId: string): void {\n const newCache: RemoteConfigCache = {\n configurations: response.configurations,\n snapshotId: response.snapshotId,\n timestamp: response.timestamp,\n userId,\n };\n\n const oldConfigs = this.configCache?.configurations || {};\n this.configCache = newCache;\n this.saveConfigCache(newCache);\n\n // Notify listeners if configurations changed\n if (JSON.stringify(oldConfigs) !== JSON.stringify(response.configurations)) {\n this.notifyConfigChangeListeners(response.configurations);\n }\n }\n\n /**\n * Add configuration change listener\n */\n addConfigChangeListener(listener: ConfigChangeListener): void {\n this.configChangeListeners.push(listener);\n }\n\n /**\n * Remove configuration change listener\n */\n removeConfigChangeListener(listener: ConfigChangeListener): void {\n const index = this.configChangeListeners.indexOf(listener);\n if (index > -1) {\n this.configChangeListeners.splice(index, 1);\n }\n }\n\n /**\n * Notify all configuration change listeners\n */\n private notifyConfigChangeListeners(configurations: Record<string, string>): void {\n this.configChangeListeners.forEach(listener => {\n try {\n listener(configurations);\n } catch (error) {\n console.error('[Grain Analytics] Config change listener error:', error);\n }\n });\n }\n\n /**\n * Start automatic configuration refresh timer\n */\n private startConfigRefreshTimer(): void {\n if (typeof window === 'undefined') return;\n \n if (this.configRefreshTimer) {\n clearInterval(this.configRefreshTimer);\n }\n\n this.configRefreshTimer = window.setInterval(() => {\n if (!this.isDestroyed) {\n // Use effective userId (will be generated if not set)\n this.fetchConfig().catch((error) => {\n const formattedError = this.formatError(error, 'auto-config refresh');\n this.logError(formattedError);\n });\n }\n }, this.config.configRefreshInterval);\n }\n\n /**\n * Stop automatic configuration refresh timer\n */\n private stopConfigRefreshTimer(): void {\n if (this.configRefreshTimer) {\n clearInterval(this.configRefreshTimer);\n this.configRefreshTimer = null;\n }\n }\n\n /**\n * Preload configurations for immediate access\n */\n async preloadConfig(immediateKeys: string[] = [], properties?: Record<string, string>): Promise<void> {\n try {\n // Use effective userId (will be generated if not set)\n const effectiveUserId = this.getEffectiveUserIdInternal();\n this.log(`Preloading config for user: ${effectiveUserId}`);\n\n const response = await this.fetchConfig({ immediateKeys, properties });\n if (response) {\n this.startConfigRefreshTimer();\n }\n } catch (error) {\n const formattedError = this.formatError(error, 'preloadConfig');\n this.logError(formattedError);\n }\n }\n\n /**\n * Split events array into chunks of specified size\n */\n private chunkEvents(events: EventPayload[], chunkSize: number): EventPayload[][] {\n const chunks: EventPayload[][] = [];\n for (let i = 0; i < events.length; i += chunkSize) {\n chunks.push(events.slice(i, i + chunkSize));\n }\n return chunks;\n }\n\n // Privacy & Consent Methods\n\n /**\n * Grant consent for tracking\n * @param categories - Optional array of consent categories (e.g., ['analytics', 'functional'])\n */\n grantConsent(categories?: string[]): void {\n try {\n this.consentManager.grantConsent(categories);\n this.log('Consent granted', categories);\n } catch (error) {\n const formattedError = this.formatError(error, 'grantConsent');\n this.logError(formattedError);\n }\n }\n\n /**\n * Revoke consent for tracking (opt-out)\n * @param categories - Optional array of categories to revoke (if not provided, revokes all)\n */\n revokeConsent(categories?: string[]): void {\n try {\n this.consentManager.revokeConsent(categories);\n this.log('Consent revoked', categories);\n \n // Clear queued events when consent is revoked\n this.eventQueue = [];\n this.waitingForConsentQueue = [];\n } catch (error) {\n const formattedError = this.formatError(error, 'revokeConsent');\n this.logError(formattedError);\n }\n }\n\n /**\n * Get current consent state\n */\n getConsentState(): ConsentState | null {\n return this.consentManager.getConsentState();\n }\n\n /**\n * Check if user has granted consent\n * @param category - Optional category to check (if not provided, checks general consent)\n */\n hasConsent(category?: string): boolean {\n return this.consentManager.hasConsent(category);\n }\n\n /**\n * Add listener for consent state changes\n */\n onConsentChange(listener: (state: ConsentState) => void): void {\n this.consentManager.addListener(listener);\n }\n\n /**\n * Remove consent change listener\n */\n offConsentChange(listener: (state: ConsentState) => void): void {\n this.consentManager.removeListener(listener);\n }\n\n /**\n * Destroy the client and clean up resources\n */\n destroy(): void {\n this.isDestroyed = true;\n \n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n\n // Stop config refresh timer\n this.stopConfigRefreshTimer();\n\n // Clear config change listeners\n this.configChangeListeners = [];\n\n // Destroy automatic tracking managers\n if (this.heartbeatManager) {\n this.heartbeatManager.destroy();\n this.heartbeatManager = null;\n }\n\n if (this.pageTrackingManager) {\n this.pageTrackingManager.destroy();\n this.pageTrackingManager = null;\n }\n\n if (this.activityDetector) {\n this.activityDetector.destroy();\n this.activityDetector = null;\n }\n\n // Send any remaining events (in chunks if necessary)\n if (this.eventQueue.length > 0) {\n const eventsToSend = [...this.eventQueue];\n this.eventQueue = [];\n \n const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);\n \n // Send first chunk with beacon (most important for page unload)\n if (chunks.length > 0) {\n this.sendEventsWithBeacon(chunks[0]).catch(() => {\n // Silently fail during cleanup\n });\n \n // If there are more chunks, try to send them with regular fetch\n for (let i = 1; i < chunks.length; i++) {\n this.sendEventsWithBeacon(chunks[i]).catch(() => {\n // Silently fail during cleanup\n });\n }\n }\n }\n }\n}\n\n/**\n * Create a new Grain Analytics client\n */\nexport function createGrainAnalytics(config: GrainConfig): GrainAnalytics {\n return new GrainAnalytics(config);\n}\n\n// Default export for convenience\nexport default GrainAnalytics;\n\n// Global interface for IIFE build\ndeclare global {\n interface Window {\n Grain?: {\n GrainAnalytics: typeof GrainAnalytics;\n createGrainAnalytics: typeof createGrainAnalytics;\n };\n }\n}\n\n// Auto-setup for IIFE build\nif (typeof window !== 'undefined') {\n window.Grain = {\n GrainAnalytics,\n createGrainAnalytics,\n };\n}", "/**\n * Consent management for Grain Analytics\n * Handles GDPR-compliant consent tracking and state management\n */\n\nexport type ConsentMode = 'opt-in' | 'opt-out' | 'disabled';\n\nexport interface ConsentState {\n granted: boolean;\n categories: string[];\n timestamp: Date;\n version: string;\n}\n\nexport const DEFAULT_CONSENT_CATEGORIES = ['necessary', 'analytics', 'functional'];\nexport const CONSENT_VERSION = '1.0.0';\n\n/**\n * Consent manager for handling user consent state\n */\nexport class ConsentManager {\n private consentState: ConsentState | null = null;\n private consentMode: ConsentMode;\n private storageKey: string;\n private listeners: Array<(state: ConsentState) => void> = [];\n\n constructor(tenantId: string, consentMode: ConsentMode = 'opt-out') {\n this.consentMode = consentMode;\n this.storageKey = `grain_consent_${tenantId}`;\n this.loadConsentState();\n }\n\n /**\n * Load consent state from localStorage\n * \n * GDPR Compliance: In opt-in mode, we can use localStorage for consent preferences\n * since storing consent choices is a legitimate interest and necessary for compliance.\n * The consent preference itself is not tracking data.\n */\n private loadConsentState(): void {\n if (typeof window === 'undefined') return;\n\n try {\n const stored = localStorage.getItem(this.storageKey);\n if (stored) {\n const parsed = JSON.parse(stored);\n this.consentState = {\n ...parsed,\n timestamp: new Date(parsed.timestamp),\n };\n } else if (this.consentMode === 'opt-out' || this.consentMode === 'disabled') {\n // Auto-grant consent for opt-out and disabled modes\n this.consentState = {\n granted: true,\n categories: DEFAULT_CONSENT_CATEGORIES,\n timestamp: new Date(),\n version: CONSENT_VERSION,\n };\n this.saveConsentState();\n }\n // Note: In opt-in mode without stored consent, consentState remains null (no consent)\n } catch (error) {\n console.error('[Grain Consent] Failed to load consent state:', error);\n }\n }\n\n /**\n * Save consent state to localStorage\n */\n private saveConsentState(): void {\n if (typeof window === 'undefined' || !this.consentState) return;\n\n try {\n localStorage.setItem(this.storageKey, JSON.stringify(this.consentState));\n } catch (error) {\n console.error('[Grain Consent] Failed to save consent state:', error);\n }\n }\n\n /**\n * Grant consent with optional categories\n */\n grantConsent(categories?: string[]): void {\n const grantedCategories = categories || DEFAULT_CONSENT_CATEGORIES;\n \n this.consentState = {\n granted: true,\n categories: grantedCategories,\n timestamp: new Date(),\n version: CONSENT_VERSION,\n };\n\n this.saveConsentState();\n this.notifyListeners();\n }\n\n /**\n * Revoke consent (opt-out)\n */\n revokeConsent(categories?: string[]): void {\n if (!this.consentState) {\n this.consentState = {\n granted: false,\n categories: [],\n timestamp: new Date(),\n version: CONSENT_VERSION,\n };\n } else if (categories) {\n // Remove specific categories\n this.consentState = {\n ...this.consentState,\n categories: this.consentState.categories.filter(cat => !categories.includes(cat)),\n granted: this.consentState.categories.length > 0,\n timestamp: new Date(),\n };\n } else {\n // Revoke all consent\n this.consentState = {\n granted: false,\n categories: [],\n timestamp: new Date(),\n version: CONSENT_VERSION,\n };\n }\n\n this.saveConsentState();\n this.notifyListeners();\n }\n\n /**\n * Get current consent state\n */\n getConsentState(): ConsentState | null {\n return this.consentState ? { ...this.consentState } : null;\n }\n\n /**\n * Check if user has granted consent\n */\n hasConsent(category?: string): boolean {\n // Disabled mode always returns true (no consent required)\n if (this.consentMode === 'disabled') {\n return true;\n }\n\n // No consent state in opt-in mode means no consent\n if (this.consentMode === 'opt-in' && !this.consentState) {\n return false;\n }\n\n // Check consent state\n if (!this.consentState?.granted) {\n return false;\n }\n\n // Check specific category if provided\n if (category) {\n return this.consentState.categories.includes(category);\n }\n\n return true;\n }\n\n /**\n * Check if we should wait for consent before tracking\n */\n shouldWaitForConsent(): boolean {\n return this.consentMode === 'opt-in' && !this.consentState?.granted;\n }\n\n /**\n * Add consent change listener\n */\n addListener(listener: (state: ConsentState) => void): void {\n this.listeners.push(listener);\n }\n\n /**\n * Remove consent change listener\n */\n removeListener(listener: (state: ConsentState) => void): void {\n const index = this.listeners.indexOf(listener);\n if (index > -1) {\n this.listeners.splice(index, 1);\n }\n }\n\n /**\n * Notify all listeners of consent state change\n */\n private notifyListeners(): void {\n if (!this.consentState) return;\n\n this.listeners.forEach(listener => {\n try {\n listener(this.consentState!);\n } catch (error) {\n console.error('[Grain Consent] Listener error:', error);\n }\n });\n }\n\n /**\n * Clear all consent data\n */\n clearConsent(): void {\n if (typeof window === 'undefined') return;\n\n try {\n localStorage.removeItem(this.storageKey);\n this.consentState = null;\n } catch (error) {\n console.error('[Grain Consent] Failed to clear consent:', error);\n }\n }\n}\n\n", "/**\n * Cookie utilities for Grain Analytics\n * Provides GDPR-compliant cookie management with configurable options\n */\n\nexport interface CookieConfig {\n domain?: string;\n path?: string;\n sameSite?: 'strict' | 'lax' | 'none';\n secure?: boolean;\n maxAge?: number; // seconds\n}\n\n/**\n * Set a cookie with configurable options\n */\nexport function setCookie(name: string, value: string, config?: CookieConfig): void {\n if (typeof document === 'undefined') return;\n\n const parts = [`${encodeURIComponent(name)}=${encodeURIComponent(value)}`];\n \n if (config?.maxAge !== undefined) {\n parts.push(`max-age=${config.maxAge}`);\n }\n \n if (config?.domain) {\n parts.push(`domain=${config.domain}`);\n }\n \n if (config?.path) {\n parts.push(`path=${config.path}`);\n } else {\n parts.push('path=/');\n }\n \n if (config?.sameSite) {\n parts.push(`samesite=${config.sameSite}`);\n }\n \n if (config?.secure) {\n parts.push('secure');\n }\n \n document.cookie = parts.join('; ');\n}\n\n/**\n * Get a cookie value by name\n */\nexport function getCookie(name: string): string | null {\n if (typeof document === 'undefined') return null;\n\n const nameEQ = encodeURIComponent(name) + '=';\n const cookies = document.cookie.split(';');\n \n for (let i = 0; i < cookies.length; i++) {\n let cookie = cookies[i];\n while (cookie.charAt(0) === ' ') {\n cookie = cookie.substring(1);\n }\n if (cookie.indexOf(nameEQ) === 0) {\n return decodeURIComponent(cookie.substring(nameEQ.length));\n }\n }\n \n return null;\n}\n\n/**\n * Delete a cookie by name\n */\nexport function deleteCookie(name: string, config?: Pick<CookieConfig, 'domain' | 'path'>): void {\n if (typeof document === 'undefined') return;\n\n const parts = [\n `${encodeURIComponent(name)}=`,\n 'max-age=0',\n ];\n \n if (config?.domain) {\n parts.push(`domain=${config.domain}`);\n }\n \n if (config?.path) {\n parts.push(`path=${config.path}`);\n } else {\n parts.push('path=/');\n }\n \n document.cookie = parts.join('; ');\n}\n\n/**\n * Check if cookies are available and working\n */\nexport function areCookiesEnabled(): boolean {\n if (typeof document === 'undefined') return false;\n\n try {\n const testCookie = '_grain_cookie_test';\n setCookie(testCookie, 'test', { maxAge: 1 });\n const result = getCookie(testCookie) === 'test';\n deleteCookie(testCookie);\n return result;\n } catch {\n return false;\n }\n}\n\n", "/**\n * Activity Detection for Grain Analytics\n * Tracks user activity (mouse, keyboard, touch, scroll) to determine if user is active\n */\n\nexport class ActivityDetector {\n private lastActivityTime: number;\n private activityThreshold: number = 30000; // 30 seconds\n private listeners: Array<() => void> = [];\n private boundActivityHandler: () => void;\n private isDestroyed = false;\n\n // Events that indicate user activity\n private readonly activityEvents = [\n 'mousemove',\n 'mousedown',\n 'keydown',\n 'scroll',\n 'touchstart',\n 'click',\n ] as const;\n\n constructor() {\n this.lastActivityTime = Date.now();\n this.boundActivityHandler = this.debounce(this.handleActivity.bind(this), 500);\n this.setupListeners();\n }\n\n /**\n * Setup event listeners for activity detection\n */\n private setupListeners(): void {\n if (typeof window === 'undefined') return;\n\n for (const event of this.activityEvents) {\n window.addEventListener(event, this.boundActivityHandler, { passive: true });\n }\n }\n\n /**\n * Handle activity event\n */\n private handleActivity(): void {\n if (this.isDestroyed) return;\n this.lastActivityTime = Date.now();\n this.notifyListeners();\n }\n\n /**\n * Debounce function to limit how often activity handler is called\n */\n private debounce(func: () => void, wait: number): () => void {\n let timeout: number | null = null;\n \n return () => {\n if (timeout !== null) {\n clearTimeout(timeout);\n }\n \n timeout = window.setTimeout(() => {\n func();\n timeout = null;\n }, wait);\n };\n }\n\n /**\n * Check if user is currently active\n * @param threshold Time in ms to consider user inactive (default: 30s)\n */\n isActive(threshold?: number): boolean {\n const thresholdToUse = threshold ?? this.activityThreshold;\n const now = Date.now();\n return (now - this.lastActivityTime) < thresholdToUse;\n }\n\n /**\n * Get time since last activity in milliseconds\n */\n getTimeSinceLastActivity(): number {\n return Date.now() - this.lastActivityTime;\n }\n\n /**\n * Get last activity timestamp\n */\n getLastActivityTime(): number {\n return this.lastActivityTime;\n }\n\n /**\n * Set activity threshold\n */\n setActivityThreshold(threshold: number): void {\n this.activityThreshold = threshold;\n }\n\n /**\n * Add listener for activity changes\n */\n addListener(listener: () => void): void {\n this.listeners.push(listener);\n }\n\n /**\n * Remove listener\n */\n removeListener(listener: () => void): void {\n const index = this.listeners.indexOf(listener);\n if (index > -1) {\n this.listeners.splice(index, 1);\n }\n }\n\n /**\n * Notify all listeners\n */\n private notifyListeners(): void {\n for (const listener of this.listeners) {\n try {\n listener();\n } catch (error) {\n console.error('[Activity Detector] Listener error:', error);\n }\n }\n }\n\n /**\n * Cleanup and remove listeners\n */\n destroy(): void {\n if (this.isDestroyed) return;\n \n if (typeof window !== 'undefined') {\n for (const event of this.activityEvents) {\n window.removeEventListener(event, this.boundActivityHandler);\n }\n }\n \n this.listeners = [];\n this.isDestroyed = true;\n }\n}\n\n", "/**\n * Heartbeat Manager for Grain Analytics\n * Tracks session activity with consent-aware behavior\n */\n\nimport type { ActivityDetector } from './activity';\n\nexport interface HeartbeatConfig {\n activeInterval: number; // Interval when user is active (ms)\n inactiveInterval: number; // Interval when user is inactive (ms)\n debug?: boolean;\n}\n\nexport interface HeartbeatTracker {\n trackSystemEvent(eventName: string, properties: Record<string, unknown>): void;\n hasConsent(category?: string): boolean;\n getEffectiveUserId(): string;\n getEphemeralSessionId(): string;\n getCurrentPage(): string | null;\n getEventCountSinceLastHeartbeat(): number;\n resetEventCountSinceLastHeartbeat(): void;\n}\n\nexport class HeartbeatManager {\n private config: HeartbeatConfig;\n private tracker: HeartbeatTracker;\n private activityDetector: ActivityDetector;\n private heartbeatTimer: number | null = null;\n private isDestroyed = false;\n private lastHeartbeatTime: number;\n private currentInterval: number;\n private hasSentPageLoadHeartbeat = false;\n\n constructor(\n tracker: HeartbeatTracker,\n activityDetector: ActivityDetector,\n config: HeartbeatConfig\n ) {\n this.tracker = tracker;\n this.activityDetector = activityDetector;\n this.config = config;\n this.lastHeartbeatTime = Date.now();\n this.currentInterval = config.activeInterval;\n\n // Send initial heartbeat when page loads (if allowed by consent)\n this.sendPageLoadHeartbeat();\n \n // Start periodic heartbeat tracking\n this.scheduleNextHeartbeat();\n }\n\n /**\n * Send initial heartbeat when page loads\n */\n private sendPageLoadHeartbeat(): void {\n if (this.isDestroyed || this.hasSentPageLoadHeartbeat) return;\n\n // Wait for page to be fully loaded\n if (typeof window !== 'undefined' && document.readyState !== 'complete') {\n const handleLoad = () => {\n this.sendHeartbeat('page_load');\n this.hasSentPageLoadHeartbeat = true;\n window.removeEventListener('load', handleLoad);\n };\n window.addEventListener('load', handleLoad);\n } else {\n // Page is already loaded or we're in a non-browser environment\n this.sendHeartbeat('page_load');\n this.hasSentPageLoadHeartbeat = true;\n }\n }\n\n /**\n * Schedule the next heartbeat based on current activity\n */\n private scheduleNextHeartbeat(): void {\n if (this.isDestroyed) return;\n\n // Clear existing timer\n if (this.heartbeatTimer !== null) {\n clearTimeout(this.heartbeatTimer);\n }\n\n // Determine interval based on activity\n const isActive = this.activityDetector.isActive(60000); // 1 minute threshold\n this.currentInterval = isActive ? this.config.activeInterval : this.config.inactiveInterval;\n\n // Schedule next heartbeat\n this.heartbeatTimer = window.setTimeout(() => {\n this.sendHeartbeat('periodic');\n this.scheduleNextHeartbeat();\n }, this.currentInterval);\n\n if (this.config.debug) {\n console.log(\n `[Heartbeat] Scheduled next heartbeat in ${this.currentInterval / 1000}s (${isActive ? 'active' : 'inactive'})`\n );\n }\n }\n\n /**\n * Send heartbeat event\n */\n private sendHeartbeat(heartbeatType: 'periodic' | 'page_load' = 'periodic'): void {\n if (this.isDestroyed) return;\n\n const now = Date.now();\n const isActive = this.activityDetector.isActive(60000); // 1 minute threshold\n const hasConsent = this.tracker.hasConsent('analytics');\n\n // Base properties (always included)\n const properties: Record<string, unknown> = {\n type: 'heartbeat',\n heartbeat_type: heartbeatType,\n status: isActive ? 'active' : 'inactive',\n timestamp: now,\n };\n\n // Enhanced properties when consent is granted\n if (hasConsent) {\n const page = this.tracker.getCurrentPage();\n if (page) {\n properties.page = page;\n }\n \n // Only include duration and event count for periodic heartbeats\n if (heartbeatType === 'periodic') {\n properties.duration = now - this.lastHeartbeatTime;\n properties.event_count = this.tracker.getEventCountSinceLastHeartbeat();\n \n // Reset event count\n this.tracker.resetEventCountSinceLastHeartbeat();\n }\n }\n\n // Track the heartbeat event\n this.tracker.trackSystemEvent('_grain_heartbeat', properties);\n\n this.lastHeartbeatTime = now;\n\n if (this.config.debug) {\n console.log('[Heartbeat] Sent heartbeat:', properties);\n }\n }\n\n /**\n * Destroy the heartbeat manager\n */\n destroy(): void {\n if (this.isDestroyed) return;\n\n if (this.heartbeatTimer !== null) {\n clearTimeout(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n\n this.isDestroyed = true;\n\n if (this.config.debug) {\n console.log('[Heartbeat] Destroyed');\n }\n }\n}\n\n", "/**\n * Page Tracking for Grain Analytics\n * Automatically tracks page views with consent-aware behavior\n */\n\nexport interface PageTrackingConfig {\n stripQueryParams: boolean;\n debug?: boolean;\n}\n\nexport interface PageTracker {\n trackSystemEvent(eventName: string, properties: Record<string, unknown>): void;\n hasConsent(category?: string): boolean;\n getEffectiveUserId(): string;\n getEphemeralSessionId(): string;\n}\n\nexport class PageTrackingManager {\n private config: PageTrackingConfig;\n private tracker: PageTracker;\n private isDestroyed = false;\n private currentPath: string | null = null;\n private originalPushState: typeof history.pushState | null = null;\n private originalReplaceState: typeof history.replaceState | null = null;\n\n constructor(tracker: PageTracker, config: PageTrackingConfig) {\n this.tracker = tracker;\n this.config = config;\n \n // Track initial page load\n this.trackCurrentPage();\n \n // Setup listeners\n this.setupHistoryListeners();\n this.setupHashChangeListener();\n }\n\n /**\n * Setup History API listeners (pushState, replaceState, popstate)\n */\n private setupHistoryListeners(): void {\n if (typeof window === 'undefined' || typeof history === 'undefined') return;\n\n // Wrap pushState\n this.originalPushState = history.pushState;\n history.pushState = (state: any, title: string, url?: string | URL | null) => {\n this.originalPushState?.call(history, state, title, url);\n this.trackCurrentPage();\n };\n\n // Wrap replaceState\n this.originalReplaceState = history.replaceState;\n history.replaceState = (state: any, title: string, url?: string | URL | null) => {\n this.originalReplaceState?.call(history, state, title, url);\n this.trackCurrentPage();\n };\n\n // Listen to popstate (back/forward buttons)\n window.addEventListener('popstate', this.handlePopState);\n }\n\n /**\n * Setup hash change listener\n */\n private setupHashChangeListener(): void {\n if (typeof window === 'undefined') return;\n window.addEventListener('hashchange', this.handleHashChange);\n }\n\n /**\n * Handle popstate event (back/forward navigation)\n */\n private handlePopState = (): void => {\n if (this.isDestroyed) return;\n this.trackCurrentPage();\n };\n\n /**\n * Handle hash change event\n */\n private handleHashChange = (): void => {\n if (this.isDestroyed) return;\n this.trackCurrentPage();\n };\n\n /**\n * Track the current page\n */\n private trackCurrentPage(): void {\n if (this.isDestroyed || typeof window === 'undefined') return;\n\n const page = this.extractPath(window.location.href);\n \n // Don't track if it's the same page\n if (page === this.currentPath) {\n return;\n }\n\n this.currentPath = page;\n\n const hasConsent = this.tracker.hasConsent('analytics');\n\n // Base properties (always included)\n const properties: Record<string, unknown> = {\n page,\n timestamp: Date.now(),\n };\n\n // Enhanced properties when consent is granted\n if (hasConsent) {\n properties.referrer = document.referrer || '';\n properties.title = document.title || '';\n properties.full_url = window.location.href;\n }\n\n // Track the page view event\n this.tracker.trackSystemEvent('page_view', properties);\n\n if (this.config.debug) {\n console.log('[Page Tracking] Tracked page view:', properties);\n }\n }\n\n /**\n * Extract path from URL, optionally stripping query parameters\n */\n private extractPath(url: string): string {\n try {\n const urlObj = new URL(url);\n let path = urlObj.pathname + urlObj.hash;\n \n if (!this.config.stripQueryParams && urlObj.search) {\n path += urlObj.search;\n }\n \n return path;\n } catch (error) {\n // If URL parsing fails, return the raw string\n if (this.config.debug) {\n console.warn('[Page Tracking] Failed to parse URL:', url, error);\n }\n return url;\n }\n }\n\n /**\n * Get the current page path\n */\n getCurrentPage(): string | null {\n return this.currentPath;\n }\n\n /**\n * Manually track a page view (for custom navigation)\n */\n trackPage(page: string, properties?: Record<string, unknown>): void {\n if (this.isDestroyed) return;\n\n const hasConsent = this.tracker.hasConsent('analytics');\n\n // Base properties\n const baseProperties: Record<string, unknown> = {\n page,\n timestamp: Date.now(),\n ...properties,\n };\n\n // Enhanced properties when consent is granted\n if (hasConsent && typeof document !== 'undefined') {\n if (!baseProperties.referrer) {\n baseProperties.referrer = document.referrer || '';\n }\n if (!baseProperties.title) {\n baseProperties.title = document.title || '';\n }\n if (!baseProperties.full_url && typeof window !== 'undefined') {\n baseProperties.full_url = window.location.href;\n }\n }\n\n this.tracker.trackSystemEvent('page_view', baseProperties);\n\n if (this.config.debug) {\n console.log('[Page Tracking] Manually tracked page:', baseProperties);\n }\n }\n\n /**\n * Destroy the page tracker\n */\n destroy(): void {\n if (this.isDestroyed) return;\n\n // Restore original history methods\n if (typeof history !== 'undefined') {\n if (this.originalPushState) {\n history.pushState = this.originalPushState;\n }\n if (this.originalReplaceState) {\n history.replaceState = this.originalReplaceState;\n }\n }\n\n // Remove event listeners\n if (typeof window !== 'undefined') {\n window.removeEventListener('popstate', this.handlePopState);\n window.removeEventListener('hashchange', this.handleHashChange);\n }\n\n this.isDestroyed = true;\n\n if (this.config.debug) {\n console.log('[Page Tracking] Destroyed');\n }\n }\n}\n\n"],
|
|
5
|
-
"mappings": "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",
|
|
3
|
+
"sources": ["../src/index.ts", "../src/consent.ts", "../src/cookies.ts", "../src/activity.ts", "../src/heartbeat.ts", "../src/attribution.ts", "../src/page-tracking.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Grain Analytics Web SDK\n * A lightweight, dependency-free TypeScript SDK for sending analytics events to Grain's REST API\n */\n\nimport { ConsentManager, ConsentState, ConsentMode } from './consent';\nimport { setCookie, getCookie, deleteCookie, areCookiesEnabled, CookieConfig } from './cookies';\nimport { ActivityDetector } from './activity';\nimport { HeartbeatManager, type HeartbeatTracker } from './heartbeat';\nimport { PageTrackingManager, type PageTracker } from './page-tracking';\nimport {\n categorizeReferrer,\n parseUTMParameters,\n getOrCreateFirstTouchAttribution,\n getSessionUTMParameters,\n type ReferrerCategory,\n type UTMParameters,\n type FirstTouchAttribution,\n} from './attribution';\n\n// Re-export attribution types and functions\nexport type { ReferrerCategory, UTMParameters, FirstTouchAttribution };\nexport { categorizeReferrer, parseUTMParameters };\n\nexport interface GrainEvent {\n eventName: string;\n userId?: string;\n properties?: Record<string, unknown>;\n timestamp?: Date;\n}\n\nexport interface EventPayload {\n eventName: string;\n userId: string;\n properties: Record<string, unknown>;\n}\n\nexport type AuthStrategy = 'NONE' | 'SERVER_SIDE' | 'JWT';\n\nexport interface AuthProvider {\n getToken(): Promise<string> | string;\n}\n\n// Re-export privacy types\nexport type { ConsentState, ConsentMode, CookieConfig };\n\nexport interface GrainConfig {\n tenantId: string;\n apiUrl?: string;\n authStrategy?: AuthStrategy;\n secretKey?: string; // For SERVER_SIDE auth\n authProvider?: AuthProvider; // For JWT auth\n userId?: string; // Global user ID for all events\n batchSize?: number;\n flushInterval?: number; // milliseconds\n retryAttempts?: number;\n retryDelay?: number; // milliseconds\n maxEventsPerRequest?: number; // Maximum events to send in a single API request\n debug?: boolean;\n // Remote Config options\n defaultConfigurations?: Record<string, string>; // Default values for configurations\n configCacheKey?: string; // Custom cache key for configurations\n configRefreshInterval?: number; // Auto-refresh interval in milliseconds (default: 5 minutes)\n enableConfigCache?: boolean; // Enable/disable configuration caching (default: true)\n // Privacy & Consent options\n consentMode?: ConsentMode; // 'opt-in' | 'opt-out' | 'disabled' (default: 'opt-out')\n waitForConsent?: boolean; // Queue events until consent is granted (default: false)\n enableCookies?: boolean; // Use cookies for persistent user ID (default: false)\n cookieOptions?: CookieConfig; // Cookie configuration options\n anonymizeIP?: boolean; // Anonymize IP addresses (default: false)\n disableAutoProperties?: boolean; // Disable automatic property collection (default: false)\n allowedProperties?: string[]; // Whitelist of allowed event properties (default: all)\n // Automatic Tracking options\n enableHeartbeat?: boolean; // Enable heartbeat tracking (default: true)\n heartbeatActiveInterval?: number; // Active interval in ms (default: 120000 - 2 min)\n heartbeatInactiveInterval?: number; // Inactive interval in ms (default: 300000 - 5 min)\n enableAutoPageView?: boolean; // Enable automatic page view tracking (default: true)\n stripQueryParams?: boolean; // Strip query params from URLs (default: true)\n}\n\nexport interface SendEventOptions {\n flush?: boolean; // Force immediate send\n}\n\nexport interface SetPropertyOptions {\n userId?: string; // Override global userId\n}\n\nexport interface LoginOptions {\n userId?: string; // User ID to set\n authToken?: string; // Auth token to set for JWT auth\n authStrategy?: AuthStrategy; // Override auth strategy\n}\n\nexport interface PropertyPayload {\n userId: string;\n [key: string]: string; // All property values must be strings\n}\n\n// Remote Config interfaces\nexport interface RemoteConfigRequest {\n userId: string;\n immediateKeys: string[];\n properties?: Record<string, string>;\n}\n\nexport interface RemoteConfigResponse {\n userId: string;\n snapshotId: string;\n configurations: Record<string, string>;\n isFinal: boolean;\n qualifiedSegments: string[];\n qualifiedRuleSets: string[];\n timestamp: string;\n isFromCache: boolean;\n}\n\nexport interface RemoteConfigOptions {\n immediateKeys?: string[];\n properties?: Record<string, string>;\n userId?: string; // Override global userId\n forceRefresh?: boolean; // Force fetch from API, bypass cache\n}\n\nexport interface RemoteConfigCache {\n configurations: Record<string, string>;\n snapshotId: string;\n timestamp: string;\n userId: string;\n}\n\nexport type ConfigChangeListener = (configurations: Record<string, string>) => void;\n\n// Template event interfaces\nexport interface LoginEventProperties extends Record<string, unknown> {\n method?: string; // 'email', 'google', 'facebook', etc.\n success?: boolean;\n errorMessage?: string;\n loginAttempt?: number;\n rememberMe?: boolean;\n twoFactorEnabled?: boolean;\n}\n\nexport interface SignupEventProperties extends Record<string, unknown> {\n method?: string; // 'email', 'google', 'facebook', etc.\n source?: string; // 'landing_page', 'referral', 'ad', etc.\n plan?: string; // 'free', 'pro', 'enterprise', etc.\n success?: boolean;\n errorMessage?: string;\n}\n\nexport interface CheckoutEventProperties extends Record<string, unknown> {\n orderId?: string;\n total?: number;\n currency?: string;\n items?: Array<{\n id: string;\n name: string;\n price: number;\n quantity: number;\n }>;\n paymentMethod?: string; // 'credit_card', 'paypal', 'stripe', etc.\n success?: boolean;\n errorMessage?: string;\n couponCode?: string;\n discount?: number;\n}\n\nexport interface PageViewEventProperties extends Record<string, unknown> {\n page?: string;\n title?: string;\n referrer?: string;\n url?: string;\n userAgent?: string;\n screenResolution?: string;\n viewportSize?: string;\n}\n\nexport interface PurchaseEventProperties extends Record<string, unknown> {\n orderId?: string;\n total?: number;\n currency?: string;\n items?: Array<{\n id: string;\n name: string;\n price: number;\n quantity: number;\n category?: string;\n }>;\n paymentMethod?: string;\n shippingMethod?: string;\n tax?: number;\n shipping?: number;\n discount?: number;\n couponCode?: string;\n}\n\nexport interface SearchEventProperties extends Record<string, unknown> {\n query?: string;\n results?: number;\n filters?: Record<string, unknown>;\n sortBy?: string;\n category?: string;\n success?: boolean;\n}\n\nexport interface AddToCartEventProperties extends Record<string, unknown> {\n itemId?: string;\n itemName?: string;\n price?: number;\n quantity?: number;\n currency?: string;\n category?: string;\n variant?: string;\n}\n\nexport interface RemoveFromCartEventProperties extends Record<string, unknown> {\n itemId?: string;\n itemName?: string;\n price?: number;\n quantity?: number;\n currency?: string;\n category?: string;\n variant?: string;\n}\n\n// Error handling interfaces\nexport interface ErrorDigest {\n eventCount: number;\n totalProperties: number;\n totalSize: number;\n eventNames: string[];\n userIds: string[];\n}\n\nexport interface FormattedError {\n code: string;\n message: string;\n digest: ErrorDigest;\n timestamp: string;\n context: string;\n originalError?: unknown;\n}\n\n/**\n * Main Grain Analytics client\n * \n * Features:\n * - Automatic UUIDv4 generation for anonymous users\n * - Login/logout functionality for dynamic auth token injection\n * - Persistent anonymous user ID across sessions (when consent is granted)\n * - Support for multiple auth strategies (NONE, SERVER_SIDE, JWT)\n * - GDPR-compliant consent management with strict opt-in mode\n * - Optional cookie support for cross-session tracking\n * \n * GDPR Compliance (Opt-in Mode):\n * - Without consent: Only ephemeral session IDs (memory-only) are used\n * - No cookies or localStorage identifiers until consent is granted\n * - Exceptions: User explicitly identified via identify()/login() or JWT auth\n * - Remote config cache and consent preferences use localStorage (functional/necessary)\n */\ntype RequiredConfig = Required<Omit<GrainConfig, 'secretKey' | 'authProvider' | 'userId' | 'cookieOptions' | 'allowedProperties'>> & {\n secretKey?: string;\n authProvider?: AuthProvider;\n userId?: string;\n cookieOptions?: CookieConfig;\n allowedProperties?: string[];\n};\n\nexport class GrainAnalytics implements HeartbeatTracker, PageTracker {\n private config: RequiredConfig;\n private eventQueue: EventPayload[] = [];\n private waitingForConsentQueue: EventPayload[] = [];\n private flushTimer: number | null = null;\n private isDestroyed = false;\n private globalUserId: string | null = null;\n private persistentAnonymousUserId: string | null = null;\n // Remote Config properties\n private configCache: RemoteConfigCache | null = null;\n private configRefreshTimer: number | null = null;\n private configChangeListeners: ConfigChangeListener[] = [];\n private configFetchPromise: Promise<RemoteConfigResponse> | null = null;\n // Privacy & Consent properties\n private consentManager: ConsentManager;\n private cookiesEnabled: boolean = false;\n // Automatic Tracking properties\n private activityDetector: ActivityDetector | null = null;\n private heartbeatManager: HeartbeatManager | null = null;\n private pageTrackingManager: PageTrackingManager | null = null;\n private ephemeralSessionId: string | null = null;\n private eventCountSinceLastHeartbeat: number = 0;\n // Session tracking\n private sessionStartTime: number = Date.now();\n private sessionEventCount: number = 0;\n\n constructor(config: GrainConfig) {\n this.config = {\n apiUrl: 'https://api.grainql.com',\n authStrategy: 'NONE',\n batchSize: 50,\n flushInterval: 5000, // 5 seconds\n retryAttempts: 3,\n retryDelay: 1000, // 1 second\n maxEventsPerRequest: 160, // Maximum events per API request\n debug: false,\n // Remote Config defaults\n defaultConfigurations: {},\n configCacheKey: 'grain_config',\n configRefreshInterval: 300000, // 5 minutes\n enableConfigCache: true,\n // Privacy defaults\n consentMode: 'opt-out',\n waitForConsent: false,\n enableCookies: false,\n anonymizeIP: false,\n disableAutoProperties: false,\n // Automatic Tracking defaults\n enableHeartbeat: true,\n heartbeatActiveInterval: 120000, // 2 minutes\n heartbeatInactiveInterval: 300000, // 5 minutes\n enableAutoPageView: true,\n stripQueryParams: true,\n ...config,\n tenantId: config.tenantId,\n };\n\n // Initialize consent manager\n this.consentManager = new ConsentManager(this.config.tenantId, this.config.consentMode);\n\n // Check if cookies are enabled\n if (this.config.enableCookies) {\n this.cookiesEnabled = areCookiesEnabled();\n if (!this.cookiesEnabled && this.config.debug) {\n console.warn('[Grain Analytics] Cookies are not available, falling back to localStorage');\n }\n }\n\n // Set global userId if provided in config\n if (config.userId) {\n this.globalUserId = config.userId;\n }\n\n this.validateConfig();\n this.initializePersistentAnonymousUserId();\n this.setupBeforeUnload();\n this.startFlushTimer();\n this.initializeConfigCache();\n\n // Initialize ephemeral session ID (memory-only, not persisted)\n this.ephemeralSessionId = this.generateUUID();\n\n // Initialize automatic tracking (browser only)\n if (typeof window !== 'undefined') {\n this.initializeAutomaticTracking();\n // Track session start\n this.trackSessionStart();\n }\n\n // Set up consent change listener to flush waiting events and handle consent upgrade\n this.consentManager.addListener((state) => {\n if (state.granted) {\n this.handleConsentGranted();\n }\n });\n }\n\n private validateConfig(): void {\n if (!this.config.tenantId) {\n throw new Error('Grain Analytics: tenantId is required');\n }\n\n if (this.config.authStrategy === 'SERVER_SIDE' && !this.config.secretKey) {\n throw new Error('Grain Analytics: secretKey is required for SERVER_SIDE auth strategy');\n }\n\n if (this.config.authStrategy === 'JWT' && !this.config.authProvider) {\n throw new Error('Grain Analytics: authProvider is required for JWT auth strategy');\n }\n }\n\n /**\n * Generate a UUID v4 string\n */\n private generateUUID(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n \n // Fallback for environments without crypto.randomUUID\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n const r = Math.random() * 16 | 0;\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n }\n\n /**\n * Check if we should allow persistent storage (GDPR compliance)\n * \n * Returns true if:\n * - Consent has been granted, OR\n * - Not in opt-in mode, OR\n * - User has been explicitly identified by the site (login/identify), OR\n * - Using JWT auth strategy (functional/essential purpose)\n */\n private shouldAllowPersistentStorage(): boolean {\n const hasConsent = this.consentManager.hasConsent('analytics');\n const isOptInMode = this.config.consentMode === 'opt-in';\n const userExplicitlyIdentified = !!this.globalUserId;\n const isJWTAuth = this.config.authStrategy === 'JWT';\n \n // Allow persistent storage if any of these conditions are met\n return hasConsent || !isOptInMode || userExplicitlyIdentified || isJWTAuth;\n }\n\n /**\n * Generate a proper UUIDv4 identifier for anonymous user ID\n */\n private generateAnonymousUserId(): string {\n return this.generateUUID();\n }\n\n /**\n * Initialize persistent anonymous user ID from cookies or localStorage\n * Priority: Cookie \u2192 localStorage \u2192 generate new\n * \n * GDPR Compliance: In opt-in mode without consent, we skip loading/saving\n * persistent IDs unless the user has been explicitly identified by the site\n * or when using JWT auth (functional/essential purpose)\n */\n private initializePersistentAnonymousUserId(): void {\n if (typeof window === 'undefined') return;\n\n // Check if we should avoid persistent storage (GDPR compliance)\n if (!this.shouldAllowPersistentStorage()) {\n this.log('Opt-in mode without consent: skipping persistent ID initialization (GDPR compliance)');\n return; // Don't load or create persistent ID\n }\n\n const storageKey = `grain_anonymous_user_id_${this.config.tenantId}`;\n const cookieName = '_grain_uid';\n \n try {\n // Try to load from cookie first if enabled\n if (this.cookiesEnabled) {\n const cookieValue = getCookie(cookieName);\n if (cookieValue) {\n this.persistentAnonymousUserId = cookieValue;\n this.log('Loaded persistent anonymous user ID from cookie:', this.persistentAnonymousUserId);\n return;\n }\n }\n\n // Fallback to localStorage\n const stored = localStorage.getItem(storageKey);\n if (stored) {\n this.persistentAnonymousUserId = stored;\n this.log('Loaded persistent anonymous user ID from localStorage:', this.persistentAnonymousUserId);\n \n // Migrate to cookie if enabled\n if (this.cookiesEnabled) {\n this.savePersistentAnonymousUserId(stored);\n }\n } else {\n // Generate new UUIDv4 anonymous user ID\n this.persistentAnonymousUserId = this.generateAnonymousUserId();\n this.savePersistentAnonymousUserId(this.persistentAnonymousUserId);\n this.log('Generated new persistent anonymous user ID:', this.persistentAnonymousUserId);\n }\n } catch (error) {\n this.log('Failed to initialize persistent anonymous user ID:', error);\n // Fallback: generate temporary ID without persistence\n this.persistentAnonymousUserId = this.generateAnonymousUserId();\n }\n }\n\n /**\n * Save persistent anonymous user ID to cookie and/or localStorage\n * \n * GDPR Compliance: In opt-in mode without consent, we don't persist IDs\n * unless the user has been explicitly identified or using JWT auth\n */\n private savePersistentAnonymousUserId(userId: string): void {\n if (typeof window === 'undefined') return;\n\n // Check if we should avoid persistent storage (GDPR compliance)\n if (!this.shouldAllowPersistentStorage()) {\n this.log('Opt-in mode without consent: skipping persistent ID save (GDPR compliance)');\n return; // Don't save persistent ID\n }\n\n const storageKey = `grain_anonymous_user_id_${this.config.tenantId}`;\n const cookieName = '_grain_uid';\n\n try {\n // Save to cookie if enabled\n if (this.cookiesEnabled) {\n const cookieOptions: CookieConfig = {\n maxAge: 365 * 24 * 60 * 60, // 365 days\n sameSite: 'lax',\n secure: typeof window !== 'undefined' && window.location.protocol === 'https:',\n ...this.config.cookieOptions,\n };\n setCookie(cookieName, userId, cookieOptions);\n }\n\n // Always save to localStorage as fallback\n localStorage.setItem(storageKey, userId);\n } catch (error) {\n this.log('Failed to save persistent anonymous user ID:', error);\n }\n }\n\n /**\n * Get the effective user ID (global userId or persistent anonymous ID)\n * \n * GDPR Compliance: In opt-in mode without consent and no explicit user identification,\n * this should not be called. Use getEphemeralSessionId() instead.\n */\n private getEffectiveUserIdInternal(): string {\n if (this.globalUserId) {\n return this.globalUserId;\n }\n \n if (this.persistentAnonymousUserId) {\n return this.persistentAnonymousUserId;\n }\n \n // Generate a new UUIDv4 identifier as fallback\n this.persistentAnonymousUserId = this.generateAnonymousUserId();\n \n // Try to persist it (will be skipped in opt-in mode without consent)\n this.savePersistentAnonymousUserId(this.persistentAnonymousUserId);\n \n return this.persistentAnonymousUserId;\n }\n\n private log(...args: unknown[]): void {\n if (this.config.debug) {\n console.log('[Grain Analytics]', ...args);\n }\n }\n\n /**\n * Create error digest from events\n */\n private createErrorDigest(events: EventPayload[]): ErrorDigest {\n const eventNames = [...new Set(events.map(e => e.eventName))];\n const userIds = [...new Set(events.map(e => e.userId))];\n \n let totalProperties = 0;\n let totalSize = 0;\n \n events.forEach(event => {\n const properties = event.properties || {};\n totalProperties += Object.keys(properties).length;\n totalSize += JSON.stringify(event).length;\n });\n\n return {\n eventCount: events.length,\n totalProperties,\n totalSize,\n eventNames,\n userIds,\n };\n }\n\n /**\n * Format error with beautiful structure\n */\n private formatError(\n error: unknown,\n context: string,\n events?: EventPayload[]\n ): FormattedError {\n const digest = events ? this.createErrorDigest(events) : {\n eventCount: 0,\n totalProperties: 0,\n totalSize: 0,\n eventNames: [],\n userIds: [],\n };\n\n let code = 'UNKNOWN_ERROR';\n let message = 'An unknown error occurred';\n\n if (error instanceof Error) {\n message = error.message;\n \n // Determine error code based on error type and message\n if (message.includes('fetch failed') || message.includes('network error')) {\n code = 'NETWORK_ERROR';\n } else if (message.includes('timeout')) {\n code = 'TIMEOUT_ERROR';\n } else if (message.includes('HTTP 4')) {\n code = 'CLIENT_ERROR';\n } else if (message.includes('HTTP 5')) {\n code = 'SERVER_ERROR';\n } else if (message.includes('JSON')) {\n code = 'PARSE_ERROR';\n } else if (message.includes('auth') || message.includes('unauthorized')) {\n code = 'AUTH_ERROR';\n } else if (message.includes('rate limit') || message.includes('429')) {\n code = 'RATE_LIMIT_ERROR';\n } else {\n code = 'GENERAL_ERROR';\n }\n } else if (typeof error === 'string') {\n message = error;\n code = 'STRING_ERROR';\n } else if (error && typeof error === 'object' && 'status' in error) {\n const status = (error as { status: number }).status;\n code = `HTTP_${status}`;\n message = `HTTP ${status} error`;\n }\n\n return {\n code,\n message,\n digest,\n timestamp: new Date().toISOString(),\n context,\n originalError: error,\n };\n }\n\n /**\n * Log formatted error gracefully\n */\n private logError(formattedError: FormattedError): void {\n const { code, message, digest, timestamp, context } = formattedError;\n \n const errorOutput = {\n '\uD83D\uDEA8 Grain Analytics Error': {\n 'Error Code': code,\n 'Message': message,\n 'Context': context,\n 'Timestamp': timestamp,\n 'Event Digest': {\n 'Events': digest.eventCount,\n 'Properties': digest.totalProperties,\n 'Size (bytes)': digest.totalSize,\n 'Event Names': digest.eventNames.length > 0 ? digest.eventNames.join(', ') : 'None',\n 'User IDs': digest.userIds.length > 0 ? digest.userIds.slice(0, 3).join(', ') + (digest.userIds.length > 3 ? '...' : '') : 'None',\n }\n }\n };\n\n console.error('\uD83D\uDEA8 Grain Analytics Error:', errorOutput);\n \n // Also log in a more compact format for debugging\n if (this.config.debug) {\n console.error(`[Grain Analytics] ${code}: ${message} (${context}) - Events: ${digest.eventCount}, Props: ${digest.totalProperties}, Size: ${digest.totalSize}B`);\n }\n }\n\n /**\n * Safely execute a function with error handling\n */\n private async safeExecute<T>(\n fn: () => Promise<T>,\n context: string,\n events?: EventPayload[]\n ): Promise<T | null> {\n try {\n return await fn();\n } catch (error) {\n const formattedError = this.formatError(error, context, events);\n this.logError(formattedError);\n return null;\n }\n }\n\n private formatEvent(event: GrainEvent): EventPayload {\n return {\n eventName: event.eventName,\n userId: event.userId || this.getEffectiveUserIdInternal(),\n properties: event.properties || {},\n };\n }\n\n private async getAuthHeaders(): Promise<Record<string, string>> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n switch (this.config.authStrategy) {\n case 'NONE':\n break;\n case 'SERVER_SIDE':\n headers['Authorization'] = `Chase ${this.config.secretKey}`;\n break;\n case 'JWT':\n if (this.config.authProvider) {\n const token = await this.config.authProvider.getToken();\n headers['Authorization'] = `Bearer ${token}`;\n }\n break;\n }\n\n return headers;\n }\n\n private async delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n\n private isRetriableError(error: unknown): boolean {\n if (error instanceof Error) {\n // Check for specific network or fetch errors\n const message = error.message.toLowerCase();\n if (message.includes('fetch failed')) return true;\n if (message === 'network error') return true; // Exact match to avoid \"Non-network error\"\n if (message.includes('timeout')) return true;\n if (message.includes('connection')) return true;\n }\n \n // Check for HTTP status codes that are retriable\n if (typeof error === 'object' && error !== null && 'status' in error) {\n const status = (error as { status: number }).status;\n return status >= 500 || status === 429; // Server errors or rate limiting\n }\n \n return false;\n }\n\n private async sendEvents(events: EventPayload[]): Promise<void> {\n if (events.length === 0) return;\n\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= this.config.retryAttempts; attempt++) {\n try {\n const headers = await this.getAuthHeaders();\n const url = `${this.config.apiUrl}/v1/events/${encodeURIComponent(this.config.tenantId)}/multi`;\n\n this.log(`Sending ${events.length} events to ${url} (attempt ${attempt + 1})`);\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(events),\n });\n\n if (!response.ok) {\n let errorMessage = `HTTP ${response.status}`;\n try {\n const errorBody = await response.json();\n if (errorBody?.message) {\n errorMessage = errorBody.message;\n }\n } catch {\n const errorText = await response.text();\n if (errorText) {\n errorMessage = errorText;\n }\n }\n \n const error = new Error(`Failed to send events: ${errorMessage}`) as Error & { status?: number };\n error.status = response.status;\n throw error;\n }\n\n this.log(`Successfully sent ${events.length} events`);\n return; // Success, exit retry loop\n \n } catch (error) {\n lastError = error;\n \n if (attempt === this.config.retryAttempts) {\n // Last attempt, don't retry - log error gracefully\n const formattedError = this.formatError(error, `sendEvents (attempt ${attempt + 1}/${this.config.retryAttempts + 1})`, events);\n this.logError(formattedError);\n return; // Don't throw, just return gracefully\n }\n \n if (!this.isRetriableError(error)) {\n // Non-retriable error, don't retry - log error gracefully\n const formattedError = this.formatError(error, `sendEvents (non-retriable error)`, events);\n this.logError(formattedError);\n return; // Don't throw, just return gracefully\n }\n \n const delayMs = this.config.retryDelay * Math.pow(2, attempt); // Exponential backoff\n this.log(`Retrying in ${delayMs}ms after error:`, error);\n await this.delay(delayMs);\n }\n }\n }\n\n private async sendEventsWithBeacon(events: EventPayload[]): Promise<void> {\n if (events.length === 0) return;\n\n try {\n const headers = await this.getAuthHeaders();\n const url = `${this.config.apiUrl}/v1/events/${encodeURIComponent(this.config.tenantId)}/multi`;\n\n const body = JSON.stringify({ events });\n\n // Try beacon API first (more reliable for page unload)\n if (typeof navigator !== 'undefined' && 'sendBeacon' in navigator) {\n const blob = new Blob([body], { type: 'application/json' });\n const success = navigator.sendBeacon(url, blob);\n \n if (success) {\n this.log(`Successfully sent ${events.length} events via beacon`);\n return;\n }\n }\n\n // Fallback to fetch with keepalive\n await fetch(url, {\n method: 'POST',\n headers,\n body,\n keepalive: true,\n });\n\n this.log(`Successfully sent ${events.length} events via fetch (keepalive)`);\n } catch (error) {\n // Log error gracefully for beacon failures (page unload scenarios)\n const formattedError = this.formatError(error, 'sendEventsWithBeacon', events);\n this.logError(formattedError);\n }\n }\n\n private startFlushTimer(): void {\n if (typeof window === 'undefined') return;\n \n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n }\n\n this.flushTimer = window.setInterval(() => {\n if (this.eventQueue.length > 0) {\n this.flush().catch((error) => {\n const formattedError = this.formatError(error, 'auto-flush');\n this.logError(formattedError);\n });\n }\n }, this.config.flushInterval);\n }\n\n private setupBeforeUnload(): void {\n if (typeof window === 'undefined') return;\n\n const handleBeforeUnload = () => {\n // Track session end\n this.trackSessionEnd();\n \n if (this.eventQueue.length > 0) {\n // Use beacon API for reliable delivery during page unload\n const eventsToSend = [...this.eventQueue];\n this.eventQueue = [];\n \n const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);\n \n // Send first chunk with beacon (most important for page unload)\n if (chunks.length > 0) {\n this.sendEventsWithBeacon(chunks[0]).catch(() => {\n // Silently fail - page is unloading\n });\n }\n }\n };\n\n // Handle page unload\n window.addEventListener('beforeunload', handleBeforeUnload);\n window.addEventListener('pagehide', handleBeforeUnload);\n \n // Handle visibility change (page hidden)\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden' && this.eventQueue.length > 0) {\n const eventsToSend = [...this.eventQueue];\n this.eventQueue = [];\n \n const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);\n \n // Send first chunk with beacon (most important for page hidden)\n if (chunks.length > 0) {\n this.sendEventsWithBeacon(chunks[0]).catch(() => {\n // Silently fail\n });\n }\n }\n });\n }\n\n /**\n * Initialize automatic tracking (heartbeat and page views)\n */\n private initializeAutomaticTracking(): void {\n if (this.config.enableHeartbeat) {\n try {\n this.activityDetector = new ActivityDetector();\n this.heartbeatManager = new HeartbeatManager(\n this,\n this.activityDetector,\n {\n activeInterval: this.config.heartbeatActiveInterval,\n inactiveInterval: this.config.heartbeatInactiveInterval,\n debug: this.config.debug,\n }\n );\n this.log('Heartbeat tracking initialized');\n } catch (error) {\n this.log('Failed to initialize heartbeat tracking:', error);\n }\n }\n\n if (this.config.enableAutoPageView) {\n try {\n this.pageTrackingManager = new PageTrackingManager(\n this,\n {\n stripQueryParams: this.config.stripQueryParams,\n debug: this.config.debug,\n tenantId: this.config.tenantId,\n }\n );\n this.log('Auto page view tracking initialized');\n } catch (error) {\n this.log('Failed to initialize page view tracking:', error);\n }\n }\n }\n\n /**\n * Track session start event\n */\n private trackSessionStart(): void {\n if (typeof window === 'undefined') return;\n\n const hasConsent = this.consentManager.hasConsent('analytics');\n \n const properties: Record<string, unknown> = {\n session_id: this.getSessionId(),\n timestamp: this.sessionStartTime,\n };\n\n if (hasConsent) {\n const referrer = document.referrer || '';\n const currentUrl = window.location.href;\n \n // Parse UTM parameters\n const utmParams = parseUTMParameters(currentUrl);\n const sessionUTMs = getSessionUTMParameters() || utmParams;\n \n // Get first-touch attribution\n const firstTouch = getOrCreateFirstTouchAttribution(\n this.config.tenantId,\n referrer,\n currentUrl,\n sessionUTMs\n );\n\n // Landing page\n properties.landing_page = window.location.pathname;\n \n // Referrer info\n if (referrer) {\n properties.referrer = referrer;\n properties.referrer_domain = new URL(referrer).hostname;\n properties.referrer_category = categorizeReferrer(referrer, currentUrl);\n } else {\n properties.referrer_category = 'direct';\n }\n\n // UTM parameters\n if (sessionUTMs.utm_source) properties.utm_source = sessionUTMs.utm_source;\n if (sessionUTMs.utm_medium) properties.utm_medium = sessionUTMs.utm_medium;\n if (sessionUTMs.utm_campaign) properties.utm_campaign = sessionUTMs.utm_campaign;\n if (sessionUTMs.utm_term) properties.utm_term = sessionUTMs.utm_term;\n if (sessionUTMs.utm_content) properties.utm_content = sessionUTMs.utm_content;\n\n // First-touch attribution\n properties.first_touch_source = firstTouch.source;\n properties.first_touch_medium = firstTouch.medium;\n properties.first_touch_campaign = firstTouch.campaign;\n properties.first_touch_referrer_category = firstTouch.referrer_category;\n\n // Device and browser info\n properties.device = this.getDeviceType();\n properties.screen_resolution = `${screen.width}x${screen.height}`;\n properties.viewport = `${window.innerWidth}x${window.innerHeight}`;\n properties.browser = this.getBrowser();\n properties.os = this.getOS();\n properties.language = navigator.language || '';\n properties.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;\n }\n\n this.trackSystemEvent('_grain_session_start', properties);\n this.log('Session started:', properties);\n }\n\n /**\n * Track session end event\n */\n private trackSessionEnd(): void {\n if (typeof window === 'undefined') return;\n\n const hasConsent = this.consentManager.hasConsent('analytics');\n const sessionDuration = Date.now() - this.sessionStartTime;\n \n const properties: Record<string, unknown> = {\n session_id: this.getSessionId(),\n session_duration: Math.floor(sessionDuration / 1000), // In seconds for easier querying\n duration: sessionDuration, // Keep for backward compatibility\n event_count: this.sessionEventCount,\n timestamp: Date.now(),\n };\n\n if (hasConsent && this.pageTrackingManager) {\n const pageCount = this.pageTrackingManager.getPageViewCount();\n properties.pages_per_session = pageCount;\n properties.page_count = pageCount; // Keep for backward compatibility\n }\n\n this.trackSystemEvent('_grain_session_end', properties);\n this.log('Session ended:', properties);\n }\n\n /**\n * Detect browser name\n */\n private getBrowser(): string {\n if (typeof navigator === 'undefined') return 'Unknown';\n const ua = navigator.userAgent;\n if (ua.includes('Firefox/')) return 'Firefox';\n if (ua.includes('Edg/')) return 'Edge';\n if (ua.includes('Chrome/')) return 'Chrome';\n if (ua.includes('Safari/') && !ua.includes('Chrome/')) return 'Safari';\n if (ua.includes('Opera/') || ua.includes('OPR/')) return 'Opera';\n return 'Unknown';\n }\n\n /**\n * Detect operating system\n */\n private getOS(): string {\n if (typeof navigator === 'undefined') return 'Unknown';\n const ua = navigator.userAgent;\n if (ua.includes('Win')) return 'Windows';\n if (ua.includes('Mac')) return 'macOS';\n if (ua.includes('Linux')) return 'Linux';\n if (ua.includes('Android')) return 'Android';\n if (ua.includes('iOS') || ua.includes('iPhone') || ua.includes('iPad')) return 'iOS';\n return 'Unknown';\n }\n\n /**\n * Detect device type (Mobile, Tablet, Desktop)\n */\n private getDeviceType(): string {\n if (typeof window === 'undefined' || typeof navigator === 'undefined') return 'Unknown';\n \n const ua = navigator.userAgent;\n const width = window.innerWidth;\n \n // Check for tablet-specific indicators\n if (ua.includes('iPad') || (ua.includes('Android') && !ua.includes('Mobile'))) {\n return 'Tablet';\n }\n \n // Check for mobile indicators\n if (ua.includes('Mobile') || ua.includes('iPhone') || ua.includes('Android')) {\n return 'Mobile';\n }\n \n // Fallback to screen width detection\n if (width < 768) {\n return 'Mobile';\n } else if (width >= 768 && width < 1024) {\n return 'Tablet';\n }\n \n return 'Desktop';\n }\n\n /**\n * Handle consent granted - upgrade ephemeral session to persistent user\n */\n private handleConsentGranted(): void {\n this.flushWaitingForConsentQueue();\n\n // Initialize persistent ID now that consent is granted (if not already initialized)\n if (!this.persistentAnonymousUserId) {\n this.initializePersistentAnonymousUserId();\n this.log('Initialized persistent ID after consent grant');\n }\n\n // Track consent granted event with mapping from ephemeral to persistent ID\n if (this.ephemeralSessionId) {\n this.trackSystemEvent('_grain_consent_granted', {\n previous_session_id: this.ephemeralSessionId,\n new_user_id: this.getEffectiveUserId(),\n timestamp: Date.now(),\n });\n }\n }\n\n /**\n * Track system events that bypass consent checks (for necessary/functional tracking)\n */\n trackSystemEvent(eventName: string, properties: Record<string, unknown>): void {\n if (this.isDestroyed) return;\n\n const hasConsent = this.consentManager.hasConsent('analytics');\n\n // Create event with appropriate user ID\n const event: EventPayload = {\n eventName,\n userId: hasConsent ? this.getEffectiveUserId() : this.getEphemeralSessionId(),\n properties: {\n ...properties,\n _minimal: !hasConsent, // Flag to indicate minimal tracking\n _consent_status: hasConsent ? 'granted' : 'pending',\n },\n };\n\n // Bypass consent check for necessary system events\n this.eventQueue.push(event);\n this.eventCountSinceLastHeartbeat++;\n\n this.log(`Queued system event: ${eventName}`, properties);\n\n // Consider flushing\n if (this.eventQueue.length >= this.config.batchSize) {\n this.flush().catch((error) => {\n const formattedError = this.formatError(error, 'flush system event');\n this.logError(formattedError);\n });\n }\n }\n\n /**\n * Get ephemeral session ID (memory-only, not persisted)\n */\n getEphemeralSessionId(): string {\n if (!this.ephemeralSessionId) {\n this.ephemeralSessionId = this.generateUUID();\n }\n return this.ephemeralSessionId;\n }\n\n /**\n * Get the current page path from page tracker\n */\n getCurrentPage(): string | null {\n return this.pageTrackingManager?.getCurrentPage() || null;\n }\n\n /**\n * Get event count since last heartbeat\n */\n getEventCountSinceLastHeartbeat(): number {\n return this.eventCountSinceLastHeartbeat;\n }\n\n /**\n * Reset event count since last heartbeat\n */\n resetEventCountSinceLastHeartbeat(): void {\n this.eventCountSinceLastHeartbeat = 0;\n }\n\n /**\n * Get the effective user ID (public method)\n */\n getEffectiveUserId(): string {\n return this.getEffectiveUserIdInternal();\n }\n\n /**\n * Get the session ID (ephemeral or persistent based on consent)\n */\n getSessionId(): string {\n const hasConsent = this.consentManager.hasConsent('analytics');\n return hasConsent ? this.getEffectiveUserId() : this.getEphemeralSessionId();\n }\n\n /**\n * Track an analytics event\n */\n async track(eventName: string, properties?: Record<string, unknown>, options?: SendEventOptions): Promise<void>;\n async track(event: GrainEvent, options?: SendEventOptions): Promise<void>;\n async track(\n eventOrName: string | GrainEvent,\n propertiesOrOptions?: Record<string, unknown> | SendEventOptions,\n options?: SendEventOptions\n ): Promise<void> {\n try {\n if (this.isDestroyed) {\n const error = new Error('Grain Analytics: Client has been destroyed');\n const formattedError = this.formatError(error, 'track (client destroyed)');\n this.logError(formattedError);\n return;\n }\n\n let event: GrainEvent;\n let opts: SendEventOptions = {};\n\n if (typeof eventOrName === 'string') {\n event = {\n eventName: eventOrName,\n properties: propertiesOrOptions as Record<string, unknown>,\n };\n opts = options || {};\n } else {\n event = eventOrName;\n opts = propertiesOrOptions as SendEventOptions || {};\n }\n\n // Filter properties if whitelist is enabled\n if (this.config.allowedProperties && event.properties) {\n const filtered: Record<string, unknown> = {};\n for (const key of this.config.allowedProperties) {\n if (key in event.properties) {\n filtered[key] = event.properties[key];\n }\n }\n event.properties = filtered;\n }\n\n const formattedEvent = this.formatEvent(event);\n\n // Check consent before tracking\n if (this.consentManager.shouldWaitForConsent() && this.config.waitForConsent) {\n // Queue event until consent is granted\n this.waitingForConsentQueue.push(formattedEvent);\n this.log(`Event waiting for consent: ${event.eventName}`, event.properties);\n return;\n }\n\n if (!this.consentManager.hasConsent('analytics')) {\n this.log(`Event blocked by consent: ${event.eventName}`);\n return;\n }\n\n this.eventQueue.push(formattedEvent);\n this.eventCountSinceLastHeartbeat++;\n this.sessionEventCount++;\n this.log(`Queued event: ${event.eventName}`, event.properties);\n\n // Check if we should flush immediately\n if (opts.flush || this.eventQueue.length >= this.config.batchSize) {\n await this.flush();\n }\n } catch (error) {\n const formattedError = this.formatError(error, 'track');\n this.logError(formattedError);\n }\n }\n\n /**\n * Flush events that were waiting for consent\n */\n private flushWaitingForConsentQueue(): void {\n if (this.waitingForConsentQueue.length === 0) return;\n\n this.log(`Flushing ${this.waitingForConsentQueue.length} events waiting for consent`);\n \n // Move waiting events to main queue\n this.eventQueue.push(...this.waitingForConsentQueue);\n this.waitingForConsentQueue = [];\n\n // Flush immediately\n this.flush().catch((error) => {\n const formattedError = this.formatError(error, 'flush waiting for consent queue');\n this.logError(formattedError);\n });\n }\n\n /**\n * Identify a user (sets userId for subsequent events)\n */\n identify(userId: string): void {\n this.log(`Identified user: ${userId}`);\n this.globalUserId = userId;\n // Clear persistent anonymous user ID since we now have a real user ID\n this.persistentAnonymousUserId = null;\n }\n\n /**\n * Set global user ID for all subsequent events\n */\n setUserId(userId: string | null): void {\n this.log(`Set global user ID: ${userId}`);\n this.globalUserId = userId;\n \n if (userId) {\n // Clear persistent anonymous user ID if setting a real user ID\n this.persistentAnonymousUserId = null;\n } else {\n // If clearing user ID, ensure we have an identifier\n if (!this.persistentAnonymousUserId) {\n this.persistentAnonymousUserId = this.generateAnonymousUserId();\n \n // Try to persist the new anonymous ID (respects GDPR opt-in mode)\n this.savePersistentAnonymousUserId(this.persistentAnonymousUserId);\n }\n }\n }\n\n /**\n * Get current global user ID\n */\n getUserId(): string | null {\n return this.globalUserId;\n }\n\n /**\n * Get current effective user ID (global userId or persistent anonymous ID)\n */\n getEffectiveUserIdPublic(): string {\n return this.getEffectiveUserIdInternal();\n }\n\n /**\n * Login with auth token or userId on the fly\n * \n * @example\n * // Login with userId only\n * client.login({ userId: 'user123' });\n * \n * // Login with auth token (automatically sets authStrategy to JWT)\n * client.login({ authToken: 'jwt-token-here' });\n * \n * // Login with both userId and auth token\n * client.login({ userId: 'user123', authToken: 'jwt-token-here' });\n * \n * // Override auth strategy\n * client.login({ userId: 'user123', authStrategy: 'SERVER_SIDE' });\n */\n login(options: LoginOptions): void {\n try {\n if (this.isDestroyed) {\n const error = new Error('Grain Analytics: Client has been destroyed');\n const formattedError = this.formatError(error, 'login (client destroyed)');\n this.logError(formattedError);\n return;\n }\n\n // Set userId if provided\n if (options.userId) {\n this.log(`Login: Setting user ID to ${options.userId}`);\n this.globalUserId = options.userId;\n // Clear persistent anonymous user ID since we now have a real user ID\n this.persistentAnonymousUserId = null;\n }\n\n // Handle auth token if provided\n if (options.authToken) {\n this.log('Login: Setting auth token');\n // Update auth strategy to JWT if not already set\n if (this.config.authStrategy === 'NONE') {\n this.config.authStrategy = 'JWT';\n }\n \n // Create a simple auth provider that returns the provided token\n this.config.authProvider = {\n getToken: () => options.authToken!\n };\n }\n\n // Override auth strategy if provided\n if (options.authStrategy) {\n this.log(`Login: Setting auth strategy to ${options.authStrategy}`);\n this.config.authStrategy = options.authStrategy;\n }\n\n this.log(`Login successful. Effective user ID: ${this.getEffectiveUserIdInternal()}`);\n } catch (error) {\n const formattedError = this.formatError(error, 'login');\n this.logError(formattedError);\n }\n }\n\n /**\n * Logout and clear user session, fall back to UUIDv4 identifier\n * \n * @example\n * // Logout user and return to anonymous mode\n * client.logout();\n * \n * // After logout, events will use the persistent UUIDv4 identifier\n * client.track('page_view', { page: 'home' });\n */\n logout(): void {\n try {\n if (this.isDestroyed) {\n const error = new Error('Grain Analytics: Client has been destroyed');\n const formattedError = this.formatError(error, 'logout (client destroyed)');\n this.logError(formattedError);\n return;\n }\n\n this.log('Logout: Clearing user session');\n \n // Clear global user ID\n this.globalUserId = null;\n \n // Reset auth strategy to NONE\n this.config.authStrategy = 'NONE';\n this.config.authProvider = undefined;\n \n // Generate new UUIDv4 identifier if we don't have one\n if (!this.persistentAnonymousUserId) {\n this.persistentAnonymousUserId = this.generateAnonymousUserId();\n \n // Try to persist the new anonymous ID\n if (typeof window !== 'undefined') {\n try {\n const storageKey = `grain_anonymous_user_id_${this.config.tenantId}`;\n localStorage.setItem(storageKey, this.persistentAnonymousUserId);\n } catch (error) {\n this.log('Failed to persist new anonymous user ID after logout:', error);\n }\n }\n }\n \n this.log(`Logout successful. Effective user ID: ${this.getEffectiveUserIdInternal()}`);\n } catch (error) {\n const formattedError = this.formatError(error, 'logout');\n this.logError(formattedError);\n }\n }\n\n /**\n * Set user properties\n */\n async setProperty(properties: Record<string, unknown>, options?: SetPropertyOptions): Promise<void> {\n try {\n if (this.isDestroyed) {\n const error = new Error('Grain Analytics: Client has been destroyed');\n const formattedError = this.formatError(error, 'setProperty (client destroyed)');\n this.logError(formattedError);\n return;\n }\n\n const userId = options?.userId || this.getEffectiveUserIdInternal();\n \n // Validate property count (max 4 properties)\n const propertyKeys = Object.keys(properties);\n if (propertyKeys.length > 4) {\n const error = new Error('Grain Analytics: Maximum 4 properties allowed per request');\n const formattedError = this.formatError(error, 'setProperty (validation)');\n this.logError(formattedError);\n return;\n }\n\n if (propertyKeys.length === 0) {\n const error = new Error('Grain Analytics: At least one property is required');\n const formattedError = this.formatError(error, 'setProperty (validation)');\n this.logError(formattedError);\n return;\n }\n\n // Serialize all values to strings\n const serializedProperties: Record<string, string> = {};\n for (const [key, value] of Object.entries(properties)) {\n if (value === null || value === undefined) {\n serializedProperties[key] = '';\n } else if (typeof value === 'string') {\n serializedProperties[key] = value;\n } else {\n serializedProperties[key] = JSON.stringify(value);\n }\n }\n\n const payload: PropertyPayload = {\n userId,\n ...serializedProperties,\n };\n\n await this.sendProperties(payload);\n } catch (error) {\n const formattedError = this.formatError(error, 'setProperty');\n this.logError(formattedError);\n }\n }\n\n /**\n * Send properties to the API\n */\n private async sendProperties(payload: PropertyPayload): Promise<void> {\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= this.config.retryAttempts; attempt++) {\n try {\n const headers = await this.getAuthHeaders();\n const url = `${this.config.apiUrl}/v1/events/${encodeURIComponent(this.config.tenantId)}/properties`;\n\n this.log(`Setting properties for user ${payload.userId} (attempt ${attempt + 1})`);\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n let errorMessage = `HTTP ${response.status}`;\n try {\n const errorBody = await response.json();\n if (errorBody?.message) {\n errorMessage = errorBody.message;\n }\n } catch {\n const errorText = await response.text();\n if (errorText) {\n errorMessage = errorText;\n }\n }\n \n const error = new Error(`Failed to set properties: ${errorMessage}`) as Error & { status?: number };\n error.status = response.status;\n throw error;\n }\n\n this.log(`Successfully set properties for user ${payload.userId}`);\n return; // Success, exit retry loop\n \n } catch (error) {\n lastError = error;\n \n if (attempt === this.config.retryAttempts) {\n // Last attempt, don't retry - log error gracefully\n const formattedError = this.formatError(error, `sendProperties (attempt ${attempt + 1}/${this.config.retryAttempts + 1})`);\n this.logError(formattedError);\n return; // Don't throw, just return gracefully\n }\n \n if (!this.isRetriableError(error)) {\n // Non-retriable error, don't retry - log error gracefully\n const formattedError = this.formatError(error, 'sendProperties (non-retriable error)');\n this.logError(formattedError);\n return; // Don't throw, just return gracefully\n }\n \n const delayMs = this.config.retryDelay * Math.pow(2, attempt); // Exponential backoff\n this.log(`Retrying in ${delayMs}ms after error:`, error);\n await this.delay(delayMs);\n }\n }\n }\n\n // Template event methods\n\n /**\n * Track user login event\n */\n async trackLogin(properties?: LoginEventProperties, options?: SendEventOptions): Promise<void> {\n try {\n return await this.track('login', properties, options);\n } catch (error) {\n const formattedError = this.formatError(error, 'trackLogin');\n this.logError(formattedError);\n }\n }\n\n /**\n * Track user signup event\n */\n async trackSignup(properties?: SignupEventProperties, options?: SendEventOptions): Promise<void> {\n try {\n return await this.track('signup', properties, options);\n } catch (error) {\n const formattedError = this.formatError(error, 'trackSignup');\n this.logError(formattedError);\n }\n }\n\n /**\n * Track checkout event\n */\n async trackCheckout(properties?: CheckoutEventProperties, options?: SendEventOptions): Promise<void> {\n try {\n return await this.track('checkout', properties, options);\n } catch (error) {\n const formattedError = this.formatError(error, 'trackCheckout');\n this.logError(formattedError);\n }\n }\n\n /**\n * Track page view event\n */\n async trackPageView(properties?: PageViewEventProperties, options?: SendEventOptions): Promise<void> {\n try {\n return await this.track('page_view', properties, options);\n } catch (error) {\n const formattedError = this.formatError(error, 'trackPageView');\n this.logError(formattedError);\n }\n }\n\n /**\n * Track purchase event\n */\n async trackPurchase(properties?: PurchaseEventProperties, options?: SendEventOptions): Promise<void> {\n try {\n return await this.track('purchase', properties, options);\n } catch (error) {\n const formattedError = this.formatError(error, 'trackPurchase');\n this.logError(formattedError);\n }\n }\n\n /**\n * Track search event\n */\n async trackSearch(properties?: SearchEventProperties, options?: SendEventOptions): Promise<void> {\n try {\n return await this.track('search', properties, options);\n } catch (error) {\n const formattedError = this.formatError(error, 'trackSearch');\n this.logError(formattedError);\n }\n }\n\n /**\n * Track add to cart event\n */\n async trackAddToCart(properties?: AddToCartEventProperties, options?: SendEventOptions): Promise<void> {\n try {\n return await this.track('add_to_cart', properties, options);\n } catch (error) {\n const formattedError = this.formatError(error, 'trackAddToCart');\n this.logError(formattedError);\n }\n }\n\n /**\n * Track remove from cart event\n */\n async trackRemoveFromCart(properties?: RemoveFromCartEventProperties, options?: SendEventOptions): Promise<void> {\n try {\n return await this.track('remove_from_cart', properties, options);\n } catch (error) {\n const formattedError = this.formatError(error, 'trackRemoveFromCart');\n this.logError(formattedError);\n }\n }\n\n /**\n * Manually flush all queued events\n */\n async flush(): Promise<void> {\n try {\n if (this.eventQueue.length === 0) return;\n\n const eventsToSend = [...this.eventQueue];\n this.eventQueue = [];\n\n // Split events into chunks to respect maxEventsPerRequest limit\n const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);\n \n // Send all chunks sequentially to maintain order\n for (const chunk of chunks) {\n await this.sendEvents(chunk);\n }\n } catch (error) {\n const formattedError = this.formatError(error, 'flush');\n this.logError(formattedError);\n }\n }\n\n // Remote Config Methods\n\n /**\n * Initialize configuration cache from localStorage\n */\n private initializeConfigCache(): void {\n if (!this.config.enableConfigCache || typeof window === 'undefined') return;\n\n try {\n const cached = localStorage.getItem(this.config.configCacheKey);\n if (cached) {\n this.configCache = JSON.parse(cached);\n this.log('Loaded configuration from cache:', this.configCache);\n }\n } catch (error) {\n this.log('Failed to load configuration cache:', error);\n }\n }\n\n /**\n * Save configuration cache to localStorage\n */\n private saveConfigCache(cache: RemoteConfigCache): void {\n if (!this.config.enableConfigCache || typeof window === 'undefined') return;\n\n try {\n localStorage.setItem(this.config.configCacheKey, JSON.stringify(cache));\n this.log('Saved configuration to cache:', cache);\n } catch (error) {\n this.log('Failed to save configuration cache:', error);\n }\n }\n\n /**\n * Get configuration value with fallback to defaults\n */\n getConfig(key: string): string | undefined {\n // First check cache\n if (this.configCache?.configurations?.[key]) {\n return this.configCache.configurations[key];\n }\n\n // Then check defaults\n if (this.config.defaultConfigurations?.[key]) {\n return this.config.defaultConfigurations[key];\n }\n\n return undefined;\n }\n\n /**\n * Get all configurations with fallback to defaults\n */\n getAllConfigs(): Record<string, string> {\n const configs = { ...this.config.defaultConfigurations };\n \n if (this.configCache?.configurations) {\n Object.assign(configs, this.configCache.configurations);\n }\n\n return configs;\n }\n\n /**\n * Fetch configurations from API\n */\n async fetchConfig(options: RemoteConfigOptions = {}): Promise<RemoteConfigResponse | null> {\n try {\n if (this.isDestroyed) {\n const error = new Error('Grain Analytics: Client has been destroyed');\n const formattedError = this.formatError(error, 'fetchConfig (client destroyed)');\n this.logError(formattedError);\n return null;\n }\n\n const userId = options.userId || this.getEffectiveUserIdInternal();\n const immediateKeys = options.immediateKeys || [];\n const properties = options.properties || {};\n\n const request: RemoteConfigRequest = {\n userId,\n immediateKeys,\n properties,\n };\n\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= this.config.retryAttempts; attempt++) {\n try {\n const headers = await this.getAuthHeaders();\n const url = `${this.config.apiUrl}/v1/client/${encodeURIComponent(this.config.tenantId)}/config/configurations`;\n\n this.log(`Fetching configurations for user ${userId} (attempt ${attempt + 1})`);\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n let errorMessage = `HTTP ${response.status}`;\n try {\n const errorBody = await response.json();\n if (errorBody?.message) {\n errorMessage = errorBody.message;\n }\n } catch {\n const errorText = await response.text();\n if (errorText) {\n errorMessage = errorText;\n }\n }\n \n const error = new Error(`Failed to fetch configurations: ${errorMessage}`) as Error & { status?: number };\n error.status = response.status;\n throw error;\n }\n\n const configResponse: RemoteConfigResponse = await response.json();\n \n // Update cache if successful\n if (configResponse.configurations) {\n this.updateConfigCache(configResponse, userId);\n }\n\n this.log(`Successfully fetched configurations for user ${userId}:`, configResponse);\n return configResponse;\n \n } catch (error) {\n lastError = error;\n \n if (attempt === this.config.retryAttempts) {\n // Last attempt, don't retry - log error gracefully\n const formattedError = this.formatError(error, `fetchConfig (attempt ${attempt + 1}/${this.config.retryAttempts + 1})`);\n this.logError(formattedError);\n return null;\n }\n \n if (!this.isRetriableError(error)) {\n // Non-retriable error, don't retry - log error gracefully\n const formattedError = this.formatError(error, 'fetchConfig (non-retriable error)');\n this.logError(formattedError);\n return null;\n }\n \n const delayMs = this.config.retryDelay * Math.pow(2, attempt);\n this.log(`Retrying config fetch in ${delayMs}ms after error:`, error);\n await this.delay(delayMs);\n }\n }\n\n return null;\n } catch (error) {\n const formattedError = this.formatError(error, 'fetchConfig');\n this.logError(formattedError);\n return null;\n }\n }\n\n /**\n * Get configuration asynchronously (cache-first with fallback to API)\n */\n async getConfigAsync(key: string, options: RemoteConfigOptions = {}): Promise<string | undefined> {\n try {\n // Return immediately if we have it in cache and not forcing refresh\n if (!options.forceRefresh && this.configCache?.configurations?.[key]) {\n return this.configCache.configurations[key];\n }\n\n // Return default if available and not forcing refresh\n if (!options.forceRefresh && this.config.defaultConfigurations?.[key]) {\n return this.config.defaultConfigurations[key];\n }\n\n // Fetch from API\n const response = await this.fetchConfig(options);\n if (response) {\n return response.configurations[key];\n }\n \n // Return default as fallback\n return this.config.defaultConfigurations?.[key];\n } catch (error) {\n const formattedError = this.formatError(error, 'getConfigAsync');\n this.logError(formattedError);\n // Return default as fallback\n return this.config.defaultConfigurations?.[key];\n }\n }\n\n /**\n * Get all configurations asynchronously (cache-first with fallback to API)\n */\n async getAllConfigsAsync(options: RemoteConfigOptions = {}): Promise<Record<string, string>> {\n try {\n // Return cache if available and not forcing refresh\n if (!options.forceRefresh && this.configCache?.configurations) {\n return { ...this.config.defaultConfigurations, ...this.configCache.configurations };\n }\n\n // Fetch from API\n const response = await this.fetchConfig(options);\n if (response) {\n return { ...this.config.defaultConfigurations, ...response.configurations };\n }\n \n // Return defaults as fallback\n return { ...this.config.defaultConfigurations };\n } catch (error) {\n const formattedError = this.formatError(error, 'getAllConfigsAsync');\n this.logError(formattedError);\n // Return defaults as fallback\n return { ...this.config.defaultConfigurations };\n }\n }\n\n /**\n * Update configuration cache and notify listeners\n */\n private updateConfigCache(response: RemoteConfigResponse, userId: string): void {\n const newCache: RemoteConfigCache = {\n configurations: response.configurations,\n snapshotId: response.snapshotId,\n timestamp: response.timestamp,\n userId,\n };\n\n const oldConfigs = this.configCache?.configurations || {};\n this.configCache = newCache;\n this.saveConfigCache(newCache);\n\n // Notify listeners if configurations changed\n if (JSON.stringify(oldConfigs) !== JSON.stringify(response.configurations)) {\n this.notifyConfigChangeListeners(response.configurations);\n }\n }\n\n /**\n * Add configuration change listener\n */\n addConfigChangeListener(listener: ConfigChangeListener): void {\n this.configChangeListeners.push(listener);\n }\n\n /**\n * Remove configuration change listener\n */\n removeConfigChangeListener(listener: ConfigChangeListener): void {\n const index = this.configChangeListeners.indexOf(listener);\n if (index > -1) {\n this.configChangeListeners.splice(index, 1);\n }\n }\n\n /**\n * Notify all configuration change listeners\n */\n private notifyConfigChangeListeners(configurations: Record<string, string>): void {\n this.configChangeListeners.forEach(listener => {\n try {\n listener(configurations);\n } catch (error) {\n console.error('[Grain Analytics] Config change listener error:', error);\n }\n });\n }\n\n /**\n * Start automatic configuration refresh timer\n */\n private startConfigRefreshTimer(): void {\n if (typeof window === 'undefined') return;\n \n if (this.configRefreshTimer) {\n clearInterval(this.configRefreshTimer);\n }\n\n this.configRefreshTimer = window.setInterval(() => {\n if (!this.isDestroyed) {\n // Use effective userId (will be generated if not set)\n this.fetchConfig().catch((error) => {\n const formattedError = this.formatError(error, 'auto-config refresh');\n this.logError(formattedError);\n });\n }\n }, this.config.configRefreshInterval);\n }\n\n /**\n * Stop automatic configuration refresh timer\n */\n private stopConfigRefreshTimer(): void {\n if (this.configRefreshTimer) {\n clearInterval(this.configRefreshTimer);\n this.configRefreshTimer = null;\n }\n }\n\n /**\n * Preload configurations for immediate access\n */\n async preloadConfig(immediateKeys: string[] = [], properties?: Record<string, string>): Promise<void> {\n try {\n // Use effective userId (will be generated if not set)\n const effectiveUserId = this.getEffectiveUserIdInternal();\n this.log(`Preloading config for user: ${effectiveUserId}`);\n\n const response = await this.fetchConfig({ immediateKeys, properties });\n if (response) {\n this.startConfigRefreshTimer();\n }\n } catch (error) {\n const formattedError = this.formatError(error, 'preloadConfig');\n this.logError(formattedError);\n }\n }\n\n /**\n * Split events array into chunks of specified size\n */\n private chunkEvents(events: EventPayload[], chunkSize: number): EventPayload[][] {\n const chunks: EventPayload[][] = [];\n for (let i = 0; i < events.length; i += chunkSize) {\n chunks.push(events.slice(i, i + chunkSize));\n }\n return chunks;\n }\n\n // Privacy & Consent Methods\n\n /**\n * Grant consent for tracking\n * @param categories - Optional array of consent categories (e.g., ['analytics', 'functional'])\n */\n grantConsent(categories?: string[]): void {\n try {\n this.consentManager.grantConsent(categories);\n this.log('Consent granted', categories);\n } catch (error) {\n const formattedError = this.formatError(error, 'grantConsent');\n this.logError(formattedError);\n }\n }\n\n /**\n * Revoke consent for tracking (opt-out)\n * @param categories - Optional array of categories to revoke (if not provided, revokes all)\n */\n revokeConsent(categories?: string[]): void {\n try {\n this.consentManager.revokeConsent(categories);\n this.log('Consent revoked', categories);\n \n // Clear queued events when consent is revoked\n this.eventQueue = [];\n this.waitingForConsentQueue = [];\n } catch (error) {\n const formattedError = this.formatError(error, 'revokeConsent');\n this.logError(formattedError);\n }\n }\n\n /**\n * Get current consent state\n */\n getConsentState(): ConsentState | null {\n return this.consentManager.getConsentState();\n }\n\n /**\n * Check if user has granted consent\n * @param category - Optional category to check (if not provided, checks general consent)\n */\n hasConsent(category?: string): boolean {\n return this.consentManager.hasConsent(category);\n }\n\n /**\n * Add listener for consent state changes\n */\n onConsentChange(listener: (state: ConsentState) => void): void {\n this.consentManager.addListener(listener);\n }\n\n /**\n * Remove consent change listener\n */\n offConsentChange(listener: (state: ConsentState) => void): void {\n this.consentManager.removeListener(listener);\n }\n\n /**\n * Destroy the client and clean up resources\n */\n destroy(): void {\n this.isDestroyed = true;\n \n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n\n // Stop config refresh timer\n this.stopConfigRefreshTimer();\n\n // Clear config change listeners\n this.configChangeListeners = [];\n\n // Destroy automatic tracking managers\n if (this.heartbeatManager) {\n this.heartbeatManager.destroy();\n this.heartbeatManager = null;\n }\n\n if (this.pageTrackingManager) {\n this.pageTrackingManager.destroy();\n this.pageTrackingManager = null;\n }\n\n if (this.activityDetector) {\n this.activityDetector.destroy();\n this.activityDetector = null;\n }\n\n // Send any remaining events (in chunks if necessary)\n if (this.eventQueue.length > 0) {\n const eventsToSend = [...this.eventQueue];\n this.eventQueue = [];\n \n const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);\n \n // Send first chunk with beacon (most important for page unload)\n if (chunks.length > 0) {\n this.sendEventsWithBeacon(chunks[0]).catch(() => {\n // Silently fail during cleanup\n });\n \n // If there are more chunks, try to send them with regular fetch\n for (let i = 1; i < chunks.length; i++) {\n this.sendEventsWithBeacon(chunks[i]).catch(() => {\n // Silently fail during cleanup\n });\n }\n }\n }\n }\n}\n\n/**\n * Create a new Grain Analytics client\n */\nexport function createGrainAnalytics(config: GrainConfig): GrainAnalytics {\n return new GrainAnalytics(config);\n}\n\n// Default export for convenience\nexport default GrainAnalytics;\n\n// Global interface for IIFE build\ndeclare global {\n interface Window {\n Grain?: {\n GrainAnalytics: typeof GrainAnalytics;\n createGrainAnalytics: typeof createGrainAnalytics;\n };\n }\n}\n\n// Auto-setup for IIFE build\nif (typeof window !== 'undefined') {\n window.Grain = {\n GrainAnalytics,\n createGrainAnalytics,\n };\n}", "/**\n * Consent management for Grain Analytics\n * Handles GDPR-compliant consent tracking and state management\n */\n\nexport type ConsentMode = 'opt-in' | 'opt-out' | 'disabled';\n\nexport interface ConsentState {\n granted: boolean;\n categories: string[];\n timestamp: Date;\n version: string;\n}\n\nexport const DEFAULT_CONSENT_CATEGORIES = ['necessary', 'analytics', 'functional'];\nexport const CONSENT_VERSION = '1.0.0';\n\n/**\n * Consent manager for handling user consent state\n */\nexport class ConsentManager {\n private consentState: ConsentState | null = null;\n private consentMode: ConsentMode;\n private storageKey: string;\n private listeners: Array<(state: ConsentState) => void> = [];\n\n constructor(tenantId: string, consentMode: ConsentMode = 'opt-out') {\n this.consentMode = consentMode;\n this.storageKey = `grain_consent_${tenantId}`;\n this.loadConsentState();\n }\n\n /**\n * Load consent state from localStorage\n * \n * GDPR Compliance: In opt-in mode, we can use localStorage for consent preferences\n * since storing consent choices is a legitimate interest and necessary for compliance.\n * The consent preference itself is not tracking data.\n */\n private loadConsentState(): void {\n if (typeof window === 'undefined') return;\n\n try {\n const stored = localStorage.getItem(this.storageKey);\n if (stored) {\n const parsed = JSON.parse(stored);\n this.consentState = {\n ...parsed,\n timestamp: new Date(parsed.timestamp),\n };\n } else if (this.consentMode === 'opt-out' || this.consentMode === 'disabled') {\n // Auto-grant consent for opt-out and disabled modes\n this.consentState = {\n granted: true,\n categories: DEFAULT_CONSENT_CATEGORIES,\n timestamp: new Date(),\n version: CONSENT_VERSION,\n };\n this.saveConsentState();\n }\n // Note: In opt-in mode without stored consent, consentState remains null (no consent)\n } catch (error) {\n console.error('[Grain Consent] Failed to load consent state:', error);\n }\n }\n\n /**\n * Save consent state to localStorage\n */\n private saveConsentState(): void {\n if (typeof window === 'undefined' || !this.consentState) return;\n\n try {\n localStorage.setItem(this.storageKey, JSON.stringify(this.consentState));\n } catch (error) {\n console.error('[Grain Consent] Failed to save consent state:', error);\n }\n }\n\n /**\n * Grant consent with optional categories\n */\n grantConsent(categories?: string[]): void {\n const grantedCategories = categories || DEFAULT_CONSENT_CATEGORIES;\n \n this.consentState = {\n granted: true,\n categories: grantedCategories,\n timestamp: new Date(),\n version: CONSENT_VERSION,\n };\n\n this.saveConsentState();\n this.notifyListeners();\n }\n\n /**\n * Revoke consent (opt-out)\n */\n revokeConsent(categories?: string[]): void {\n if (!this.consentState) {\n this.consentState = {\n granted: false,\n categories: [],\n timestamp: new Date(),\n version: CONSENT_VERSION,\n };\n } else if (categories) {\n // Remove specific categories\n this.consentState = {\n ...this.consentState,\n categories: this.consentState.categories.filter(cat => !categories.includes(cat)),\n granted: this.consentState.categories.length > 0,\n timestamp: new Date(),\n };\n } else {\n // Revoke all consent\n this.consentState = {\n granted: false,\n categories: [],\n timestamp: new Date(),\n version: CONSENT_VERSION,\n };\n }\n\n this.saveConsentState();\n this.notifyListeners();\n }\n\n /**\n * Get current consent state\n */\n getConsentState(): ConsentState | null {\n return this.consentState ? { ...this.consentState } : null;\n }\n\n /**\n * Check if user has granted consent\n */\n hasConsent(category?: string): boolean {\n // Disabled mode always returns true (no consent required)\n if (this.consentMode === 'disabled') {\n return true;\n }\n\n // No consent state in opt-in mode means no consent\n if (this.consentMode === 'opt-in' && !this.consentState) {\n return false;\n }\n\n // Check consent state\n if (!this.consentState?.granted) {\n return false;\n }\n\n // Check specific category if provided\n if (category) {\n return this.consentState.categories.includes(category);\n }\n\n return true;\n }\n\n /**\n * Check if we should wait for consent before tracking\n */\n shouldWaitForConsent(): boolean {\n return this.consentMode === 'opt-in' && !this.consentState?.granted;\n }\n\n /**\n * Add consent change listener\n */\n addListener(listener: (state: ConsentState) => void): void {\n this.listeners.push(listener);\n }\n\n /**\n * Remove consent change listener\n */\n removeListener(listener: (state: ConsentState) => void): void {\n const index = this.listeners.indexOf(listener);\n if (index > -1) {\n this.listeners.splice(index, 1);\n }\n }\n\n /**\n * Notify all listeners of consent state change\n */\n private notifyListeners(): void {\n if (!this.consentState) return;\n\n this.listeners.forEach(listener => {\n try {\n listener(this.consentState!);\n } catch (error) {\n console.error('[Grain Consent] Listener error:', error);\n }\n });\n }\n\n /**\n * Clear all consent data\n */\n clearConsent(): void {\n if (typeof window === 'undefined') return;\n\n try {\n localStorage.removeItem(this.storageKey);\n this.consentState = null;\n } catch (error) {\n console.error('[Grain Consent] Failed to clear consent:', error);\n }\n }\n}\n\n", "/**\n * Cookie utilities for Grain Analytics\n * Provides GDPR-compliant cookie management with configurable options\n */\n\nexport interface CookieConfig {\n domain?: string;\n path?: string;\n sameSite?: 'strict' | 'lax' | 'none';\n secure?: boolean;\n maxAge?: number; // seconds\n}\n\n/**\n * Set a cookie with configurable options\n */\nexport function setCookie(name: string, value: string, config?: CookieConfig): void {\n if (typeof document === 'undefined') return;\n\n const parts = [`${encodeURIComponent(name)}=${encodeURIComponent(value)}`];\n \n if (config?.maxAge !== undefined) {\n parts.push(`max-age=${config.maxAge}`);\n }\n \n if (config?.domain) {\n parts.push(`domain=${config.domain}`);\n }\n \n if (config?.path) {\n parts.push(`path=${config.path}`);\n } else {\n parts.push('path=/');\n }\n \n if (config?.sameSite) {\n parts.push(`samesite=${config.sameSite}`);\n }\n \n if (config?.secure) {\n parts.push('secure');\n }\n \n document.cookie = parts.join('; ');\n}\n\n/**\n * Get a cookie value by name\n */\nexport function getCookie(name: string): string | null {\n if (typeof document === 'undefined') return null;\n\n const nameEQ = encodeURIComponent(name) + '=';\n const cookies = document.cookie.split(';');\n \n for (let i = 0; i < cookies.length; i++) {\n let cookie = cookies[i];\n while (cookie.charAt(0) === ' ') {\n cookie = cookie.substring(1);\n }\n if (cookie.indexOf(nameEQ) === 0) {\n return decodeURIComponent(cookie.substring(nameEQ.length));\n }\n }\n \n return null;\n}\n\n/**\n * Delete a cookie by name\n */\nexport function deleteCookie(name: string, config?: Pick<CookieConfig, 'domain' | 'path'>): void {\n if (typeof document === 'undefined') return;\n\n const parts = [\n `${encodeURIComponent(name)}=`,\n 'max-age=0',\n ];\n \n if (config?.domain) {\n parts.push(`domain=${config.domain}`);\n }\n \n if (config?.path) {\n parts.push(`path=${config.path}`);\n } else {\n parts.push('path=/');\n }\n \n document.cookie = parts.join('; ');\n}\n\n/**\n * Check if cookies are available and working\n */\nexport function areCookiesEnabled(): boolean {\n if (typeof document === 'undefined') return false;\n\n try {\n const testCookie = '_grain_cookie_test';\n setCookie(testCookie, 'test', { maxAge: 1 });\n const result = getCookie(testCookie) === 'test';\n deleteCookie(testCookie);\n return result;\n } catch {\n return false;\n }\n}\n\n", "/**\n * Activity Detection for Grain Analytics\n * Tracks user activity (mouse, keyboard, touch, scroll) to determine if user is active\n */\n\nexport class ActivityDetector {\n private lastActivityTime: number;\n private activityThreshold: number = 30000; // 30 seconds\n private listeners: Array<() => void> = [];\n private boundActivityHandler: () => void;\n private isDestroyed = false;\n\n // Events that indicate user activity\n private readonly activityEvents = [\n 'mousemove',\n 'mousedown',\n 'keydown',\n 'scroll',\n 'touchstart',\n 'click',\n ] as const;\n\n constructor() {\n this.lastActivityTime = Date.now();\n this.boundActivityHandler = this.debounce(this.handleActivity.bind(this), 500);\n this.setupListeners();\n }\n\n /**\n * Setup event listeners for activity detection\n */\n private setupListeners(): void {\n if (typeof window === 'undefined') return;\n\n for (const event of this.activityEvents) {\n window.addEventListener(event, this.boundActivityHandler, { passive: true });\n }\n }\n\n /**\n * Handle activity event\n */\n private handleActivity(): void {\n if (this.isDestroyed) return;\n this.lastActivityTime = Date.now();\n this.notifyListeners();\n }\n\n /**\n * Debounce function to limit how often activity handler is called\n */\n private debounce(func: () => void, wait: number): () => void {\n let timeout: number | null = null;\n \n return () => {\n if (timeout !== null) {\n clearTimeout(timeout);\n }\n \n timeout = window.setTimeout(() => {\n func();\n timeout = null;\n }, wait);\n };\n }\n\n /**\n * Check if user is currently active\n * @param threshold Time in ms to consider user inactive (default: 30s)\n */\n isActive(threshold?: number): boolean {\n const thresholdToUse = threshold ?? this.activityThreshold;\n const now = Date.now();\n return (now - this.lastActivityTime) < thresholdToUse;\n }\n\n /**\n * Get time since last activity in milliseconds\n */\n getTimeSinceLastActivity(): number {\n return Date.now() - this.lastActivityTime;\n }\n\n /**\n * Get last activity timestamp\n */\n getLastActivityTime(): number {\n return this.lastActivityTime;\n }\n\n /**\n * Set activity threshold\n */\n setActivityThreshold(threshold: number): void {\n this.activityThreshold = threshold;\n }\n\n /**\n * Add listener for activity changes\n */\n addListener(listener: () => void): void {\n this.listeners.push(listener);\n }\n\n /**\n * Remove listener\n */\n removeListener(listener: () => void): void {\n const index = this.listeners.indexOf(listener);\n if (index > -1) {\n this.listeners.splice(index, 1);\n }\n }\n\n /**\n * Notify all listeners\n */\n private notifyListeners(): void {\n for (const listener of this.listeners) {\n try {\n listener();\n } catch (error) {\n console.error('[Activity Detector] Listener error:', error);\n }\n }\n }\n\n /**\n * Cleanup and remove listeners\n */\n destroy(): void {\n if (this.isDestroyed) return;\n \n if (typeof window !== 'undefined') {\n for (const event of this.activityEvents) {\n window.removeEventListener(event, this.boundActivityHandler);\n }\n }\n \n this.listeners = [];\n this.isDestroyed = true;\n }\n}\n\n", "/**\n * Heartbeat Manager for Grain Analytics\n * Tracks session activity with consent-aware behavior\n */\n\nimport type { ActivityDetector } from './activity';\n\nexport interface HeartbeatConfig {\n activeInterval: number; // Interval when user is active (ms)\n inactiveInterval: number; // Interval when user is inactive (ms)\n debug?: boolean;\n}\n\nexport interface HeartbeatTracker {\n trackSystemEvent(eventName: string, properties: Record<string, unknown>): void;\n hasConsent(category?: string): boolean;\n getEffectiveUserId(): string;\n getEphemeralSessionId(): string;\n getSessionId(): string;\n getCurrentPage(): string | null;\n getEventCountSinceLastHeartbeat(): number;\n resetEventCountSinceLastHeartbeat(): void;\n}\n\nexport class HeartbeatManager {\n private config: HeartbeatConfig;\n private tracker: HeartbeatTracker;\n private activityDetector: ActivityDetector;\n private heartbeatTimer: number | null = null;\n private isDestroyed = false;\n private lastHeartbeatTime: number;\n private currentInterval: number;\n private hasSentPageLoadHeartbeat = false;\n\n constructor(\n tracker: HeartbeatTracker,\n activityDetector: ActivityDetector,\n config: HeartbeatConfig\n ) {\n this.tracker = tracker;\n this.activityDetector = activityDetector;\n this.config = config;\n this.lastHeartbeatTime = Date.now();\n this.currentInterval = config.activeInterval;\n\n // Send initial heartbeat when page loads (if allowed by consent)\n this.sendPageLoadHeartbeat();\n \n // Start periodic heartbeat tracking\n this.scheduleNextHeartbeat();\n }\n\n /**\n * Send initial heartbeat when page loads\n */\n private sendPageLoadHeartbeat(): void {\n if (this.isDestroyed || this.hasSentPageLoadHeartbeat) return;\n\n // Wait for page to be fully loaded\n if (typeof window !== 'undefined' && document.readyState !== 'complete') {\n const handleLoad = () => {\n this.sendHeartbeat('page_load');\n this.hasSentPageLoadHeartbeat = true;\n window.removeEventListener('load', handleLoad);\n };\n window.addEventListener('load', handleLoad);\n } else {\n // Page is already loaded or we're in a non-browser environment\n this.sendHeartbeat('page_load');\n this.hasSentPageLoadHeartbeat = true;\n }\n }\n\n /**\n * Schedule the next heartbeat based on current activity\n */\n private scheduleNextHeartbeat(): void {\n if (this.isDestroyed) return;\n\n // Clear existing timer\n if (this.heartbeatTimer !== null) {\n clearTimeout(this.heartbeatTimer);\n }\n\n // Determine interval based on activity\n const isActive = this.activityDetector.isActive(60000); // 1 minute threshold\n this.currentInterval = isActive ? this.config.activeInterval : this.config.inactiveInterval;\n\n // Schedule next heartbeat\n this.heartbeatTimer = window.setTimeout(() => {\n this.sendHeartbeat('periodic');\n this.scheduleNextHeartbeat();\n }, this.currentInterval);\n\n if (this.config.debug) {\n console.log(\n `[Heartbeat] Scheduled next heartbeat in ${this.currentInterval / 1000}s (${isActive ? 'active' : 'inactive'})`\n );\n }\n }\n\n /**\n * Send heartbeat event\n */\n private sendHeartbeat(heartbeatType: 'periodic' | 'page_load' = 'periodic'): void {\n if (this.isDestroyed) return;\n\n const now = Date.now();\n const isActive = this.activityDetector.isActive(60000); // 1 minute threshold\n const hasConsent = this.tracker.hasConsent('analytics');\n\n // Base properties (always included)\n const properties: Record<string, unknown> = {\n heartbeat_type: heartbeatType,\n status: isActive ? 'active' : 'inactive',\n timestamp: now,\n };\n\n // Enhanced properties when consent is granted\n if (hasConsent) {\n const page = this.tracker.getCurrentPage();\n if (page) {\n properties.page = page;\n }\n \n properties.session_id = this.tracker.getSessionId();\n \n // Only include duration and event count for periodic heartbeats\n if (heartbeatType === 'periodic') {\n properties.duration = now - this.lastHeartbeatTime;\n properties.event_count = this.tracker.getEventCountSinceLastHeartbeat();\n \n // Reset event count\n this.tracker.resetEventCountSinceLastHeartbeat();\n }\n }\n\n // Track the heartbeat event\n this.tracker.trackSystemEvent('_grain_heartbeat', properties);\n\n this.lastHeartbeatTime = now;\n\n if (this.config.debug) {\n console.log('[Heartbeat] Sent heartbeat:', properties);\n }\n }\n\n /**\n * Destroy the heartbeat manager\n */\n destroy(): void {\n if (this.isDestroyed) return;\n\n if (this.heartbeatTimer !== null) {\n clearTimeout(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n\n this.isDestroyed = true;\n\n if (this.config.debug) {\n console.log('[Heartbeat] Destroyed');\n }\n }\n}\n\n", "/**\n * Attribution and Referral Tracking for Grain Analytics\n * Handles referral categorization and first-touch attribution\n */\n\nexport type ReferrerCategory = 'organic' | 'paid' | 'social' | 'direct' | 'email' | 'referral';\n\nexport interface UTMParameters {\n utm_source?: string;\n utm_medium?: string;\n utm_campaign?: string;\n utm_term?: string;\n utm_content?: string;\n}\n\nexport interface FirstTouchAttribution {\n source: string;\n medium: string;\n campaign: string;\n referrer: string;\n referrer_category: ReferrerCategory;\n timestamp: number;\n}\n\n/**\n * Known paid search parameters\n */\nconst PAID_SEARCH_PARAMS = [\n 'gclid', // Google Ads\n 'msclkid', // Microsoft Ads\n 'fbclid', // Facebook Ads\n 'ttclid', // TikTok Ads\n 'li_fat_id', // LinkedIn Ads\n 'twclid', // Twitter Ads\n 'ScCid', // Snapchat Ads\n];\n\n/**\n * Known social media domains\n */\nconst SOCIAL_DOMAINS = [\n 'facebook.com',\n 'twitter.com',\n 'x.com',\n 'linkedin.com',\n 'instagram.com',\n 'pinterest.com',\n 'reddit.com',\n 'tiktok.com',\n 'youtube.com',\n 'snapchat.com',\n 't.co', // Twitter short links\n 'fb.me', // Facebook short links\n 'lnkd.in', // LinkedIn short links\n];\n\n/**\n * Known organic search engines\n */\nconst SEARCH_ENGINES = [\n 'google.',\n 'bing.com',\n 'yahoo.com',\n 'duckduckgo.com',\n 'baidu.com',\n 'yandex.com',\n 'ecosia.org',\n 'ask.com',\n];\n\n/**\n * Email client domains\n */\nconst EMAIL_DOMAINS = [\n 'mail.google.com',\n 'outlook.live.com',\n 'mail.yahoo.com',\n 'mail.aol.com',\n];\n\n/**\n * Extract domain from URL\n */\nfunction extractDomain(url: string): string {\n try {\n const urlObj = new URL(url);\n return urlObj.hostname.toLowerCase();\n } catch {\n return '';\n }\n}\n\n/**\n * Check if URL contains paid search parameters\n */\nfunction hasPaidSearchParams(url: string): boolean {\n try {\n const urlObj = new URL(url);\n return PAID_SEARCH_PARAMS.some(param => urlObj.searchParams.has(param));\n } catch {\n return false;\n }\n}\n\n/**\n * Categorize referrer based on domain and parameters\n */\nexport function categorizeReferrer(referrer: string, currentUrl: string = ''): ReferrerCategory {\n // Direct traffic (no referrer)\n if (!referrer || referrer.trim() === '') {\n return 'direct';\n }\n\n const domain = extractDomain(referrer);\n \n // Same domain = direct\n if (currentUrl) {\n const currentDomain = extractDomain(currentUrl);\n if (domain === currentDomain) {\n return 'direct';\n }\n }\n\n // Check for paid search parameters\n if (hasPaidSearchParams(referrer) || hasPaidSearchParams(currentUrl)) {\n return 'paid';\n }\n\n // Email clients\n if (EMAIL_DOMAINS.some(emailDomain => domain.includes(emailDomain))) {\n return 'email';\n }\n\n // Social media\n if (SOCIAL_DOMAINS.some(socialDomain => domain.includes(socialDomain))) {\n return 'social';\n }\n\n // Organic search engines\n if (SEARCH_ENGINES.some(searchEngine => domain.includes(searchEngine))) {\n return 'organic';\n }\n\n // Everything else is referral\n return 'referral';\n}\n\n/**\n * Parse UTM parameters from URL\n */\nexport function parseUTMParameters(url: string): UTMParameters {\n try {\n const urlObj = new URL(url);\n const params: UTMParameters = {};\n\n const utmSource = urlObj.searchParams.get('utm_source');\n const utmMedium = urlObj.searchParams.get('utm_medium');\n const utmCampaign = urlObj.searchParams.get('utm_campaign');\n const utmTerm = urlObj.searchParams.get('utm_term');\n const utmContent = urlObj.searchParams.get('utm_content');\n\n if (utmSource) params.utm_source = utmSource;\n if (utmMedium) params.utm_medium = utmMedium;\n if (utmCampaign) params.utm_campaign = utmCampaign;\n if (utmTerm) params.utm_term = utmTerm;\n if (utmContent) params.utm_content = utmContent;\n\n return params;\n } catch {\n return {};\n }\n}\n\n/**\n * Get first-touch attribution from localStorage\n */\nexport function getFirstTouchAttribution(tenantId: string): FirstTouchAttribution | null {\n if (typeof window === 'undefined' || typeof localStorage === 'undefined') {\n return null;\n }\n\n try {\n const key = `_grain_first_touch_${tenantId}`;\n const stored = localStorage.getItem(key);\n if (stored) {\n return JSON.parse(stored) as FirstTouchAttribution;\n }\n } catch (error) {\n console.warn('[Grain Attribution] Failed to retrieve first-touch attribution:', error);\n }\n\n return null;\n}\n\n/**\n * Set first-touch attribution in localStorage\n */\nexport function setFirstTouchAttribution(\n tenantId: string,\n attribution: FirstTouchAttribution\n): void {\n if (typeof window === 'undefined' || typeof localStorage === 'undefined') {\n return;\n }\n\n try {\n const key = `_grain_first_touch_${tenantId}`;\n localStorage.setItem(key, JSON.stringify(attribution));\n } catch (error) {\n console.warn('[Grain Attribution] Failed to store first-touch attribution:', error);\n }\n}\n\n/**\n * Get or create first-touch attribution\n */\nexport function getOrCreateFirstTouchAttribution(\n tenantId: string,\n referrer: string,\n currentUrl: string,\n utmParams: UTMParameters\n): FirstTouchAttribution {\n // Try to get existing first-touch\n const existing = getFirstTouchAttribution(tenantId);\n if (existing) {\n return existing;\n }\n\n // Create new first-touch attribution\n const referrerCategory = categorizeReferrer(referrer, currentUrl);\n const referrerDomain = extractDomain(referrer);\n\n const firstTouch: FirstTouchAttribution = {\n source: utmParams.utm_source || referrerDomain || 'direct',\n medium: utmParams.utm_medium || referrerCategory,\n campaign: utmParams.utm_campaign || 'none',\n referrer: referrer || 'direct',\n referrer_category: referrerCategory,\n timestamp: Date.now(),\n };\n\n // Store it\n setFirstTouchAttribution(tenantId, firstTouch);\n\n return firstTouch;\n}\n\n/**\n * Get session UTM parameters (memory-only, not persisted across page loads)\n */\nlet sessionUTMParams: UTMParameters | null = null;\n\nexport function getSessionUTMParameters(): UTMParameters | null {\n return sessionUTMParams;\n}\n\nexport function setSessionUTMParameters(params: UTMParameters): void {\n sessionUTMParams = params;\n}\n\n/**\n * Clear session UTM parameters\n */\nexport function clearSessionUTMParameters(): void {\n sessionUTMParams = null;\n}\n\n", "/**\n * Page Tracking for Grain Analytics\n * Automatically tracks page views with consent-aware behavior\n */\n\nimport {\n categorizeReferrer,\n parseUTMParameters,\n getOrCreateFirstTouchAttribution,\n getSessionUTMParameters,\n setSessionUTMParameters,\n type UTMParameters,\n type FirstTouchAttribution,\n} from './attribution';\n\nexport interface PageTrackingConfig {\n stripQueryParams: boolean;\n debug?: boolean;\n tenantId: string;\n}\n\nexport interface PageTracker {\n trackSystemEvent(eventName: string, properties: Record<string, unknown>): void;\n hasConsent(category?: string): boolean;\n getEffectiveUserId(): string;\n getEphemeralSessionId(): string;\n getSessionId(): string;\n}\n\nexport class PageTrackingManager {\n private config: PageTrackingConfig;\n private tracker: PageTracker;\n private isDestroyed = false;\n private currentPath: string | null = null;\n private originalPushState: typeof history.pushState | null = null;\n private originalReplaceState: typeof history.replaceState | null = null;\n private previousPage: string | null = null;\n private landingPage: string | null = null;\n private pageViewCount = 0;\n\n constructor(tracker: PageTracker, config: PageTrackingConfig) {\n this.tracker = tracker;\n this.config = config;\n \n // Track initial page load (this is the landing page)\n this.trackCurrentPage(true);\n \n // Setup listeners\n this.setupHistoryListeners();\n this.setupHashChangeListener();\n }\n\n /**\n * Setup History API listeners (pushState, replaceState, popstate)\n */\n private setupHistoryListeners(): void {\n if (typeof window === 'undefined' || typeof history === 'undefined') return;\n\n // Wrap pushState\n this.originalPushState = history.pushState;\n history.pushState = (state: any, title: string, url?: string | URL | null) => {\n this.originalPushState?.call(history, state, title, url);\n this.trackCurrentPage();\n };\n\n // Wrap replaceState\n this.originalReplaceState = history.replaceState;\n history.replaceState = (state: any, title: string, url?: string | URL | null) => {\n this.originalReplaceState?.call(history, state, title, url);\n this.trackCurrentPage();\n };\n\n // Listen to popstate (back/forward buttons)\n window.addEventListener('popstate', this.handlePopState);\n }\n\n /**\n * Setup hash change listener\n */\n private setupHashChangeListener(): void {\n if (typeof window === 'undefined') return;\n window.addEventListener('hashchange', this.handleHashChange);\n }\n\n /**\n * Handle popstate event (back/forward navigation)\n */\n private handlePopState = (): void => {\n if (this.isDestroyed) return;\n this.trackCurrentPage();\n };\n\n /**\n * Handle hash change event\n */\n private handleHashChange = (): void => {\n if (this.isDestroyed) return;\n this.trackCurrentPage();\n };\n\n /**\n * Track the current page\n */\n private trackCurrentPage(isLanding: boolean = false): void {\n if (this.isDestroyed || typeof window === 'undefined') return;\n\n const page = this.extractPath(window.location.href);\n \n // Don't track if it's the same page (unless it's the landing page)\n if (!isLanding && page === this.currentPath) {\n return;\n }\n\n // Store previous page before updating\n if (this.currentPath) {\n this.previousPage = this.currentPath;\n }\n\n this.currentPath = page;\n this.pageViewCount++;\n\n // Set landing page on first view\n if (isLanding) {\n this.landingPage = page;\n }\n\n const hasConsent = this.tracker.hasConsent('analytics');\n const currentUrl = window.location.href;\n const referrer = document.referrer || '';\n\n // Parse UTM parameters from current URL\n const utmParams = parseUTMParameters(currentUrl);\n \n // Store session UTM if they exist (first time only or if new UTMs appear)\n if (Object.keys(utmParams).length > 0) {\n const existing = getSessionUTMParameters();\n if (!existing || isLanding) {\n setSessionUTMParameters(utmParams);\n }\n }\n\n // Get or create first-touch attribution\n const sessionUTMs = getSessionUTMParameters() || {};\n const firstTouch = getOrCreateFirstTouchAttribution(\n this.config.tenantId,\n referrer,\n currentUrl,\n sessionUTMs\n );\n\n // Base properties (always included)\n const properties: Record<string, unknown> = {\n page,\n timestamp: Date.now(),\n };\n\n // Enhanced properties when consent is granted\n if (hasConsent) {\n properties.title = document.title || '';\n properties.full_url = currentUrl;\n properties.session_id = this.tracker.getSessionId();\n\n // Add referrer info\n if (referrer) {\n properties.referrer = referrer;\n properties.referrer_domain = this.extractDomain(referrer);\n properties.referrer_category = categorizeReferrer(referrer, currentUrl);\n }\n\n // Add landing page if this is not the first view\n if (this.landingPage && !isLanding) {\n properties.landing_page = this.landingPage;\n }\n\n // Add previous page if available\n if (this.previousPage) {\n properties.previous_page = this.previousPage;\n }\n\n // Add UTM parameters if present (from session)\n if (sessionUTMs.utm_source) properties.utm_source = sessionUTMs.utm_source;\n if (sessionUTMs.utm_medium) properties.utm_medium = sessionUTMs.utm_medium;\n if (sessionUTMs.utm_campaign) properties.utm_campaign = sessionUTMs.utm_campaign;\n if (sessionUTMs.utm_term) properties.utm_term = sessionUTMs.utm_term;\n if (sessionUTMs.utm_content) properties.utm_content = sessionUTMs.utm_content;\n\n // Add first-touch attribution\n properties.first_touch_source = firstTouch.source;\n properties.first_touch_medium = firstTouch.medium;\n properties.first_touch_campaign = firstTouch.campaign;\n properties.first_touch_referrer_category = firstTouch.referrer_category;\n\n // Browser and device info\n properties.device = this.getDeviceType();\n properties.browser = this.getBrowser();\n properties.os = this.getOS();\n properties.language = navigator.language || '';\n properties.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;\n properties.screen_resolution = `${screen.width}x${screen.height}`;\n properties.viewport = `${window.innerWidth}x${window.innerHeight}`;\n }\n\n // Track the page view event\n this.tracker.trackSystemEvent('page_view', properties);\n\n if (this.config.debug) {\n console.log('[Page Tracking] Tracked page view:', properties);\n }\n }\n\n /**\n * Extract domain from URL\n */\n private extractDomain(url: string): string {\n try {\n const urlObj = new URL(url);\n return urlObj.hostname;\n } catch {\n return '';\n }\n }\n\n /**\n * Detect browser name\n */\n private getBrowser(): string {\n const ua = navigator.userAgent;\n if (ua.includes('Firefox/')) return 'Firefox';\n if (ua.includes('Edg/')) return 'Edge';\n if (ua.includes('Chrome/')) return 'Chrome';\n if (ua.includes('Safari/') && !ua.includes('Chrome/')) return 'Safari';\n if (ua.includes('Opera/') || ua.includes('OPR/')) return 'Opera';\n return 'Unknown';\n }\n\n /**\n * Detect operating system\n */\n private getOS(): string {\n const ua = navigator.userAgent;\n if (ua.includes('Win')) return 'Windows';\n if (ua.includes('Mac')) return 'macOS';\n if (ua.includes('Linux')) return 'Linux';\n if (ua.includes('Android')) return 'Android';\n if (ua.includes('iOS') || ua.includes('iPhone') || ua.includes('iPad')) return 'iOS';\n return 'Unknown';\n }\n\n /**\n * Detect device type (Mobile, Tablet, Desktop)\n */\n private getDeviceType(): string {\n const ua = navigator.userAgent;\n const width = window.innerWidth;\n \n // Check for tablet-specific indicators\n if (ua.includes('iPad') || (ua.includes('Android') && !ua.includes('Mobile'))) {\n return 'Tablet';\n }\n \n // Check for mobile indicators\n if (ua.includes('Mobile') || ua.includes('iPhone') || ua.includes('Android')) {\n return 'Mobile';\n }\n \n // Fallback to screen width detection\n if (width < 768) {\n return 'Mobile';\n } else if (width >= 768 && width < 1024) {\n return 'Tablet';\n }\n \n return 'Desktop';\n }\n\n /**\n * Extract path from URL, optionally stripping query parameters\n */\n private extractPath(url: string): string {\n try {\n const urlObj = new URL(url);\n let path = urlObj.pathname + urlObj.hash;\n \n if (!this.config.stripQueryParams && urlObj.search) {\n path += urlObj.search;\n }\n \n return path;\n } catch (error) {\n // If URL parsing fails, return the raw string\n if (this.config.debug) {\n console.warn('[Page Tracking] Failed to parse URL:', url, error);\n }\n return url;\n }\n }\n\n /**\n * Get the current page path\n */\n getCurrentPage(): string | null {\n return this.currentPath;\n }\n\n /**\n * Manually track a page view (for custom navigation)\n */\n trackPage(page: string, properties?: Record<string, unknown>): void {\n if (this.isDestroyed) return;\n\n const hasConsent = this.tracker.hasConsent('analytics');\n\n // Base properties\n const baseProperties: Record<string, unknown> = {\n page,\n timestamp: Date.now(),\n ...properties,\n };\n\n // Enhanced properties when consent is granted\n if (hasConsent && typeof document !== 'undefined' && typeof window !== 'undefined') {\n if (!baseProperties.referrer) {\n baseProperties.referrer = document.referrer || '';\n }\n if (!baseProperties.title) {\n baseProperties.title = document.title || '';\n }\n if (!baseProperties.full_url) {\n baseProperties.full_url = window.location.href;\n }\n if (!baseProperties.session_id) {\n baseProperties.session_id = this.tracker.getSessionId();\n }\n if (!baseProperties.browser) {\n baseProperties.browser = this.getBrowser();\n }\n if (!baseProperties.os) {\n baseProperties.os = this.getOS();\n }\n }\n\n this.tracker.trackSystemEvent('page_view', baseProperties);\n\n if (this.config.debug) {\n console.log('[Page Tracking] Manually tracked page:', baseProperties);\n }\n }\n\n /**\n * Get page view count for current session\n */\n getPageViewCount(): number {\n return this.pageViewCount;\n }\n\n /**\n * Destroy the page tracker\n */\n destroy(): void {\n if (this.isDestroyed) return;\n\n // Restore original history methods\n if (typeof history !== 'undefined') {\n if (this.originalPushState) {\n history.pushState = this.originalPushState;\n }\n if (this.originalReplaceState) {\n history.replaceState = this.originalReplaceState;\n }\n }\n\n // Remove event listeners\n if (typeof window !== 'undefined') {\n window.removeEventListener('popstate', this.handlePopState);\n window.removeEventListener('hashchange', this.handleHashChange);\n }\n\n this.isDestroyed = true;\n\n if (this.config.debug) {\n console.log('[Page Tracking] Destroyed');\n }\n }\n}\n\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|