# @flamioai/web-sdk

Lightweight JavaScript SDK for continuous UX session recording on production
sites. It captures rrweb DOM recordings, console errors, network failures, Web
Vitals, scroll depth and SPA navigation, then batches and uploads them to
FlamioAI for real-time UX analysis.

**Dashboard:** create a project, grab your `projectKey`, and watch the recorded
sessions & AI analysis at **[admin.flamio.org](https://admin.flamio.org)**
(Projects → On-Live → Setup).

## Installation

```bash
npm install @flamioai/web-sdk
```

Or load it directly in the browser:

```html
<script type="module">
  import { Flamio } from 'https://esm.sh/@flamioai/web-sdk';
  Flamio.init({ projectKey: 'flm_...', endpoint: 'https://api.flamio.org/api' });
</script>
```

## Quick start

```ts
import { Flamio } from '@flamioai/web-sdk';

Flamio.init({
  projectKey: 'flm_your_project_key', // from admin.flamio.org → On-Live → Setup
  endpoint: 'https://api.flamio.org/api',
  version: '1.4.2', // your release version (optional but recommended)
});
```

`init()` starts recording immediately and returns the instance, or `null` when
recording doesn't start (session not sampled, running server-side, or consent is
`denied`/`wait`). Call it once, as early as possible in your app.

## Configuration

All options are passed to `Flamio.init(config)`.

