@stinkycomputing/sesame-api-client 1.4.1-alpha.8 → 1.4.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,16 +1,10 @@
1
1
  # @stinkycomputing/sesame-api-client
2
2
 
3
- Official TypeScript/JavaScript client library for the Sesame video production server.
3
+ TypeScript client library for the Sesame video production server. Provides type-safe protobuf definitions, a WebSocket RPC client with reconnection, and helpers for building command lists.
4
4
 
5
- ## Features
5
+ All websocket traffic uses protobuf wire framing — see [Wire Protocol](#wire-protocol).
6
6
 
7
- - 🎯 **Full Protobuf API** - Complete type-safe protobuf definitions for all Sesame APIs
8
- - 🔧 **Command List Helper** - Fluent API for building command lists
9
- - 🌐 **RPC Client** - WebSocket-based RPC client with automatic reconnection
10
- - 📦 **Modular Design** - New v1 API with domain-specific modules
11
- - 🔄 **Backward Compatible** - Supports both legacy and new API structures
12
-
13
- ## Installation
7
+ ## Install
14
8
 
15
9
  ```bash
16
10
  npm install @stinkycomputing/sesame-api-client
@@ -18,196 +12,124 @@ npm install @stinkycomputing/sesame-api-client
18
12
 
19
13
  ## Quick Start
20
14
 
21
- ### Node.js (Full Client with RPC)
22
-
23
- For Node.js applications that need the full RPC client:
24
-
25
15
  ```typescript
26
16
  import { SesameClient, CommandList } from '@stinkycomputing/sesame-api-client';
27
17
 
28
- // Create client
29
- const client = new SesameClient(8080);
18
+ const client = new SesameClient(9000);
30
19
 
31
- // Build command list
32
20
  const cl = new CommandList();
33
- cl.add_source('my-source', {
34
- type: 'file',
35
- path: '/path/to/video.mp4'
36
- });
37
- cl.add_compositor('main', 1920, 1080, false);
21
+ cl.sourceAdd('my-source', { type: 'file', path: '/path/to/video.mp4' });
22
+ cl.compositorAdd('main', 1920, 1080, false);
38
23
 
39
- // Execute commands
40
24
  await client.execute(cl);
41
-
42
- // Listen to events
43
- client.on('status', (status) => {
44
- console.log('Status update:', status);
45
- });
46
25
  ```
47
26
 
48
- ### Browser (Protobuf Types Only)
27
+ Works in the browser too — the browser entry point bundles `events` and other Node built-ins so `SesameClient`, `RPCClient`, and `WireProtocol` all work without polyfills.
49
28
 
50
- For browser applications that only need protobuf types (e.g., for decoding messages from WebSocket):
29
+ ## Protobuf Modules
51
30
 
52
- ```typescript
53
- import { Sesame } from '@stinkycomputing/sesame-api-client/browser';
31
+ Types are generated from `.proto` files under `sesame.v1.*`:
54
32
 
55
- // Decode a status message received from WebSocket
56
- const statusBytes = new Uint8Array(data);
57
- const status = Sesame.PB.StatusMessage.decode(statusBytes);
58
- const statusObj = Sesame.PB.StatusMessage.toObject(status, { longs: Number });
59
- console.log('Status:', statusObj);
33
+ ```typescript
34
+ import { sesame } from '@stinkycomputing/sesame-api-client';
60
35
  ```
61
36
 
62
- **Browser entry point (`/browser`) includes:**
63
- - ✅ Protobuf types (`Sesame`, `sesame`, `Message`)
64
- - `CommandList` helper
65
- - `SesameBinaryProtocol` utilities
66
- - Logger abstraction
67
- - No Node.js dependencies (`events`, `ws`)
68
- - ❌ No `RPCClient` or `SesameConnection` classes
37
+ | Module | Contents |
38
+ |--------|----------|
39
+ | `sesame.v1.wire` | Wire framing (FrameHeader, FrameType, MediaCodecData) |
40
+ | `sesame.v1.common` | Shared types (Empty, Vec4, PropValue, EventTopic) |
41
+ | `sesame.v1.sources` | Source config and transport |
42
+ | `sesame.v1.outputs` | Output config and encoder settings |
43
+ | `sesame.v1.compositor` | Scene graph, nodes, properties, animations |
44
+ | `sesame.v1.audio` | Audio mixer and channels |
45
+ | `sesame.v1.recorder` | Recorder, clips, playlists |
46
+ | `sesame.v1.jobs` | Background export/import jobs |
47
+ | `sesame.v1.status` | Status polling and event subscriptions |
48
+ | `sesame.v1.commands` | Command list items |
49
+ | `sesame.v1.rpc` | RPC message envelope (Request/Response/Event) |
69
50
 
70
- ## API Structure
51
+ ## Wire Protocol
71
52
 
72
- ### Legacy API (Sesame.PB)
53
+ Every websocket message is framed as:
73
54
 
74
- The legacy API uses the `Sesame.PB` namespace:
75
-
76
- ```typescript
77
- import { Sesame } from '@stinkycomputing/sesame-api-client';
78
-
79
- const msg: Sesame.PB.AddSourceMessage = {
80
- id: 'source1',
81
- type: Sesame.PB.SourceType.ST_FILE,
82
- // ...
83
- };
55
+ ```
56
+ [4-byte LE header_size][FrameHeader protobuf][payload bytes]
84
57
  ```
85
58
 
86
- ### New Modular API (sesame.v1.*)
87
-
88
- The new API is organized into domain-specific modules:
59
+ `WireProtocol.serialize` / `WireProtocol.parse` handle this:
89
60
 
90
61
  ```typescript
91
- import { sesame } from '@stinkycomputing/sesame-api-client';
62
+ import { WireProtocol, sesame } from '@stinkycomputing/sesame-api-client';
92
63
 
93
- const msg: sesame.v1.sources.SourceAddRequest = {
94
- id: 'source1',
95
- type: sesame.v1.sources.SourceType.SOURCE_TYPE_FILE,
96
- // ...
97
- };
98
- ```
64
+ // serialize
65
+ const frame = WireProtocol.serialize(
66
+ { type: sesame.v1.wire.FrameType.FRAME_TYPE_RPC },
67
+ rpcPayload,
68
+ );
99
69
 
100
- #### Available Modules
70
+ // parse
71
+ const parsed = WireProtocol.parse(incoming);
72
+ if (parsed.valid) {
73
+ // parsed.header.type, parsed.payload
74
+ }
75
+ ```
101
76
 
102
- - `sesame.v1.common` - Common types (Empty, Vec4, PropValue, etc.)
103
- - `sesame.v1.sources` - Source management
104
- - `sesame.v1.outputs` - Output management
105
- - `sesame.v1.compositor` - Compositor and scene graph
106
- - `sesame.v1.audio` - Audio mixer
107
- - `sesame.v1.recorder` - Recorder and clips
108
- - `sesame.v1.jobs` - Background jobs (export/import)
109
- - `sesame.v1.status` - Status and events
110
- - `sesame.v1.commands` - Command list system
111
- - `sesame.v1.rpc` - RPC protocol
77
+ Frame types: `FRAME_TYPE_RPC`, `FRAME_TYPE_VIDEO`, `FRAME_TYPE_AUDIO`, `FRAME_TYPE_MUXED`, `FRAME_TYPE_DECODER_DATA`.
112
78
 
113
- ## Command List Helper
79
+ ## Command List
114
80
 
115
- The `CommandList` class provides a fluent API for building command sequences:
81
+ `CommandList` builds a batch of operations to send in one `execute` call:
116
82
 
117
83
  ```typescript
118
84
  const cl = new CommandList();
119
85
 
120
- // Add source
121
- cl.add_source('cam1', {
122
- type: 'decklink',
123
- deviceIndex: 0
124
- });
125
-
126
- // Add compositor
127
- cl.add_compositor('main', 1920, 1080, false);
128
-
129
- // Add node to compositor
130
- cl.add_node('main', 'cam1-node', 'source', {
131
- sourceId: 'cam1'
132
- });
86
+ cl.sourceAdd('cam1', { type: 'decklink', deviceIndex: 0 });
87
+ cl.compositorAdd('main', 1920, 1080, false);
88
+ cl.nodeAdd('main', 'cam1-node', 'source', { sourceId: 'cam1' });
133
89
 
134
- // Set properties
135
- cl.set_property(
90
+ cl.propertySet(
136
91
  { compositor: 'main', node: 'cam1-node' },
137
- 'transform',
138
- 'position',
139
- { vecValue: { r: 100, g: 100 } }
92
+ 'transform', 'position',
93
+ { vecValue: { r: 100, g: 100 } },
140
94
  );
141
95
 
142
- // Transport control
143
- cl.add_transport_command('cam1', { type: 'play' });
96
+ cl.transportCommand('cam1', { type: 'play' });
144
97
 
145
- // Execute all commands
146
98
  await client.execute(cl);
147
99
  ```
148
100
 
149
101
  ### Animations
150
102
 
151
- 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:
152
-
153
103
  ```typescript
154
104
  import { CommandList, keyframe, EaseKind, sesame } from '@stinkycomputing/sesame-api-client';
155
105
 
156
106
  const cl = new CommandList();
157
-
158
- cl.animate_property(
159
- { compositor: 'main', node: 'cam1-node' }, // property domain
160
- 'cam1-node', // address
161
- 'opacity', // property name
162
- sesame.v1.compositor.AnimationChannelEvaluationMode.HOLD, // before
163
- sesame.v1.compositor.AnimationChannelEvaluationMode.HOLD, // after
107
+ cl.propertyAnimate(
108
+ { compositor: 'main', node: 'cam1-node' },
109
+ 'cam1-node', 'opacity',
110
+ sesame.v1.compositor.AnimationChannelEvaluationMode.HOLD,
111
+ sesame.v1.compositor.AnimationChannelEvaluationMode.HOLD,
164
112
  [
165
113
  keyframe(0, { floatValue: 0.0 }),
166
114
  keyframe(500000, { floatValue: 1.0 }, EaseKind.QUADRATIC_INOUT),
167
115
  keyframe(1000000, { floatValue: 0.5 }, EaseKind.CUBIC_IN, EaseKind.CUBIC_OUT),
168
- ]
116
+ ],
169
117
  );
170
118
  ```
171
119
 
172
- The `keyframe(timeUs, value, easingIn?, easingOut?)` parameters:
173
- - **timeUs** — time in microseconds
174
- - **value** — a `PropValue` (`floatValue`, `intValue`, `vec4Value`, `stringValue`, or `boolValue`)
175
- - **easingIn** *(optional)* — easing curve entering this keyframe (`EaseKind`)
176
- - **easingOut** *(optional)* — easing curve leaving this keyframe (`EaseKind`)
120
+ `keyframe(timeUs, value, easingIn?, easingOut?)` — time in microseconds, value is a `PropValue`, easing uses `EaseKind`.
177
121
 
178
- ## Deployment Notes
122
+ ## Bundling
179
123
 
180
- When using this package in a bundled application (e.g., with esbuild):
181
-
182
- - **Bundle the package**: Include `@stinkycomputing/sesame-api-client` in your bundle for zero-dependency deployment
183
- - **External native deps**: Only mark native binary modules as external (`ws`, `bufferutil`, `utf-8-validate`)
184
- - **Production install**: Only `npm install` the native dependencies in production
185
-
186
- This approach gives you:
187
- - ✅ Single bundle file with all code
188
- - ✅ Minimal production dependencies (3 packages)
189
- - ✅ Fast deployment
190
- - ✅ No version conflicts
124
+ All dependencies (`events`, `long`, `protobufjs`) are pure JS — no native modules. The package bundles cleanly with esbuild or similar without any special `external` config.
191
125
 
192
126
  ## Publishing
193
127
 
194
- The package is published manually to npm from a local machine.
195
-
196
- ### Prerequisites (one-time setup)
197
-
198
- 1. Create the `@stinkycomputing` organization on [npmjs.com](https://www.npmjs.com/org/create)
199
- 2. Log in to npm: `npm login`
200
-
201
- ### Release process
202
-
203
- 1. Update the version in `package.json`
204
- 2. Build: `npm run build`
205
- 3. Publish:
206
- - **Stable release:** `npm publish --access public`
207
- - **Prerelease:** `npm publish --access public --tag alpha` (or `beta`, etc.)
208
- 4. Commit and tag: `git tag api-client-vX.Y.Z && git push origin api-client-vX.Y.Z`
128
+ 1. Bump version in `package.json`
129
+ 2. `npm run build`
130
+ 3. `npm publish --access public` (or `--tag alpha` for prereleases)
131
+ 4. `git tag api-client-vX.Y.Z && git push origin api-client-vX.Y.Z`
209
132
 
210
133
  ## License
211
134
 
212
135
  MIT
213
-
package/dist/browser.cjs CHANGED
@@ -21149,7 +21149,7 @@ var CommandList = class {
21149
21149
  this.cl.commandList.splice(this.cl.commandList.indexOf(item.del), 1);
21150
21150
  });
21151
21151
  }
