npm package: @sessionsight/insights
Description: Session replay, heatmaps, and user analytics SDK for SessionSight.
Author: SessionSight
Latest version: 1.0.1

# @sessionsight/insights

The SessionSight SDK captures user sessions on your website — recording DOM state, clicks, scrolls, mouse movement, form interactions, and page navigations. The data powers session replay, heatmaps, conversion funnels, and form analytics in the SessionSight dashboard.

## Installation

```bash
npm install @sessionsight/insights
```

Or via script tag:

```html
<script src="https://cdn.sessionsight.com/sessionsight.js"></script>
```

## Quick Start

### npm / ES Module

```ts
import SessionSight from '@sessionsight/insights';

SessionSight.init({
  publicApiKey: 'sessionsight_pub_...',
  propertyId: 'your-property-id',
});
```

### Script tag

```html
<script src="https://cdn.sessionsight.com/sessionsight.js"></script>
<script>
  SessionSight.init({
    publicApiKey: 'sessionsight_pub_...',
    propertyId: 'your-property-id',
  });
</script>
```

That's it. Recording starts automatically.

## API

### `SessionSight.init(config)`

Initializes the SDK. Must be called before any other method.

```ts
SessionSight.init({
  publicApiKey: 'sessionsight_pub_...', // Required. Your public API key from the dashboard.
  propertyId: 'your-property-id', // Required. Your property ID from the dashboard.
  apiUrl: 'https://api.sessionsight.com', // Optional. Override the API endpoint.
  autoRecord: true, // Optional. Set to false for manual recording control.
});
```

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `publicApiKey` | `string` | **required** | Your public API key from the SessionSight dashboard (API Keys page). |
| `propertyId` | `string` | `'dev'` | Your property ID from the SessionSight dashboard (Settings → Properties). Defaults to `'dev'` for local development. |
| `apiUrl` | `string` | `https://api.sessionsight.com` | API endpoint. Override for self-hosted or local development. |
| `autoRecord` | `boolean` | `true` | When `true`, recording starts immediately. When `false`, the SDK captures into a 5-second rolling pre-buffer but doesn't send data until `record()` is called. |
| `enabled` | `boolean \| (() => boolean)` | `true` | Controls whether the SDK is active. Pass `false` to defer setup, or a getter function for reactive consent (the SDK polls the getter every second and automatically starts/stops when the value changes). |

### `SessionSight.setEnabled(enabled)`

Manually toggles the SDK on or off after `init()` has been called. Only needed when `enabled` was passed as a static `boolean`. If you passed a getter function, the SDK manages this automatically.

```ts
// Initialize with tracking deferred
SessionSight.init({
  publicApiKey: 'sessionsight_pub_...',
  propertyId: 'your-property-id',
  enabled: false,
});

// Later, after user grants consent:
SessionSight.setEnabled(true);

// If user revokes consent:
SessionSight.setEnabled(false);
```

When disabled, the SDK tears down all listeners and flushes remaining events. When re-enabled, it creates a fresh session. Calling `setEnabled(true)` when already enabled (or `false` when already disabled) is a no-op.

### `SessionSight.record(options?)`

Starts recording and sending data to the server. Only needed when `autoRecord: false`.

```ts
// Start recording from this moment
SessionSight.record();

// Start recording, including the last 3 seconds of activity
SessionSight.record({ preRecordSecs: 3 });
```

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `preRecordSecs` | `number` | `0` | Include up to 5 seconds of pre-buffered events captured before `record()` was called. |

Calling `record()` when already recording is a no-op.

### `SessionSight.identify(userId, properties?)`

Associates the current session with a user ID and optional user properties. Call this after your user logs in.

```ts
// Identify with just a user ID
SessionSight.identify('user-123');

// Identify with user properties for segmentation
SessionSight.identify('user-123', {
  plan: 'pro',
  accountType: 'enterprise',
  role: 'admin',
});
```

The user ID appears in the SessionSight dashboard alongside session data. No PII is required — use any stable identifier.

Properties are stored on the visitor profile and can be used for segmentation — filtering sessions, heatmaps, and funnels by user attributes. Use categorical/descriptive values here (plan tier, account type, role). For monetary values (purchase amounts, revenue), use the [@sessionsight/goals](../goals/README.md) SDK instead.

### `SessionSight.getVisitorId()`

Returns the current visitor ID, or `null` if the SDK hasn't been initialized.

```ts
const visitorId = SessionSight.getVisitorId();
// Send to your backend for use with the Flags SDK
```

The visitor ID is also available as a first-party cookie (`ss_vid`) on your domain, so your backend can read it directly from the request without any frontend code.

### `SessionSight.stop()`

Stops recording, flushes any remaining events, and tears down all listeners.

```ts
SessionSight.stop();
```

After calling `stop()`, you must call `init()` again to start a new session.

## User-Triggered Recording

For cases where you only want to record specific interactions (e.g. after a user hits an error, or during a specific flow):

