npm package: @splitwisp/tracker
Description: Ultra-lightweight A/B testing SDK (~3KB gzipped). Zero dependencies. Script tag, npm, and GTM installation options.
Latest version: 1.0.3

# @splitwisp/tracker

Ultra-lightweight A/B testing SDK. **Zero dependencies. ~3KB gzipped.**

Designed for the scale-to-zero Visual A/B Testing MVP architecture.

## Features

- **Tiny** — no heavy dependencies, no plugins, no framework lock-in
- **Sticky sessions** — auto-generated session ID persisted in localStorage
- **Offline queue** — failed track calls are queued and retried on next page load
- **Beacon support** — fire-and-forget tracking on page unload via `navigator.sendBeacon`
- **SPA-aware** — optional auto-tracking of page views including pushState navigations
- **GTM-compatible** — UMD build works as a `<script>` tag or GTM Custom HTML tag

## Installation

### Script tag (CDN / GTM)

The SDK is automatically deployed to CloudFront CDN on every release. Choose the URL that best fits your needs:

```html
<!-- Recommended: Latest v1.x version (auto-updates with patches/features) -->
<script src="https://cdn.yourdomain.com/sdk/v1/tracker.umd.js"></script>

<!-- Alternative: Pinned version (immutable, never changes) -->
<script src="https://cdn.yourdomain.com/sdk/v1.2.3/tracker.umd.js"></script>
<script>
  SplitWisp.init({
    apiKey: 'pk_live_abc123',
    endpoint: 'https://api.yourdomain.com',
  }).then(function (t) {
    // Impressions are tracked automatically for all assigned experiments.
    // For code-based experiments, read the variant and apply changes:
    var variant = t.getVariant('exp_hero_headline');
    if (variant && variant.variantName === 'short') {
      document.querySelector('h1').textContent = 'Ship faster.';
    }
  });
</script>
```

### GTM Custom HTML Tag

```html
<script>
  (function () {
    var s = document.createElement('script');
    s.src = 'https://cdn.yourdomain.com/sdk/v1/tracker.umd.js';
    s.onload = function () {
      SplitWisp.init({
        apiKey: '{{YOUR_API_KEY}}',
        endpoint: '{{YOUR_API_ENDPOINT}}',
      }).then(function (t) {
        window.__tracker = t;
        t.autoTrackPageViews();
      });
    };
    document.head.appendChild(s);
  })();
</script>
```

### npm / bundler

```bash
npm install @splitwisp/tracker
```

```js
import { Tracker } from '@splitwisp/tracker';

const t = new Tracker({
  apiKey: 'pk_live_abc123',
  endpoint: 'https://api.yourdomain.com',
});

await t.init();

// Impressions are tracked automatically — just read the variant and apply your logic:
const variant = t.getVariant('exp_pricing_cta');
```

## API

### `new Tracker(config)`

| Option      | Type     | Required | Default | Description                          |
| ----------- | -------- | -------- | ------- | ------------------------------------ |
| `apiKey`    | `string` | ✅       |         | Project API key                      |
| `endpoint`  | `string` | ✅       |         | API base URL (no trailing slash)     |
| `sessionId` | `string` |          | auto    | Override auto-generated session ID   |
| `userId`    | `string` |          |         | Known user ID from your auth system  |
| `timeout`   | `number` |          | 5000    | API call timeout in ms               |
| `quiet`     | `boolean`|          | false   | Suppress console warnings            |

### `t.init(): Promise<Assignment[]>`

Fetches variant assignments for this session. Call once on page load. Automatically tracks an `impression` event for each assigned experiment and sets up any configured conversion goals.

### `t.getVariant(experimentId): Assignment | null`

Returns the assigned variant for a specific experiment.

### `t.track(opts): Promise<void>`

Tracks an event. Queues to localStorage on failure.

**Options:**

| Option         | Type     | Required | Description                                    |
| -------------- | -------- | -------- | ---------------------------------------------- |
| `event`        | `string` | ✅       | Event name (e.g. "pageview", "conversion")    |
| `experimentId` | `string` |          | Experiment this event relates to (optional)    |
| `value`        | `number` |          | Numeric value (e.g. revenue in cents)          |
| `properties`   | `object` |          | Arbitrary metadata                             |

**When to include `experimentId`:**

```js
// ✅ Conversion tracking (impressions are tracked automatically by init())
t.trackConversion('exp_hero_headline', 4999); // $49.99 in cents
```

**When to omit `experimentId`:**

```js
// ✅ General analytics events (not tied to an experiment)
t.track({ event: 'pageview' });
t.track({ event: 'signup' });
t.track({ event: 'button_click', properties: { button: 'cta' } });
```

**Note:** Events without an `experimentId` are stored with a `_general` experiment ID on the server and won't affect experiment results. Use them for general product analytics.

### `t.trackConversion(experimentId, value?, properties?): Promise<void>`

Convenience wrapper for conversion events. Always requires an `experimentId`.

```js
// Track a conversion with revenue
t.trackConversion('exp_pricing_cta', 9900); // $99.00 in cents

// Track a conversion without revenue
t.trackConversion('exp_signup_flow');

// Track a conversion with metadata
t.trackConversion('exp_checkout', 14999, { 
  plan: 'pro',
  coupon: 'SAVE20' 
});
```

### `t.trackBeacon(opts): void`

Fire-and-forget tracking via `sendBeacon`. Use on page unload.

### `t.autoTrackPageViews(): () => void`

Auto-tracks page views including SPA navigations. Returns a teardown function.

### `t.identify(userId): void`

Associate a known user ID with the session.

## Build

```bash
npm install
npm run build
npm test
npm run size   # check gzip size
```

## Architecture

```
Browser → tracker.umd.js (~3KB gzip)
  ├─ GET  /v1/assign?apiKey=...&sessionId=...  → variant assignments
  ├─ POST /v1/track  { event, sessionId, ... } → event ingestion
  └─ localStorage: _sw_sid (session), _sw_q (offline queue)
```

The SDK talks to API Gateway + Lambda endpoints defined in `tf-mvp/modules/api/`.
The SDK itself is distributed via S3 + CloudFront defined in `tf-mvp/modules/sdk_cdn/`.