npm package: @signalite-io/sdk
Description: Official TypeScript/JavaScript SDK for Signalite beacon assignment and signal creation API
Author: Signalite
Latest version: 0.4.1

# @signalite/sdk

Official TypeScript/JavaScript SDK for Signalite signal creation and beacon assignment API.

## Installation

```bash
npm install @signalite/sdk
```

## Quick Start

```typescript
import { SignaliteClient } from '@signalite/sdk';

const client = new SignaliteClient({
  apiKey: 'your-api-key',
  tenantId: 'your-tenant-id',
  baseUrl: 'https://your-signalite-instance.com' // optional
});

// Beacon assignment
await client.beacons.assignSingle('customer', 'user_123', 'premium_customer');

// Context-aware signal creation (New!)
const draft = await client.signals.draftWithContext({
  prompt: "Customers who spent over $500 on events",
  autoSelectTables: true,
  strictness: "balanced"
});

const signal = await client.signals.createFromDraft(draft, {
  name: "High Value Customers",
  description: "Customers with significant event spending",
  tags: ["high-value", "revenue"]
});
```

## Batch Processing (New in v0.2.0)

For large datasets, use the new `runBatchAssignment` method that automatically handles chunking, retries, and progress tracking:

```typescript
// Basic batch processing
await client.beacons.runBatchAssignment('premium_customer', {
  entities: [
    { entity: 'customer', pk: 'user_1' },
    { entity: 'customer', pk: 'user_2' },
    // ... thousands more
  ]
});

// Advanced batch processing with options
const result = await client.beacons.runBatchAssignment('millennial', {
  // Can be array or async function
  entities: async () => {
    const users = await database.query('SELECT id FROM users WHERE age BETWEEN 25 AND 34');
    return users.map(user => ({ entity: 'customer', pk: user.id }));
  },
  
  batchSize: 150, // Default: 200
  retryAttempts: 5, // Default: 3
  retryDelayMs: 2000, // Default: 1000
  
  onProgress: (completed, total) => {
    console.log(`Progress: ${completed}/${total} (${Math.round(completed/total*100)}%)`);
  },
  
  onError: async (error, failedBatch) => {
    console.error(`Batch failed:`, error);
    // Optional: log to monitoring system, save to retry queue, etc.
  }
});

console.log(`Processed: ${result.totalProcessed}, Failed: ${result.totalFailed}`);
```

## Features

### Context-Aware Signal Creation (New in v0.3.0)
Create signals with automatic schema discovery and table selection - no need to know internal database structure.

### Schema Discovery
Get available tables, understand data structure, and auto-select relevant tables for signal creation.

### Automatic Chunking
Large datasets are automatically split into API-friendly chunks (max 200 per request).

### Retry Logic
Failed batches are automatically retried with exponential backoff.

### Progress Tracking
Get real-time progress updates for large batch operations.

### Error Handling
Comprehensive error handling with detailed error information.

### TypeScript Support
Full TypeScript support with detailed type definitions.

## API Reference

### SignaliteClient

Main client class for interacting with Signalite.

```typescript
const client = new SignaliteClient({
  apiKey: string;
  tenantId: string;
  baseUrl?: string; // defaults to current domain
});
```

#### Schema Discovery Methods (New!)

```typescript
// Get available tables
const tables = await client.getAvailableTables();

// Get full schema context
const schema = await client.getSchemaContext();

// Get detailed context for specific tables
const context = await client.getTableContext(['users', 'events']);

// Auto-select relevant tables based on prompt
const relevantTables = await client.getRelevantTables("customers who purchased tickets");
```

### BeaconClient

Accessed via `client.beacons`, provides beacon assignment methods.

#### assign(assignments)

Assign multiple entities to beacons (max 200 per call).

```typescript
await client.beacons.assign([
  {
    entity: string;
    pk: string;
    beacon_slug: string;
    occurred_at?: string; // ISO8601 timestamp
  }
]);
```

#### assignSingle(entity, pk, beaconSlug, options?)

Assign a single entity to a beacon.

```typescript
await client.beacons.assignSingle('customer', 'user_123', 'premium_customer', {
  occurredAt?: Date;
});
```

#### runBatchAssignment(beaconSlug, options)

Process large datasets with automatic chunking and retry logic.

```typescript
interface BatchAssignmentOptions {
  entities: EntityDefinition[] | (() => Promise<EntityDefinition[]>);
  batchSize?: number; // default 200
  onProgress?: (completed: number, total: number) => void;
  onError?: (error: Error, batch: BeaconAssignment[]) => Promise<void> | void;
  retryAttempts?: number; // default 3
  retryDelayMs?: number; // default 1000
}

interface EntityDefinition {
  entity: string;
  pk: string;
}

interface BatchAssignmentResult {
  success: boolean;
  totalProcessed: number;
  totalFailed: number;
  errors: BatchError[];
}
```

### SignalsClient (New!)

Accessed via `client.signals`, provides signal creation and management methods.

#### Context-Aware Signal Creation

```typescript
// Draft signal with automatic context discovery
const draft = await client.signals.draftWithContext({
  prompt: "Customers who spent over $500 on electronic music events",
  autoSelectTables: true, // Let SDK pick relevant tables
  strictness: "balanced" // or "broad" | "strict"
});

// Create signal from draft
const signal = await client.signals.createFromDraft(draft, {
  name: "High Value Electronic Music Customers",
  description: "Customers with significant spending on electronic music events",
  tags: ["high-value", "electronic-music", "revenue"]
});
```

#### Traditional Signal Creation

