npm package: @signal-js/node
Description: Signal SDK for Node.js - Server-side event capture and analytics
Latest version: 1.0.6

# @signal-js/node

Signal SDK for Node.js - Server-side event capture and analytics. Uses **@signal-js/core** and **@signal-js/browser** (for `HttpEventSink`); both are included as dependencies.

## Installation

```bash
npm install @signal-js/node
# or
pnpm add @signal-js/node
# or
yarn add @signal-js/node
```

## Quick Start

```typescript
import { createSignalNode } from '@signal-js/node';

const signal = createSignalNode({
  endpoint: 'https://your-api.com',
  apiKey: 'your-api-key',
});

// Capture an event
signal.capture({
  distinctId: 'user-123',
  event: 'purchase_completed',
  properties: {
    amount: 99.99,
    currency: 'USD',
  },
});

// Identify a user
signal.identify({
  distinctId: 'user-123',
  properties: {
    email: 'user@example.com',
    name: 'John Doe',
    plan: 'premium',
  },
});

// Associate with a group
signal.group('user-123', 'company', 'acme-corp', {
  name: 'Acme Corp',
  plan: 'enterprise',
});

// Graceful shutdown
await signal.shutdown();
```

## Configuration

```typescript
const signal = createSignalNode({
  // Required
  endpoint: 'https://your-api.com',
  apiKey: 'your-api-key',
  
  // Optional
  projectId: 'my-project',          // Default: 'default'
  debug: false,                      // Enable debug logging
  flushBatchSize: 20,               // Events per batch
  flushInterval: 10000,             // Flush interval (ms)
  timeout: 30000,                   // Request timeout (ms)
  compression: true,                // Enable gzip compression
});
```

## API

### `capture(options)`

Capture an event.

```typescript
signal.capture({
  distinctId: 'user-123',
  event: 'page_viewed',
  properties: {
    page: '/dashboard',
    referrer: '/home',
  },
  timestamp: new Date(),  // Optional
  groups: {               // Optional
    company: 'acme-corp',
    team: 'engineering',
  },
});
```

### `identify(options)`

Identify a user.

```typescript
signal.identify({
  distinctId: 'user-123',
  properties: {
    email: 'user@example.com',
    name: 'John Doe',
    createdAt: '2024-01-01',
  },
});
```

### `setPersonProperties(distinctId, properties)`

Set user properties.

```typescript
signal.setPersonProperties('user-123', {
  plan: 'enterprise',
  last_seen: new Date().toISOString(),
});
```

### `setPersonPropertiesOnce(distinctId, properties)`

Set user properties only if not already set.

```typescript
signal.setPersonPropertiesOnce('user-123', {
  first_seen: new Date().toISOString(),
});
```

### `group(distinctId, groupType, groupKey, properties?)`

Associate a user with a group.

```typescript
signal.group('user-123', 'company', 'acme-corp', {
  name: 'Acme Corp',
  plan: 'enterprise',
  employee_count: 500,
});
```

### `groupIdentify(options)`

Identify a group (set group properties).

```typescript
signal.groupIdentify({
  groupType: 'company',
  groupKey: 'acme-corp',
  properties: {
    name: 'Acme Corp',
    plan: 'enterprise',
  },
});
```

### `alias(distinctId, alias)`

Create an alias for a user.

```typescript
signal.alias('user-123', 'email:user@example.com');
```

### `flush()`

Flush all queued events immediately.

```typescript
await signal.flush();
```

### `shutdown()`

Gracefully shutdown (flush remaining events).

```typescript
await signal.shutdown();
```

## Edge Runtime Support

The package includes an edge-compatible build for Cloudflare Workers, Vercel Edge, etc.

```typescript
// Automatic (runtime detection)
import { createSignalNode } from '@signal-js/node';

// Or explicit edge import
import { createSignalNode } from '@signal-js/node/edge';
```

## Usage Examples

### Express.js

```typescript
import express from 'express';
import { createSignalNode } from '@signal-js/node';

const app = express();
const signal = createSignalNode({
  endpoint: process.env.SIGNAL_ENDPOINT!,
  apiKey: process.env.SIGNAL_API_KEY!,
});

app.post('/api/checkout', async (req, res) => {
  const { userId, amount, items } = req.body;
  
  signal.capture({
    distinctId: userId,
    event: 'checkout_completed',
    properties: { amount, item_count: items.length },
  });
  
  res.json({ success: true });
});

// Graceful shutdown
process.on('SIGTERM', async () => {
  await signal.shutdown();
  process.exit(0);
});
```

### Next.js API Routes

```typescript
// pages/api/track.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { createSignalNode } from '@signal-js/node';

const signal = createSignalNode({
  endpoint: process.env.SIGNAL_ENDPOINT!,
  apiKey: process.env.SIGNAL_API_KEY!,
});

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method === 'POST') {
    const { userId, event, properties } = req.body;
    
    signal.capture({
      distinctId: userId,
      event,
      properties,
    });
    
    res.status(200).json({ success: true });
  }
}
```

## License

MIT