@marianmeres/webrtc 0.0.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Marian Meres
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,408 @@
1
+ # @marianmeres/webrtc
2
+
3
+ > **Full Disclosure:** This code was written by Claude (Anthropic's AI). The human (that is @marianmeres) just asked nicely, occasionally said "thanks", and went through about 47 iterations of "could you improve this", "what about that", and "make it more Svelte-friendly". To be fair, the prompt engineering was top-notch. So if you find bugs, we'll split the blame 50/50. If it works perfectly, Claude gets 95% of the credit and @marianmeres gets the remaining 5% for excellent taste in asking the right questions. 🤖
4
+
5
+ A lightweight, framework-agnostic WebRTC manager with state machine-based lifecycle management and event-driven architecture.
6
+
7
+ ## Features
8
+
9
+ - **State Machine-based**: Clean state transitions (IDLE → INITIALIZING → CONNECTING → CONNECTED)
10
+ - **Event-driven**: Subscribe to specific events or overall state changes
11
+ - **Svelte Store Compatible**: Works seamlessly with Svelte's reactive `$` syntax
12
+ - **Audio Management**: Microphone enable/disable, device switching, device change detection
13
+ - **Data Channels**: Easy creation and management of RTCDataChannels
14
+ - **Auto-reconnection**: Optional automatic reconnection with exponential backoff
15
+ - **TypeScript**: Full type safety and excellent IDE support
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install @marianmeres/webrtc
21
+ ```
22
+
23
+ ## High-Level Overview
24
+
25
+ The `WebRtcManager` class handles the complete WebRTC connection lifecycle:
26
+
27
+ 1. **Initialization**: Sets up RTCPeerConnection, media streams, and data channels
28
+ 2. **Connection Management**: Handles state transitions, reconnection, and cleanup
29
+ 3. **Signaling**: Provides methods for offer/answer exchange and ICE candidate handling
30
+ 4. **Media Control**: Manages local/remote streams and microphone switching
31
+ 5. **Events**: Emits events for all important state changes
32
+
33
+ The manager doesn't handle the signaling transport layer - you're responsible for sending/receiving offers, answers, and ICE candidates through your own signaling mechanism (WebSocket, HTTP, etc.).
34
+
35
+ ## Core API
36
+
37
+ ### Constructor
38
+
39
+ ```typescript
40
+ const manager = new WebRtcManager(factory, config);
41
+ ```
42
+
43
+ - `factory`: Object implementing `WebRtcFactory` interface (provides `createPeerConnection`, `getUserMedia`, `enumerateDevices`)
44
+ - `config`: Optional configuration object
45
+
46
+ **Configuration Options:**
47
+ - `peerConfig`: RTCConfiguration (ICE servers, etc.)
48
+ - `enableMicrophone`: Enable microphone on initialization (default: false)
49
+ - `dataChannelLabel`: Create a default data channel with this label
50
+ - `autoReconnect`: Enable automatic reconnection (default: false)
51
+ - `maxReconnectAttempts`: Max reconnection attempts (default: 5)
52
+ - `reconnectDelay`: Initial reconnection delay in ms (default: 1000)
53
+ - `debug`: Enable debug logging (default: false)
54
+
55
+ ### State and Properties
56
+
57
+ ```typescript
58
+ manager.state // Current WebRtcState
59
+ manager.localStream // MediaStream | null
60
+ manager.remoteStream // MediaStream | null
61
+ manager.dataChannels // ReadonlyMap<string, RTCDataChannel>
62
+ manager.peerConnection // RTCPeerConnection | null
63
+ ```
64
+
65
+ ### Lifecycle Methods
66
+
67
+ ```typescript
68
+ await manager.initialize() // Initialize peer connection
69
+ await manager.connect() // Transition to CONNECTING state
70
+ manager.disconnect() // Disconnect and cleanup
71
+ manager.reset() // Reset to IDLE state
72
+ ```
73
+
74
+ ### Audio Methods
75
+
76
+ ```typescript
77
+ await manager.enableMicrophone(true) // Enable/disable microphone
78
+ await manager.switchMicrophone(deviceId) // Switch to different audio input
79
+ await manager.getAudioInputDevices() // Get available audio inputs
80
+ ```
81
+
82
+ ### Signaling Methods
83
+
84
+ ```typescript
85
+ const offer = await manager.createOffer()
86
+ const answer = await manager.createAnswer()
87
+ await manager.setLocalDescription(offer)
88
+ await manager.setRemoteDescription(answer)
89
+ await manager.addIceCandidate(candidate)
90
+ await manager.iceRestart() // Trigger ICE restart
91
+ ```
92
+
93
+ ### Data Channel Methods
94
+
95
+ ```typescript
96
+ const dc = manager.createDataChannel(label, options)
97
+ const dc = manager.getDataChannel(label)
98
+ manager.sendData(label, data) // Returns boolean
99
+ ```
100
+
101
+ ### Event Subscription
102
+
103
+ ```typescript
104
+ // Subscribe to specific event
105
+ const unsub = manager.on(WebRtcManager.EVENT_STATE_CHANGE, (state) => {
106
+ console.log('State changed:', state);
107
+ });
108
+
109
+ // Subscribe to overall state (Svelte store compatible)
110
+ const unsub = manager.subscribe((state) => {
111
+ console.log('Overall state:', state);
112
+ // state = { state, localStream, remoteStream, dataChannels, peerConnection }
113
+ });
114
+ ```
115
+
116
+ **Available Event Constants:**
117
+ - `EVENT_STATE_CHANGE`
118
+ - `EVENT_LOCAL_STREAM`
119
+ - `EVENT_REMOTE_STREAM`
120
+ - `EVENT_DATA_CHANNEL_OPEN`
121
+ - `EVENT_DATA_CHANNEL_MESSAGE`
122
+ - `EVENT_DATA_CHANNEL_CLOSE`
123
+ - `EVENT_ICE_CANDIDATE`
124
+ - `EVENT_RECONNECTING`
125
+ - `EVENT_RECONNECT_FAILED`
126
+ - `EVENT_DEVICE_CHANGED`
127
+ - `EVENT_MICROPHONE_FAILED`
128
+ - `EVENT_ERROR`
129
+
130
+ ## Examples
131
+
132
+ ### Basic Usage (Vanilla JavaScript)
133
+
134
+ ```typescript
135
+ import { WebRtcManager, WebRtcState } from '@marianmeres/webrtc';
136
+
137
+ // Create factory (browser implementation)
138
+ const factory = {
139
+ createPeerConnection: (config) => new RTCPeerConnection(config),
140
+ getUserMedia: (constraints) => navigator.mediaDevices.getUserMedia(constraints),
141
+ enumerateDevices: () => navigator.mediaDevices.enumerateDevices(),
142
+ };
143
+
144
+ // Create manager
145
+ const manager = new WebRtcManager(factory, {
146
+ peerConfig: {
147
+ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }],
148
+ },
149
+ enableMicrophone: true,
150
+ autoReconnect: true,
151
+ });
152
+
153
+ // Subscribe to events
154
+ manager.on(WebRtcManager.EVENT_ICE_CANDIDATE, (candidate) => {
155
+ // Send candidate to remote peer via your signaling channel
156
+ signalingChannel.send({ type: 'candidate', candidate });
157
+ });
158
+
159
+ manager.on(WebRtcManager.EVENT_REMOTE_STREAM, (stream) => {
160
+ // Attach remote stream to audio element
161
+ audioElement.srcObject = stream;
162
+ });
163
+
164
+ // Initialize and create offer
165
+ await manager.initialize();
166
+ await manager.connect();
167
+ const offer = await manager.createOffer();
168
+ await manager.setLocalDescription(offer);
169
+
170
+ // Send offer to remote peer via your signaling channel
171
+ signalingChannel.send({ type: 'offer', offer });
172
+
173
+ // Handle incoming signaling messages
174
+ signalingChannel.onmessage = async (msg) => {
175
+ if (msg.type === 'answer') {
176
+ await manager.setRemoteDescription(msg.answer);
177
+ } else if (msg.type === 'candidate') {
178
+ await manager.addIceCandidate(msg.candidate);
179
+ }
180
+ };
181
+ ```
182
+
183
+ ### Svelte 5 Integration
184
+
185
+ ```svelte
186
+ <script>
187
+ import { WebRtcManager, WebRtcState } from '@marianmeres/webrtc';
188
+ import { onMount } from 'svelte';
189
+
190
+ const factory = {
191
+ createPeerConnection: (config) => new RTCPeerConnection(config),
192
+ getUserMedia: (constraints) => navigator.mediaDevices.getUserMedia(constraints),
193
+ enumerateDevices: () => navigator.mediaDevices.enumerateDevices(),
194
+ };
195
+
196
+ const manager = new WebRtcManager(factory, {
197
+ peerConfig: {
198
+ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }],
199
+ },
200
+ enableMicrophone: true,
201
+ });
202
+
203
+ // Subscribe to overall state (Svelte store compatible!)
204
+ const managerState = $derived(manager.subscribe((state) => state));
205
+
206
+ // Or use individual event subscriptions
207
+ let devices = $state([]);
208
+
209
+ onMount(() => {
210
+ const unsubDevices = manager.on(
211
+ WebRtcManager.EVENT_DEVICE_CHANGED,
212
+ (devs) => devices = devs
213
+ );
214
+
215
+ return () => {
216
+ unsubDevices();
217
+ manager.disconnect();
218
+ };
219
+ });
220
+
221
+ async function startCall() {
222
+ await manager.initialize();
223
+ await manager.connect();
224
+ const offer = await manager.createOffer();
225
+ await manager.setLocalDescription(offer);
226
+ // Send offer via your signaling channel
227
+ }
228
+
229
+ async function switchMic(deviceId) {
230
+ await manager.switchMicrophone(deviceId);
231
+ }
232
+ </script>
233
+
234
+ <div>
235
+ <p>State: {$managerState.state}</p>
236
+ <p>Microphone: {$managerState.localStream ? 'Enabled' : 'Disabled'}</p>
237
+
238
+ <button onclick={startCall}>Start Call</button>
239
+
240
+ <select onchange={(e) => switchMic(e.target.value)}>
241
+ {#each devices as device}
242
+ <option value={device.deviceId}>{device.label}</option>
243
+ {/each}
244
+ </select>
245
+
246
+ <audio bind:this={remoteAudio} autoplay></audio>
247
+ </div>
248
+ ```
249
+
250
+ ### Complete Peer-to-Peer Example
251
+
252
+ ```typescript
253
+ import { WebRtcManager } from '@marianmores/webrtc';
254
+
255
+ class P2PConnection {
256
+ manager: WebRtcManager;
257
+ signalingChannel: WebSocket;
258
+
259
+ constructor(signalingUrl: string) {
260
+ this.manager = new WebRtcManager(
261
+ {
262
+ createPeerConnection: (config) => new RTCPeerConnection(config),
263
+ getUserMedia: (constraints) => navigator.mediaDevices.getUserMedia(constraints),
264
+ enumerateDevices: () => navigator.mediaDevices.enumerateDevices(),
265
+ },
266
+ {
267
+ peerConfig: {
268
+ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }],
269
+ },
270
+ enableMicrophone: true,
271
+ dataChannelLabel: 'chat',
272
+ autoReconnect: true,
273
+ }
274
+ );
275
+
276
+ this.signalingChannel = new WebSocket(signalingUrl);
277
+ this.setupSignaling();
278
+ this.setupManagerEvents();
279
+ }
280
+
281
+ setupSignaling() {
282
+ this.signalingChannel.onmessage = async (event) => {
283
+ const msg = JSON.parse(event.data);
284
+
285
+ switch (msg.type) {
286
+ case 'offer':
287
+ await this.handleOffer(msg.offer);
288
+ break;
289
+ case 'answer':
290
+ await this.manager.setRemoteDescription(msg.answer);
291
+ break;
292
+ case 'candidate':
293
+ await this.manager.addIceCandidate(msg.candidate);
294
+ break;
295
+ }
296
+ };
297
+ }
298
+
299
+ setupManagerEvents() {
300
+ // Send ICE candidates to remote peer
301
+ this.manager.on(WebRtcManager.EVENT_ICE_CANDIDATE, (candidate) => {
302
+ this.signalingChannel.send(JSON.stringify({
303
+ type: 'candidate',
304
+ candidate,
305
+ }));
306
+ });
307
+
308
+ // Handle remote audio stream
309
+ this.manager.on(WebRtcManager.EVENT_REMOTE_STREAM, (stream) => {
310
+ const audio = document.getElementById('remote-audio') as HTMLAudioElement;
311
+ audio.srcObject = stream;
312
+ });
313
+
314
+ // Handle data channel messages
315
+ this.manager.on(WebRtcManager.EVENT_DATA_CHANNEL_MESSAGE, ({ data }) => {
316
+ console.log('Received message:', data);
317
+ });
318
+
319
+ // Handle reconnection
320
+ this.manager.on(WebRtcManager.EVENT_RECONNECTING, ({ attempt, strategy }) => {
321
+ console.log(`Reconnecting (attempt ${attempt}, strategy: ${strategy})`);
322
+ if (strategy === 'full') {
323
+ // For full reconnection, we need to re-do the signaling handshake
324
+ this.createOffer();
325
+ }
326
+ });
327
+ }
328
+
329
+ async createOffer() {
330
+ await this.manager.initialize();
331
+ await this.manager.connect();
332
+ const offer = await this.manager.createOffer();
333
+ await this.manager.setLocalDescription(offer);
334
+
335
+ this.signalingChannel.send(JSON.stringify({
336
+ type: 'offer',
337
+ offer,
338
+ }));
339
+ }
340
+
341
+ async handleOffer(offer: RTCSessionDescriptionInit) {
342
+ await this.manager.initialize();
343
+ await this.manager.setRemoteDescription(offer);
344
+ const answer = await this.manager.createAnswer();
345
+ await this.manager.setLocalDescription(answer);
346
+
347
+ this.signalingChannel.send(JSON.stringify({
348
+ type: 'answer',
349
+ answer,
350
+ }));
351
+ }
352
+
353
+ sendMessage(text: string) {
354
+ this.manager.sendData('chat', text);
355
+ }
356
+
357
+ disconnect() {
358
+ this.manager.disconnect();
359
+ this.signalingChannel.close();
360
+ }
361
+ }
362
+
363
+ // Usage
364
+ const connection = new P2PConnection('wss://your-signaling-server.com');
365
+ await connection.createOffer();
366
+ connection.sendMessage('Hello!');
367
+ ```
368
+
369
+ ## State Machine
370
+
371
+ The manager uses a finite state machine with the following states:
372
+
373
+ ![State Diagram](states.png "State Diagram")
374
+
375
+ ## Testing
376
+
377
+ The project includes two types of tests:
378
+
379
+ ### Unit Tests (Deno)
380
+
381
+ Mock-based tests for the manager's logic and state transitions:
382
+
383
+ ```bash
384
+ deno task test
385
+ ```
386
+
387
+ ### Browser Integration Tests
388
+
389
+ Real peer-to-peer connection tests running in a browser environment:
390
+
391
+ ```bash
392
+ deno task test:browser
393
+ ```
394
+
395
+ This builds the test bundle and starts a local server. Open the provided URL in your browser to run the tests interactively.
396
+
397
+ The browser tests verify:
398
+ - Actual P2P connections between peers
399
+ - Data channel message exchange
400
+ - ICE candidate exchange
401
+ - Connection state transitions
402
+ - Resource cleanup
403
+
404
+ See [tests/browser/README.md](tests/browser/README.md) for more details.
405
+
406
+ ## License
407
+
408
+ MIT
package/dist/mod.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./types.js";
2
+ export * from "./webrtc-manager.js";
package/dist/mod.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./types.js";
2
+ export * from "./webrtc-manager.js";
@@ -0,0 +1,70 @@
1
+ export interface WebRtcManagerConfig {
2
+ /** Initial peer configuration (ICE servers, etc.) */
3
+ peerConfig?: RTCConfiguration;
4
+ /** Whether to enable microphone initially. Defaults to false. */
5
+ enableMicrophone?: boolean;
6
+ /** Label for the default data channel. If provided, a data channel will be created on connect. */
7
+ dataChannelLabel?: string;
8
+ /** Enable automatic reconnection on connection failure. Defaults to false. */
9
+ autoReconnect?: boolean;
10
+ /** Maximum number of reconnection attempts. Defaults to 5. */
11
+ maxReconnectAttempts?: number;
12
+ /** Initial reconnection delay in ms. Doubles with each attempt. Defaults to 1000. */
13
+ reconnectDelay?: number;
14
+ /** Debug mode for logging */
15
+ debug?: boolean;
16
+ }
17
+ export interface WebRtcFactory {
18
+ createPeerConnection(config?: RTCConfiguration): RTCPeerConnection;
19
+ getUserMedia(constraints: MediaStreamConstraints): Promise<MediaStream>;
20
+ enumerateDevices(): Promise<MediaDeviceInfo[]>;
21
+ }
22
+ export declare enum WebRtcState {
23
+ IDLE = "IDLE",
24
+ INITIALIZING = "INITIALIZING",
25
+ CONNECTING = "CONNECTING",
26
+ CONNECTED = "CONNECTED",
27
+ RECONNECTING = "RECONNECTING",
28
+ DISCONNECTED = "DISCONNECTED",
29
+ ERROR = "ERROR"
30
+ }
31
+ export declare enum WebRtcFsmEvent {
32
+ INIT = "initialize",
33
+ CONNECT = "connect",
34
+ CONNECTED = "connected",
35
+ RECONNECTING = "reconnecting",
36
+ DISCONNECT = "disconnect",
37
+ ERROR = "error",
38
+ RESET = "reset"
39
+ }
40
+ export interface WebRtcEvents {
41
+ state_change: WebRtcState;
42
+ local_stream: MediaStream | null;
43
+ remote_stream: MediaStream | null;
44
+ data_channel_open: RTCDataChannel;
45
+ data_channel_message: {
46
+ channel: RTCDataChannel;
47
+ data: any;
48
+ };
49
+ data_channel_close: RTCDataChannel;
50
+ ice_candidate: RTCIceCandidate | null;
51
+ /**
52
+ * Emitted when reconnection is being attempted.
53
+ * For 'full' strategy reconnections, consumers should listen for this event
54
+ * and re-establish signaling (create new offer/answer exchange).
55
+ * The manager will call connect() but cannot handle the signaling automatically.
56
+ */
57
+ reconnecting: {
58
+ attempt: number;
59
+ strategy: "ice-restart" | "full";
60
+ };
61
+ reconnect_failed: {
62
+ attempts: number;
63
+ };
64
+ device_changed: MediaDeviceInfo[];
65
+ microphone_failed: {
66
+ error?: any;
67
+ reason?: string;
68
+ };
69
+ error: Error;
70
+ }
package/dist/types.js ADDED
@@ -0,0 +1,20 @@
1
+ export var WebRtcState;
2
+ (function (WebRtcState) {
3
+ WebRtcState["IDLE"] = "IDLE";
4
+ WebRtcState["INITIALIZING"] = "INITIALIZING";
5
+ WebRtcState["CONNECTING"] = "CONNECTING";
6
+ WebRtcState["CONNECTED"] = "CONNECTED";
7
+ WebRtcState["RECONNECTING"] = "RECONNECTING";
8
+ WebRtcState["DISCONNECTED"] = "DISCONNECTED";
9
+ WebRtcState["ERROR"] = "ERROR";
10
+ })(WebRtcState || (WebRtcState = {}));
11
+ export var WebRtcFsmEvent;
12
+ (function (WebRtcFsmEvent) {
13
+ WebRtcFsmEvent["INIT"] = "initialize";
14
+ WebRtcFsmEvent["CONNECT"] = "connect";
15
+ WebRtcFsmEvent["CONNECTED"] = "connected";
16
+ WebRtcFsmEvent["RECONNECTING"] = "reconnecting";
17
+ WebRtcFsmEvent["DISCONNECT"] = "disconnect";
18
+ WebRtcFsmEvent["ERROR"] = "error";
19
+ WebRtcFsmEvent["RESET"] = "reset";
20
+ })(WebRtcFsmEvent || (WebRtcFsmEvent = {}));
@@ -0,0 +1,158 @@
1
+ import { type WebRtcFactory, type WebRtcManagerConfig, WebRtcState, type WebRtcEvents } from "./types.js";
2
+ export declare class WebRtcManager {
3
+ #private;
4
+ static readonly EVENT_STATE_CHANGE = "state_change";
5
+ static readonly EVENT_LOCAL_STREAM = "local_stream";
6
+ static readonly EVENT_REMOTE_STREAM = "remote_stream";
7
+ static readonly EVENT_DATA_CHANNEL_OPEN = "data_channel_open";
8
+ static readonly EVENT_DATA_CHANNEL_MESSAGE = "data_channel_message";
9
+ static readonly EVENT_DATA_CHANNEL_CLOSE = "data_channel_close";
10
+ static readonly EVENT_ICE_CANDIDATE = "ice_candidate";
11
+ static readonly EVENT_RECONNECTING = "reconnecting";
12
+ static readonly EVENT_RECONNECT_FAILED = "reconnect_failed";
13
+ static readonly EVENT_DEVICE_CHANGED = "device_changed";
14
+ static readonly EVENT_MICROPHONE_FAILED = "microphone_failed";
15
+ static readonly EVENT_ERROR = "error";
16
+ constructor(factory: WebRtcFactory, config?: WebRtcManagerConfig);
17
+ /** Returns the current state of the WebRTC connection. */
18
+ get state(): WebRtcState;
19
+ /** Returns a readonly map of all active data channels indexed by label. */
20
+ get dataChannels(): ReadonlyMap<string, RTCDataChannel>;
21
+ /** Returns the local media stream, or null if not initialized. */
22
+ get localStream(): MediaStream | null;
23
+ /** Returns the remote media stream, or null if not connected. */
24
+ get remoteStream(): MediaStream | null;
25
+ /** Returns the underlying RTCPeerConnection, or null if not initialized. */
26
+ get peerConnection(): RTCPeerConnection | null;
27
+ /** Returns a Mermaid diagram representation of the FSM state machine. */
28
+ toMermaid(): string;
29
+ /**
30
+ * Subscribe to a specific WebRTC event.
31
+ * @returns Unsubscribe function to remove the event listener.
32
+ */
33
+ on(event: keyof WebRtcEvents, handler: (data: any) => void): () => void;
34
+ /**
35
+ * Subscribe to the overall state of the WebRTC manager.
36
+ * Compatible with Svelte stores - immediately calls handler with current state,
37
+ * then notifies on any changes to state, streams, or data channels.
38
+ * @param handler - Callback that receives the overall state object
39
+ * @returns Unsubscribe function to remove the event listener.
40
+ */
41
+ subscribe(handler: (state: {
42
+ state: WebRtcState;
43
+ localStream: MediaStream | null;
44
+ remoteStream: MediaStream | null;
45
+ dataChannels: ReadonlyMap<string, RTCDataChannel>;
46
+ peerConnection: RTCPeerConnection | null;
47
+ }) => void): () => void;
48
+ /**
49
+ * Retrieves all available audio input devices.
50
+ * @returns Array of audio input devices, or empty array on error.
51
+ */
52
+ getAudioInputDevices(): Promise<MediaDeviceInfo[]>;
53
+ /**
54
+ * Switches the active microphone to a different audio input device.
55
+ * @param deviceId - The device ID of the audio input to switch to.
56
+ * @returns True if the switch was successful, false otherwise.
57
+ */
58
+ switchMicrophone(deviceId: string): Promise<boolean>;
59
+ /**
60
+ * Initializes the WebRTC peer connection and sets up media tracks.
61
+ * Must be called before creating offers or answers. Can only be called from IDLE state.
62
+ */
63
+ initialize(): Promise<void>;
64
+ /**
65
+ * Transitions to the CONNECTING state. Automatically initializes if needed.
66
+ * If disconnected, reinitializes the peer connection.
67
+ */
68
+ connect(): Promise<void>;
69
+ /**
70
+ * Enables or disables the microphone and adds/removes audio tracks to the peer connection.
71
+ * @param enable - True to enable microphone, false to disable.
72
+ * @returns True if successful, false if failed to get user media.
73
+ */
74
+ enableMicrophone(enable: boolean): Promise<boolean>;
75
+ /**
76
+ * Disconnects the peer connection and cleans up all resources.
77
+ * Transitions to DISCONNECTED state.
78
+ */
79
+ disconnect(): void;
80
+ /**
81
+ * Resets the manager to IDLE state from any state.
82
+ * Cleans up all resources and allows reinitialization.
83
+ */
84
+ reset(): void;
85
+ /**
86
+ * Creates a new data channel with the specified label.
87
+ * Returns existing channel if one with the same label already exists.
88
+ * @param label - The label for the data channel.
89
+ * @param options - Optional RTCDataChannelInit configuration.
90
+ * @returns The created data channel, or null if peer connection not initialized.
91
+ */
92
+ createDataChannel(label: string, options?: RTCDataChannelInit): RTCDataChannel | null;
93
+ /**
94
+ * Retrieves an existing data channel by label.
95
+ * @param label - The label of the data channel to retrieve.
96
+ * @returns The data channel if found, undefined otherwise.
97
+ */
98
+ getDataChannel(label: string): RTCDataChannel | undefined;
99
+ /**
100
+ * Sends data through a data channel identified by label.
101
+ * Checks that the channel exists and is in open state before sending.
102
+ * @param label - The label of the data channel to send through.
103
+ * @param data - The data to send (string, Blob, or ArrayBuffer).
104
+ * @returns True if data was sent successfully, false otherwise.
105
+ */
106
+ sendData(label: string, data: string | Blob | ArrayBuffer | ArrayBufferView<ArrayBuffer>): boolean;
107
+ /**
108
+ * Creates an SDP offer for initiating a WebRTC connection.
109
+ * @param options - Optional offer configuration.
110
+ * @returns The offer SDP, or null if peer connection not initialized.
111
+ */
112
+ createOffer(options?: RTCOfferOptions): Promise<RTCSessionDescriptionInit | null>;
113
+ /**
114
+ * Creates an SDP answer in response to a received offer.
115
+ * @param options - Optional answer configuration.
116
+ * @returns The answer SDP, or null if peer connection not initialized.
117
+ */
118
+ createAnswer(options?: RTCAnswerOptions): Promise<RTCSessionDescriptionInit | null>;
119
+ /**
120
+ * Sets the local description for the peer connection.
121
+ * @param description - The SDP description (offer or answer).
122
+ * @returns True if successful, false otherwise.
123
+ */
124
+ setLocalDescription(description: RTCSessionDescriptionInit): Promise<boolean>;
125
+ /**
126
+ * Sets the remote description received from the peer.
127
+ * @param description - The remote SDP description.
128
+ * @returns True if successful, false otherwise.
129
+ */
130
+ setRemoteDescription(description: RTCSessionDescriptionInit): Promise<boolean>;
131
+ /**
132
+ * Adds an ICE candidate received from the remote peer.
133
+ * @param candidate - The ICE candidate to add, or null for end-of-candidates.
134
+ * @returns True if successful, false otherwise.
135
+ */
136
+ addIceCandidate(candidate: RTCIceCandidateInit | null): Promise<boolean>;
137
+ /**
138
+ * Performs an ICE restart to recover from connection issues.
139
+ * Creates a new offer with iceRestart flag and sets it as local description.
140
+ * @returns True if successful, false otherwise.
141
+ */
142
+ iceRestart(): Promise<boolean>;
143
+ /**
144
+ * Returns the current local session description.
145
+ * @returns The local description, or null if not set.
146
+ */
147
+ getLocalDescription(): RTCSessionDescription | null;
148
+ /**
149
+ * Returns the current remote session description.
150
+ * @returns The remote description, or null if not set.
151
+ */
152
+ getRemoteDescription(): RTCSessionDescription | null;
153
+ /**
154
+ * Retrieves WebRTC statistics for the peer connection.
155
+ * @returns Stats report, or null if peer connection not initialized.
156
+ */
157
+ getStats(): Promise<RTCStatsReport | null>;
158
+ }