# @stinkycomputing/sesame-api-client

Official TypeScript/JavaScript client library for the Sesame video production server.

## Features

- 🎯 **Full Protobuf API** - Complete type-safe protobuf definitions for all Sesame APIs
- 🔧 **Command List Helper** - Fluent API for building command lists
- 🌐 **RPC Client** - WebSocket-based RPC client with automatic reconnection
- 📦 **Modular Design** - New v1 API with domain-specific modules
- 🔄 **Backward Compatible** - Supports both legacy and new API structures

## Installation

```bash
npm install @stinkycomputing/sesame-api-client
```

## Quick Start

### Node.js (Full Client with RPC)

For Node.js applications that need the full RPC client:

```typescript
import { SesameClient, CommandList } from '@stinkycomputing/sesame-api-client';

// Create client
const client = new SesameClient(8080);

// Build command list
const cl = new CommandList();
cl.add_source('my-source', {
  type: 'file',
  path: '/path/to/video.mp4'
});
cl.add_compositor('main', 1920, 1080, false);

// Execute commands
await client.execute(cl);

// Listen to events
client.on('status', (status) => {
  console.log('Status update:', status);
});
```

### Browser (Protobuf Types Only)

For browser applications that only need protobuf types (e.g., for decoding messages from WebSocket):

```typescript
import { Sesame } from '@stinkycomputing/sesame-api-client/browser';

// Decode a status message received from WebSocket
const statusBytes = new Uint8Array(data);
const status = Sesame.PB.StatusMessage.decode(statusBytes);
const statusObj = Sesame.PB.StatusMessage.toObject(status, { longs: Number });
console.log('Status:', statusObj);
```

**Browser entry point (`/browser`) includes:**
- ✅ Protobuf types (`Sesame`, `sesame`, `Message`)
- ✅ `CommandList` helper
- ✅ `SesameBinaryProtocol` utilities
- ✅ Logger abstraction
- ❌ No Node.js dependencies (`events`, `ws`)
- ❌ No `RPCClient` or `SesameConnection` classes

## API Structure

### Legacy API (Sesame.PB)

The legacy API uses the `Sesame.PB` namespace:

```typescript
import { Sesame } from '@stinkycomputing/sesame-api-client';

const msg: Sesame.PB.AddSourceMessage = {
  id: 'source1',
  type: Sesame.PB.SourceType.ST_FILE,
  // ...
};
```

### New Modular API (sesame.v1.*)

The new API is organized into domain-specific modules:

```typescript
import { sesame } from '@stinkycomputing/sesame-api-client';

const msg: sesame.v1.sources.SourceAddRequest = {
  id: 'source1',
  type: sesame.v1.sources.SourceType.SOURCE_TYPE_FILE,
  // ...
};
```

#### Available Modules

- `sesame.v1.common` - Common types (Empty, Vec4, PropValue, etc.)
- `sesame.v1.sources` - Source management
- `sesame.v1.outputs` - Output management
- `sesame.v1.compositor` - Compositor and scene graph
- `sesame.v1.audio` - Audio mixer
- `sesame.v1.recorder` - Recorder and clips
- `sesame.v1.jobs` - Background jobs (export/import)
- `sesame.v1.status` - Status and events
- `sesame.v1.commands` - Command list system
- `sesame.v1.rpc` - RPC protocol

## Command List Helper

The `CommandList` class provides a fluent API for building command sequences:

```typescript
const cl = new CommandList();

// Add source
cl.add_source('cam1', {
  type: 'decklink',
  deviceIndex: 0
});

// Add compositor
cl.add_compositor('main', 1920, 1080, false);

// Add node to compositor
cl.add_node('main', 'cam1-node', 'source', {
  sourceId: 'cam1'
});

// Set properties
cl.set_property(
  { compositor: 'main', node: 'cam1-node' },
  'transform',
  'position',
  { vecValue: { r: 100, g: 100 } }
);

// Transport control
cl.add_transport_command('cam1', { type: 'play' });

// Execute all commands
await client.execute(cl);
```

### Animations

Use the `keyframe()` helper to build type-safe animation keyframes. The easing parameters are constrained to the `EaseKind` enum, so invalid values are caught at compile time:

```typescript
import { CommandList, keyframe, EaseKind, sesame } from '@stinkycomputing/sesame-api-client';

const cl = new CommandList();

cl.animate_property(
  { compositor: 'main', node: 'cam1-node' },  // property domain
  'cam1-node',                                  // address
  'opacity',                                    // property name
  sesame.v1.compositor.AnimationChannelEvaluationMode.HOLD,  // before
  sesame.v1.compositor.AnimationChannelEvaluationMode.HOLD,  // after
  [
    keyframe(0,       { floatValue: 0.0 }),
    keyframe(500000,  { floatValue: 1.0 }, EaseKind.QUADRATIC_INOUT),
    keyframe(1000000, { floatValue: 0.5 }, EaseKind.CUBIC_IN, EaseKind.CUBIC_OUT),
  ]
);
```

The `keyframe(timeUs, value, easingIn?, easingOut?)` parameters:
- **timeUs** — time in microseconds
- **value** — a `PropValue` (`floatValue`, `intValue`, `vec4Value`, `stringValue`, or `boolValue`)
- **easingIn** *(optional)* — easing curve entering this keyframe (`EaseKind`)
- **easingOut** *(optional)* — easing curve leaving this keyframe (`EaseKind`)

## Deployment Notes

When using this package in a bundled application (e.g., with esbuild):

- **Bundle the package**: Include `@stinkycomputing/sesame-api-client` in your bundle for zero-dependency deployment
- **External native deps**: Only mark native binary modules as external (`ws`, `bufferutil`, `utf-8-validate`)
- **Production install**: Only `npm install` the native dependencies in production

This approach gives you:
- ✅ Single bundle file with all code
- ✅ Minimal production dependencies (3 packages)
- ✅ Fast deployment
- ✅ No version conflicts

## Publishing

The package is published manually to npm from a local machine.

### Prerequisites (one-time setup)

1. Create the `@stinkycomputing` organization on [npmjs.com](https://www.npmjs.com/org/create)
2. Log in to npm: `npm login`

### Release process

1. Update the version in `package.json`
2. Build: `npm run build`
3. Publish:
   - **Stable release:** `npm publish --access public`
   - **Prerelease:** `npm publish --access public --tag alpha` (or `beta`, etc.)
4. Commit and tag: `git tag api-client-vX.Y.Z && git push origin api-client-vX.Y.Z`

## License

MIT

