JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 44
  • Score
    100M100P100Q59793F
  • License MIT

Universal user activity tracking library

Package Exports

  • @vigneshwaranbs/activity-tracker
  • @vigneshwaranbs/activity-tracker/dist/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@vigneshwaranbs/activity-tracker) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

๐ŸŽฏ Activity Tracker

Universal user activity tracking library for web applications

npm version npm downloads License: MIT

Track user activity across your web applications with automatic inactivity detection, cross-tab synchronization, and real-time heartbeat monitoring.


โœจ Features

  • ๐Ÿ–ฑ๏ธ Activity Detection - Tracks mouse, keyboard, scroll, and touch events
  • โฑ๏ธ Inactivity Monitoring - Automatic detection after configurable timeout (default: 15 minutes)
  • ๐Ÿ’“ Heartbeat System - Regular server sync (default: 60 seconds)
  • ๐Ÿ”„ Cross-Tab Sync - Real-time activity sync across browser tabs (same origin)
  • ๐ŸŒ Cross-Domain Support - Server-based sync for different domains
  • ๐Ÿ“Š Page Visibility Tracking - Detects when users switch tabs
  • ๐ŸŽฏ Window Focus Tracking - Monitors when users switch windows
  • ๐Ÿ”’ TypeScript Support - Full type definitions included
  • โšก Performance Optimized - Debounced events, passive listeners
  • ๐Ÿ”ง Highly Configurable - 20+ configuration options
  • ๐Ÿ” Retry Logic - Automatic retry for failed heartbeats
  • ๐Ÿ“ฑ Framework Agnostic - Works with React, Vue, Angular, vanilla JS

๐Ÿ“ฆ Installation

npm install @vigneshwaranbs/activity-tracker
yarn add @vigneshwaranbs/activity-tracker
pnpm add @vigneshwaranbs/activity-tracker

๐Ÿš€ Quick Start

Basic Usage

import { ActivityTracker } from '@vigneshwaranbs/activity-tracker';

// Initialize tracker
const tracker = new ActivityTracker({
  appId: 'my-app',
  apiEndpoint: 'https://api.yourapp.com/api/user/activity',
  getAuthToken: () => localStorage.getItem('userId') || ''
});

// Start tracking
tracker.start();

// Stop tracking (cleanup)
tracker.stop();

React Example

import { useEffect } from 'react';
import { ActivityTracker } from '@vigneshwaranbs/activity-tracker';

function App() {
  useEffect(() => {
    const tracker = new ActivityTracker({
      appId: 'my-react-app',
      apiEndpoint: 'https://api.yourapp.com/api/user/activity',
      getAuthToken: () => localStorage.getItem('userId') || '',
      
      // Callbacks
      onActive: (data) => {
        console.log('โœ… User is active', data);
      },
      onInactive: (data) => {
        console.warn('โš ๏ธ User inactive for', data.inactivityDuration / 60000, 'minutes');
      }
    });

    tracker.start();

    return () => tracker.stop(); // Cleanup on unmount
  }, []);

  return <div>Your App</div>;
}

Vue Example

<script setup>
import { onMounted, onUnmounted } from 'vue';
import { ActivityTracker } from '@vigneshwaranbs/activity-tracker';

let tracker;

onMounted(() => {
  tracker = new ActivityTracker({
    appId: 'my-vue-app',
    apiEndpoint: 'https://api.yourapp.com/api/user/activity',
    getAuthToken: () => localStorage.getItem('userId') || ''
  });
  
  tracker.start();
});

onUnmounted(() => {
  tracker?.stop();
});
</script>

โš™๏ธ Configuration

Required Options

Option Type Description
appId string Unique identifier for your application
apiEndpoint string API endpoint URL for heartbeat requests
userId or authToken or getAuthToken string | function User identification

Optional Options

Option Type Default Description
inactivityTimeout number 900000 (15 min) Time before marking user inactive (ms)
heartbeatInterval number 60000 (1 min) Interval between heartbeat requests (ms)
checkInterval number 10000 (10 sec) Interval for checking inactivity (ms)
crossTabSync boolean true Enable cross-tab activity sync
serverSync boolean true Enable server heartbeat sync
trackPageVisibility boolean true Track page visibility changes
trackWindowFocus boolean true Track window focus/blur
debounceDelay number 1000 (1 sec) Debounce delay for activity events (ms)
retryAttempts number 3 Number of retry attempts for failed heartbeats
logLevel string 'warn' Log level: 'debug', 'info', 'warn', 'error', 'none'
customHeaders object {} Custom HTTP headers for API requests
metadata object {} Custom metadata to include in heartbeat

Callbacks

Callback Parameters Description
onActive (data: ActivityData) => void Called when user becomes active
onInactive (data: InactivityData) => void Called when user becomes inactive
onHeartbeatSuccess (response: HeartbeatResponse) => void Called on successful heartbeat
onHeartbeatError (error: Error) => void Called on heartbeat failure