| Option                  | Type                              | Default     | Description                                                                                                                                                          |
| ----------------------- | --------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `projectKey`            | `string`                          | —           | **Required.** Your project's ingest key from the dashboard (On-Live → Setup).                                                                                        |
| `endpoint`              | `string`                          | —           | **Required.** FlamioAI ingestion URL, e.g. `https://api.flamio.org/api`.                                                                                             |
| `version`               | `string`                          | —           | Your site/release version (e.g. `'1.4.2'`). Attached to every session so the dashboard can filter sitemap & analytics per version and measure release impact.        |
| `sampleRate`            | `number` (0–1)                    | `1.0`       | Fraction of sessions to record. `0.25` records ~25%. The decision is sticky per visitor for the session.                                                             |
| `maxConcurrentSessions` | `number`                          | `15`        | Max sessions recorded at once for this project (hard-capped at `100` server-side). Beyond it, new visitors aren't recorded — a safety valve against traffic spikes.  |
| `consent`               | `'granted' \| 'denied' \| 'wait'` | `'granted'` | Consent gate. `'granted'` records now; `'denied'` records nothing; `'wait'` holds until you call `Flamio.consent('granted')`. See [Consent](#consent).               |
| `maskTextContent`       | `boolean`                         | `false`     | Mask **all visible text** in the recording, not just inputs. Use for text-sensitive apps. (Input values are **always** masked regardless — see [Privacy](#privacy).) |
| `maskSelectors`         | `string[]`                        | `[]`        | CSS selectors whose **text** is blanked (same effect as the `flamio-mask` class, no markup changes). See [Privacy](#privacy).                                        |
| `blockSelectors`        | `string[]`                        | `[]`        | CSS selectors whose **elements** are omitted from the recording (same effect as the `flamio-block` class).                                                           |
| `urlAllowlist`          | `string[]`                        | `[]`        | If non-empty, record **only** on URLs matching one of these patterns. `*` is the only wildcard; case-insensitive substring. Enforced in the browser.                 |
| `urlBlocklist`          | `string[]`                        | `[]`        | **Never** record on URLs matching one of these patterns (takes precedence over the allowlist). Blocked pages send nothing.                                           |
| `checkoutEveryNms`      | `number` (ms)                     | `30000`     | How often rrweb takes a full DOM snapshot. Snapshots are seek anchors for replay and screenshots — lower = more accurate seeking, larger recordings.                 |
| `batchInterval`         | `number` (ms)                     | `5000`      | How often buffered events are flushed and uploaded.                                                                                                                  |
| `batchMaxSize`          | `number` (bytes)                  | `50000`     | Flush early once the buffer reaches this size, regardless of `batchInterval`.                                                                                        |
| `retryDelayMs`          | `number` (ms)                     | `1000`      | Base delay for retrying a failed upload (exponential backoff from here).                                                                                             |
| `debug`                 | `boolean`                         | `false`     | Log SDK lifecycle events to the console. Turn off in production.                                                                                                     |

## API

### `Flamio.init(config): Flamio | null`

Starts recording. See [Configuration](#configuration). Returns `null` if the
session isn't recorded (not sampled / SSR / consent not granted). Safe to call
once; a second call is a no-op.

### `Flamio.identify(userId, traits?)`

Attach a stable user id (and optional traits) to the current session, so the
dashboard can group sessions by real user and answer "what did user X do".

```ts
Flamio.identify('user_123', { email: 'a@b.com', plan: 'pro' });
```

### `Flamio.consent(status)`

Update consent at runtime — the counterpart to the `consent: 'wait'` config.

```ts
Flamio.init({ projectKey, endpoint, consent: 'wait' }); // recording deferred
// ...after the user accepts your cookie banner:
Flamio.consent('granted'); // starts recording
// or:
Flamio.consent('denied'); // stops and purges the session
```

### `Flamio.stop()`

Stop recording and flush the final batch (e.g. on logout).

## Consent

- **`granted`** (default) — record immediately.
- **`denied`** — record nothing; nothing leaves the browser.
- **`wait`** — don't record until you call `Flamio.consent('granted')`. Ideal
  behind a GDPR/cookie banner: call `init({ consent: 'wait' })` on load, then
  grant/deny from the banner. `consent('denied')` after a grant stops recording
  and drops the in-flight session.

## User identification

Sessions are anonymous by default — tied to a first-party `visitorId` kept in
`localStorage`. Once you know who the user is (e.g. after login), call
`Flamio.identify()` to attach a real identity:

```ts
Flamio.identify('user_123', {
  email: 'a@b.com',
  plan: 'pro',
  company: 'Acme',
});
```

- **`userId`** — your stable id for the user. Lets the dashboard group all of a
  person's sessions and answer "what did user X do".
- **`traits`** — optional key/value metadata (email, plan, role, …). Shown on the
  session and searchable/filterable in the dashboard.

Both are attached to every subsequent upload (headers `X-Flamio-User-Id` /
`X-Flamio-User-Traits`), so call `identify()` as soon as the identity is known —
the whole session is attributed to that user, including events recorded before
the call.

## Privacy

**Input values are never recorded** (`maskAllInputs`, always on) — password,
email and phone fields are masked at the input level too.

Mark elements to skip from recordings. These conventions work identically in the
SDK and the browser extension:

| Marker                                      | Effect                                            |
| ------------------------------------------- | ------------------------------------------------- |
| `class="flamio-mask"` or `data-flamio-mask` | Blank the element's **text**                      |
| `class="flamio-block"`                      | Omit the **element** entirely (replaced by a box) |
| `class="flamio-ignore"`                     | Don't record **input changes** inside it          |

You can also drive the same rules from `Flamio.init()` (CSS selectors, no markup
changes needed):

```ts
Flamio.init({
  projectKey: 'flm_xxx',
  endpoint: 'https://api.flamio.org/api',
  maskSelectors: ['.card-number', '#ssn'], // blank text
  blockSelectors: ['.id-photo'], // omit element
  // Control WHICH pages record (enforced in the browser — blocked pages never
  // send anything). `*` is the only wildcard; matching is case-insensitive substring.
  urlBlocklist: ['/admin', '/account/*'], // never record here (wins over allowlist)
  urlAllowlist: ['/checkout*'], // if set, record ONLY here
});
```

Opt into masking **all** page text with `maskTextContent: true`.

## What it captures

DOM interactions (rrweb), console errors/warnings, failed & slow network
requests (with TTFB / transfer size / initiator when available), Web Vitals
(LCP, INP, CLS), scroll depth, SPA route changes, and tab visibility. Recording
pauses on a long-hidden tab and ends after 30 minutes of inactivity.

## Session metadata

Each session carries context collected automatically on the first upload — no
config or calls required:

| Field      | Source                                                                 |
| ---------- | ---------------------------------------------------------------------- |
| Visitor id | first-party id in `localStorage` (anonymous until you `identify`)      |
| Device     | user agent, screen size, language, platform                            |
| Entry URL  | the first page of the visit (`location.href`)                          |
| Referrer   | `document.referrer`                                                    |
| UTM params | `utm_source` / `medium` / `campaign` / `term` / `content` from the URL |
| Version    | your `version` config, when set                                        |

## Single-page apps

Set `version` at deploy time so the dashboard can attribute sessions to a
release. Route changes are captured automatically — no manual `pageview` calls.

## License

MIT
