@ceralive/cerastream 2026.8.1 → 2026.9.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
@@ -8,6 +8,7 @@ engine's JSON-RPC 2.0 / NDJSON control plane over a Unix domain socket.
8
8
  - The eight v1 control methods (Zod params + result schemas + inferred types)
9
9
  - The nine server-push event payloads (discriminated union)
10
10
  - Two-tier error codes (RPC + runtime)
11
+ - Typed capture-probe causes retained on runtime events and rejected-start exceptions
11
12
  - A unified engine config schema (= `start` params)
12
13
  - A `CerastreamClient` interface + `connect()` factory (UDS transport)
13
14
  - Additive `client.getCapabilities()` discovery for platform, source, encoder, and
package/dist/client.js CHANGED
@@ -183,7 +183,7 @@ class ClientImpl {
183
183
  if (!parsed.success)
184
184
  return;
185
185
  const { id, error } = parsed.data;
186
- const rpcErr = new CerastreamRpcError(error.code, error.message, error.data?.code, id);
186
+ const rpcErr = new CerastreamRpcError(error.code, error.message, error.data?.code, id, error.data);
187
187
  if (id == null)
188
188
  return; // parse error with null id — no request to settle.
189
189
  const pending = this.pending.get(id);
@@ -8,7 +8,7 @@ export declare const PROTOCOL_VERSION: 'cerastream-ipc/1';
8
8
  * additive-only within protocol major `cerastream-ipc/1` (ADR-0002 §4); this value
9
9
  * only moves when the wire schema itself does, in lockstep across both languages.
10
10
  */
11
- export declare const SCHEMA_VERSION: '0.11.0';
11
+ export declare const SCHEMA_VERSION: '0.13.0';
12
12
  /** Runtime dir holding both control + preview sockets. systemd `RuntimeDirectory=cerastream`. */
13
13
  export declare const DEFAULT_IPC_DIR: '/run/cerastream';
14
14
  /** Env override for the IPC dir (tests/dev). Defaults to {@link DEFAULT_IPC_DIR}. */
package/dist/constants.js CHANGED
@@ -11,7 +11,7 @@ export const PROTOCOL_VERSION = 'cerastream-ipc/1';
11
11
  * additive-only within protocol major `cerastream-ipc/1` (ADR-0002 §4); this value
12
12
  * only moves when the wire schema itself does, in lockstep across both languages.
13
13
  */
14
- export const SCHEMA_VERSION = '0.11.0';
14
+ export const SCHEMA_VERSION = '0.13.0';
15
15
  /** Runtime dir holding both control + preview sockets. systemd `RuntimeDirectory=cerastream`. */
16
16
  export const DEFAULT_IPC_DIR = '/run/cerastream';
17
17
  /** Env override for the IPC dir (tests/dev). Defaults to {@link DEFAULT_IPC_DIR}. */
package/dist/errors.d.ts CHANGED
@@ -20,7 +20,11 @@ export declare class CerastreamRpcError extends Error {
20
20
  readonly dataCode: string | undefined;
21
21
  /** The request id this error answered, when present. */
22
22
  readonly requestId: number | string | null;
23
- constructor(code: number, message: string, dataCode: string | undefined, requestId: number | string | null);
23
+ /** Full JSON-RPC `error.data` object, retained for additive typed detail. */
24
+ readonly data?: unknown;
25
+ constructor(code: number, message: string, dataCode: string | undefined, requestId: number | string | null, data?: unknown);
26
+ /** Typed capture-probe failures carried by a rejected stream start. */
27
+ captureCauses(): readonly CaptureCauseEntry[];
24
28
  }
25
29
  /**
26
30
  * Machine-readable classification of a {@link CerastreamConnectionError}, stable
@@ -91,6 +95,21 @@ export declare const processErrorCodeSchema: z.ZodEnum<{
91
95
  srtla_no_connections: "srtla_no_connections";
92
96
  }>;
93
97
  export type ProcessErrorCode = z.infer<typeof processErrorCodeSchema>;
98
+ export declare const captureCauseSchema: z.ZodEnum<{
99
+ device_busy: "device_busy";
100
+ negotiation_failed: "negotiation_failed";
101
+ no_signal: "no_signal";
102
+ }>;
103
+ export type CaptureCause = z.infer<typeof captureCauseSchema>;
104
+ export declare const captureCauseEntrySchema: z.ZodObject<{
105
+ device: z.ZodString;
106
+ cause: z.ZodEnum<{
107
+ device_busy: "device_busy";
108
+ negotiation_failed: "negotiation_failed";
109
+ no_signal: "no_signal";
110
+ }>;
111
+ }, z.core.$strip>;
112
+ export type CaptureCauseEntry = z.infer<typeof captureCauseEntrySchema>;
94
113
  /**
95
114
  * Why a capture device became `capture_unrecoverable` — the `reason` field of the
96
115
  * `error` event. Mirrors the Rust `CaptureUnrecoverableReason`.
package/dist/errors.js CHANGED
@@ -23,12 +23,21 @@ export class CerastreamRpcError extends Error {
23
23
  dataCode;
24
24
  /** The request id this error answered, when present. */
25
25
  requestId;
26
- constructor(code, message, dataCode, requestId) {
26
+ /** Full JSON-RPC `error.data` object, retained for additive typed detail. */
27
+ data;
28
+ constructor(code, message, dataCode, requestId, data) {
27
29
  super(message);
28
30
  this.name = 'CerastreamRpcError';
29
31
  this.code = code;
30
32
  this.dataCode = dataCode;
31
33
  this.requestId = requestId;
34
+ if (data !== undefined)
35
+ this.data = data;
36
+ }
37
+ /** Typed capture-probe failures carried by a rejected stream start. */
38
+ captureCauses() {
39
+ const parsed = rpcCaptureDataSchema.safeParse(this.data);
40
+ return parsed.success ? parsed.data.capture_causes : [];
32
41
  }
33
42
  }
34
43
  /**
@@ -129,6 +138,16 @@ export const processErrorCodeSchema = z.enum([
129
138
  // would be lying. Always carries a captureUnrecoverableReasonSchema `reason`.
130
139
  'capture_unrecoverable',
131
140
  ]);
141
+ export const captureCauseSchema = z.enum(['negotiation_failed', 'no_signal', 'device_busy']);
142
+ export const captureCauseEntrySchema = z.object({
143
+ device: z.string(),
144
+ cause: captureCauseSchema,
145
+ });
146
+ const rpcCaptureDataSchema = z
147
+ .object({
148
+ capture_causes: z.array(captureCauseEntrySchema),
149
+ })
150
+ .passthrough();
132
151
  /**
133
152
  * Why a capture device became `capture_unrecoverable` — the `reason` field of the
134
153
  * `error` event. Mirrors the Rust `CaptureUnrecoverableReason`.
package/dist/events.d.ts CHANGED
@@ -239,6 +239,11 @@ export declare const runtimeErrorEventSchema: z.ZodObject<{
239
239
  }>;
240
240
  reason: z.ZodOptional<z.ZodString>;
241
241
  selected: z.ZodOptional<z.ZodBoolean>;
242
+ capture_cause: z.ZodOptional<z.ZodEnum<{
243
+ device_busy: "device_busy";
244
+ negotiation_failed: "negotiation_failed";
245
+ no_signal: "no_signal";
246
+ }>>;
242
247
  }, z.core.$strip>;
243
248
  /** Payload of a {@link runtimeErrorEventSchema} event. */
244
249
  export type RuntimeErrorEvent = z.infer<typeof runtimeErrorEventSchema>;
@@ -457,6 +462,11 @@ export declare const eventParamsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
457
462
  }>;
