npm package: @powerit/cms-tracker
Description: Session recording plugin for PowerIT CMS Client using rrweb
Author: PowerIT
Latest version: 1.0.1

# @powerit/cms-tracker

Session recording plugin for PowerIT CMS Client using [rrweb](https://github.com/rrweb-io/rrweb).

## Features

- Session recording with rrweb for pixel-perfect replay
- Privacy-first design with comprehensive masking options
- GDPR-friendly CSS class-based exclusions
- Event batching and gzip compression
- Automatic session management
- Integration with @powerit/cms-analytics

## Installation

```bash
npm install @powerit/cms-tracker
```

## Usage

### Basic Setup

```javascript
import { createClient } from '@powerit/cms-client';
import { trackerPlugin } from '@powerit/cms-tracker';

const client = createClient({
  baseUrl: 'https://cms.example.com',
  apiKey: 'your-api-key'
});

// Add tracker plugin
client.use(trackerPlugin, {
  privacy: {
    maskAllInputs: true
  },
  compress: true
});

// Initialize and start recording
await client.tracker.init();
client.startRecording();
```

### With Analytics Integration

```javascript
import { createClient } from '@powerit/cms-client';
import { analyticsPlugin } from '@powerit/cms-analytics';
import { trackerPlugin } from '@powerit/cms-tracker';

const client = createClient({
  baseUrl: 'https://cms.example.com',
  apiKey: 'your-api-key'
});

// Add both plugins - tracker will link to analytics session
client.use(analyticsPlugin, { transport: 'grpc' });
client.use(trackerPlugin, { compress: true });

// Initialize both
await client.analytics.init();
await client.tracker.init();

// Track page and start recording
await client.trackPageView();
client.startRecording();
```

### Custom Events

```javascript
// Add custom event to recording
client.addCustomEvent('button_click', {
  buttonId: 'cta-hero',
  label: 'Sign Up'
});

// Identify user (after login)
client.identifyUser('user_123', {
  plan: 'premium',
  company: 'Acme Inc'
});

// Take manual snapshot
client.takeSnapshot();
```

### Recording Controls

```javascript
// Start recording
client.startRecording();

// Pause (keeps session active)
client.pauseRecording();

// Resume
client.resumeRecording();

// Stop and flush
await client.stopRecording();

// Check status
client.tracker.isRecording(); // true/false
client.tracker.isEnabled();   // true/false
```

## Configuration

### Full Configuration Example

```javascript
client.use(trackerPlugin, {
  // Transport settings
  transport: 'http',        // 'http' or 'grpc'
  transportUrl: null,       // Custom URL (defaults to client baseUrl)

  // Batching & compression
  batchSize: 50,            // Events per batch
  flushInterval: 5000,      // Auto-flush interval (ms)
  maxQueueSize: 500,        // Force flush threshold
  compress: true,           // Gzip compression

  // Checkpoints
  checkoutEveryNth: 200,    // Full snapshot every N events
  checkoutEveryNms: 300000, // Full snapshot every 5 minutes

  // Session
  sessionTimeout: 1800000,  // 30 minutes
  storageKey: 'powerit_tracker',

  // Logging
  logger: true,             // Enable console logging

  // Error handling
  onError: (error) => console.error('Tracker error:', error),

  // Privacy settings (see below)
  privacy: { ... },

  // Sampling settings (see below)
  sampling: { ... },

  // Recording settings (see below)
  recording: { ... }
});
```

### Privacy Configuration

```javascript
{
  privacy: {
    // Do Not Track
    respectDoNotTrack: true,

    // CSS classes for privacy control
    blockClass: 'rr-block',      // Element replaced with placeholder
    ignoreClass: 'rr-ignore',    // Input events not recorded
    maskTextClass: 'rr-mask',    // Text content masked

    // CSS selectors for additional control
    blockSelector: '.private-section',
    ignoreSelector: '[data-no-track]',
    maskTextSelector: '.sensitive-data',

    // Input masking (privacy-first defaults)
    maskAllInputs: true,
    maskInputOptions: {
      password: true,
      email: true,
      tel: true,
      creditCard: true,
      text: false,
      textarea: false,
      select: false
    },

    // Custom masking functions
    maskInputFn: (text, element) => '*'.repeat(text.length),
    maskTextFn: (text, element) => text.replace(/./g, '*'),

    // Page filters
    blockedPages: [
      '/admin',
      '/checkout',
      /^\/account/    // Regex pattern
    ],
    allowedPages: []  // If set, only these pages are recorded
  }
}
```

### Sampling Configuration

```javascript
{
  sampling: {
    enabled: true,
    sessionRate: 10,         // Record 10% of sessions
    mousemove: 50,           // Events per second
    mouseInteraction: true,
    scroll: 10,              // Events per second
    media: true,
    input: 'last'            // Only capture final value
  }
}
```

### Recording Configuration

```javascript
{
  recording: {
    recordCanvas: false,
    recordCrossOriginIframes: false,
    inlineStylesheet: true,
    inlineImages: false,
    collectFonts: false,
    slimDOMOptions: {
      script: true,
      comment: true,
      headFavicon: true,
      headWhitespace: true,
      headMetaDescKeywords: true,
      headMetaSocial: true,
      headMetaRobots: true,
      headMetaHttpEquiv: true,
      headMetaAuthorship: true,
      headMetaVerification: true
    }
  }
}
```

## Privacy Controls

### CSS Classes

Add these classes to your HTML to control recording:

| Class | Effect |
|-------|--------|
| `.rr-block` | Element replaced with a placeholder (same dimensions) |
| `.rr-ignore` | Input events not recorded |
| `.rr-mask` | Text content masked with `*` characters |

### HTML Examples

```html
<!-- Block entire element from recording -->
<div class="rr-block">
  <p>This content will be replaced with a placeholder</p>
</div>

<!-- Ignore input events but show element -->
<input class="rr-ignore" type="text" name="search" />

<!-- Mask text content -->
<span class="rr-mask">user@email.com</span>

<!-- Custom data attributes -->
<div data-sensitive>Credit card ending in 4242</div>
<section data-no-record>Private notes</section>
```

### Auto-Blocked Elements

The following selectors are automatically blocked/masked:

- `input[type="password"]`
- `input[name*="credit"]`
- `input[name*="card"]`
- `input[autocomplete="cc-number"]`
- `[data-sensitive]`
- `[data-private]`
- `[data-no-record]`

## Session Management

Sessions are automatically managed with:

- **Session ID**: Stored in `sessionStorage` (cleared on tab close)
- **Visitor ID**: Stored in `localStorage` (persistent across sessions)
- **Session timeout**: 30 minutes of inactivity (configurable)

Access session info:

```javascript
const session = client.tracker.getSession();
// {
//   sessionId: 'abc-123',
//   visitorId: 'xyz-789',
//   startedAt: 1703123456789,
//   lastActivityAt: 1703123556789,
//   eventCount: 42,
//   linkedToAnalytics: true
// }

client.tracker.getSessionId();  // 'abc-123'
client.tracker.getVisitorId();  // 'xyz-789'
```

## Compression

Events are compressed using the browser's `CompressionStream` API (gzip) when available. This typically reduces payload size by 70-90%.

```javascript
import { compressEvents, decompressEvents, estimateCompression } from '@powerit/cms-tracker';

// Compress events manually
const compressed = await compressEvents(events);
// { type: 'gzip', data: 'base64...', originalSize: 10000, compressedSize: 2000 }

// Decompress for debugging
const events = await decompressEvents(compressed);

// Estimate compression ratio
const stats = await estimateCompression(events);
// { ratio: '5.00', savings: '80.0%', type: 'gzip' }
```

## API Reference

### SessionRecorder

| Method | Description |
|--------|-------------|
| `init()` | Initialize the recorder |
| `start()` | Start recording |
| `pause()` | Pause recording |
| `resume()` | Resume recording |
| `stop()` | Stop and flush |
| `isRecording()` | Check if recording |
| `isEnabled()` | Check if enabled |
| `getSession()` | Get session info |
| `flush()` | Flush pending events |
| `addCustomEvent(tag, payload)` | Add custom event |
| `identify(userId, traits)` | Identify user |
| `takeSnapshot()` | Take full snapshot |
| `updatePrivacy(config)` | Update privacy settings |
| `enable()` | Enable recording |
| `disable()` | Disable recording |
| `destroy()` | Cleanup |

### Client Methods (via plugin)

| Method | Description |
|--------|-------------|
| `client.tracker` | Access recorder instance |
| `client.startRecording()` | Start recording |
| `client.stopRecording()` | Stop recording |
| `client.pauseRecording()` | Pause recording |
| `client.resumeRecording()` | Resume recording |
| `client.addCustomEvent(tag, payload)` | Add custom event |
| `client.identifyUser(userId, traits)` | Identify user |
| `client.takeSnapshot()` | Take snapshot |

## Backend Integration

The tracker sends events to `/api/public/tracker/events`:

```json
POST /api/public/tracker/events
{
  "session_id": "abc-123",
  "visitor_id": "xyz-789",
  "events": {
    "type": "gzip",
    "data": "base64-encoded-gzip-data"
  },
  "event_count": 50,
  "timestamp": 1703123456789,
  "is_exit": false
}
```

## License

MIT