```typescript
// Create deterministic signal (manual table specification)
const signal = await client.signals.createDeterministicSignal({
  name: "High Spenders",
  description: "Customers with high spending",
  prompt: "customers who spent over $500",
  tables: ["app_user", "ticket_booking"],
  strictness: "balanced",
  tags: ["revenue", "high-value"]
});

// Create beacon signal (based on assigned beacons)
const beaconSignal = await client.signals.createBeaconSignal({
  name: "Social Music Enthusiasts",
  description: "Customers who are both social connectors and music enthusiasts",
  beacon_ids: ["social_connector", "music_enthusiast"],
  min_hits: 2,
  window_days: 30,
  tags: ["social", "music"]
});

// Draft signals before creating (preview functionality)
const draft = await client.signals.draftDeterministicSignal({
  prompt: "customers who purchased VIP tickets",
  tables: ["ticket_booking", "app_user"],
  strictness: "strict",
  tags: []
});

const beaconDraft = await client.signals.draftBeaconSignal({
  beacon_ids: ["premium_customer", "frequent_buyer"],
  min_hits: 1,
  window_days: 90
});
```

#### Signal Management

```typescript
// List signals
const signals = await client.signals.listSignals({
  active: true,
  type: "deterministic", // or "beacon"
  tags: ["high-value"],
  limit: 10
});

// Get specific signal
const signal = await client.signals.getSignal("signal-id");

// Update signal
await client.signals.updateSignal("signal-id", {
  name: "Updated Signal Name",
  active: false
});

// Toggle signal active status
await client.signals.toggleSignal("signal-id", false);

// Delete signal
await client.signals.deleteSignal("signal-id");
```

## Error Handling

The SDK provides specific error types for different scenarios:

```typescript
import { 
  SignaliteError,
  BeaconNotFoundError, 
  ValidationError,
  RateLimitError,
  BatchProcessingError,
  SignalCreationError,
  SignalNotFoundError
} from '@signalite/sdk';

try {
  // Beacon assignment
  await client.beacons.assignSingle('customer', 'user_123', 'invalid_beacon');
  
  // Signal creation
  await client.signals.createFromDraft(draft, metadata);
} catch (error) {
  if (error instanceof BeaconNotFoundError) {
    console.error(`Beacon '${error.beacon}' does not exist`);
  } else if (error instanceof SignalCreationError) {
    console.error('Signal creation failed:', error.message);
  } else if (error instanceof ValidationError) {
    console.error('Validation failed:', error.message);
  } else if (error instanceof RateLimitError) {
    console.error('Rate limit exceeded, try again later');
  }
}
```

## Best Practices

### Use Context-Aware Signal Creation

```typescript
// ❌ Manual: Need to know database structure
await client.signals.createDeterministicSignal({
  name: "High Spenders",
  prompt: "customers with high spending",
  tables: ["app_user", "ticket_booking"], // Must know table names
  strictness: "balanced",
  tags: ["revenue"]
});

// ✅ Context-aware: SDK discovers relevant tables
const draft = await client.signals.draftWithContext({
  prompt: "customers who spent over $500 on events",
  autoSelectTables: true, // SDK picks relevant tables
  strictness: "balanced"
});

const signal = await client.signals.createFromDraft(draft, {
  name: "High Value Customers",
  description: "Customers with significant event spending", 
  tags: ["high-value", "revenue"]
});
```

### Use Batch Processing for Large Datasets

```typescript
// ❌ Inefficient: Multiple single requests
for (const user of users) {
  await client.beacons.assignSingle('customer', user.id, 'active_user');
}

// ✅ Efficient: Single batch request
await client.beacons.runBatchAssignment('active_user', {
  entities: users.map(user => ({ entity: 'customer', pk: user.id }))
});
```

### Handle Errors Gracefully

```typescript
// ❌ Don't let beacon assignment block critical flows
await processPayment(order);
await client.beacons.assignSingle('order', order.id, 'paid'); // Could fail

// ✅ Process asynchronously with error handling
await processPayment(order);
client.beacons.assignSingle('order', order.id, 'paid').catch(console.error);
```

### Use Progress Callbacks for Long Operations

```typescript
await client.beacons.runBatchAssignment('demographics_processed', {
  entities: getAllCustomers(),
  onProgress: (completed, total) => {
    updateProgressBar(completed / total);
  },
  onError: (error, batch) => {
    logToMonitoring('batch_processing_error', { error, batchSize: batch.length });
  }
});
```

## Changelog

### v0.4.0 (Latest)
- **🎯 Context-Aware Signal Creation**: Create signals without knowing database structure
- **⚡ Complete Signal Management**: Full CRUD operations for deterministic and beacon signals
- Added `draftWithContext` and `createFromDraft` methods to SignalsClient
- Added `createDeterministicSignal`, `createBeaconSignal`, `draftDeterministicSignal`, `draftBeaconSignal` methods
- Added schema discovery methods: `getAvailableTables`, `getSchemaContext`, `getTableContext`, `getRelevantTables`
- Added automatic table selection based on prompt analysis
- Added comprehensive Socialite integration example with `SocialiteSignalManager` helper class
- Enhanced TypeScript types for signal creation and schema discovery
- Full signal management: list, get, update, delete, toggle signals
- Added `toggleSignal` method for easy active/inactive switching

### v0.3.0
- Enhanced SDK with context APIs implementation
- Added foundational signal creation infrastructure

### v0.2.0
- Added `runBatchAssignment` method for large dataset processing
- Added automatic chunking and retry logic
- Added progress tracking and error callbacks
- Added comprehensive TypeScript types for batch processing
- Added `BatchProcessingError` for detailed error handling

### v0.1.0
- Initial release
- Basic beacon assignment functionality
- `assign` and `assignSingle` methods
- Error handling for common scenarios

## Support

For issues and feature requests, please visit: https://github.com/Uplift-Digital-Strategies/signalite-sdk-js/issues