unified-analytics
Version:
Unified analytics library for web applications
497 lines (388 loc) โข 17.1 kB
Markdown
# 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