npm package: @splitlab/node
Description: Node.js server SDK for SplitLab A/B testing and feature flags
Latest version: 0.11.0

# @splitlab/node

Node.js server SDK for SplitLab A/B testing and feature flags. Per-request evaluation, event batching, and HTTP request context enrichment.

## Installation

```bash
npm install @splitlab/node
```

## Quick Start

```typescript
import { SplitLabServer } from '@splitlab/node';

const splitlab = new SplitLabServer({
  apiKey: 'ot_live_abc123',
  baseUrl: 'https://splitlab.cc',
});
await splitlab.initialize();

// Per-request evaluation (< 1ms, no network)
const variant = splitlab.getVariant('checkout-btn', 'user-42');
const enabled = splitlab.isFeatureEnabled('dark-mode', 'user-42', { plan: 'pro' });

// Event tracking (batched and flushed automatically)
splitlab.track('user-42', 'purchase', { value: 49.99, currency: 'USD' });

// Clean shutdown
await splitlab.destroy();
```

## Request Context

For full-stack apps, `withContext()` parses the HTTP request and enriches events with server-side properties (UA, URL, cookies, UTM params, device/session IDs):

```typescript
import express from 'express';

const app = express();

app.get('/checkout', (req, res) => {
  const ctx = splitlab.withContext(req);

  // Evaluate with device ID from cookie (stable across anonymous → logged-in)
  const variant = ctx.getVariant('checkout-btn');

  // Track enriched with browser, OS, pathname, UTM, etc.
  ctx.track('page_view');

  // Set device/session cookies on response
  for (const cookie of ctx.getResponseCookies()) {
    res.setHeader('Set-Cookie', cookie);
  }

  res.render('checkout', { variant });
});
```

### Server-Side Bootstrap (SSR)

Generate bootstrap data on the server to hydrate the browser SDK with zero flicker:

```typescript
app.get('/app', (req, res) => {
  const ctx = splitlab.withContext(req);
  const bootstrap = ctx.getBootstrapData(req.user?.id);

  // Pass to client — the browser SDK hydrates instantly, no config fetch needed
  res.render('app', { bootstrap: JSON.stringify(bootstrap) });
});
```

On the client:

```typescript
import { SplitLabClient } from '@splitlab/js-client';

const client = new SplitLabClient({
  apiKey: 'ot_live_abc123',
  baseUrl: 'https://splitlab.cc',
  bootstrap: JSON.parse(window.__BOOTSTRAP__),
});
// Hydration is synchronous — getVariant() works immediately
```

## Configuration

```typescript
const splitlab = new SplitLabServer({
  apiKey: string;               // Your org API key
  baseUrl: string;              // API server URL
  ingestUrl?: string;           // Ingest server URL (defaults to baseUrl)
  configRefreshInterval?: number; // Config polling interval in ms (default: 30000)
  flushInterval?: number;       // Event flush interval in ms (default: 10000)
  flushSize?: number;           // Max buffered events before auto-flush (default: 100)
  onConfigUpdate?: () => void;  // Callback when config changes
  environment?: string;         // Environment key (default: 'production')
});
```

## API

### `splitlab.initialize(): Promise<void>`
Fetches config, starts polling and flush timers.

### `splitlab.getVariant(experimentKey, distinctId, attributes?): string | null`
Returns the assigned variant, or `null` if excluded.

### `splitlab.isFeatureEnabled(flagKey, distinctId, attributes?): boolean`
Returns whether the flag is enabled for this user.

### `splitlab.evaluateAll(distinctId, attributes?): EvalResult`
Evaluates all experiments and flags at once.

### `splitlab.track(distinctId, eventName, properties?): void`
Queues an event. Auto-flushes when buffer reaches `flushSize`.

### `splitlab.flush(): Promise<void>`
Sends all buffered events to the ingest service.

### `splitlab.withContext(req): RequestContext`
Creates a request-scoped context with enriched properties from HTTP headers/cookies.

### `splitlab.destroy(): Promise<void>`
Flushes remaining events and stops all timers.

### `ctx.getVariant(experimentKey, distinctId?): string | null`
Evaluate with optional distinct ID override (defaults to device ID from cookie).

### `ctx.track(eventName, properties?, distinctId?): void`
Track an event enriched with request context (UA, URL, referrer, UTM, device/session IDs).

### `ctx.getResponseCookies(): string[]`
Returns `Set-Cookie` headers for device ID (2yr) and session ID (30min sliding).

### `ctx.getBootstrapData(distinctId?, attributes?): BootstrapData`
Generates bootstrap data for client-side SDK hydration.

## Building

```bash
npm run build   # ESM + CJS + types → dist/
npm run dev     # Watch mode
```