@ceralive/cerastream 2026.7.5 → 2026.8.0

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/dist/messages.js CHANGED
@@ -1,7 +1,7 @@
1
- import { z } from "zod";
2
- import { AUDIO_DELAY_MAX_MS } from "./constants.js";
3
- import { helloParamsSchema, helloResultSchema, } from "./envelope.js";
4
- import { balancerAlgorithmSchema, bitrateControlSchema, captureDeviceKindSchema, captureDeviceSchema, cerastreamConfigSchema, configChangePhaseSchema, inputModeSchema, mediaClassSchema, previewTierSchema, streamStateSchema, videoCodecSchema, } from "./types.js";
1
+ import { z } from 'zod';
2
+ import { AUDIO_DELAY_MAX_MS } from './constants.js';
3
+ import { helloParamsSchema, helloResultSchema } from './envelope.js';
4
+ import { balancerAlgorithmSchema, bitrateControlSchema, captureDeviceKindSchema, captureDeviceSchema, cerastreamConfigSchema, configChangePhaseSchema, inputModeSchema, mediaClassSchema, previewEncodeModeSchema, previewTierSchema, streamStateSchema, videoCodecSchema, } from './types.js';
5
5
  // The eight v1 control methods (schema.md "v1 messages"). Each is matched
6
6
  // byte-for-byte to schema.md: the JSON shape on the wire is the contract.
7
7
  // ---- 1. start ----
@@ -45,6 +45,12 @@ export const reloadConfigParamsSchema = z.object({
45
45
  meter_device: z.string().nullable().optional(),
46
46
  })
47
47
  .optional(),
48
+ // Absent leaves the engine's current preview-encoder selection unchanged. This
49
+ // belongs on `reload-config` rather than `change-config`: the preview encoder is
50
+ // fixed when a session graph is built, so it takes effect on the NEXT stream
51
+ // session, and routing it through the transactional method would restart a live
52
+ // stream to change a preview setting.
53
+ preview_encode: previewEncodeModeSchema.optional(),
48
54
  });
