npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

websparks-analytics-sdk

v1.5.0

Published

JavaScript SDK for WebSparks analytics tracking with geolocation, fingerprinting, and session management

Downloads

25

Readme

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

npm install websparks-analytics-sdk

Quick Start

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: '[email protected]'
});

// Track page views
analytics.page('Home Page', {
  section: 'landing'
});

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: '[email protected]'
};

analytics.identify(123, userProps);

// Track page views
analytics.page('Home Page', { section: 'landing' });

Table of Contents

  1. Initialization
  2. Configuration Options
  3. API Methods
  4. Anonymous vs Identified Users
  5. Session Management
  6. Data Collected
  7. Examples
  8. TypeScript Support
  9. Browser Support
  10. Privacy

Initialization

Basic Initialization

JavaScript:

import WebSparksAnalytics from 'websparks-analytics-sdk';

const analytics = new WebSparksAnalytics({
  apiKey: 'your-api-key-here',
  projectId: 'your-project-id-here'
});

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:

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:

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)

import WebSparksAnalytics from 'websparks-analytics-sdk';

const analytics = new WebSparksAnalytics({
  apiKey: 'prod-api-key',
  projectId: 'prod-project-id'
});

Development Setup (Full Debugging)

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

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:

// 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:

// Basic identification
analytics.identify(123);

// With user properties
analytics.identify(456, {
  name: 'Jane Smith',
  email: '[email protected]',
  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: '[email protected]',
  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:

// 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:

// 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:

// 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:

// 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:

// 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:

interface LocationData {
  latitude: number;
  longitude: number;
  accuracy: number; // in meters
}

Examples:

// 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

// 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: '[email protected]',
  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)

// On signup/login, provide your own distinct_id
analytics.identify(12345, {
  email: '[email protected]',
  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

// Don't provide distinctId - keeps the fingerprint-based one
analytics.identify(12345, {
  email: '[email protected]',
  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:

{
  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:

{
  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

// 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:

{
  sessionId: "1704123456789-abc123def",
  startTime: 1704123456789,
  userId: 30 // if identified
}

Session Start

✨ Automatic Tracking (Recommended):

// 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):

// Only for custom session management flows
analytics.session('start', {
  custom_property: 'value'
});

Session End

Automatic Tracking (Default):

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:

// 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:

// 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:

// Format: timestamp-randomstring
// Example: "1704123456789-xyz789abc"

Accessing Session ID:

// 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:

analytics.endSession();
// Sends:
{
  sessionDuration: 300000,        // 300,000ms = 5 minutes
  sessionDurationMinutes: 5,      // 5 minutes
  sessionDurationSeconds: 300     // 300 seconds
}

Session Examples

Example 1: Normal Session Flow

// 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

// 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

// 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

// 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

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

{
  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

{
  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)

{
  location: {
    latitude: 23.8103,
    longitude: 90.4125,
    accuracy: 50  // meters
  }
}

4. Network Data

{
  clientIp: '103.45.67.89'  // Public IP address
}

5. Page Data (for page views)

{
  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

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

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

const analytics = new WebSparksAnalytics({
  apiKey: 'saas-key',
  projectId: 'saas-project'
});

// User signup
analytics.identify(789, {
  email: '[email protected]',
  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

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

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

// 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

import WebSparksAnalytics, {
  AnalyticsConfig,
  EventData,
  UserProperties,
  FingerprintData,
  LocationData
} from 'websparks-analytics-sdk';

Type Definitions

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

// 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: '[email protected]',
  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

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

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

// 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

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

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:

// 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
// Use environment variables
const analytics = new WebSparksAnalytics({
  apiKey: process.env.WEBSPARKS_API_KEY,
  projectId: process.env.WEBSPARKS_PROJECT_ID
});

Development

Install Dependencies

npm install

Build the SDK

npm run build

Run Tests

npm test

Start Dev Server

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)
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
// 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


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