UNPKG

unified-analytics

Version:

Unified analytics library for web applications

497 lines (388 loc) โ€ข 17.1 kB
# Unified Analytics A unified analytics library for web applications that provides a consistent interface for multiple analytics providers. This library uses the Observer pattern to allow easy integration with multiple analytics providers (like CleverTap and custom providers) through a single consistent API. ## Table of Contents - [Unified Analytics](#unified-analytics) - [Table of Contents](#table-of-contents) - [Features](#features) - [Installation](#installation) - [Package Exports](#package-exports) - [Usage](#usage) - [Basic Usage](#basic-usage) - [API Reference](#api-reference) - [Core Analytics APIs](#core-analytics-apis) - [Available Analytics Providers](#available-analytics-providers) - [Hooks](#hooks) - [Common Attributes](#common-attributes) - [Hooks](#hooks-1) - [useScreenDuration](#usescreenduration) - [Browser Tab Visibility](#browser-tab-visibility) - [useScrollDepthTracking](#usescrolldepthtracking) - [Best Practices](#best-practices) - [Supported Providers](#supported-providers) - [CleverTap](#clevertap) - [Google Analytics via Firebase](#google-analytics-via-firebase) - [Google Analytics 4 (using gtag.js)](#google-analytics-4-using-gtagjs) - [Custom Provider](#custom-provider) - [Recipes](#recipes) - [React Integration](#react-integration) - [Testing](#testing) - [Development](#development) - [Analytics Architecture](#analytics-architecture) - [Key Components](#key-components) - [License](#license) ## Features - ๐Ÿ”„ **Unified API** - Track events across multiple analytics providers through a single consistent interface - ๐Ÿ”Œ **Pluggable Architecture** - Easily add, remove, or swap analytics providers - ๐Ÿ” **Type Safety** - Written in TypeScript with comprehensive type definitions - ๐ŸŽฏ **Common Properties** - Set global properties that are included with every event - ๐Ÿงช **Testing Support** - Comprehensive Vitest test suite - ๐Ÿ“ฆ **Small Footprint** - Minimal bundle size with size limits enforced - ๐Ÿ“š **Comprehensive Documentation** - Detailed JSDoc comments and examples ## Installation ```bash npm install unified-analytics # or yarn add unified-analytics # or bun add unified-analytics ``` ### Package Exports The package supports subpath imports for better tree-shaking: ```typescript // Main import import analytics from 'unified-analytics'; import type { AnalyticsEvent, AnalyticsObserver } from 'unified-analytics'; // Specific imports import { cleverTapService } from 'unified-analytics/providers'; import { useScreenDuration } from 'unified-analytics/hooks'; ``` ## Usage ### Basic Usage ```typescript import analytics from 'unified-analytics'; // Initialize analytics with providers // Note: Analytics must be initialized before tracking events analytics.initialize([providerService]); // Attach/detach providers as needed analytics.attach(providerService); // When detaching, make sure to pass the same service that was attached/initialized analytics.detach(providerService); // Set common attributes that will be attached to all events analytics.setCommonAttributes({ userAgent: navigator.userAgent }); // Track events - these will only be processed after initialization analytics.sendEvent('PAGE_VIEW', { page: 'home', title: 'Homepage' }); analytics.sendEvent('BUTTON_CLICKED', { buttonId: 'submit-form', location: 'header' }); // You can also use the fluent API for chaining operations analytics .initialize([providerService]) .setCommonAttributes({ userAgent: navigator.userAgent }) .sendEvent('APP_STARTED', { timestamp: Date.now() }); ``` ### API Reference #### Core Analytics APIs | Method | Description | Parameters | |--------|-------------|------------| | `initialize()` | Initialize the analytics system with observers and options | `observers: AnalyticsObserver[]`, `options: InitOptions` | | `attach()` | Register a new analytics provider to receive events | `observer: AnalyticsObserver` | | `detach()` | Remove an analytics provider from receiving events | `observer: AnalyticsObserver` | | `sendEvent()` | Track an analytics event and notify registered providers | `eventName: string`, `attributes?: EventAttributes`, `observers?: AnalyticsObserver \| AnalyticsObserver[]` | | `setCommonAttributes()` | Update attributes included with every tracked event | `attributes: EventAttributes` | | `getCommonAttributes()` | Get the current common attributes object | - | | `setInHouseAttributes()` | Update in-house attributes for internal use | `attributes: EventAttributes` | | `getInHouseAttributes()` | Get the current in-house attributes object | - | | `resetAttributes()` | Reset all analytics attributes (common and in-house) | - | | `setDebug()` | Enable or disable debug mode at runtime | `value: boolean` | | `isInitialized()` | Check if analytics has been initialized | - | | `cloneProvider()` | Create a clone of an existing analytics provider | `observer: T`, `options?: { autoAttach?: boolean }` | | `use()` | Register middleware to process events before sending | `middleware: AnalyticsMiddleware` | #### Available Analytics Providers | Provider | Description | |----------|--------------| | `Firebase Google Analytics` | Integration with Firebase Analytics | | `CleverTap` | Integration with CleverTap analytics platform | | `Google Analytics 4` | Direct integration with Google Analytics 4 using gtag.js | #### Hooks | Hook | Description | |------|-------------| | `useScreenDuration` | React hook for tracking screen/page view duration | | `useScrollDepthTracking` | React hook for tracking scroll depth | Each provider implements the `AnalyticsObserver` interface with methods like `getInstance()`, `onEvent()`, and `clone()`. ### Common Attributes Common attributes are key-value pairs that are automatically included with every analytics event you track. They're useful for: - Adding global context (user agent, app version, environment) - Including user information (userId, accountId) - Adding device or platform details (device type, screen size) ```typescript // Set common attributes that will be included in all subsequent events // Make sure analytics is initialized first analytics .initialize([/* your providers */]) .setCommonAttributes({ appVersion: '1.2.3', platform: 'web', environment: isDevelopment() ? 'development' : 'production', userId: user.id, accountType: user.accountType }); // You can update common attributes at any time analytics.setCommonAttributes({ // This merges with existing common attributes lastActive: new Date().toISOString() }); // Clear specific common attributes when needed (e.g., on user logout) analytics.clearCommonProperties(['userId', 'accountType']); // In-house attributes are for internal or advanced analytics needs analytics.setInHouseAttributes({ internalUserType: 'beta', experimentGroup: 'A', }); // Retrieve in-house attributes const inHouseAttrs = analytics.getInHouseAttributes(); // Reset all attributes (both common and in-house) analytics.resetAttributes(); // Common and in-house attributes are automatically included in all events analytics.sendEvent('FEATURE_USED', { featureId: 'dark-mode' // Common attributes will be merged with the event attributes }); ``` ### Hooks The library provides React hooks to simplify analytics integration in React applications. #### useScreenDuration Tracks how long a user spends on a screen or component. Useful for measuring engagement and session length. ```typescript useScreenDuration(screenName: string, additionalAttributes: Record<string, any> = {}) ``` - `screenName`: The event name to track (will be used as the event name when sending to analytics) - `additionalAttributes`: Optional object with additional data to include with the duration event ```typescript import { useScreenDuration } from 'unified-analytics/hooks'; function MyComponent() { // Track the time spent on this component useScreenDuration( 'MY_COMPONENT_DURATION', // Event name to track { componentVersion: '1.0.0', section: 'dashboard' } // Optional additional attributes ); return <div>My Component</div>; } ``` The hook will automatically track: - When the component mounts (screen view start) - When the component unmounts (screen view end) - The total duration spent on the screen - Any additional attributes you specify ##### Browser Tab Visibility The hook intelligently handles browser tab visibility changes to provide accurate engagement metrics: - Pauses tracking when the tab is not visible (user switches tabs) - Resumes tracking when the tab becomes visible again - Only counts actual engagement time (when the tab is in focus) #### useScrollDepthTracking Tracks how far users scroll down a page. Useful for content engagement analysis and identifying content visibility issues. ```typescript useScrollDepthTracking(eventName: string, throttleMs: number = 500, additionalAttributes: Record<string, any> = {}) ``` - `eventName`: The name of the event to track when scrolling - `throttleMs`: Optional throttle time in milliseconds (default: 500ms) to avoid excessive event firing - `additionalAttributes`: Optional object with additional data to include with the scroll events ```typescript import { useScrollDepthTracking } from 'unified-analytics/hooks'; function LongContentPage() { // Track how far users scroll down this page useScrollDepthTracking( 'CONTENT_SCROLL_DEPTH', // Event name 1000, // Throttle to once per second { pageId: 'article-123', contentType: 'blog' } // Additional attributes ); return ( <div className="long-content"> {/* Long scrollable content */} </div> ); } ``` The hook will automatically track: - Scroll position as a percentage (0-100%) - Throttled scroll events to avoid overwhelming analytics - The current timestamp of each scroll event - Any additional attributes you specify ## Best Practices - **Set early in app lifecycle**: Set common attributes as early as possible, ideally during app initialization - **Keep them lightweight**: Include only essential information that's relevant across all events - **Prefer specific attributes for one-time events**: Don't overload common attributes with data only relevant to specific events - **Update when user context changes**: Update common attributes when user logs in/out or changes settings - **User consent**: Respect user privacy and obtain necessary consents before tracking. This responsibility falls on the application code. The analytics library does not handle user consent. - **Using the Analytics Service**: Always fire events through the Analytics Service, not directly to providers. This is a critical best practice for maintaining consistency and ensuring all configured providers receive events properly. - Fire events directly via providers is still technically possible but events won't include common attributes. - **Provider Configuration**: Initialize providers before adding them to analytics. This ensures providers are ready to receive events. - **Provider Instance**: Use the provider instance returned by `getInstance()` to access provider instances if they are provided by the provider. - **Provider Initialization**: Due to the differences between providers, the initialization process may vary. Always see what's available in each provider service, as well as referring to the provider's documentation for specific initialization instructions. - **Google Analytics Implementations**: Do not use Firebase Google Analytics and Google Analytics 4 (gtag.js) providers simultaneously. Both implementations modify the same global window objects under the hood, which can cause one configuration to overwrite the other. This behavior is outside the control of the unified-analytics service. ```typescript // โœ… CORRECT: Fire events through the Analytics Service // Single provider analytics.sendEvent('USER_ACTION', { actionId: 'submit' }, googleAnalytics4Service); // Multiple providers analytics.sendEvent('USER_ACTION', { actionId: 'submit' }, [googleAnalytics4Service, cleverTapService]); // โŒ INCORRECT: Don't call provider methods directly // This is still technically works, but events won't include common attributes cleverTapService.onEvent({ ... }); googleAnalytics4Service.onEvent({ ... }); ``` ## Supported Providers ### CleverTap ```typescript import analytics from 'unified-analytics'; import { cleverTapService } from 'unified-analytics/providers'; // Initialize CleverTap with your API key const cleverTapInstance = cleverTapService.getInstance(); cleverTapInstance.init('YOUR_CLEVERTAP_API_KEY'); // Add to analytics analytics .initialize([cleverTapService]) .setCommonAttributes({ userAgent: navigator.userAgent, }); // Track events analytics.sendEvent('USER_LOGIN', { userId: '12345' }); ``` ### Google Analytics via Firebase ```typescript import analytics from 'unified-analytics'; import { firebaseGoogleAnalyticsService } from 'unified-analytics/providers'; // Initialize Google Analytics firebaseGoogleAnalyticsService.init({ // Pass your initialization here apiKey: 'YOUR_GOOGLE_ANALYTICS_API_KEY', projectId: "YOUR_GOOGLE_ANALYTICS_PROJECT_ID", // Optional: Additional initialization options // ... }); // Add to analytics analytics .initialize([firebaseGoogleAnalyticsService]) .setCommonAttributes({ userAgent: navigator.userAgent, }); // Track events analytics.sendEvent('PAGE_VIEW', { page: '/home', title: 'Home Page' }); ``` ### Google Analytics 4 (using gtag.js) ```typescript import analytics from 'unified-analytics'; import { googleAnalytics4Service } from 'unified-analytics/providers'; // Initialize Google Analytics 4 googleAnalytics4Service.init({ measurementId: 'G-XXXXXXX', // Your GA4 measurement ID debug: true, // Optional: Enable debug logging config: { // Optional: Additional configuration parameters send_page_view: true, cookie_domain: 'auto' }, }); // Add to analytics analytics .initialize([googleAnalytics4Service]) .setCommonAttributes({ userAgent: navigator.userAgent, }); // Track events analytics.sendEvent('PAGE_VIEW', { page: '/home', title: 'Home Page' }); ``` ### Custom Provider Create your own provider by implementing the `AnalyticsObserver` interface: ```typescript import { AnalyticsEvent, AnalyticsObserver } from 'unified-analytics'; class MyCustomProvider implements AnalyticsObserver { private apiKey: string | null = null; init(apiKey: string): void { this.apiKey = apiKey; } getInstance(): MyCustomProvider { return this; } onEvent(event: AnalyticsEvent): void { // Your implementation to handle the event console.log(`Sending ${event.name} to MyCustomProvider`); // Send to your analytics service... } } export const myCustomProvider = new MyCustomProvider(); ``` ## Recipes ### React Integration An example React application is included in the `examples/react-app` directory, which demonstrates how to use the analytics library in a real-world React application. The example app showcases: - Setting up analytics providers with proper initialization - Managing analytics for authentication states (login/logout) - Setting different common properties for authenticated vs. anonymous users - Implementing protected routes with React Router - Tracking page views and user interactions - Properly cleaning up analytics on component unmount Refer to the example app for best practices on integrating the analytics library with React applications. ## Testing The library includes a Vitest-based test suite, which is fast and ESM-compatible. ```bash # Run tests bun run test # Run tests in watch mode during development bun run test:watch # Generate test coverage report bun run test:coverage ``` ## Development ```bash # Install dependencies bun install # Build the library bun run build # Run type checking bun run typecheck ``` ## Analytics Architecture The application uses a flexible analytics system that supports multiple providers (CleverTap, Google Analytics) through a modular observer pattern implementation. ```mermaid graph TD subgraph Subject A[Analytics Class] -.implements.-> B[AnalyticsSubject Interface] A -->|maintains| J[Observer Set] A -->|attach/detach| J end subgraph Observers E[AnalyticsObserver Interface] G[CleverTap Service] -.implements.-> E H[Google Analytics Service] -.implements.-> E end J -->|notifies| G J -->|notifies| H I[Application Code] -->|sendEvent| A subgraph Event Types L[AnalyticsEvent Interface] -->|contains| M1[name: AppEventName] L -->|contains| M2[attributes?: Record] end ``` ### Key Components **Observer Pattern** - Analytics class acts as the subject - Analytics providers (CleverTap, Google Analytics) are observers - All observers implement the AnalyticsObserver interface ## License MIT