websparks-analytics-sdk
Version:
JavaScript SDK for WebSparks analytics tracking with geolocation, fingerprinting, and session management
1,891 lines (1,498 loc) β’ 47.3 kB
Markdown
# WebSparks Analytics SDK
JavaScript/TypeScript SDK for tracking analytics events with comprehensive user data collection including geolocation, browser fingerprinting, IP detection, and session management.
## Features
- π― **Event Tracking** - Track custom events with properties
- π€ **User Identification** - Identify users and track their properties
- π **Anonymous & Identified Users** - Fingerprint-based distinct IDs for anonymous users, custom distinct IDs for identified users
- π **Page View Tracking** - Automatic page view tracking
- πΊοΈ **Geolocation** - Browser GPS + IP-based geolocation
- π **Browser Fingerprinting** - Device, browser, OS detection
- π **IP Detection** - WebRTC + external services for public IP
- β±οΈ **Session Management** - Persistent sessions across page refreshes
- π **Session End Tracking** - Automatic tracking when tab/browser closes
- π **API Key Validation** - Secure authentication with backend
- π **Debug Mode** - Comprehensive logging for development
## Installation
```bash
npm install websparks-analytics-sdk
```
## Quick Start
**JavaScript:**
```javascript
import WebSparksAnalytics from 'websparks-analytics-sdk';
// Initialize the SDK
const analytics = new WebSparksAnalytics({
apiKey: 'your-api-key-here',
projectId: 'your-project-id-here',
userId: 30, // Optional
debug: true, // Enable console logging
trackSessionEnd: true, // Track session end on tab close
trackLocation: false // Request location permission (optional)
});
// β¨ session_start is automatically tracked on initialization (for new sessions)
// Track events
analytics.track('button_clicked', {
button_name: 'donate',
amount: 100
});
// Identify users
analytics.identify(123, {
name: 'John Doe',
email: 'john@example.com'
});
// Track page views
analytics.page('Home Page', {
section: 'landing'
});
```
**TypeScript:**
```typescript
import WebSparksAnalytics, {
AnalyticsConfig,
EventData,
UserProperties,
FingerprintData,
LocationData
} from 'websparks-analytics-sdk';
// Typed configuration
const config: AnalyticsConfig = {
apiKey: 'your-api-key-here',
projectId: 'your-project-id-here',
userId: 30,
debug: true,
trackSessionEnd: true,
trackLocation: false
};
const analytics = new WebSparksAnalytics(config);
// β¨ session_start is automatically tracked on initialization (for new sessions)
// Track events with typed data
const eventData: EventData = {
button_name: 'donate',
amount: 100
};
analytics.track('button_clicked', eventData);
// Identify users with typed properties
const userProps: UserProperties = {
name: 'John Doe',
email: 'john@example.com'
};
analytics.identify(123, userProps);
// Track page views
analytics.page('Home Page', { section: 'landing' });
```
---
## Table of Contents
1. [Initialization](#initialization)
2. [Configuration Options](#configuration-options)
3. [API Methods](#api-methods)
4. [Anonymous vs Identified Users](#anonymous-vs-identified-users)
5. [Session Management](#session-management)
6. [Data Collected](#data-collected)
7. [Examples](#examples)
8. [TypeScript Support](#typescript-support)
9. [Browser Support](#browser-support)
10. [Privacy](#privacy)
---
## Initialization
### Basic Initialization
**JavaScript:**
```javascript
import WebSparksAnalytics from 'websparks-analytics-sdk';
const analytics = new WebSparksAnalytics({
apiKey: 'your-api-key-here',
projectId: 'your-project-id-here'
});
```
**TypeScript:**
```typescript
import WebSparksAnalytics, { AnalyticsConfig } from 'websparks-analytics-sdk';
const config: AnalyticsConfig = {
apiKey: 'your-api-key-here',
projectId: 'your-project-id-here'
};
const analytics = new WebSparksAnalytics(config);
```
### Full Initialization with All Options
**JavaScript:**
```javascript
import WebSparksAnalytics from 'websparks-analytics-sdk';
const analytics = new WebSparksAnalytics({
apiKey: 'EBvk5C...............', // Required: Your API key
projectId: 'websparks-....-...-fui893j', // Required: Your project ID
userId: 30, // Optional: User ID (can be set later)
debug: true, // Optional: Enable debug logs (default: false)
trackSessionEnd: true, // Optional: Auto-track session end (default: true)
trackLocation: false // Optional: Request GPS location (default: false)
});
```
**TypeScript:**
```typescript
import WebSparksAnalytics, { AnalyticsConfig } from 'websparks-analytics-sdk';
const config: AnalyticsConfig = {
apiKey: 'EBvk5C...............', // Required: Your API key
projectId: 'websparks-....-...-fui893j', // Required: Your project ID
userId: 30, // Optional: User ID (can be set later)
debug: true, // Optional: Enable debug logs (default: false)
trackSessionEnd: true, // Optional: Auto-track session end (default: true)
trackLocation: false // Optional: Request GPS location (default: false)
};
const analytics = new WebSparksAnalytics(config);
```
### What Happens During Initialization?
1. **API Key Validation** - Validates your API key with the backend
2. **Session Creation/Restoration** - Creates new session or restores existing from localStorage
3. **Fingerprint Generation** - Generates browser fingerprint automatically
4. **IP Detection** - Detects client IP address (both local and public)
5. **Location Detection** - Requests GPS location if `trackLocation: true`
6. **β¨ Automatic Session Start** - Automatically calls `session('start')` for **new sessions only**
- If session exists in localStorage (page refresh), session is restored without new `session_start`
- If no session exists, a new session is created and `session_start` is tracked
7. **Session End Listener** - Sets up automatic `session_end` tracking on tab close
---
## Configuration Options
| Option | Type | Required | Default | Description |
|--------|------|----------|---------|-------------|
| `apiKey` | `string` | β
Yes | - | Your API key from WebSparks backend |
| `projectId` | `string` | β
Yes | - | Your project ID from WebSparks |
| `userId` | `number` | β No | `null` | User ID to associate with events |
| `debug` | `boolean` | β No | `false` | Enable detailed console logging |
| `trackSessionEnd` | `boolean` | β No | `true` | Automatically track when user closes tab/browser |
| `trackLocation` | `boolean` | β No | `false` | Request GPS location permission on init |
### Configuration Examples
#### Production Setup (Minimal)
```javascript
import WebSparksAnalytics from 'websparks-analytics-sdk';
const analytics = new WebSparksAnalytics({
apiKey: 'prod-api-key',
projectId: 'prod-project-id'
});
```
#### Development Setup (Full Debugging)
```javascript
import WebSparksAnalytics from 'websparks-analytics-sdk';
const analytics = new WebSparksAnalytics({
apiKey: 'dev-api-key',
projectId: 'dev-project-id',
debug: true,
trackLocation: true
});
```
#### With User Already Logged In
```javascript
import WebSparksAnalytics from 'websparks-analytics-sdk';
const analytics = new WebSparksAnalytics({
apiKey: 'your-api-key',
projectId: 'your-project-id',
userId: getCurrentUserId() // Get from your auth system
});
```
---
## API Methods
### 1. `track(eventName, eventData?)`
Track a custom event with optional properties.
**Parameters:**
- `eventName` (string, required): Name of the event
- `eventData` (object, optional): Event properties/data
**Returns:** `Promise<void>`
**Examples:**
```javascript
// Simple event
analytics.track('button_clicked');
// Event with data
analytics.track('product_viewed', {
product_id: 'SKU-12345',
product_name: 'Winter Jacket',
price: 99.99,
category: 'Clothing'
});
// Async/await usage
await analytics.track('purchase_completed', {
order_id: 'ORD-789',
total_amount: 299.99,
items_count: 3
});
// Promise usage
analytics.track('video_played', {
video_id: 'VID-123',
duration: 180
})
.then(() => console.log('Event tracked'))
.catch(err => console.error('Tracking failed:', err));
```
**Automatic Data Included:**
- `timestamp`: ISO timestamp when event occurred
- `sessionId`: Current session ID
- `userId`: User ID (if identified)
- `fingerprint`: Browser fingerprint
- `location`: GPS coordinates (if available)
- `clientIp`: User's IP address
---
### 2. `identify(userId, userProperties?)`
Identify a user and optionally set their properties. Transitions user from anonymous to identified.
**Parameters:**
- `userId` (number, required): Unique user identifier
- `userProperties` (object, optional): User properties to store
- `distinctId` (string, optional): Custom distinct ID from your backend
**Returns:** `Promise<void>`
**Examples:**
```javascript
// Basic identification
analytics.identify(123);
// With user properties
analytics.identify(456, {
name: 'Jane Smith',
email: 'jane@example.com',
plan: 'premium',
signup_date: '2025-01-15'
});
// With custom distinct ID from backend (NEW in v1.4.0)
analytics.identify(456, {
name: 'Jane Smith',
email: 'jane@example.com',
distinctId: 'user-uuid-abc123-from-backend' // Your custom distinct_id
});
// β distinct_id changes from "anon-{fingerprint-hash}" to "user-uuid-abc123-from-backend"
// Update user properties later
analytics.identify(456, {
last_login: new Date().toISOString(),
login_count: 42
});
// On user login
async function handleLogin(user) {
await analytics.identify(user.id, {
name: user.name,
email: user.email,
role: user.role,
distinctId: user.distinct_id // Optional: from your backend
});
}
```
**Important Notes:**
- Once identified, `userId` persists in session storage
- All subsequent events include this `userId`
- **Distinct ID Transition:** Anonymous users get fingerprint-based distinct_id, identified users can use custom distinct_id
- Use `reset()` to clear user identification
---
### 3. `page(pageName?, pageProperties?)`
Track a page view.
**Parameters:**
- `pageName` (string, optional): Name of the page (defaults to `document.title`)
- `pageProperties` (object, optional): Page-specific properties
**Returns:** `Promise<void>`
**Examples:**
```javascript
// Simple page view (uses document.title)
analytics.page();
// Named page view
analytics.page('Home Page');
// With properties
analytics.page('Product Page', {
category: 'electronics',
product_id: 'SKU-12345',
referrer: document.referrer
});
// In React Router
function ProductPage({ productId }) {
useEffect(() => {
analytics.page('Product Detail', {
product_id: productId,
section: 'catalog'
});
}, [productId]);
}
// In Vue Router
router.afterEach((to, from) => {
analytics.page(to.name, {
path: to.path,
previous_page: from.name
});
});
```
**Automatic Data Included:**
- `url`: Full current URL
- `path`: URL pathname
- `timestamp`: ISO timestamp
- `sessionId`: Current session ID
- `userId`: User ID (if identified)
---
### 4. `reset()`
Clear current user identification and start a new session.
**Parameters:** None
**Returns:** `void`
**Examples:**
```javascript
// On user logout
function handleLogout() {
analytics.reset(); // Clears userId and starts new session
// Your logout logic...
}
// Clear and restart
analytics.reset();
// The reset method:
// 1. Clears userId
// 2. Clears session from localStorage
// 3. Generates new sessionId
// 4. Tracks new session_start event
```
**What Gets Reset:**
- User ID β `null`
- Session ID β New ID generated
- Session start time β Reset to now
- localStorage session data β Cleared
**What Stays the Same:**
- Browser fingerprint (device-based)
- IP address
- Location data (if already collected)
---
### 5. `endSession(eventData?)`
Manually end the current session.
**Parameters:**
- `eventData` (object, optional): Additional data to include with session_end event
**Returns:** `Promise<void>`
**Examples:**
```javascript
// Simple session end
analytics.endSession();
// With custom data
analytics.endSession({
reason: 'user_logout',
logout_method: 'button_click'
});
// On user logout
async function handleLogout() {
await analytics.endSession({ reason: 'logout' });
// Then logout user...
}
// On inactivity timeout
let inactivityTimer;
function resetInactivityTimer() {
clearTimeout(inactivityTimer);
inactivityTimer = setTimeout(() => {
analytics.endSession({
reason: 'inactivity_timeout',
timeout_duration: 1800000 // 30 minutes in ms
});
}, 1800000);
}
// On navigation away
router.beforeEach((to, from, next) => {
if (to.path === '/goodbye') {
analytics.endSession({
reason: 'navigation',
destination: to.path
});
}
next();
});
```
**Automatic Data Included:**
- `sessionDuration`: Total session time in milliseconds
- `sessionDurationMinutes`: Duration in minutes (rounded)
- `sessionDurationSeconds`: Duration in seconds (rounded)
- `sessionId`: The session being ended
- `timestamp`: When session ended
---
### 6. `sessionEnd(eventData?)`
Alias for `endSession()`. Works exactly the same.
**Parameters:**
- `eventData` (object, optional): Additional data to include
**Returns:** `Promise<void>`
**Examples:**
```javascript
// Same as endSession()
analytics.sessionEnd();
// With data
analytics.sessionEnd({ reason: 'user_action' });
```
---
### 7. `session(eventType, eventData?)`
Dedicated method for tracking session events with `'session'` event_type (separate from general tracking).
**Parameters:**
- `eventType` (string, required): Either `'start'` or `'end'`
- `eventData` (object, optional): Additional event data
**Returns:** `Promise<void>`
**Examples:**
```javascript
// NOTE: session('start') is automatically called when SDK initializes
// You typically don't need to call it manually
// Track session end manually
analytics.session('end', {
reason: 'user_logout',
duration_visible: 1200
});
// Session end automatically includes duration
analytics.session('end', { reason: 'timeout' });
// Automatically adds:
// - sessionDuration (ms)
// - sessionDurationMinutes
// - sessionDurationSeconds
// Manual session start (only for custom flows)
analytics.session('start', {
source: 'homepage',
campaign: 'winter_2025'
});
// On user logout
async function handleLogout() {
await analytics.session('end', {
reason: 'user_logout',
logout_method: 'button_click'
});
analytics.reset(); // This also triggers a new session_start
}
```
**Important Notes:**
- **Session start is automatic** - Called automatically on SDK initialization for new sessions
- Uses `event_type: 'session'` (not `'track'`)
- Session end automatically calculates duration
- Typically used internally by SDK, but available for custom session flows
- All `endSession()` and automatic session tracking use this method
- When you call `reset()`, it automatically triggers a new `session('start')`
---
### 8. `requestLocation()`
Manually request user's GPS location.
**Parameters:** None
**Returns:** `Promise<LocationData>`
**LocationData Type:**
```typescript
interface LocationData {
latitude: number;
longitude: number;
accuracy: number; // in meters
}
```
**Examples:**
```javascript
// Request location
analytics.requestLocation()
.then(location => {
console.log('Latitude:', location.latitude);
console.log('Longitude:', location.longitude);
console.log('Accuracy:', location.accuracy, 'meters');
})
.catch(error => {
console.error('Location denied:', error);
});
// Async/await
async function getLocation() {
try {
const location = await analytics.requestLocation();
console.log('User location:', location);
} catch (error) {
console.log('User denied location permission');
}
}
// Request on button click
document.getElementById('shareLocation').addEventListener('click', async () => {
try {
const location = await analytics.requestLocation();
analytics.track('location_shared', {
lat: location.latitude,
lng: location.longitude
});
} catch (error) {
analytics.track('location_denied');
}
});
```
**Important Notes:**
- Requires user permission
- Browser will show permission prompt
- If already granted, returns cached location
- Works even if `trackLocation: false` in config
---
## Anonymous vs Identified Users
The SDK automatically tracks both anonymous and identified users with persistent distinct IDs.
### How It Works
**Anonymous Users** (Before signup/login):
- Get a fingerprint-based `distinct_id` automatically
- Format: `anon-{fingerprint-hash}-{timestamp}`
- Based on device/browser characteristics
- Persists across sessions via localStorage
**Identified Users** (After signup/login):
- Can use custom `distinct_id` from your backend
- Or keep the fingerprint-based one
- User transitions tracked with `previous_distinct_id`
### User Journey Example
```javascript
// 1. User arrives anonymously
const analytics = new WebSparksAnalytics({
apiKey: 'your-key',
projectId: 'your-project'
});
// β distinct_id: "anon-abc123def-xyz789"
// β user_type: "anonymous"
// β is_anonymous: true
// User browses (all events include anonymous distinct_id)
analytics.track('product_viewed', {
product_id: 'SKU-123'
});
// Event includes:
// - distinct_id: "anon-abc123def-xyz789"
// - user_id: null
// - user_type: "anonymous"
// 2. User signs up / logs in
analytics.identify(12345, {
email: 'user@example.com',
name: 'John Doe',
distinctId: 'user-uuid-from-backend-12345' // Optional: Your custom distinct_id
});
// β distinct_id: "user-uuid-from-backend-12345" (transitioned)
// β user_id: 12345
// β user_type: "identified"
// β is_identified: true
// β previous_distinct_id: "anon-abc123def-xyz789" (for tracking transition)
// User continues browsing (all events now include identified data)
analytics.track('purchase_completed', {
order_id: 'ORD-789',
amount: 99.99
});
// Event includes:
// - distinct_id: "user-uuid-from-backend-12345"
// - user_id: 12345
// - user_type: "identified"
```
### Distinct ID Options
#### Option 1: Use Custom Distinct ID from Backend (Recommended)
```javascript
// On signup/login, provide your own distinct_id
analytics.identify(12345, {
email: 'user@example.com',
name: 'John Doe',
distinctId: 'user-uuid-abc123-from-backend' // Your custom distinct_id
});
// β distinct_id: "user-uuid-abc123-from-backend"
```
**Use Case:** When you have your own user identification system with UUIDs or distinct IDs in your database.
#### Option 2: Keep Fingerprint-Based Distinct ID
```javascript
// Don't provide distinctId - keeps the fingerprint-based one
analytics.identify(12345, {
email: 'user@example.com',
name: 'John Doe'
});
// β distinct_id: "anon-abc123def-xyz789" (stays the same)
```
**Use Case:** When you want consistent tracking across anonymous and identified states without managing distinct IDs.
### Automatic Data Included in All Events
Every event automatically includes these user identification fields:
```javascript
{
distinct_id: "user-uuid-from-backend" or "anon-{hash}",
user_id: 12345 or null,
user_id_component: 12345 or "anonymous",
user_type: "identified" or "anonymous",
is_anonymous: false or true,
is_identified: true or false
}
```
### Tracking Anonymous to Identified Transition
When a user transitions from anonymous to identified, the identify event includes:
```javascript
{
distinct_id: "user-uuid-from-backend-12345", // New distinct_id
user_id: 12345,
user_type: "identified",
previous_distinct_id: "anon-abc123def-xyz789" // Previous anonymous distinct_id
}
```
This allows you to link anonymous activity to the identified user in your analytics.
### Full Example: E-commerce User Journey
```javascript
// Initialize SDK
const analytics = new WebSparksAnalytics({
apiKey: 'your-key',
projectId: 'your-project'
});
// 1. Anonymous user browses products
analytics.track('product_viewed', {
product_id: 'PRODUCT-123',
category: 'Electronics'
});
// distinct_id: "anon-abc123-xyz"
// user_type: "anonymous"
analytics.track('add_to_cart', {
product_id: 'PRODUCT-123',
price: 99.99
});
// distinct_id: "anon-abc123-xyz"
// user_type: "anonymous"
// 2. User creates account / logs in
async function handleSignup(user) {
await analytics.identify(user.id, {
email: user.email,
name: user.name,
distinctId: user.distinct_id // From your backend
});
analytics.track('user_signed_up', {
signup_method: 'email',
referral_source: 'google'
});
}
// β User transitions to identified
// β distinct_id: "user-uuid-from-backend"
// β previous_distinct_id: "anon-abc123-xyz"
// 3. Identified user completes purchase
analytics.track('purchase_completed', {
order_id: 'ORD-789',
total: 99.99,
items: 1
});
// distinct_id: "user-uuid-from-backend"
// user_id: 12345
// user_type: "identified"
// 4. User logs out
analytics.reset();
// β Clears user_id
// β Creates new anonymous distinct_id
// β New session starts
```
### Benefits
β
**Track anonymous users across sessions** - Using fingerprint-based distinct_id
β
**Link anonymous activity to identified users** - Via previous_distinct_id field
β
**Use your own distinct IDs** - Optionally provide custom distinct_id from backend
β
**Consistent tracking** - Works seamlessly across anonymous and identified states
β
**No data loss** - All events tracked from first visit to conversion
---
## Session Management
### What is a Session?
A **session** represents a single visit to your website/app. It includes:
- Unique `sessionId`
- Session start time
- User ID (if identified)
- All events during that visit
### Session Lifecycle
```
1. User opens page
β
2. SDK initializes
β
3. Check localStorage for existing session
β
βββ Session found β Restore session (same sessionId)
β βββ No session_start event (continuing session)
β
βββ No session found β Create new session
βββ Generate new sessionId
βββ Track session_start event
βββ Save to localStorage
β
4. User interacts (events tracked with sessionId)
β
5. User closes tab/browser
β
6. Track session_end event
βββ Clear localStorage
```
### Session Persistence
Sessions persist across:
- β
Page refreshes
- β
Navigation within site
- β
Browser back/forward
- β
Tab switching
- β
Browser minimize
Sessions end when:
- β Tab is closed
- β Browser is closed
- β `analytics.reset()` is called
- β `analytics.endSession()` is called
- β localStorage is cleared
### Session Storage
Session data is stored in `localStorage` with key:
```
websparks_analytics_session
```
Stored data:
```javascript
{
sessionId: "1704123456789-abc123def",
startTime: 1704123456789,
userId: 30 // if identified
}
```
### Session Start
**β¨ Automatic Tracking (Recommended):**
```javascript
// When SDK initializes - session_start is AUTOMATICALLY tracked
const analytics = new WebSparksAnalytics({
apiKey: 'your-key',
projectId: 'your-project'
});
// β Automatically calls: session('start') for NEW sessions
// β On page refresh: Restores existing session (NO new session_start)
```
**How it works:**
- **First visit / New tab**: New session created β `session('start')` automatically tracked
- **Page refresh / Navigation**: Existing session restored β No new `session_start`
- **After `reset()`**: New session created β `session('start')` automatically tracked
**Manual Tracking (Rare):**
```javascript
// Only for custom session management flows
analytics.session('start', {
custom_property: 'value'
});
```
### Session End
**Automatic Tracking** (Default):
```javascript
const analytics = new WebSparksAnalytics({
apiKey: 'your-key',
projectId: 'your-project',
trackSessionEnd: true // default
});
// Now when user closes tab/browser:
// β Automatically tracks: session_end
// β Includes: sessionDuration, sessionId, etc.
```
**Manual Tracking:**
```javascript
// Method 1: Using endSession()
analytics.endSession({ reason: 'logout' });
// Method 2: Using sessionEnd() alias
analytics.sessionEnd({ reason: 'logout' });
// Method 3: Using track()
analytics.track('session_end', {
reason: 'manual',
custom_data: 'value'
});
```
### Session End Detection
The SDK automatically detects tab/browser close using:
1. **`beforeunload` event** - Fires when tab/browser closing
2. **`pagehide` event** - More reliable on mobile (iOS Safari)
3. **`freeze` event** - Safari iOS when page is frozen
**Implementation:**
```javascript
// Automatic - just enable it
const analytics = new WebSparksAnalytics({
trackSessionEnd: true // Listens for tab/browser close
});
// When user closes tab:
// β session_end event sent via navigator.sendBeacon
// β Session cleared from localStorage
// β sessionId included in event
```
### Session ID
Every session has a unique ID:
```javascript
// Format: timestamp-randomstring
// Example: "1704123456789-xyz789abc"
```
**Accessing Session ID:**
```javascript
// Session ID is private, but included in all events
analytics.track('my_event', {
// Custom properties
});
// β Event payload includes: sessionId automatically
```
### Session Duration
Session duration is calculated on `session_end`:
```javascript
analytics.endSession();
// Sends:
{
sessionDuration: 300000, // 300,000ms = 5 minutes
sessionDurationMinutes: 5, // 5 minutes
sessionDurationSeconds: 300 // 300 seconds
}
```
### Session Examples
**Example 1: Normal Session Flow**
```javascript
// Page load (first visit)
const analytics = new WebSparksAnalytics({ ... });
// β β¨ Automatically calls: session('start')
// β session_start tracked (sessionId: "1234-abc")
// User clicks button
analytics.track('button_clicked');
// β Includes sessionId: "1234-abc"
// User refreshes page
// β SDK restores session "1234-abc" from localStorage
// β β¨ No new session_start (session continues)
// User closes tab
// β session_end (sessionId: "1234-abc", duration: 180s)
```
**Example 2: Multi-Tab Session**
```javascript
// Tab 1: User opens page
const analytics1 = new WebSparksAnalytics({ ... });
// β β¨ Automatically calls: session('start')
// β session_start (sessionId: "1234-abc")
// Tab 2: User opens same site in new tab
const analytics2 = new WebSparksAnalytics({ ... });
// β β¨ Automatically calls: session('start')
// β session_start (sessionId: "5678-def") - NEW session (different tab = different session)
// User closes Tab 1
// β session_end (sessionId: "1234-abc")
// Tab 2 still running
// β sessionId: "5678-def" continues
```
**Example 3: User Login Mid-Session**
```javascript
// User arrives (anonymous)
const analytics = new WebSparksAnalytics({ ... });
// β β¨ Automatically calls: session('start')
// β session_start (sessionId: "1234-abc", userId: null)
// User logs in
analytics.identify(789, { name: 'John Doe' });
// β Now all events include userId: 789
// User clicks
analytics.track('button_clicked');
// β sessionId: "1234-abc", userId: 789
// User logs out
analytics.reset();
// β session_end (old session)
// β β¨ Automatically calls: session('start')
// β session_start (new session, userId: null)
```
**Example 4: Session Timeout**
```javascript
// Implement custom timeout
let inactivityTimer;
const SESSION_TIMEOUT = 30 * 60 * 1000; // 30 minutes
function resetTimer() {
clearTimeout(inactivityTimer);
inactivityTimer = setTimeout(() => {
analytics.endSession({
reason: 'inactivity_timeout',
timeout_duration: SESSION_TIMEOUT
});
// Optionally start new session
analytics.reset();
}, SESSION_TIMEOUT);
}
// Reset timer on user activity
window.addEventListener('mousemove', resetTimer);
window.addEventListener('keypress', resetTimer);
window.addEventListener('click', resetTimer);
window.addEventListener('scroll', resetTimer);
// Start timer on init
resetTimer();
```
### Disable Session End Tracking
```javascript
const analytics = new WebSparksAnalytics({
apiKey: 'your-key',
projectId: 'your-project',
trackSessionEnd: false // Disable automatic tracking
});
// Now session_end won't fire automatically
// You must call it manually:
analytics.endSession();
```
---
## Data Collected
### Automatic Data Collection
Every event automatically includes:
#### 1. Event Metadata
```javascript
{
event_type: 'track', // 'track', 'identify', 'page', or 'session'
event_name: 'button_clicked', // Your event name
timestamp: '2025-10-10T...', // ISO timestamp
sessionId: '1234-abc', // Current session ID
userId: 123 // User ID (if identified)
}
```
#### 2. Browser Fingerprint
```javascript
{
fingerprint: {
userAgent: 'Mozilla/5.0...',
browserName: 'Chrome',
browserVersion: '120.0.6099.109',
osName: 'Windows 10/11',
deviceType: 'Desktop',
platform: 'Win32',
language: 'en-US',
languages: ['en-US', 'en'],
screenResolution: '1920x1080',
screenColorDepth: 24,
timezone: 'Asia/Dhaka',
timezoneOffset: -360,
deviceMemory: 8, // GB
hardwareConcurrency: 8, // CPU cores
cookieEnabled: true,
doNotTrack: null,
vendor: 'Google Inc.'
}
}
```
#### 3. Location Data (if available)
```javascript
{
location: {
latitude: 23.8103,
longitude: 90.4125,
accuracy: 50 // meters
}
}
```
#### 4. Network Data
```javascript
{
clientIp: '103.45.67.89' // Public IP address
}
```
#### 5. Page Data (for page views)
```javascript
{
url: 'https://example.com/products/123',
path: '/products/123',
name: 'Product Page'
}
```
### Data Privacy
All data collection is transparent and includes:
- **No PII by default** - Unless you explicitly pass it
- **Browser fingerprinting** - For fraud detection
- **IP addresses** - For geolocation and analytics
- **GPS location** - Only if `trackLocation: true` and user grants permission
---
## Examples
### E-Commerce Tracking
```javascript
const analytics = new WebSparksAnalytics({
apiKey: 'ecommerce-key',
projectId: 'ecommerce-project'
});
// Product viewed
analytics.track('product_viewed', {
product_id: 'SKU-12345',
product_name: 'Winter Jacket',
category: 'Clothing',
price: 99.99,
currency: 'BDT'
});
// Add to cart
analytics.track('add_to_cart', {
product_id: 'SKU-12345',
quantity: 2,
price: 99.99,
cart_total: 199.98
});
// Checkout started
analytics.track('checkout_started', {
cart_total: 199.98,
items_count: 2,
shipping_method: 'express'
});
// Purchase completed
analytics.track('purchase_completed', {
order_id: 'ORD-789',
total_amount: 299.99,
items_count: 2,
payment_method: 'credit_card',
shipping_address: 'Dhaka, Bangladesh'
});
```
### Donation Platform Tracking
```javascript
const analytics = new WebSparksAnalytics({
apiKey: 'donation-key',
projectId: 'donation-project'
});
// Donation started
analytics.track('donation_started', {
amount: 1000,
currency: 'BDT',
campaign: 'winter_shelter',
recurring: false
});
// Payment method selected
analytics.track('payment_method_selected', {
method: 'bkash',
amount: 1000
});
// Donation completed
analytics.track('donation_completed', {
donation_id: 'DON-456',
amount: 1000,
currency: 'BDT',
campaign: 'winter_shelter',
payment_method: 'bkash',
transaction_id: 'TXN-789'
});
// Thank you page viewed
analytics.page('Thank You', {
donation_id: 'DON-456',
amount: 1000
});
```
### SaaS Application Tracking
```javascript
const analytics = new WebSparksAnalytics({
apiKey: 'saas-key',
projectId: 'saas-project'
});
// User signup
analytics.identify(789, {
email: 'user@example.com',
plan: 'free',
signup_date: new Date().toISOString()
});
// Feature used
analytics.track('feature_used', {
feature_name: 'export_pdf',
usage_count: 5
});
// Upgrade initiated
analytics.track('upgrade_initiated', {
from_plan: 'free',
to_plan: 'premium',
billing_cycle: 'monthly'
});
// Subscription upgraded
analytics.identify(789, {
plan: 'premium',
billing_cycle: 'monthly',
mrr: 19.99
});
analytics.track('subscription_upgraded', {
from_plan: 'free',
to_plan: 'premium',
payment_method: 'stripe'
});
```
### React Application
```javascript
import { useEffect } from 'react';
import WebSparksAnalytics from '@websparks/analytics-sdk';
// Initialize once
const analytics = new WebSparksAnalytics({
apiKey: process.env.REACT_APP_ANALYTICS_KEY,
projectId: process.env.REACT_APP_PROJECT_ID,
debug: process.env.NODE_ENV === 'development'
});
// Track page views
function ProductPage({ productId }) {
useEffect(() => {
analytics.page('Product Detail', {
product_id: productId
});
}, [productId]);
const handleAddToCart = () => {
analytics.track('add_to_cart', {
product_id: productId,
source: 'product_page'
});
};
return (
<div>
<button onClick={handleAddToCart}>Add to Cart</button>
</div>
);
}
// Track user login
function LoginPage() {
const handleLogin = async (user) => {
await analytics.identify(user.id, {
email: user.email,
name: user.name
});
analytics.track('user_logged_in', {
method: 'email_password'
});
};
return <LoginForm onSuccess={handleLogin} />;
}
// Track user logout
function LogoutButton() {
const handleLogout = async () => {
await analytics.endSession({ reason: 'user_logout' });
analytics.reset();
// Your logout logic...
};
return <button onClick={handleLogout}>Logout</button>;
}
```
### Vue Application
```javascript
import WebSparksAnalytics from '@websparks/analytics-sdk';
// main.js
const analytics = new WebSparksAnalytics({
apiKey: import.meta.env.VITE_ANALYTICS_KEY,
projectId: import.meta.env.VITE_PROJECT_ID
});
// Make available globally
app.config.globalProperties.$analytics = analytics;
// Router tracking
router.afterEach((to, from) => {
analytics.page(to.name, {
path: to.path,
previous_page: from.name
});
});
// Component usage
export default {
methods: {
trackButtonClick() {
this.$analytics.track('button_clicked', {
button_name: 'subscribe',
page: this.$route.name
});
}
}
};
```
### Next.js Application
```javascript
// lib/analytics.js
import WebSparksAnalytics from '@websparks/analytics-sdk';
export const analytics = new WebSparksAnalytics({
apiKey: process.env.NEXT_PUBLIC_ANALYTICS_KEY,
projectId: process.env.NEXT_PUBLIC_PROJECT_ID
});
// pages/_app.js
import { useEffect } from 'react';
import { useRouter } from 'next/router';
import { analytics } from '../lib/analytics';
function MyApp({ Component, pageProps }) {
const router = useRouter();
useEffect(() => {
const handleRouteChange = (url) => {
analytics.page(url);
};
router.events.on('routeChangeComplete', handleRouteChange);
return () => {
router.events.off('routeChangeComplete', handleRouteChange);
};
}, [router.events]);
return <Component {...pageProps} />;
}
// pages/product/[id].js
import { analytics } from '../../lib/analytics';
export default function Product({ product }) {
useEffect(() => {
analytics.track('product_viewed', {
product_id: product.id,
product_name: product.name
});
}, [product]);
return <div>{product.name}</div>;
}
```
---
## TypeScript Support
The SDK is written in TypeScript and includes full type definitions.
### Import Types
```typescript
import WebSparksAnalytics, {
AnalyticsConfig,
EventData,
UserProperties,
FingerprintData,
LocationData
} from 'websparks-analytics-sdk';
```
### Type Definitions
```typescript
interface AnalyticsConfig {
apiKey: string;
projectId: string;
userId?: number;
debug?: boolean;
trackSessionEnd?: boolean;
trackLocation?: boolean;
}
interface EventData {
[key: string]: any;
}
interface UserProperties {
[key: string]: any;
}
interface FingerprintData {
userAgent: string;
language: string;
languages: string[];
platform: string;
screenResolution: string;
screenColorDepth: number;
timezone: string;
timezoneOffset: number;
deviceMemory?: number;
hardwareConcurrency?: number;
cookieEnabled: boolean;
doNotTrack: string | null;
vendor: string;
browserName: string;
browserVersion: string;
osName: string;
deviceType: string;
}
interface LocationData {
latitude: number;
longitude: number;
accuracy: number;
}
```
### Usage with TypeScript
```typescript
// Typed configuration
const config: AnalyticsConfig = {
apiKey: 'your-api-key',
projectId: 'your-project-id',
userId: 30,
debug: true
};
const analytics = new WebSparksAnalytics(config);
// Typed event data
const eventData: EventData = {
product_id: 'SKU-12345',
price: 99.99
};
analytics.track('product_viewed', eventData);
// Typed user properties
const userProps: UserProperties = {
name: 'John Doe',
email: 'john@example.com',
plan: 'premium'
};
analytics.identify(123, userProps);
// Location with type
analytics.requestLocation()
.then((location: LocationData) => {
console.log(location.latitude, location.longitude);
});
```
---
## Browser Support
### Supported Browsers
- β
Chrome/Edge (latest 2 versions)
- β
Firefox (latest 2 versions)
- β
Safari (latest 2 versions)
- β
Opera (latest 2 versions)
- β
iOS Safari (latest 2 versions)
- β
Chrome Mobile (latest 2 versions)
### Browser Features Used
- **localStorage** - For session persistence
- **navigator.sendBeacon** - For reliable session_end tracking
- **Geolocation API** - For GPS location (optional)
- **WebRTC** - For local IP detection
- **Fetch API** - For API requests
### Fallbacks
- If `sendBeacon` unavailable β Falls back to synchronous XHR
- If `localStorage` unavailable β Session won't persist across refreshes
- If `Geolocation` unavailable β Location features disabled
---
## Debug Mode
### Enable Debug Mode
```javascript
const analytics = new WebSparksAnalytics({
apiKey: 'your-key',
projectId: 'your-project',
debug: true // Enable detailed logging
});
```
### Console Output
When debug mode is enabled, you'll see:
```
β API Key is valid
[Websparks Analytics] New session started: 1704123456789-abc123
[Websparks Analytics] Public IP: 103.45.67.89
[Websparks Analytics] Event sent successfully
[Websparks Analytics] Sending session_end event for session: 1704123456789-abc123
[Websparks Analytics] sendBeacon result: true
```
### Debug Information Includes:
- β
API key validation results
- β
Session creation/restoration
- β
IP address detection (WebRTC + external)
- β
Geolocation requests/results
- β
All event tracking
- β
Session end tracking
- β
Error messages
### Production vs Development
```javascript
const analytics = new WebSparksAnalytics({
apiKey: 'your-key',
projectId: 'your-project',
debug: process.env.NODE_ENV === 'development' // Only in dev
});
```
---
## Privacy & GDPR Compliance
### Data Collection
The SDK collects:
- β
Browser fingerprint (for fraud detection)
- β
IP address (for geolocation)
- β
GPS location (only if `trackLocation: true` and user consents)
- β
Page views and custom events
- β
Session data
### User Consent
```javascript
// Wait for consent before initializing
let analytics;
function handleConsentGranted() {
analytics = new WebSparksAnalytics({
apiKey: 'your-key',
projectId: 'your-project',
trackLocation: false // Don't request GPS by default
});
}
// Show consent banner
showConsentBanner({
onAccept: handleConsentGranted
});
```
### Disable Location Tracking
```javascript
const analytics = new WebSparksAnalytics({
apiKey: 'your-key',
projectId: 'your-project',
trackLocation: false // Never request GPS permission
});
```
### Privacy Best Practices
1. β
Have a clear privacy policy
2. β
Get user consent where required (GDPR, CCPA)
3. β
Set `trackLocation: false` unless GPS is needed
4. β
Don't send PII in event properties
5. β
Anonymize user data when possible
---
## API Key Setup
### Get Your API Key
1. Login to WebSparks backend admin panel
2. Navigate to Projects
3. Create or select a project
4. Copy your API key and project ID
### Use in SDK
```javascript
const analytics = new WebSparksAnalytics({
apiKey: 'EBvk5C...............', // From backend
projectId: 'websparks-....-...-fui893j' // From backend
});
```
### API Key Validation
The SDK automatically validates your API key on initialization:
```javascript
// Valid key
β API Key is valid
// Invalid key
β API Key is invalid
Message: Invalid or inactive API key
```
### Security
- β
API keys are validated on every initialization
- β
Invalid keys prevent all tracking
- β
Keys are sent securely via HTTPS
- β οΈ Don't commit API keys to public repositories
```javascript
// Use environment variables
const analytics = new WebSparksAnalytics({
apiKey: process.env.WEBSPARKS_API_KEY,
projectId: process.env.WEBSPARKS_PROJECT_ID
});
```
---
## Development
### Install Dependencies
```bash
npm install
```
### Build the SDK
```bash
npm run build
```
### Run Tests
```bash
npm test
```
### Start Dev Server
```bash
npm run dev
```
---
## Troubleshooting
### Session End Not Firing
**Problem:** `session_end` event not being tracked
**Solutions:**
1. Ensure `trackSessionEnd: true` in config
2. Enable `debug: true` to see console logs
3. Check browser console for errors
4. Test by actually closing tab (not just switching tabs)
```javascript
const analytics = new WebSparksAnalytics({
apiKey: 'your-key',
projectId: 'your-project',
trackSessionEnd: true, // Make sure this is true
debug: true // See what's happening
});
```
### Events Not Being Tracked
**Problem:** Events not showing in backend
**Solutions:**
1. Check API key is valid
2. Check network tab for failed requests
3. Enable debug mode
4. Verify project ID is correct
### Location Not Working
**Problem:** GPS location not being collected
**Solutions:**
1. Set `trackLocation: true` in config
2. User must grant permission
3. HTTPS required (not HTTP)
4. Check browser console for permission errors
```javascript
// Correct setup
const analytics = new WebSparksAnalytics({
apiKey: 'your-key',
projectId: 'your-project',
trackLocation: true // Enable location
});
// Or request manually
analytics.requestLocation()
.then(location => console.log('Got location:', location))
.catch(err => console.log('Permission denied:', err));
```
---
## Support
- π **Website:** [https://websparks.ai](https://websparks.ai)
- π **Documentation:** [https://docs.websparks.ai](https://docs.websparks.ai)
- π§ **Email:** support@websparks.ai
- π¬ **Discord:** [Join our community](https://discord.gg/websparks)
---
## License
MIT License - See LICENSE file for details
---
## Changelog
### Version 1.4.0 (2025-10-11)
- β¨ **Anonymous vs Identified User Tracking** - Automatic tracking of anonymous and identified users
- β¨ **Fingerprint-Based Distinct IDs** - Anonymous users get distinct_id based on device fingerprint
- β¨ **Custom Distinct IDs** - Support for custom distinct_id from backend via `identify()` method
- β¨ **User Transition Tracking** - Track when users transition from anonymous to identified
- β¨ **User Type Classification** - All events include `user_type`, `is_anonymous`, `is_identified`
- β¨ **Previous Distinct ID** - Identify events include `previous_distinct_id` for linking
- π Added comprehensive "Anonymous vs Identified Users" section to README
- π§ Enhanced `identify()` method to accept `distinctId` parameter
- π§ Updated `getUserIdentificationData()` to use custom distinct IDs
### Version 1.2.0 (2025-10-10)
- β¨ Added dedicated `session()` method for session tracking
- β¨ New `event_type: 'session'` for session events (separate from `'track'`)
- β¨ Session events now use dedicated event type instead of track
- β¨ Improved session tracking architecture
- π Updated README with `session()` method documentation
- π§ Refactored all session tracking to use `session()` internally
### Version 1.1.0 (2025-10-09)
- β¨ Enhanced session end tracking with multiple event listeners
- β¨ Added `sessionEnd()` alias for `endSession()`
- β¨ Improved mobile browser support (iOS Safari)
- β¨ Added `endReason` to session_end events
- β¨ Better duplicate prevention for session_end
- π Fixed session_end not firing on tab switch
- π Fixed pagehide event for back/forward cache
- π Comprehensive README documentation
### Version 1.0.0 (2025-10-01)
- π Initial release
- β¨ Event tracking (track, identify, page)
- β¨ Browser fingerprinting
- β¨ IP detection (WebRTC + external services)
- β¨ Geolocation (GPS + IP-based)
- β¨ Session management with localStorage
- β¨ API key validation
- β¨ Debug mode with comprehensive logging
---
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
---
**Made with β€οΈ by WebSparks Team**