458
463
  reason: z.ZodOptional<z.ZodString>;
459
464
  selected: z.ZodOptional<z.ZodBoolean>;
465
+ capture_cause: z.ZodOptional<z.ZodEnum<{
466
+ device_busy: "device_busy";
467
+ negotiation_failed: "negotiation_failed";
468
+ no_signal: "no_signal";
469
+ }>>;
460
470
  }, z.core.$strip>, z.ZodObject<{
461
471
  type: z.ZodLiteral<"preview">;
462
472
  seq: z.ZodNumber;
@@ -649,6 +659,11 @@ export declare const cerastreamEventSchema: z.ZodObject<{
649
659
  }>;
650
660
  reason: z.ZodOptional<z.ZodString>;
651
661
  selected: z.ZodOptional<z.ZodBoolean>;
662
+ capture_cause: z.ZodOptional<z.ZodEnum<{
663
+ device_busy: "device_busy";
664
+ negotiation_failed: "negotiation_failed";
665
+ no_signal: "no_signal";
666
+ }>>;
652
667
  }, z.core.$strip>, z.ZodObject<{
653
668
  type: z.ZodLiteral<"preview">;
654
669
  seq: z.ZodNumber;
@@ -850,6 +865,11 @@ export declare const eventSchemas: {
850
865
  }>;
851
866
  reason: z.ZodOptional<z.ZodString>;
852
867
  selected: z.ZodOptional<z.ZodBoolean>;
868
+ capture_cause: z.ZodOptional<z.ZodEnum<{
869
+ device_busy: "device_busy";
870
+ negotiation_failed: "negotiation_failed";
871
+ no_signal: "no_signal";
872
+ }>>;
853
873
  }, z.core.$strip>;