๐Ÿ“– Advanced Examples

With Custom Metadata

const tracker = new ActivityTracker({
  appId: 'my-app',
  apiEndpoint: 'https://api.yourapp.com/api/user/activity',
  getAuthToken: () => localStorage.getItem('userId') || '',
  
  // Custom metadata
  metadata: {
    userAgent: navigator.userAgent,
    screenResolution: `${window.screen.width}x${window.screen.height}`,
    timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
    language: navigator.language,
    referrer: document.referrer
  }
});

tracker.start();

With Custom Headers (Authentication)

const tracker = new ActivityTracker({
  appId: 'my-app',
  apiEndpoint: 'https://api.yourapp.com/api/user/activity',
  userId: 'user-123',
  
  // Custom headers for API authentication
  customHeaders: {
    'Authorization': `Bearer ${localStorage.getItem('authToken')}`,
    'X-API-Key': 'your-api-key',
    'X-App-Version': '1.0.0'
  }
});

tracker.start();

With All Callbacks

const tracker = new ActivityTracker({
  appId: 'my-app',
  apiEndpoint: 'https://api.yourapp.com/api/user/activity',
  getAuthToken: () => localStorage.getItem('userId') || '',
  
  // Activity callbacks
  onActive: (data) => {
    console.log('โœ… User is active');
    console.log('Last activity:', new Date(data.lastActivityTime));
    // Send to analytics
    analytics.track('user_active', data);
  },
  
  onInactive: (data) => {
    console.warn('โš ๏ธ User inactive');
    console.warn('Duration:', data.inactivityDuration / 60000, 'minutes');
    // Show warning modal
    showInactivityWarning();
  },
  
  onHeartbeatSuccess: (response) => {
    console.log('๐Ÿ’“ Heartbeat sent successfully');
    // Handle server response
    if (response.action === 'logout') {
      logout();
    }
  },
  
  onHeartbeatError: (error) => {
    console.error('โŒ Heartbeat failed:', error.message);
    // Send to error tracking (e.g., Sentry)
    Sentry.captureException(error);
  }
});

tracker.start();

Development vs Production Config

const isDevelopment = process.env.NODE_ENV === 'development';

const tracker = new ActivityTracker({
  appId: isDevelopment ? 'my-app-dev' : 'my-app',
  apiEndpoint: isDevelopment 
    ? 'http://localhost:4000/api/user/activity'
    : 'https://api.yourapp.com/api/user/activity',
  getAuthToken: () => localStorage.getItem('userId') || '',
  
  // Development: shorter intervals for testing
  inactivityTimeout: isDevelopment ? 2 * 60 * 1000 : 15 * 60 * 1000,
  heartbeatInterval: isDevelopment ? 10 * 1000 : 60 * 1000,
  
  // Development: verbose logging
  logLevel: isDevelopment ? 'debug' : 'warn'
});

tracker.start();

React Custom Hook

// hooks/useActivityTracker.ts
import { useEffect, useState } from 'react';
import { ActivityTracker } from '@vigneshwaranbs/activity-tracker';

export function useActivityTracker(appId: string) {
  const [isActive, setIsActive] = useState(true);
  const [tracker, setTracker] = useState<ActivityTracker | null>(null);

  useEffect(() => {
    const activityTracker = new ActivityTracker({
      appId,
      apiEndpoint: 'https://api.yourapp.com/api/user/activity',
      getAuthToken: () => localStorage.getItem('userId') || '',
      
      onActive: () => setIsActive(true),
      onInactive: () => setIsActive(false)
    });

    activityTracker.start();
    setTracker(activityTracker);

    return () => activityTracker.stop();
  }, [appId]);

  return { isActive, tracker };
}

// Usage
function MyComponent() {
  const { isActive } = useActivityTracker('my-app');
  
  return (
    <div>
      <StatusBadge active={isActive} />
      {!isActive && <InactivityWarning />}
    </div>
  );
}

๐Ÿ”Œ Backend API Integration

Expected API Endpoint

Your backend should handle POST requests to the configured apiEndpoint:

Request:

POST /api/user/activity
Content-Type: application/json

{
  "userId": "676966c6c0b4b40f8cc2db9a",
  "appId": "my-app",
  "isActive": true,
  "lastActivityTime": 1703456789000,
  "tabVisible": true,
  "windowFocused": true,
  "metadata": {
    "userAgent": "Mozilla/5.0...",
    "platform": "MacIntel",
    "language": "en-US",
    "screenResolution": "1920x1080",
    "viewport": "1440x900",
    "timezone": "America/New_York",
    "url": "https://app.example.com/dashboard"
  }
}

Response:

{
  "success": true,
  "message": "Activity tracked successfully",
  "activeInOtherApp": false,
  "lastActivityTime": 1703456789000,
  "action": "continue" // or "logout", "warn"
}

Express.js Backend Example

// server.js
import express from 'express';
import { MongoClient, ObjectId } from 'mongodb';

const app = express();
app.use(express.json());

const MONGODB_URI = process.env.MONGODB_URI;
const client = await MongoClient.connect(MONGODB_URI);
const db = client.db('your-database');

app.post('/api/user/activity', async (req, res) => {
  try {
    const { userId, appId, isActive, lastActivityTime, tabVisible, windowFocused, metadata } = req.body;
    
    const now = new Date();
    const sessionData = {
      userId: new ObjectId(userId),
      appId,
      isActive,
      lastActivityTime: new Date(lastActivityTime),
      lastHeartbeat: now,
      tabVisible,
      windowFocused,
      metadata,
      updatedAt: now
    };
    
    // Upsert: one document per userId + appId
    await db.collection('user_sessions').updateOne(
      { userId: new ObjectId(userId), appId },
      { $set: sessionData, $setOnInsert: { createdAt: now } },
      { upsert: true }
    );
    
    res.json({ success: true, message: 'Activity tracked successfully' });
  } catch (error) {
    console.error('Activity tracking error:', error);
    res.status(500).json({ success: false, error: error.message });
  }
});

app.listen(4000, () => console.log('Server running on port 4000'));

MongoDB Schema

// Collection: user_sessions
{
  "_id": ObjectId("..."),
  "userId": ObjectId("676966c6c0b4b40f8cc2db9a"),
  "appId": "my-app",
  "isActive": true,
  "lastActivityTime": ISODate("2024-12-24T10:30:00.000Z"),
  "lastHeartbeat": ISODate("2024-12-24T10:30:00.000Z"),
  "tabVisible": true,
  "windowFocused": true,
  "metadata": {
    "userAgent": "Mozilla/5.0...",
    "platform": "MacIntel",
    "language": "en-US",
    "screenResolution": "1920x1080",
    "viewport": "1440x900",
    "timezone": "America/New_York",
    "url": "https://app.example.com/dashboard"
  },
  "createdAt": ISODate("2024-12-24T09:00:00.000Z"),
  "updatedAt": ISODate("2024-12-24T10:30:00.000Z")
}

// Recommended indexes
db.user_sessions.createIndex({ userId: 1, appId: 1 }, { unique: true });
db.user_sessions.createIndex({ lastActivityTime: -1 });
db.user_sessions.createIndex({ isActive: 1 });
db.user_sessions.createIndex({ updatedAt: -1 });

๐ŸŽฏ Use Cases

1. Session Timeout Management

const tracker = new ActivityTracker({
  appId: 'my-app',
  apiEndpoint: 'https://api.yourapp.com/api/user/activity',
  getAuthToken: () => localStorage.getItem('userId') || '',
  inactivityTimeout: 15 * 60 * 1000, // 15 minutes
  
  onInactive: (data) => {
    // Show warning modal
    showModal({
      title: 'Session Timeout Warning',
      message: `You've been inactive for ${data.inactivityDuration / 60000} minutes. Your session will expire soon.`,
      actions: ['Stay Logged In', 'Logout']
    });
  }
});

2. Multi-Tab Activity Sync

const tracker = new ActivityTracker({
  appId: 'my-app',
  apiEndpoint: 'https://api.yourapp.com/api/user/activity',
  getAuthToken: () => localStorage.getItem('userId') || '',
  crossTabSync: true, // Enable cross-tab sync
  
  onActive: () => {
    // Activity detected in any tab syncs to all tabs
    console.log('User active in this or another tab');
  }
});

3. Analytics Integration

const tracker = new ActivityTracker({
  appId: 'my-app',
  apiEndpoint: 'https://api.yourapp.com/api/user/activity',
  getAuthToken: () => localStorage.getItem('userId') || '',
  
  onActive: (data) => {
    // Track active sessions in analytics
    analytics.track('session_active', {
      userId: data.userId,
      appId: data.appId,
      timestamp: data.lastActivityTime
    });
  },
  
  onInactive: (data) => {
    // Track session end in analytics
    analytics.track('session_inactive', {
      userId: data.userId,
      duration: data.inactivityDuration
    });
  }
});

4. Real-Time User Presence

const tracker = new ActivityTracker({
  appId: 'collaboration-app',
  apiEndpoint: 'https://api.yourapp.com/api/user/activity',
  getAuthToken: () => localStorage.getItem('userId') || '',
  heartbeatInterval: 30 * 1000, // 30 seconds for real-time presence
  
  onHeartbeatSuccess: (response) => {
    // Update UI with active users from server response
    updateActiveUsersList(response.activeUsers);
  }
});

๐Ÿ” What Gets Tracked?