21152
- get_command_list_msg() {
21152
+ getCommandListMsg() {
21153
21153
  return this.cl;
21154
21154
  }
21155
21155
  callback(event, data, timeOffsetMs) {
@@ -21157,32 +21157,32 @@ var CommandList = class {
21157
21157
  item.callback = { event, data };
21158
21158
  return this.add(item, timeOffsetMs);
21159
21159
  }
21160
- add_compositor(id, width, height, multisample, timeOffsetMs) {
21160
+ compositorAdd(id, width, height, multisample, timeOffsetMs) {
21161
21161
  let item = new sesame.v1.commands.CommandListItem();
21162
21162
  item.addCompositor = { id, multisample, width, height };
21163
21163
  return this.add(item, timeOffsetMs);
21164
21164
  }
21165
- remove_compositor(id, timeOffsetMs) {
21165
+ compositorRemove(id, timeOffsetMs) {
21166
21166
  let item = new sesame.v1.commands.CommandListItem();
21167
21167
  item.removeCompositor = { id };
21168
21168
  return this.add(item, timeOffsetMs);
21169
21169
  }
21170
- clear_compositor(id, timeOffsetMs) {
21170
+ compositorClear(id, timeOffsetMs) {
21171
21171
  let item = new sesame.v1.commands.CommandListItem();
21172
21172
  item.clearCompositor = { id };
21173
21173
  return this.add(item, timeOffsetMs);
21174
21174
  }
21175
- add_source(id, cfg, timeOffsetMs) {
21175
+ sourceAdd(id, cfg, timeOffsetMs) {
21176
21176
  let item = new sesame.v1.commands.CommandListItem();
21177
21177
  item.addSource = { id, config: cfg };
21178
21178
  return this.add(item, timeOffsetMs);
21179
21179
  }
21180
- remove_source(id, timeOffsetMs) {
21180
+ sourceRemove(id, timeOffsetMs) {
21181
21181
  let item = new sesame.v1.commands.CommandListItem();
21182
21182
  item.removeSource = { id };
21183
21183
  return this.add(item, timeOffsetMs);
21184
21184
  }
21185
- add_node(id, cfg, timeOffsetMs) {
21185
+ nodeAdd(id, cfg, timeOffsetMs) {
21186
21186
  let item = new sesame.v1.commands.CommandListItem();
21187
21187
  item.addNode = {
21188
21188
  compositorId: cfg.compositorId,
@@ -21192,12 +21192,12 @@ var CommandList = class {
21192
21192
  };
21193
21193
  return this.add(item, timeOffsetMs);
21194
21194
  }
21195
- remove_node(compositorId, address, timeOffsetMs) {
21195
+ nodeRemove(compositorId, address, timeOffsetMs) {
21196
21196
  let item = new sesame.v1.commands.CommandListItem();
21197
21197
  item.removeNode = { compositorId, address };
21198
21198
  return this.add(item, timeOffsetMs);
21199
21199
  }
21200
- set_property(propertyDomain, addr, prop, val, timeOffsetMs) {
21200
+ propertySet(propertyDomain, addr, prop, val, timeOffsetMs) {
21201
21201
  let item = new sesame.v1.commands.CommandListItem();
21202
21202
  item.setProperty = {
21203
21203
  address: addr,
@@ -21207,7 +21207,7 @@ var CommandList = class {
21207
21207
  };
21208
21208
  return this.add(item, timeOffsetMs);
21209
21209
  }
21210
- add_load_playlist_command(sourceId, playlist) {
21210
+ playlistLoad(sourceId, playlist) {
21211
21211
  const item = new sesame.v1.commands.CommandListItem();
21212
21212
  const loadPlaylistCmd = {
21213
21213
  items: playlist.clips.map((clip) => ({
@@ -21230,13 +21230,13 @@ var CommandList = class {
21230
21230
  item.loadPlaylist = loadPlaylistCmd;
21231
21231
  return this.add(item, 0);
21232
21232
  }
21233
- add_eject_playlist_command(sourceId) {
21233
+ playlistEject(sourceId) {
21234
21234
  const item = new sesame.v1.commands.CommandListItem();
21235
21235
  const ejectPlaylistCmd = { sourceId };
21236
21236
  item.ejectPlaylist = ejectPlaylistCmd;
21237
21237
  return this.add(item, 0);
21238
21238
  }
21239
- add_transport_command(id, msg, timeOffsetMs) {
21239
+ transportCommand(id, msg, timeOffsetMs) {
21240
21240
  let item = new sesame.v1.commands.CommandListItem();
21241
21241
  let transportCmd = { sourceId: id };
21242
21242
  let cmd;
@@ -21309,17 +21309,17 @@ var CommandList = class {
21309
21309
  item.updateSourceTransport = transportCmd;
21310
21310
  return this.add(item, timeOffsetMs, { transactionId: msg.transactionId, dependencies: [] });
21311
21311
  }
21312
- update_source(id, cfg, timeOffsetMs) {
21312
+ sourceUpdate(id, cfg, timeOffsetMs) {
21313
21313
  let item = new sesame.v1.commands.CommandListItem();
21314
21314
  item.updateSource = { id, config: cfg };
21315
21315
  return this.add(item, timeOffsetMs);
21316
21316
  }
21317
- update_source_metadata(id, metadata, timeOffsetMs) {
21317
+ sourceUpdateMetadata(id, metadata, timeOffsetMs) {
21318
21318
  let item = new sesame.v1.commands.CommandListItem();
21319
21319
  item.setSourceMetadata = { sourceId: id, metadata };
21320
21320
  return this.add(item, timeOffsetMs);
21321
21321
  }
21322
- update_source_transport(id, cfg, timeOffsetMs) {
21322
+ sourceUpdateTransport(id, cfg, timeOffsetMs) {
21323
21323
  let item = new sesame.v1.commands.CommandListItem();
21324
21324
  item.updateSourceTransport = {
21325
21325
  sourceId: id,
@@ -21328,47 +21328,47 @@ var CommandList = class {
21328
21328
  };
21329
21329
  return this.add(item, timeOffsetMs);
21330
21330
  }
21331
- add_output(id, cfg, timeOffsetMs) {
21331
+ outputAdd(id, cfg, timeOffsetMs) {
21332
21332
  let item = new sesame.v1.commands.CommandListItem();
21333
21333
  item.addOutput = { id, ...cfg };
21334
21334
  return this.add(item, timeOffsetMs);
21335
21335
  }
21336
- update_output(id, cfg, timeOffsetMs) {
21336
+ outputUpdate(id, cfg, timeOffsetMs) {
21337
21337
  let item = new sesame.v1.commands.CommandListItem();
21338
21338
  item.updateOutput = { id, ...cfg };
21339
21339
  return this.add(item, timeOffsetMs);
21340
21340
  }
21341
- remove_output(id, timeOffsetMs) {
21341
+ outputRemove(id, timeOffsetMs) {
21342
21342
  let item = new sesame.v1.commands.CommandListItem();
21343
21343
  item.removeOutput = { id };
21344
21344
  return this.add(item, timeOffsetMs);
21345
21345
  }
21346
- add_audio_mixer(id, cfg, timeOffsetMs) {
21346
+ audioMixerAdd(id, cfg, timeOffsetMs) {
21347
21347
  let item = new sesame.v1.commands.CommandListItem();
21348
21348
  item.addAudioMixer = { id, config: cfg };
21349
21349
  return this.add(item, timeOffsetMs);
21350
21350
  }
21351
- update_audio_mixer(id, cfg, timeOffsetMs) {
21351
+ audioMixerUpdate(id, cfg, timeOffsetMs) {
21352
21352
  let item = new sesame.v1.commands.CommandListItem();
21353
21353
  item.updateAudioMixer = { id, config: cfg };
21354
21354
  return this.add(item, timeOffsetMs);
21355
21355
  }
21356
- add_audio_mixer_channel(mixerId, cfg, timeOffsetMs) {
21356
+ audioMixerChannelAdd(mixerId, cfg, timeOffsetMs) {
21357
21357
  let item = new sesame.v1.commands.CommandListItem();
21358
21358
  item.addAudioChannel = { mixerId, channelConfig: cfg };
21359
21359
  return this.add(item, timeOffsetMs);
21360
21360
  }
21361
- remove_audio_mixer(mixerId, timeOffsetMs) {
21361
+ audioMixerRemove(mixerId, timeOffsetMs) {
21362
21362
  let item = new sesame.v1.commands.CommandListItem();
21363
21363
  item.removeAudioMixer = { id: mixerId };
21364
21364
  return this.add(item, timeOffsetMs);
21365
21365
  }
21366
- remove_audio_mixer_channel(mixerId, channelId, timeOffsetMs) {
21366
+ audioMixerChannelRemove(mixerId, channelId, timeOffsetMs) {
21367
21367
  let item = new sesame.v1.commands.CommandListItem();
21368
21368
  item.removeAudioChannel = { mixerId, channelId };
21369
21369
  return this.add(item, timeOffsetMs);
21370
21370
  }
21371
- animate_property(propertyDomain, addr, prop, before, after, keyframes, timeOffsetMs) {
21371
+ propertyAnimate(propertyDomain, addr, prop, before, after, keyframes, timeOffsetMs) {
21372
21372
  let item = new sesame.v1.commands.CommandListItem();
21373
21373
  item.animateProperty = {
21374
21374
  address: addr,