854
874
  readonly preview: z.ZodObject<{
855
875
  type: z.ZodLiteral<"preview">;
package/dist/events.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { processErrorCodeSchema, processErrorSourceSchema } from './errors.js';
2
+ import { captureCauseSchema, processErrorCodeSchema, processErrorSourceSchema } from './errors.js';
3
3
  import { captureDeviceSchema, configChangePhaseSchema, inputModeSchema, mediaClassSchema, previewEncodeFallbackSchema, previewEncodeModeSchema, streamStateSchema, } from './types.js';
4
4
  // Server → client `event` notifications, delivered after `subscribe-events`
5
5
  // (schema.md "Events"). Each event is the inner `params` of an rpcEventSchema:
@@ -119,6 +119,8 @@ export const runtimeErrorEventSchema = z.object({
119
119
  source: processErrorSourceSchema,
120
120
  reason: z.string().optional(), // structured replacement for the stderr <reason>
121
121
  selected: z.boolean().optional(), // additive (0.11.0): the degraded input is the operator's OWN selection, so the stream will not move itself and only they can pick another — a materially different operator response than a degraded non-selected input, and one to branch on rather than string-match. Absent ⇒ false
122
+ // The code stays capture_video_error because older consumers reject unknown code values.
123
+ capture_cause: captureCauseSchema.optional(),
122
124
  });
123
125
  /** `preview` event — a preview-session lifecycle/phase change. */
124
126
  export const previewEventSchema = z.object({
@@ -28,6 +28,10 @@ export declare const startParamsSchema: z.ZodObject<{
28
28
  resolution: z.ZodOptional<z.ZodString>;
29
29
  framerate: z.ZodOptional<z.ZodNumber>;
30
30
  audio: z.ZodOptional<z.ZodObject<{
31
+ backend: z.ZodOptional<z.ZodEnum<{
32
+ alsa: "alsa";
33
+ pipewire: "pipewire";
34
+ }>>;
31
35
  mode: z.ZodOptional<z.ZodEnum<{
32
36
  default: "default";
33
37
  device: "device";
@@ -91,6 +95,10 @@ export declare const reloadConfigParamsSchema: z.ZodObject<{
91
95
  latency_ms: z.ZodOptional<z.ZodNumber>;
92
96
  }, z.core.$strip>>;
93
97
  audio: z.ZodOptional<z.ZodObject<{
98
+ backend: z.ZodOptional<z.ZodEnum<{
99
+ alsa: "alsa";
100
+ pipewire: "pipewire";
101
+ }>>;
94
102
  delay_ms: z.ZodOptional<z.ZodNumber>;
95
103
  delay_ms_signed: z.ZodOptional<z.ZodNumber>;
96
104
  meter_device: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -116,6 +124,10 @@ export declare const reloadConfigResultSchema: z.ZodObject<{
116
124
  latency_ms: z.ZodOptional<z.ZodNumber>;
117
125
  }, z.core.$strip>>;
118
126
  audio: z.ZodOptional<z.ZodObject<{
127
+ backend: z.ZodOptional<z.ZodEnum<{
128
+ alsa: "alsa";
129
+ pipewire: "pipewire";
130
+ }>>;
119
131
  delay_ms: z.ZodOptional<z.ZodNumber>;
120
132
  delay_ms_signed: z.ZodOptional<z.ZodNumber>;
121
133
  meter_device: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -130,6 +142,7 @@ export declare const reloadConfigResultSchema: z.ZodObject<{
130
142
  "source-fixed": "source-fixed";
131
143
  }>>;
132
144
  reason: z.ZodOptional<z.ZodString>;
145
+ applies: z.ZodOptional<z.ZodLiteral<"next-session">>;
133
146
  }, z.core.$strip>;
134
147
  export type ReloadConfigResult = z.infer<typeof reloadConfigResultSchema>;
135
148
  export declare const setBitrateParamsSchema: z.ZodObject<{
@@ -402,6 +415,16 @@ export declare const getCapabilitiesResultSchema: z.ZodObject<{
402
415
  default_framerate: z.ZodNumber;
403
416
  }, z.core.$strip>>;
404
417
  audio_live_switch: z.ZodOptional<z.ZodBoolean>;
418
+ audio_backends: z.ZodOptional<z.ZodObject<{
419
+ supported: z.ZodArray<z.ZodEnum<{
420
+ alsa: "alsa";
421
+ pipewire: "pipewire";
422
+ }>>;
423
+ active: z.ZodEnum<{
424
+ alsa: "alsa";
425
+ pipewire: "pipewire";
426
+ }>;
427
+ }, z.core.$strip>>;
405
428
  latency_range: z.ZodOptional<z.ZodObject<{
406
429
  min: z.ZodNumber;
407
430
  default: z.ZodNumber;
@@ -516,6 +539,10 @@ export declare const requestSchemas: {
516
539
  resolution: z.ZodOptional<z.ZodString>;
517
540
  framerate: z.ZodOptional<z.ZodNumber>;
518
541
  audio: z.ZodOptional<z.ZodObject<{
542
+ backend: z.ZodOptional<z.ZodEnum<{
543
+ alsa: "alsa";
544
+ pipewire: "pipewire";
545
+ }>>;
519
546
  mode: z.ZodOptional<z.ZodEnum<{
520
547
  default: "default";
521
548
  device: "device";
@@ -579,6 +606,10 @@ export declare const requestSchemas: {
579
606
  latency_ms: z.ZodOptional<z.ZodNumber>;
580
607
  }, z.core.$strip>>;
581
608
  audio: z.ZodOptional<z.ZodObject<{
609
+ backend: z.ZodOptional<z.ZodEnum<{
610
+ alsa: "alsa";
611
+ pipewire: "pipewire";
612
+ }>>;
582
613
  delay_ms: z.ZodOptional<z.ZodNumber>;
583
614
  delay_ms_signed: z.ZodOptional<z.ZodNumber>;
584
615
  meter_device: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -603,6 +634,10 @@ export declare const requestSchemas: {
603
634
  latency_ms: z.ZodOptional<z.ZodNumber>;
604
635
  }, z.core.$strip>>;
605
636
  audio: z.ZodOptional<z.ZodObject<{
637
+ backend: z.ZodOptional<z.ZodEnum<{
638
+ alsa: "alsa";
639
+ pipewire: "pipewire";
640
+ }>>;
606
641
  delay_ms: z.ZodOptional<z.ZodNumber>;
607
642
  delay_ms_signed: z.ZodOptional<z.ZodNumber>;
608
643
  meter_device: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -617,6 +652,7 @@ export declare const requestSchemas: {
617
652
  "source-fixed": "source-fixed";
618
653
  }>>;
619
654
  reason: z.ZodOptional<z.ZodString>;
655
+ applies: z.ZodOptional<z.ZodLiteral<"next-session">>;
620
656
  }, z.core.$strip>;
621
657
  };
622
658
  readonly 'set-bitrate': {
package/dist/messages.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { AUDIO_DELAY_MAX_MS } from './constants.js';
3
3
  import { helloParamsSchema, helloResultSchema } from './envelope.js';
4
- import { balancerAlgorithmSchema, bitrateControlSchema, captureDeviceKindSchema, captureDeviceSchema, cerastreamConfigSchema, configChangePhaseSchema, inputModeSchema, mediaClassSchema, previewEncodeModeSchema, previewTierSchema, streamStateSchema, videoCodecSchema, } from './types.js';
4
+ import { audioBackendSchema, 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 ----
@@ -37,6 +37,7 @@ export const reloadConfigParamsSchema = z.object({
37
37
  .optional(),
38
38
  audio: z
39
39
  .object({
40
+ backend: audioBackendSchema.optional(),
40
41
  delay_ms: z.number().int().min(0).max(AUDIO_DELAY_MAX_MS).optional(), // legacy unsigned; kept for 0.3.0 callers
41
42
  delay_ms_signed: z.number().int().optional(), // signed sibling; clamped at apply, so unbounded
42
43
  // additive (0.9.0): idle-meter card preference — absent leaves it unchanged,
@@ -58,6 +59,7 @@ export const reloadConfigResultSchema = z.object({
58
59
  // "source-fixed" + reason "passthrough" report that. Absent on a transcode reload.
59
60
  bitrate_control: bitrateControlSchema.optional(),
60
61
  reason: z.string().optional(),
62
+ applies: z.literal('next-session').optional(),
61
63
  });
62
64
  // ---- 4. set-bitrate ----
63
65
  export const setBitrateParamsSchema = z.object({
@@ -200,6 +202,12 @@ export const getCapabilitiesResultSchema = z.object({
200
202
  encoder: encoderCapsSchema,
201
203
  sources: z.array(videoSourceCapSchema),
202
204
  audio_live_switch: z.boolean().optional(),
205
+ audio_backends: z
206
+ .object({
207
+ supported: z.array(audioBackendSchema),
208
+ active: audioBackendSchema,
209
+ })
210
+ .optional(),
203
211
  latency_range: latencyRangeCapsSchema.optional(),
204
212
  fec_capable: z.boolean().optional(), // engine can encode FEC on egress (SRTO_PACKETFILTER)
205
213
  supported_profiles: z.array(z.string()).optional(), // SRT receive presets the device offers
package/dist/types.d.ts CHANGED
@@ -116,7 +116,16 @@ export declare const audioModeSchema: z.ZodEnum<{
116
116
  none: "none";
117
117
  }>;
118
118
  export type AudioMode = z.infer<typeof audioModeSchema>;
119
+ export declare const audioBackendSchema: z.ZodEnum<{
120
+ alsa: "alsa";
121
+ pipewire: "pipewire";
122
+ }>;
123
+ export type AudioBackend = z.infer<typeof audioBackendSchema>;
119
124
  export declare const audioConfigSchema: z.ZodObject<{
125
+ backend: z.ZodOptional<z.ZodEnum<{
126
+ alsa: "alsa";
127
+ pipewire: "pipewire";
128
+ }>>;
120
129
  mode: z.ZodOptional<z.ZodEnum<{
121
130
  default: "default";
122
131
  device: "device";
@@ -162,6 +171,10 @@ export declare const cerastreamConfigSchema: z.ZodObject<{
162
171
  resolution: z.ZodOptional<z.ZodString>;
163
172
  framerate: z.ZodOptional<z.ZodNumber>;
164
173
  audio: z.ZodOptional<z.ZodObject<{
174
+ backend: z.ZodOptional<z.ZodEnum<{
175
+ alsa: "alsa";
176
+ pipewire: "pipewire";
177
+ }>>;
165
178
  mode: z.ZodOptional<z.ZodEnum<{
166
179
  default: "default";
167
180
  device: "device";
package/dist/types.js CHANGED
@@ -113,7 +113,9 @@ export const bitrateConfigSchema = z
113
113
  // so a pre-0.6.0 caller keeps working. Replaces leaking pseudo-source strings
114
114
  // (e.g. "No audio") into `device`.
115
115
  export const audioModeSchema = z.enum(['none', 'default', 'device']);
116
+ export const audioBackendSchema = z.enum(['alsa', 'pipewire']);
116
117
  export const audioConfigSchema = z.object({
118
+ backend: audioBackendSchema.optional(),
117
119
  mode: audioModeSchema.optional(),
118
120
  device: z.string().optional(), // ALSA capture device id; absent ⇒ test-tone fallback
119
121
  codec: z.string().optional(), // audio encoder codec id (e.g. "aac", "opus")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ceralive/cerastream",
3
- "version": "2026.8.1",
3
+ "version": "2026.9.1",
4
4
  "description": "Type-safe TypeScript schema + IPC client for the cerastream streaming engine (JSON-RPC 2.0 / NDJSON over a Unix domain socket).",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",