```ts
// Initialize without auto-recording
SessionSight.init({
  publicApiKey: 'sessionsight_pub_...',
  propertyId: 'your-property-id',
  autoRecord: false,
});

// Later, when something interesting happens:
document.getElementById('checkout-btn').addEventListener('click', () => {
  // Start recording, including the last 5 seconds of what the user was doing
  SessionSight.record({ preRecordSecs: 5 });
});
```

The pre-buffer captures DOM state, mouse movements, clicks, and all other events in a rolling 5-second window. When `record()` is called, those buffered events are included in the recording so you can see what led up to the trigger.

## Form Tracking

Forms are automatically tracked. The SDK captures:
- When a user starts interacting with a form
- Which fields they focus on and how long they spend on each
- Whether each field was filled (boolean only — **no field values are captured**)
- Form submission with completion metrics

### Naming Forms

By default, forms are identified by their page URL and position. Add a `data-ss-form` attribute for a custom name:

```html
<form data-ss-form="Signup Form">
  <!-- fields -->
</form>
```

The name appears in the Form Analytics section of the dashboard and can also be changed from there.

## What Gets Captured

| Data | Captured | Notes |
|------|----------|-------|
| DOM state | Yes | Full page snapshot with inline CSS for replay |
| Mouse movement | Yes | Position sampled every 500ms |
| Clicks | Yes | All clicks with coordinates; button/link clicks with labels |
| Scroll depth | Yes | Scroll position sampled every 1s |
| Page navigations | Yes | SPA navigations detected via History API |
| Form field interactions | Yes | Focus/blur timing, filled state (boolean) |
| Form field values | **No** | Never captured — only whether a field has a value |
| Passwords | **No** | Never captured |
| Cookies / localStorage | **No** | Never captured |
| Network requests | **No** | Not captured |

## Privacy & Data Attributes

The SDK uses a three-layer privacy system:

1. **Automatic PII detection** (always active): emails, phone numbers, credit card numbers, SSNs, IBANs, and API keys are automatically replaced with `[REDACTED]` before data leaves the browser. This cannot be disabled.

2. **Privacy mode** (configured per-property in the dashboard):
   - **Default**: all text is scrambled using a character-width-preserving algorithm. Replays show layout and behavior but text is unreadable.
   - **Relaxed**: text is shown as-is, with only detected PII redacted.

3. **HTML data attributes** (element-level overrides):

| Attribute | Effect |
|-----------|--------|
| `data-ss-mask` | Force scrambling on this element and its children, even in relaxed mode |
| `data-ss-unmask` | Allow readable text (PII still redacted) on this element and its children, even in default mode |
| `data-ss-exclude` | Block recording entirely. Element is replaced with an empty placeholder that preserves dimensions |

```html
<nav data-ss-unmask>Readable navigation links</nav>
<div data-ss-mask>Scrambled even in relaxed mode</div>
<div data-ss-exclude>Completely hidden from recordings</div>
```

Data attributes cascade to child elements. The nearest ancestor with `data-ss-mask` or `data-ss-unmask` wins. `data-ss-exclude` always takes priority.

You can also exclude entire pages from recording using glob patterns configured in the dashboard (e.g., `/admin/*`, `/checkout`).

## Visitor Tracking

The SDK generates a persistent `visitorId` stored in both `localStorage` (`sessionsight_visitor_id`) and a first-party cookie (`ss_vid`). This allows tracking the same visitor across sessions on the same device/browser. The ID is a random UUID with no connection to personal information.

The cookie makes the visitor ID available to your backend on every request, which is useful for passing it to the Feature Flags SDK for segment-based targeting.

In incognito/private browsing, a session-only ID is used instead.

## Cookie Consent

If your site requires cookie consent before tracking, pass a getter function to `enabled`. The SDK polls it every second and automatically starts or stops when the value changes:

```ts
SessionSight.init({
  publicApiKey: 'sessionsight_pub_...',
  propertyId: 'your-property-id',
  enabled: () => hasConsent,
});
```

No data is collected, no cookies are set, and no listeners are attached until the getter returns `true`. If the user later revokes consent, the SDK tears down automatically.

This works with any framework's reactive state:

```ts
// Svelte 5
let consent = $state(false);
SessionSight.init({ ..., enabled: () => consent });

// Vue
const consent = ref(false);
SessionSight.init({ ..., enabled: () => consent.value });

// React (via ref)
const consentRef = useRef(false);
SessionSight.init({ ..., enabled: () => consentRef.current });

// Solid
const [consent] = createSignal(false);
SessionSight.init({ ..., enabled: consent });
```

Alternatively, pass a static `boolean` and use `setEnabled()` to toggle manually.

## Build Outputs

| File | Format | Use |
|------|--------|-----|
| `dist/index.js` | ES Module | `import SessionSight from '@sessionsight/insights'` |
| `dist/index.d.ts` | TypeScript declarations | Type support |
| `dist/sessionsight.js` | IIFE (minified) | `<script>` tag, exposes `window.SessionSight` |

Build with:

```bash
bun run build
```