Activity Events

  • Mouse: mousedown, mousemove, click
  • Keyboard: keypress, keydown
  • Touch: touchstart (mobile devices)
  • Scroll: scroll (page scrolling)

Browser State

  • Page Visibility: Tab active/hidden (Visibility API)
  • Window Focus: Window focused/blurred
  • Tab Visibility: User switched tabs

Metadata (Optional)

  • User agent
  • Platform (OS)
  • Language
  • Screen resolution
  • Viewport size
  • Timezone
  • Current URL
  • Referrer
  • Custom metadata

๐Ÿ—๏ธ Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                   Browser Tab 1                         โ”‚
โ”‚  ActivityTracker Instance                               โ”‚
โ”‚    โ”œโ”€ DOM Event Listeners (mouse, keyboard, scroll)    โ”‚
โ”‚    โ”œโ”€ BroadcastChannel (cross-tab sync)                โ”‚
โ”‚    โ””โ”€ Heartbeat Timer (60s)                            โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                          โ”‚
                          โ”‚ POST /api/user/activity
                          โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                   Backend API                           โ”‚
โ”‚  Express / Next.js / Fastify                            โ”‚
โ”‚    โ””โ”€ POST /api/user/activity                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                          โ”‚
                          โ”‚ Upsert (one doc per user)
                          โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                   MongoDB Database                      โ”‚
โ”‚  Collection: user_sessions                              โ”‚
โ”‚    โ””โ”€ { userId, appId, isActive, lastActivityTime }    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โšก Performance

Optimizations

  • Debouncing: Activity events debounced by 1 second (configurable)
  • Passive Listeners: Non-blocking event listeners for scroll/touch
  • Efficient Intervals: Minimal timer overhead
  • Retry Queue: Failed heartbeats queued and retried
  • Cross-Tab Sync: BroadcastChannel for instant sync (no polling)

Benchmarks

  • Memory: ~50KB (minified + gzipped)
  • CPU: < 0.1% average
  • Network: 1 request per 60 seconds (configurable)
  • Event Overhead: < 1ms per debounced event

๐Ÿ› ๏ธ TypeScript Support

Full TypeScript definitions included:

import { 
  ActivityTracker, 
  ActivityTrackerConfig,
  ActivityData,
  InactivityData,
  HeartbeatPayload,
  HeartbeatResponse 
} from '@vigneshwaranbs/activity-tracker';

const config: ActivityTrackerConfig = {
  appId: 'my-app',
  apiEndpoint: 'https://api.yourapp.com/api/user/activity',
  getAuthToken: () => localStorage.getItem('userId') || '',
  inactivityTimeout: 15 * 60 * 1000,
  onActive: (data: ActivityData) => console.log(data),
  onInactive: (data: InactivityData) => console.warn(data)
};

const tracker = new ActivityTracker(config);
tracker.start();

๐Ÿงช Testing

Manual Testing

// Create tracker with short timeouts for testing
const tracker = new ActivityTracker({
  appId: 'test-app',
  apiEndpoint: 'http://localhost:4000/api/user/activity',
  userId: 'test-user',
  
  // Short intervals for testing
  inactivityTimeout: 30 * 1000,  // 30 seconds
  heartbeatInterval: 10 * 1000,  // 10 seconds
  checkInterval: 5 * 1000,       // 5 seconds
  
  // Verbose logging
  logLevel: 'debug',
  
  // Test callbacks
  onActive: () => console.log('โœ… ACTIVE'),
  onInactive: () => console.log('โš ๏ธ INACTIVE'),
  onHeartbeatSuccess: (res) => console.log('๐Ÿ’“ HEARTBEAT', res),
  onHeartbeatError: (err) => console.error('โŒ ERROR', err)
});

tracker.start();

// Test inactivity: Stop moving mouse for 30 seconds
// Test heartbeat: Check network tab for POST requests every 10 seconds
// Test cross-tab: Open multiple tabs and check sync

๐Ÿ“š API Reference

Constructor

new ActivityTracker(config: ActivityTrackerConfig)

Methods

Method Description
start() Start tracking user activity
stop() Stop tracking and cleanup
isUserActive() Check if user is currently active
getLastActivityTime() Get timestamp of last activity
forceHeartbeat() Manually trigger a heartbeat

Example

const tracker = new ActivityTracker({ /* config */ });

tracker.start();                        // Start tracking
const active = tracker.isUserActive();  // Check status
const lastTime = tracker.getLastActivityTime(); // Get timestamp
tracker.forceHeartbeat();              // Force sync
tracker.stop();                        // Stop and cleanup

๐Ÿค Contributing

Contributions are welcome! Please feel free to submit a Pull Request.


๐Ÿ“„ License

MIT ยฉ Vigneshwaran BS



๐Ÿ’ก Support

For questions, issues, or feature requests:


Made with โค๏ธ by Vigneshwaran BS