@powerit/cms-tracker
v1.0.1
Published
Session recording plugin for PowerIT CMS Client using rrweb
Maintainers
Readme
@powerit/cms-tracker
Session recording plugin for PowerIT CMS Client using 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
npm install @powerit/cms-trackerUsage
Basic Setup
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
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
// 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
// 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/falseConfiguration
Full Configuration Example
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
{
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
{
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
{
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
<!-- 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">[email protected]</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:
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%.
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:
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