49
55
  export const reloadConfigResultSchema = z.object({
50
56
  applied: reloadConfigParamsSchema, // post-clamp values actually applied
@@ -67,7 +73,7 @@ export const setBitrateResultSchema = z.object({
67
73
  // ---- 5. switch-input ----
68
74
  export const switchInputParamsSchema = z.object({
69
75
  input_id: z.string(), // device/input identifier from list-devices
70
- mode: inputModeSchema.default("manual"),
76
+ mode: inputModeSchema.default('manual'),
71
77
  });
72
78
  export const switchInputResultSchema = z.object({
73
79
  active_input: z.string(),
@@ -96,15 +102,15 @@ export const listDevicesResultSchema = z.object({
96
102
  });
97
103
  // ---- 7. subscribe-events ----
98
104
  export const eventTopicSchema = z.enum([
99
- "status",
100
- "switch",
101
- "device",
102
- "bitrate",
103
- "srt-stats",
104
- "error",
105
- "preview",
106
- "audio-level",
107
- "config-change",
105
+ 'status',
106
+ 'switch',
107
+ 'device',
108
+ 'bitrate',
109
+ 'srt-stats',
110
+ 'error',
111
+ 'preview',
112
+ 'audio-level',
113
+ 'config-change',
108
114
  ]);
109
115
  export const subscribeEventsParamsSchema = z.object({
110
116
  topics: z.array(eventTopicSchema).optional(), // default: all topics
@@ -113,14 +119,14 @@ export const subscribeEventsResultSchema = z.object({
113
119
  subscribed: z.array(eventTopicSchema),
114
120
  });
115
121
  // ---- 8. preview-session ----
116
- export const previewSessionParamsSchema = z.discriminatedUnion("action", [
122
+ export const previewSessionParamsSchema = z.discriminatedUnion('action', [
117
123
  z.object({
118
- action: z.literal("start"),
124
+ action: z.literal('start'),
119
125
  tier: previewTierSchema,
120
126
  }),
121
- z.object({ action: z.literal("stop"), session_id: z.string() }),
127
+ z.object({ action: z.literal('stop'), session_id: z.string() }),
122
128
  z.object({
123
- action: z.literal("signal"), // webrtc only
129
+ action: z.literal('signal'), // webrtc only
124
130
  session_id: z.string(),
125
131
  sdp: z.string().optional(),
126
132
  ice: z.unknown().optional(),
@@ -129,12 +135,12 @@ export const previewSessionParamsSchema = z.discriminatedUnion("action", [
129
135
  export const previewSessionResultSchema = z.object({
130
136
  session_id: z.string(),
131
137
  tier: previewTierSchema,
132
- transport: z.discriminatedUnion("kind", [
138
+ transport: z.discriminatedUnion('kind', [
133
139
  z.object({
134
- kind: z.literal("uds-binary"),
135
- socket: z.literal("/run/cerastream/preview.sock"),
140
+ kind: z.literal('uds-binary'),
141
+ socket: z.literal('/run/cerastream/preview.sock'),
136
142
  }),
137
- z.object({ kind: z.literal("webrtc"), sdp: z.string().optional() }),
143
+ z.object({ kind: z.literal('webrtc'), sdp: z.string().optional() }),
138
144
  ]),
139
145
  });
140
146
  // ---- 9. get-capabilities ----
@@ -170,8 +176,8 @@ export const platformCapsSchema = z.object({
170
176
  max_resolution: z.string(),
171
177
  // additive: the engine's resolved hardware kind and how it was resolved.
172
178
  // Optional so a pre-field engine (which omits both) still parses.
173
- hardware_kind: z.enum(["rk3588", "jetson", "n100", "generic"]).optional(),
174
- source: z.enum(["detected", "override"]).optional(),
179
+ hardware_kind: z.enum(['rk3588', 'jetson', 'n100', 'generic']).optional(),
180
+ source: z.enum(['detected', 'override']).optional(),
175
181
  });
176
182
  // Preview-server availability (0.4.0, additive). Lets the UI tell an unbound /
177
183
  // port-conflicted preview (enabled:true, bound:false) from a down engine.
@@ -179,6 +185,15 @@ export const previewAvailabilitySchema = z.object({
179
185
  enabled: z.boolean(),
180
186
  port: z.number().int().min(1).max(65535).optional(), // present when a listener is (or would be) bound
181
187
  bound: z.boolean(),
188
+ // Whether this PLATFORM publishes a hardware preview encoder at all — the fact
189
+ // that decides whether a hardware-preview toggle may be offered. It rides the
190
+ // capability fetch rather than stream status because a consumer reads
191
+ // capabilities while IDLE, which is exactly when the operator picks the setting.
192
+ // Three readings, none interchangeable: absent ⇒ legacy engine, capability
193
+ // UNKNOWN (never assume false); false ⇒ this board publishes none, hide the
194
+ // toggle; true ⇒ it does. Independent of whether any session is realizing it
195
+ // (`status.preview_encoder_realized`).
196
+ preview_hw_capability: z.boolean().optional(),
182
197
  });
183
198
  export const getCapabilitiesResultSchema = z.object({
184
199
  platform: platformCapsSchema,
@@ -216,7 +231,7 @@ export const changeConfigParamsSchema = z
216
231
  input_mode: captureDeviceKindSchema.optional(),
217
232
  })
218
233
  .refine((delta) => Object.keys(delta).length > 0, {
219
- message: "change-config delta must carry at least one field",
234
+ message: 'change-config delta must carry at least one field',
220
235
  });
221
236
  // `Ok` for every phase the transaction actually reached — INCLUDING
222
237
  // `rollback_failed`, an honest terminal outcome rather than an RPC fault. A
@@ -235,18 +250,18 @@ export const changeConfigResultSchema = z.object({
235
250
  * refuses to build a second session that would race it. Mirrors the Rust
236
251
  * `REASON_TEARDOWN_TIMEOUT`; consumers render it distinctly.
237
252
  */
238
- export const REASON_TEARDOWN_TIMEOUT = "teardown_timeout";
253
+ export const REASON_TEARDOWN_TIMEOUT = 'teardown_timeout';
239
254
  // ---- method registry (count-assertion source of truth) ----
240
255
  /** The eight v1 control methods (the literal JSON-RPC `method` strings). */
241
256
  export const V1_METHODS = [
242
- "start",
243
- "stop",
244
- "reload-config",
245
- "set-bitrate",
246
- "switch-input",
247
- "list-devices",
248
- "subscribe-events",
249
- "preview-session",
257
+ 'start',
258
+ 'stop',
259
+ 'reload-config',
260
+ 'set-bitrate',
261
+ 'switch-input',
262
+ 'list-devices',
263
+ 'subscribe-events',
264
+ 'preview-session',
250
265
  ];
251
266
  /**
252
267
  * method → { params, result } Zod schema pair. `hello` is the handshake; the
@@ -257,27 +272,27 @@ export const requestSchemas = {
257
272
  hello: { params: helloParamsSchema, result: helloResultSchema },
258
273
  start: { params: startParamsSchema, result: startResultSchema },
259
274
  stop: { params: stopParamsSchema, result: stopResultSchema },
260
- "reload-config": {
275
+ 'reload-config': {
261
276
  params: reloadConfigParamsSchema,
262
277
  result: reloadConfigResultSchema,
263
278
  },
264
- "set-bitrate": {
279
+ 'set-bitrate': {
265
280
  params: setBitrateParamsSchema,
266
281
  result: setBitrateResultSchema,
267
282
  },
268
- "switch-input": {
283
+ 'switch-input': {
269
284
  params: switchInputParamsSchema,
270
285
  result: switchInputResultSchema,
271
286
  },
272
- "list-devices": {
287
+ 'list-devices': {
273
288
  params: listDevicesParamsSchema,
274
289
  result: listDevicesResultSchema,
275
290
  },
276
- "subscribe-events": {
291
+ 'subscribe-events': {
277
292
  params: subscribeEventsParamsSchema,
278
293
  result: subscribeEventsResultSchema,
279
294
  },
280
- "preview-session": {
295
+ 'preview-session': {
281
296
  params: previewSessionParamsSchema,
282
297
  result: previewSessionResultSchema,
283
298
  },
package/dist/paths.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { CONTROL_SOCKET_NAME, DEFAULT_IPC_DIR, PREVIEW_SOCKET_NAME } from "./constants.js";
1
+ import { CONTROL_SOCKET_NAME, DEFAULT_IPC_DIR, PREVIEW_SOCKET_NAME } from './constants.js';
2
2
  /** Resolve the IPC runtime dir: explicit override → {@link IPC_DIR_ENV} → default. */
3
3
  export declare function resolveIpcDir(override?: string): string;
4
4
  /** Resolve the control-plane socket path within the (possibly overridden) IPC dir. */
@@ -11,4 +11,4 @@ export declare function previewSocketPath(dir?: string): string;
11
11
  * `/usr/bin/cerastream` → the bare name (let PATH decide).
12
12
  */
13
13
  export declare function getCerastreamExec(execPath?: string): string;
14
- export { DEFAULT_IPC_DIR, CONTROL_SOCKET_NAME, PREVIEW_SOCKET_NAME };
14
+ export { CONTROL_SOCKET_NAME, DEFAULT_IPC_DIR, PREVIEW_SOCKET_NAME };
package/dist/paths.js CHANGED
@@ -1,7 +1,7 @@
1
- import { execSync } from "node:child_process";
2
- import { existsSync, statSync } from "node:fs";
3
- import { join } from "node:path";
4
- import { CERASTREAM_BIN, CONTROL_SOCKET_NAME, DEFAULT_IPC_DIR, IPC_DIR_ENV, PREVIEW_SOCKET_NAME, } from "./constants.js";
1
+ import { execSync } from 'node:child_process';
2
+ import { existsSync, statSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { CERASTREAM_BIN, CONTROL_SOCKET_NAME, DEFAULT_IPC_DIR, IPC_DIR_ENV, PREVIEW_SOCKET_NAME, } from './constants.js';
5
5
  // Socket-path + engine-exec helpers (ADR-0002 §3). They resolve the runtime IPC
6
6
  // dir from the CERASTREAM_IPC_DIR override (then the explicit arg), defaulting to
7
7
  // /run/cerastream. cerastream is a systemd-owned service (ADR-0005) — CeraUI
@@ -34,9 +34,7 @@ export function getCerastreamExec(execPath) {
34
34
  const inDir = join(execPath, CERASTREAM_BIN);
35
35
  if (isFile(inDir))
36
36
  return inDir;
37
- return execPath.endsWith(CERASTREAM_BIN)
38
- ? execPath
39
- : join(execPath, CERASTREAM_BIN);
37
+ return execPath.endsWith(CERASTREAM_BIN) ? execPath : join(execPath, CERASTREAM_BIN);
40
38
  }
41
39
  const onPath = findInPath(CERASTREAM_BIN);
42
40
  if (onPath)
@@ -57,10 +55,10 @@ function isFile(p) {
57
55
  function findInPath(name) {
58
56
  try {
59
57
  const out = execSync(`command -v ${name}`, {
60
- encoding: "utf-8",
61
- stdio: ["pipe", "pipe", "pipe"],
58
+ encoding: 'utf-8',
59
+ stdio: ['pipe', 'pipe', 'pipe'],
62
60
  }).trim();
63
- const first = out.split("\n")[0]?.trim();
61
+ const first = out.split('\n')[0]?.trim();
64
62
  if (first && existsSync(first))
65
63
  return first;
66
64
  }
@@ -69,4 +67,4 @@ function findInPath(name) {
69
67
  }
70
68
  return undefined;
71
69
  }
72
- export { DEFAULT_IPC_DIR, CONTROL_SOCKET_NAME, PREVIEW_SOCKET_NAME };
70
+ export { CONTROL_SOCKET_NAME, DEFAULT_IPC_DIR, PREVIEW_SOCKET_NAME };
package/dist/transport.js CHANGED
@@ -1,4 +1,4 @@
1
- import { MAX_LINE_BYTES } from "./constants.js";
1
+ import { MAX_LINE_BYTES } from './constants.js';
2
2
  /**
3
3
  * A single Unix-socket connection that frames bytes into NDJSON lines. Construct,
4
4
  * `open()` (rejects if the socket can't be reached), `send()` lines, `close()`
@@ -7,7 +7,7 @@ import { MAX_LINE_BYTES } from "./constants.js";
7
7
  export class LineSocket {
8
8
  socketPath;
9
9
  socket;
10
- buffer = "";
10
+ buffer = '';
11
11
  bufferedBytes = 0;
12
12
  handlers;
13
13
  closed = false;
@@ -39,7 +39,7 @@ export class LineSocket {
39
39
  */
40
40
  send(line) {
41
41
  if (this.closed || !this.socket) {
42
- throw new Error("LineSocket.send: socket is not open");
42
+ throw new Error('LineSocket.send: socket is not open');
43
43
  }
44
44
  this.socket.write(`${line}\n`);
45
45
  this.socket.flush();
@@ -61,9 +61,9 @@ export class LineSocket {
61
61
  onData(chunk) {
62
62
  if (this.closed)
63
63
  return;
64
- this.buffer += chunk.toString("utf8");
64
+ this.buffer += chunk.toString('utf8');
65
65
  this.bufferedBytes += chunk.byteLength;
66
- let nl = this.buffer.indexOf("\n");
66
+ let nl = this.buffer.indexOf('\n');
67
67
  if (nl === -1) {
68
68
  // No complete line yet — guard the unbounded-line case (fatal framing error).
69
69
  if (this.bufferedBytes > MAX_LINE_BYTES) {
@@ -80,9 +80,9 @@ export class LineSocket {
80
80
  }
81
81
  if (line.length > 0)
82
82
  this.handlers?.onLine(line);
83
- nl = this.buffer.indexOf("\n");
83
+ nl = this.buffer.indexOf('\n');
84
84
  }
85
- this.bufferedBytes = Buffer.byteLength(this.buffer, "utf8");
85
+ this.bufferedBytes = Buffer.byteLength(this.buffer, 'utf8');
86
86
  }
87
87
  fail(err) {
88
88
  this.fireClose(err);
package/dist/types.d.ts CHANGED
@@ -1,25 +1,25 @@
1
- import { z } from "zod";
1
+ import { z } from 'zod';
2
2
  export declare const streamStateSchema: z.ZodEnum<{
3
3
  idle: "idle";
4
4
  starting: "starting";
5
- streaming: "streaming";
6
5
  stopping: "stopping";
6
+ streaming: "streaming";
7
7
  }>;
8
8
  export type StreamState = z.infer<typeof streamStateSchema>;
9
9
  export declare const inputModeSchema: z.ZodEnum<{
10
- manual: "manual";
11
10
  auto: "auto";
11
+ manual: "manual";
12
12
  }>;
13
13
  export type InputMode = z.infer<typeof inputModeSchema>;
14
14
  export declare const balancerAlgorithmSchema: z.ZodEnum<{
15
15
  adaptive: "adaptive";
16
- fixed: "fixed";
17
16
  aimd: "aimd";
17
+ fixed: "fixed";
18
18
  }>;
19
19
  export type BalancerAlgorithm = z.infer<typeof balancerAlgorithmSchema>;
20
20
  export declare const mediaClassSchema: z.ZodEnum<{
21
- video: "video";
22
21
  audio: "audio";
22
+ video: "video";
23
23
  }>;
24
24
  export type MediaClass = z.infer<typeof mediaClassSchema>;
25
25
  export declare const previewTierSchema: z.ZodEnum<{
@@ -27,6 +27,18 @@ export declare const previewTierSchema: z.ZodEnum<{
27
27
  webrtc: "webrtc";
28
28
  }>;
29
29
  export type PreviewTier = z.infer<typeof previewTierSchema>;
30
+ export declare const previewEncodeModeSchema: z.ZodEnum<{
31
+ hardware: "hardware";
32
+ software: "software";
33
+ }>;
34
+ export type PreviewEncodeMode = z.infer<typeof previewEncodeModeSchema>;
35
+ export declare const previewEncodeFallbackSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
36
+ code: z.ZodLiteral<"factory-missing">;
37
+ }, z.core.$strip>, z.ZodObject<{
38
+ code: z.ZodLiteral<"property-failure">;
39
+ property: z.ZodString;
40
+ }, z.core.$strip>], "code">;
41
+ export type PreviewEncodeFallback = z.infer<typeof previewEncodeFallbackSchema>;
30
42
  export declare const videoCodecSchema: z.ZodEnum<{
31
43
  h264: "h264";
32
44
  h265: "h265";
@@ -45,25 +57,25 @@ export declare const bitrateControlSchema: z.ZodEnum<{
45
57
  export type BitrateControl = z.infer<typeof bitrateControlSchema>;
46
58
  export declare const captureDeviceKindSchema: z.ZodEnum<{
47
59
  audio: "audio";
60
+ camlink: "camlink";
48
61
  hdmi: "hdmi";
49
- uvc_h264: "uvc_h264";
50
- uvc_h265: "uvc_h265";
51
62
  mjpeg: "mjpeg";
52
- camlink: "camlink";
53
- test: "test";
54
63
  network: "network";
64
+ test: "test";
65
+ uvc_h264: "uvc_h264";
66
+ uvc_h265: "uvc_h265";
55
67
  }>;
56
68
  export type CaptureDeviceKind = z.infer<typeof captureDeviceKindSchema>;
57
69
  export declare const deviceTransportSchema: z.ZodEnum<{
58
- hdmi: "hdmi";
59
- usb: "usb";
60
70
  bluetooth: "bluetooth";
71
+ hdmi: "hdmi";
61
72
  onboard: "onboard";
73
+ usb: "usb";
62
74
  }>;
63
75
  export type DeviceTransport = z.infer<typeof deviceTransportSchema>;
64
76
  export declare const configChangePhaseSchema: z.ZodEnum<{
65
- applying: "applying";
66
77
  applied: "applied";
78
+ applying: "applying";
67
79
  reverted: "reverted";
68
80
  rollback_failed: "rollback_failed";
69
81
  }>;
@@ -86,8 +98,8 @@ export declare const bitrateConfigSchema: z.ZodObject<{
86
98
  max_bitrate: z.ZodNumber;
87
99
  balancer: z.ZodDefault<z.ZodEnum<{
88
100
  adaptive: "adaptive";
89
- fixed: "fixed";
90
101
  aimd: "aimd";
102
+ fixed: "fixed";
91
103
  }>>;
92
104
  }, z.core.$strip>;
93
105
  export type BitrateConfig = z.infer<typeof bitrateConfigSchema>;
@@ -100,15 +112,15 @@ export type BitrateConfig = z.infer<typeof bitrateConfigSchema>;
100
112
  */
101
113
  export declare const audioModeSchema: z.ZodEnum<{
102
114
  default: "default";
103
- none: "none";
104
115
  device: "device";
116
+ none: "none";
105
117
  }>;
106
118
  export type AudioMode = z.infer<typeof audioModeSchema>;
107
119
  export declare const audioConfigSchema: z.ZodObject<{
108
120
  mode: z.ZodOptional<z.ZodEnum<{
109
121
  default: "default";
110
- none: "none";
111
122
  device: "device";
123
+ none: "none";
112
124
  }>>;
113
125
  device: z.ZodOptional<z.ZodString>;
114
126
  codec: z.ZodOptional<z.ZodString>;
@@ -138,8 +150,8 @@ export declare const cerastreamConfigSchema: z.ZodObject<{
138
150
  max_bitrate: z.ZodNumber;
139
151
  balancer: z.ZodDefault<z.ZodEnum<{
140
152
  adaptive: "adaptive";
141
- fixed: "fixed";
142
153
  aimd: "aimd";
154
+ fixed: "fixed";
143
155
  }>>;
144
156
  }, z.core.$strip>;
145
157
  input_id: z.ZodOptional<z.ZodString>;
@@ -152,8 +164,8 @@ export declare const cerastreamConfigSchema: z.ZodObject<{
152
164
  audio: z.ZodOptional<z.ZodObject<{
153
165
  mode: z.ZodOptional<z.ZodEnum<{
154
166
  default: "default";
155
- none: "none";
156
167
  device: "device";
168
+ none: "none";
157
169
  }>>;
158
170
  device: z.ZodOptional<z.ZodString>;
159
171
  codec: z.ZodOptional<z.ZodString>;
@@ -166,13 +178,13 @@ export declare const cerastreamConfigSchema: z.ZodObject<{
166
178
  }>>;
167
179
  input_mode: z.ZodOptional<z.ZodEnum<{
168
180
  audio: "audio";
181
+ camlink: "camlink";
169
182
  hdmi: "hdmi";
170
- uvc_h264: "uvc_h264";
171
- uvc_h265: "uvc_h265";
172
183
  mjpeg: "mjpeg";
173
- camlink: "camlink";
174
- test: "test";
175
184
  network: "network";
185
+ test: "test";
186
+ uvc_h264: "uvc_h264";
187
+ uvc_h265: "uvc_h265";
176
188
  }>>;
177
189
  }, z.core.$strip>;
178
190
  export type CerastreamConfig = z.infer<typeof cerastreamConfigSchema>;
@@ -194,13 +206,13 @@ export declare const captureModeSchema: z.ZodObject<{
194
206
  media_type: z.ZodString;
195
207
  pipeline_kind: z.ZodEnum<{
196
208
  audio: "audio";
209
+ camlink: "camlink";
197
210
  hdmi: "hdmi";
198
- uvc_h264: "uvc_h264";
199
- uvc_h265: "uvc_h265";
200
211
  mjpeg: "mjpeg";
201
- camlink: "camlink";
202
- test: "test";
203
212
  network: "network";
213
+ test: "test";
214
+ uvc_h264: "uvc_h264";
215
+ uvc_h265: "uvc_h265";
204
216
  }>;
205
217
  caps: z.ZodArray<z.ZodObject<{
206
218
  width: z.ZodOptional<z.ZodNumber>;
@@ -215,8 +227,8 @@ export declare const captureDeviceSchema: z.ZodObject<{
215
227
  device_path: z.ZodString;
216
228
  display_name: z.ZodString;
217
229
  media_class: z.ZodEnum<{
218
- video: "video";
219
230
  audio: "audio";
231
+ video: "video";
220
232
  }>;
221
233
  caps: z.ZodOptional<z.ZodArray<z.ZodObject<{
222
234
  width: z.ZodOptional<z.ZodNumber>;
@@ -226,25 +238,25 @@ export declare const captureDeviceSchema: z.ZodObject<{
226
238
  }, z.core.$strip>>>;
227
239
  kind: z.ZodOptional<z.ZodEnum<{
228
240
  audio: "audio";
241
+ camlink: "camlink";
229
242
  hdmi: "hdmi";
230
- uvc_h264: "uvc_h264";
231
- uvc_h265: "uvc_h265";
232
243
  mjpeg: "mjpeg";
233
- camlink: "camlink";
234
- test: "test";
235
244
  network: "network";
245
+ test: "test";
246
+ uvc_h264: "uvc_h264";
247
+ uvc_h265: "uvc_h265";
236
248
  }>>;
237
249
  modes: z.ZodOptional<z.ZodArray<z.ZodObject<{
238
250
  media_type: z.ZodString;
239
251
  pipeline_kind: z.ZodEnum<{
240
252
  audio: "audio";
253
+ camlink: "camlink";
241
254
  hdmi: "hdmi";
242
- uvc_h264: "uvc_h264";
243
- uvc_h265: "uvc_h265";
244
255
  mjpeg: "mjpeg";
245
- camlink: "camlink";
246
- test: "test";
247
256
  network: "network";
257
+ test: "test";
258
+ uvc_h264: "uvc_h264";
259
+ uvc_h265: "uvc_h265";
248
260
  }>;
249
261
  caps: z.ZodArray<z.ZodObject<{
250
262
  width: z.ZodOptional<z.ZodNumber>;
@@ -256,10 +268,10 @@ export declare const captureDeviceSchema: z.ZodObject<{
256
268
  alsa_card_id: z.ZodOptional<z.ZodString>;
257
269
  product_name: z.ZodOptional<z.ZodString>;
258
270
  transport: z.ZodOptional<z.ZodEnum<{
259
- hdmi: "hdmi";
260
- usb: "usb";
261
271
  bluetooth: "bluetooth";
272
+ hdmi: "hdmi";
262
273
  onboard: "onboard";
274
+ usb: "usb";
263
275
  }>>;
264
276
  stable_id: z.ZodOptional<z.ZodString>;
265
277
  physical_group_id: z.ZodOptional<z.ZodString>;
package/dist/types.js CHANGED
@@ -1,57 +1,63 @@
1
- import { z } from "zod";
2
- import { DEFAULT_BALANCER, DEFAULT_MAX_BITRATE, DEFAULT_MIN_BITRATE, DEFAULT_SRT_LATENCY, } from "./constants.js";
1
+ import { z } from 'zod';
2
+ import { DEFAULT_BALANCER, DEFAULT_MAX_BITRATE, DEFAULT_MIN_BITRATE, DEFAULT_SRT_LATENCY, } from './constants.js';
3
3
  // Shared domain enums + the unified engine config. Field names are domain-level
4
4
  // (snake_case, matching the ceracoder config convention) — never engine
5
5
  // internals (schema.md "Conventions").
6
- export const streamStateSchema = z.enum([
7
- "idle",
8
- "starting",
9
- "streaming",
10
- "stopping",
11
- ]);
12
- export const inputModeSchema = z.enum(["manual", "auto"]);
6
+ export const streamStateSchema = z.enum(['idle', 'starting', 'streaming', 'stopping']);
7
+ export const inputModeSchema = z.enum(['manual', 'auto']);
13
8
  // re-used from ceracoder (schema.md §"v1 messages" shared fragments)
14
- export const balancerAlgorithmSchema = z.enum(["adaptive", "fixed", "aimd"]);
15
- export const mediaClassSchema = z.enum(["video", "audio"]);
16
- export const previewTierSchema = z.enum(["webcodecs", "webrtc"]);
9
+ export const balancerAlgorithmSchema = z.enum(['adaptive', 'fixed', 'aimd']);
10
+ export const mediaClassSchema = z.enum(['video', 'audio']);
11
+ export const previewTierSchema = z.enum(['webcodecs', 'webrtc']);
12
+ // Which encoder the LIVE preview branch uses. Set through `reload-config`
13
+ // (additive) and reported back as the ACTIVE mode on stream status. Applies to
14
+ // the NEXT stream session: the preview encoder is fixed when a session graph is
15
+ // built, so this never swaps an encoder mid-stream.
16
+ export const previewEncodeModeSchema = z.enum(['software', 'hardware']);
17
+ // Why a `hardware` preview request is encoding in software anyway — a tagged
18
+ // union so a consumer branches on `code` and still receives the detail a code
19
+ // carries. `property-failure` names the property the element refused, which is
20
+ // the difference between "hardware preview is broken" and "this image's encoder
21
+ // plugin does not take `rc-mode`". ABSENT is the normal case (software was
22
+ // chosen, hardware was delivered, or the board never offered the toggle) — there
23
+ // is deliberately no `{code:"none"}` sentinel.
24
+ export const previewEncodeFallbackSchema = z.discriminatedUnion('code', [
25
+ z.object({ code: z.literal('factory-missing') }),
26
+ z.object({ code: z.literal('property-failure'), property: z.string() }),
27
+ ]);
17
28
  // Requested egress video codec (0.4.0, additive). Absent ⇒ the engine picks its
18
29
  // platform default. Wire values are the codec id strings the encoder caps also
19
30
  // advertise ("h264"/"h265") — never an encoder element name.
20
- export const videoCodecSchema = z.enum(["h264", "h265"]);
31
+ export const videoCodecSchema = z.enum(['h264', 'h265']);
21
32
  // Same-codec passthrough policy (0.5.0, additive). Absent ⇒ "auto". "auto" =
22
33
  // passthrough only when adaptive bitrate is inactive and eligibility holds;
23
34
  // "force" = passthrough whenever eligible (typed start failure otherwise);
24
35
  // "off" = always transcode. See schema.md and the engine's layer-C matrix.
25
- export const videoPassthroughSchema = z.enum(["auto", "force", "off"]);
36
+ export const videoPassthroughSchema = z.enum(['auto', 'force', 'off']);
26
37
  // Which surface owns the running bitrate, reported additively on a set-bitrate /
27
38
  // reload-config response (0.5.0). "encoder" = applied; "source-fixed" = a
28
39
  // passthrough graph fixes the bitrate at the source, so the request was NOT
29
40
  // applied (paired with reason "passthrough").
30
- export const bitrateControlSchema = z.enum(["encoder", "source-fixed"]);
41
+ export const bitrateControlSchema = z.enum(['encoder', 'source-fixed']);
31
42
  // Engine-typed capture-device family (0.4.0, additive). CeraUI groups a device
32
43
  // by the engine's resolved kind (a UVC/USB dongle is uvc_h264/mjpeg, never
33
44
  // mislabeled HDMI). Absent ⇒ CeraUI falls back to a bus/name heuristic.
34
45
  export const captureDeviceKindSchema = z.enum([
35
- "hdmi",
36
- "uvc_h264",
37
- "uvc_h265",
38
- "mjpeg",
39
- "camlink",
40
- "audio",
41
- "test",
42
- "network",
46
+ 'hdmi',
47
+ 'uvc_h264',
48
+ 'uvc_h265',
49
+ 'mjpeg',
50
+ 'camlink',
51
+ 'audio',
52
+ 'test',
53
+ 'network',
43
54
  ]);
44
55
  // How a capture device is physically attached (Todo 20, additive). The transport
45
56
  // tag CeraUI renders beside the product name (e.g. "RØDE NT-USB · USB"). Distinct
46
57
  // from captureDeviceKind: an HDMI-to-USB dongle is kind:uvc_h264 but transport:usb,
47
58
  // while the SoC's dedicated HDMI-RX port is transport:hdmi. Absent on a legacy
48
59
  // producer or when no hint identifies a transport.
49
- export const deviceTransportSchema = z.enum([
50
- "usb",
51
- "hdmi",
52
- "bluetooth",
53
- "onboard",
54
- ]);
60
+ export const deviceTransportSchema = z.enum(['usb', 'hdmi', 'bluetooth', 'onboard']);
55
61
  // Phase of one `change-config` transaction (0.10.0, additive). `applying` is
56
62
  // published once at entry, then exactly ONE terminal phase for the same
57
63
  // attempt_id: `applied` (the new config satisfied the outcome gate),
@@ -59,10 +65,10 @@ export const deviceTransportSchema = z.enum([
59
65
  // `rollback_failed` (no rollback was possible, or the one attempt also failed —
60
66
  // the engine is idle, which is the truthful terminal state).
61
67
  export const configChangePhaseSchema = z.enum([
62
- "applying",
63
- "applied",
64
- "reverted",
65
- "rollback_failed",
68
+ 'applying',
69
+ 'applied',
70
+ 'reverted',
71
+ 'rollback_failed',
66
72
  ]);
67
73
  // ---- config sub-schemas (canonical; reused by `start` + the unified config) ----
68
74
  /** SRT transport config. Mirrors schema.md `start.srt` exactly. */
@@ -90,8 +96,8 @@ export const bitrateConfigSchema = z
90
96
  })
91
97
  // Mirrors the Rust dispatch guard: an inverted range can't drive the balancer.
92
98
  .refine((b) => b.min_bitrate <= b.max_bitrate, {
93
- message: "min_bitrate must not exceed max_bitrate",
94
- path: ["min_bitrate"],
99
+ message: 'min_bitrate must not exceed max_bitrate',
100
+ path: ['min_bitrate'],
95
101
  });
96
102
  /**
97
103
  * Audio config section of the unified engine config (0.4.0, additive). Every
@@ -106,7 +112,7 @@ export const bitrateConfigSchema = z
106
112
  // leg (requires `device`). Absent ⇒ legacy inference from `device`/source kind,
107
113
  // so a pre-0.6.0 caller keeps working. Replaces leaking pseudo-source strings
108
114
  // (e.g. "No audio") into `device`.
109
- export const audioModeSchema = z.enum(["none", "default", "device"]);
115
+ export const audioModeSchema = z.enum(['none', 'default', 'device']);
110
116
  export const audioConfigSchema = z.object({
111
117
  mode: audioModeSchema.optional(),
112
118
  device: z.string().optional(), // ALSA capture device id; absent ⇒ test-tone fallback