@marianmeres/webrtc 0.0.2 → 1.0.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/API.md ADDED
@@ -0,0 +1,916 @@
1
+ # API Reference
2
+
3
+ Complete API documentation for `@marianmeres/webrtc`.
4
+
5
+ ## Table of Contents
6
+
7
+ - [WebRtcManager](#webrtcmanager)
8
+ - [Constructor](#constructor)
9
+ - [Properties](#properties)
10
+ - [Lifecycle Methods](#lifecycle-methods)
11
+ - [Audio Methods](#audio-methods)
12
+ - [Data Channel Methods](#data-channel-methods)
13
+ - [Signaling Methods](#signaling-methods)
14
+ - [Event Methods](#event-methods)
15
+ - [Utility Methods](#utility-methods)
16
+ - [Types](#types)
17
+ - [WebRtcFactory](#webrtcfactory)
18
+ - [WebRtcManagerConfig](#webrtcmanagerconfig)
19
+ - [WebRtcState](#webrtcstate)
20
+ - [WebRtcFsmEvent](#webrtcfsmevent)
21
+ - [WebRtcEvents](#webrtcevents)
22
+ - [Event Constants](#event-constants)
23
+ - [State Machine](#state-machine)
24
+
25
+ ---
26
+
27
+ ## WebRtcManager
28
+
29
+ The main class for managing WebRTC connections.
30
+
31
+ ### Constructor
32
+
33
+ ```typescript
34
+ new WebRtcManager(factory: WebRtcFactory, config?: WebRtcManagerConfig)
35
+ ```
36
+
37
+ **Parameters:**
38
+
39
+ | Name | Type | Required | Description |
40
+ |------|------|----------|-------------|
41
+ | factory | `WebRtcFactory` | Yes | Factory for creating WebRTC primitives |
42
+ | config | `WebRtcManagerConfig` | No | Configuration options |
43
+
44
+ **Example:**
45
+
46
+ ```typescript
47
+ import { WebRtcManager } from '@marianmeres/webrtc';
48
+
49
+ const factory = {
50
+ createPeerConnection: (config) => new RTCPeerConnection(config),
51
+ getUserMedia: (constraints) => navigator.mediaDevices.getUserMedia(constraints),
52
+ enumerateDevices: () => navigator.mediaDevices.enumerateDevices(),
53
+ };
54
+
55
+ const manager = new WebRtcManager(factory, {
56
+ peerConfig: { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] },
57
+ enableMicrophone: true,
58
+ autoReconnect: true,
59
+ });
60
+ ```
61
+
62
+ ---
63
+
64
+ ### Properties
65
+
66
+ #### state
67
+
68
+ ```typescript
69
+ get state(): WebRtcState
70
+ ```
71
+
72
+ Returns the current state of the WebRTC connection.
73
+
74
+ **Returns:** `WebRtcState` - One of: `IDLE`, `INITIALIZING`, `CONNECTING`, `CONNECTED`, `RECONNECTING`, `DISCONNECTED`, `ERROR`
75
+
76
+ ---
77
+
78
+ #### localStream
79
+
80
+ ```typescript
81
+ get localStream(): MediaStream | null
82
+ ```
83
+
84
+ Returns the local media stream (microphone audio), or `null` if not initialized.
85
+
86
+ **Returns:** `MediaStream | null`
87
+
88
+ ---
89
+
90
+ #### remoteStream
91
+
92
+ ```typescript
93
+ get remoteStream(): MediaStream | null
94
+ ```
95
+
96
+ Returns the remote media stream received from peer, or `null` if not connected.
97
+
98
+ **Returns:** `MediaStream | null`
99
+
100
+ ---
101
+
102
+ #### dataChannels
103
+
104
+ ```typescript
105
+ get dataChannels(): ReadonlyMap<string, RTCDataChannel>
106
+ ```
107
+
108
+ Returns a readonly map of all active data channels, indexed by label.
109
+
110
+ **Returns:** `ReadonlyMap<string, RTCDataChannel>`
111
+
112
+ ---
113
+
114
+ #### peerConnection
115
+
116
+ ```typescript
117
+ get peerConnection(): RTCPeerConnection | null
118
+ ```
119
+
120
+ Returns the underlying RTCPeerConnection, or `null` if not initialized.
121
+
122
+ **Returns:** `RTCPeerConnection | null`
123
+
124
+ ---
125
+
126
+ ### Lifecycle Methods
127
+
128
+ #### initialize()
129
+
130
+ ```typescript
131
+ async initialize(): Promise<void>
132
+ ```
133
+
134
+ Initializes the WebRTC peer connection and sets up media tracks.
135
+
136
+ - Creates RTCPeerConnection using the factory
137
+ - Sets up connection state listeners
138
+ - Enables microphone if configured
139
+ - Creates default data channel if configured
140
+ - Sets up device change detection
141
+
142
+ **State Requirement:** Must be in `IDLE` state.
143
+
144
+ **Transitions:** `IDLE` → `INITIALIZING`
145
+
146
+ **Example:**
147
+
148
+ ```typescript
149
+ await manager.initialize();
150
+ console.log(manager.state); // "INITIALIZING"
151
+ ```
152
+
153
+ ---
154
+
155
+ #### connect()
156
+
157
+ ```typescript
158
+ async connect(): Promise<void>
159
+ ```
160
+
161
+ Transitions to the `CONNECTING` state. Automatically calls `initialize()` if in `IDLE` state. If `DISCONNECTED`, reinitializes the peer connection.
162
+
163
+ **State Transitions:**
164
+ - From `IDLE`: Initializes first, then transitions to `CONNECTING`
165
+ - From `DISCONNECTED`: Cleans up, resets, and reinitializes
166
+ - From `INITIALIZING`: Transitions to `CONNECTING`
167
+ - From `CONNECTED` or `CONNECTING`: No-op
168
+
169
+ **Example:**
170
+
171
+ ```typescript
172
+ await manager.connect();
173
+ // Now ready to create offer/answer
174
+ ```
175
+
176
+ ---
177
+
178
+ #### disconnect()
179
+
180
+ ```typescript
181
+ disconnect(): void
182
+ ```
183
+
184
+ Disconnects the peer connection and cleans up all resources:
185
+ - Closes all data channels
186
+ - Stops local media tracks
187
+ - Closes peer connection
188
+ - Clears reconnection timers
189
+
190
+ **Transitions:** Any state → `DISCONNECTED`
191
+
192
+ **Example:**
193
+
194
+ ```typescript
195
+ manager.disconnect();
196
+ console.log(manager.state); // "DISCONNECTED"
197
+ ```
198
+
199
+ ---
200
+
201
+ #### reset()
202
+
203
+ ```typescript
204
+ reset(): void
205
+ ```
206
+
207
+ Resets the manager to `IDLE` state from any state. Performs full cleanup and allows reinitialization.
208
+
209
+ **Transitions:** Any state → `IDLE`
210
+
211
+ **Example:**
212
+
213
+ ```typescript
214
+ manager.reset();
215
+ console.log(manager.state); // "IDLE"
216
+ // Can now call initialize() again
217
+ ```
218
+
219
+ ---
220
+
221
+ ### Audio Methods
222
+
223
+ #### enableMicrophone()
224
+
225
+ ```typescript
226
+ async enableMicrophone(enable: boolean): Promise<boolean>
227
+ ```
228
+
229
+ Enables or disables the microphone.
230
+
231
+ **Parameters:**
232
+
233
+ | Name | Type | Description |
234
+ |------|------|-------------|
235
+ | enable | `boolean` | `true` to enable, `false` to disable |
236
+
237
+ **Returns:** `boolean` - `true` if successful, `false` if failed
238
+
239
+ **Events Emitted:**
240
+ - `local_stream` - When stream changes
241
+ - `microphone_failed` - On failure
242
+
243
+ **Example:**
244
+
245
+ ```typescript
246
+ const success = await manager.enableMicrophone(true);
247
+ if (success) {
248
+ console.log('Microphone enabled');
249
+ }
250
+ ```
251
+
252
+ ---
253
+
254
+ #### switchMicrophone()
255
+
256
+ ```typescript
257
+ async switchMicrophone(deviceId: string): Promise<boolean>
258
+ ```
259
+
260
+ Switches the active microphone to a different audio input device.
261
+
262
+ **Parameters:**
263
+
264
+ | Name | Type | Description |
265
+ |------|------|-------------|
266
+ | deviceId | `string` | Device ID from `getAudioInputDevices()` |
267
+
268
+ **Returns:** `boolean` - `true` if successful, `false` otherwise
269
+
270
+ **Requirements:** Peer connection must be initialized and microphone must be enabled.
271
+
272
+ **Example:**
273
+
274
+ ```typescript
275
+ const devices = await manager.getAudioInputDevices();
276
+ const success = await manager.switchMicrophone(devices[1].deviceId);
277
+ ```
278
+
279
+ ---
280
+
281
+ #### getAudioInputDevices()
282
+
283
+ ```typescript
284
+ async getAudioInputDevices(): Promise<MediaDeviceInfo[]>
285
+ ```
286
+
287
+ Retrieves all available audio input devices.
288
+
289
+ **Returns:** `MediaDeviceInfo[]` - Array of audio input devices, empty array on error
290
+
291
+ **Example:**
292
+
293
+ ```typescript
294
+ const devices = await manager.getAudioInputDevices();
295
+ devices.forEach(d => console.log(d.label, d.deviceId));
296
+ ```
297
+
298
+ ---
299
+
300
+ ### Data Channel Methods
301
+
302
+ #### createDataChannel()
303
+
304
+ ```typescript
305
+ createDataChannel(label: string, options?: RTCDataChannelInit): RTCDataChannel | null
306
+ ```
307
+
308
+ Creates a new data channel with the specified label. Returns existing channel if one with the same label already exists.
309
+
310
+ **Parameters:**
311
+
312
+ | Name | Type | Required | Description |
313
+ |------|------|----------|-------------|
314
+ | label | `string` | Yes | Unique identifier for the channel |
315
+ | options | `RTCDataChannelInit` | No | Channel configuration |
316
+
317
+ **Returns:** `RTCDataChannel | null` - The channel, or `null` if peer connection not initialized
318
+
319
+ **Example:**
320
+
321
+ ```typescript
322
+ const chatChannel = manager.createDataChannel('chat');
323
+ const fileChannel = manager.createDataChannel('file-transfer', { ordered: true });
324
+ ```
325
+
326
+ ---
327
+
328
+ #### getDataChannel()
329
+
330
+ ```typescript
331
+ getDataChannel(label: string): RTCDataChannel | undefined
332
+ ```
333
+
334
+ Retrieves an existing data channel by label.
335
+
336
+ **Parameters:**
337
+
338
+ | Name | Type | Description |
339
+ |------|------|-------------|
340
+ | label | `string` | The channel label |
341
+
342
+ **Returns:** `RTCDataChannel | undefined`
343
+
344
+ **Example:**
345
+
346
+ ```typescript
347
+ const channel = manager.getDataChannel('chat');
348
+ if (channel?.readyState === 'open') {
349
+ // Channel is ready
350
+ }
351
+ ```
352
+
353
+ ---
354
+
355
+ #### sendData()
356
+
357
+ ```typescript
358
+ sendData(label: string, data: string | Blob | ArrayBuffer | ArrayBufferView): boolean
359
+ ```
360
+
361
+ Sends data through a data channel. Verifies channel exists and is open before sending.
362
+
363
+ **Parameters:**
364
+
365
+ | Name | Type | Description |
366
+ |------|------|-------------|
367
+ | label | `string` | The channel label |
368
+ | data | `string \| Blob \| ArrayBuffer \| ArrayBufferView` | Data to send |
369
+
370
+ **Returns:** `boolean` - `true` if sent, `false` otherwise
371
+
372
+ **Example:**
373
+
374
+ ```typescript
375
+ // Send string
376
+ manager.sendData('chat', 'Hello!');
377
+
378
+ // Send binary
379
+ const buffer = new ArrayBuffer(8);
380
+ manager.sendData('binary', buffer);
381
+ ```
382
+
383
+ ---
384
+
385
+ ### Signaling Methods
386
+
387
+ #### createOffer()
388
+
389
+ ```typescript
390
+ async createOffer(options?: RTCOfferOptions): Promise<RTCSessionDescriptionInit | null>
391
+ ```
392
+
393
+ Creates an SDP offer for initiating a WebRTC connection.
394
+
395
+ **Parameters:**
396
+
397
+ | Name | Type | Description |
398
+ |------|------|-------------|
399
+ | options | `RTCOfferOptions` | Optional offer configuration |
400
+
401
+ **Returns:** `RTCSessionDescriptionInit | null` - The offer, or `null` on error
402
+
403
+ **Example:**
404
+
405
+ ```typescript
406
+ const offer = await manager.createOffer();
407
+ await manager.setLocalDescription(offer);
408
+ // Send offer to remote peer via signaling channel
409
+ ```
410
+
411
+ ---
412
+
413
+ #### createAnswer()
414
+
415
+ ```typescript
416
+ async createAnswer(options?: RTCAnswerOptions): Promise<RTCSessionDescriptionInit | null>
417
+ ```
418
+
419
+ Creates an SDP answer in response to a received offer.
420
+
421
+ **Parameters:**
422
+
423
+ | Name | Type | Description |
424
+ |------|------|-------------|
425
+ | options | `RTCAnswerOptions` | Optional answer configuration |
426
+
427
+ **Returns:** `RTCSessionDescriptionInit | null` - The answer, or `null` on error
428
+
429
+ **Example:**
430
+
431
+ ```typescript
432
+ // After receiving and setting remote offer
433
+ const answer = await manager.createAnswer();
434
+ await manager.setLocalDescription(answer);
435
+ // Send answer to remote peer
436
+ ```
437
+
438
+ ---
439
+
440
+ #### setLocalDescription()
441
+
442
+ ```typescript
443
+ async setLocalDescription(description: RTCSessionDescriptionInit): Promise<boolean>
444
+ ```
445
+
446
+ Sets the local SDP description (offer or answer).
447
+
448
+ **Parameters:**
449
+
450
+ | Name | Type | Description |
451
+ |------|------|-------------|
452
+ | description | `RTCSessionDescriptionInit` | The SDP description |
453
+
454
+ **Returns:** `boolean` - `true` if successful
455
+
456
+ **Example:**
457
+
458
+ ```typescript
459
+ const offer = await manager.createOffer();
460
+ const success = await manager.setLocalDescription(offer);
461
+ ```
462
+
463
+ ---
464
+
465
+ #### setRemoteDescription()
466
+
467
+ ```typescript
468
+ async setRemoteDescription(description: RTCSessionDescriptionInit): Promise<boolean>
469
+ ```
470
+
471
+ Sets the remote SDP description received from the peer.
472
+
473
+ **Parameters:**
474
+
475
+ | Name | Type | Description |
476
+ |------|------|-------------|
477
+ | description | `RTCSessionDescriptionInit` | The remote SDP |
478
+
479
+ **Returns:** `boolean` - `true` if successful
480
+
481
+ **Example:**
482
+
483
+ ```typescript
484
+ // Received from signaling channel
485
+ await manager.setRemoteDescription(remoteOffer);
486
+ ```
487
+
488
+ ---
489
+
490
+ #### addIceCandidate()
491
+
492
+ ```typescript
493
+ async addIceCandidate(candidate: RTCIceCandidateInit | null): Promise<boolean>
494
+ ```
495
+
496
+ Adds an ICE candidate received from the remote peer.
497
+
498
+ **Parameters:**
499
+
500
+ | Name | Type | Description |
501
+ |------|------|-------------|
502
+ | candidate | `RTCIceCandidateInit \| null` | The ICE candidate, or `null` for end-of-candidates |
503
+
504
+ **Returns:** `boolean` - `true` if successful
505
+
506
+ **Example:**
507
+
508
+ ```typescript
509
+ // From signaling channel
510
+ await manager.addIceCandidate(remoteCandidate);
511
+ ```
512
+
513
+ ---
514
+
515
+ #### iceRestart()
516
+
517
+ ```typescript
518
+ async iceRestart(): Promise<boolean>
519
+ ```
520
+
521
+ Performs an ICE restart to recover from connection issues. Creates a new offer with the `iceRestart` flag.
522
+
523
+ **Returns:** `boolean` - `true` if successful
524
+
525
+ **Example:**
526
+
527
+ ```typescript
528
+ const success = await manager.iceRestart();
529
+ // New ICE candidates will be generated
530
+ ```
531
+
532
+ ---
533
+
534
+ #### getLocalDescription()
535
+
536
+ ```typescript
537
+ getLocalDescription(): RTCSessionDescription | null
538
+ ```
539
+
540
+ Returns the current local session description.
541
+
542
+ **Returns:** `RTCSessionDescription | null`
543
+
544
+ ---
545
+
546
+ #### getRemoteDescription()
547
+
548
+ ```typescript
549
+ getRemoteDescription(): RTCSessionDescription | null
550
+ ```
551
+
552
+ Returns the current remote session description.
553
+
554
+ **Returns:** `RTCSessionDescription | null`
555
+
556
+ ---
557
+
558
+ #### getStats()
559
+
560
+ ```typescript
561
+ async getStats(): Promise<RTCStatsReport | null>
562
+ ```
563
+
564
+ Retrieves WebRTC statistics for the peer connection.
565
+
566
+ **Returns:** `RTCStatsReport | null` - Statistics report, or `null` if not initialized
567
+
568
+ **Example:**
569
+
570
+ ```typescript
571
+ const stats = await manager.getStats();
572
+ stats?.forEach(report => {
573
+ console.log(report.type, report);
574
+ });
575
+ ```
576
+
577
+ ---
578
+
579
+ ### Event Methods
580
+
581
+ #### on()
582
+
583
+ ```typescript
584
+ on<K extends keyof WebRtcEvents>(
585
+ event: K,
586
+ handler: (data: WebRtcEvents[K]) => void
587
+ ): () => void
588
+ ```
589
+
590
+ Subscribe to a specific WebRTC event.
591
+
592
+ **Parameters:**
593
+
594
+ | Name | Type | Description |
595
+ |------|------|-------------|
596
+ | event | `keyof WebRtcEvents` | Event name |
597
+ | handler | `function` | Callback receiving event data |
598
+
599
+ **Returns:** `() => void` - Unsubscribe function
600
+
601
+ **Example:**
602
+
603
+ ```typescript
604
+ const unsub = manager.on('state_change', (state) => {
605
+ console.log('New state:', state);
606
+ });
607
+
608
+ // Later: unsub();
609
+ ```
610
+
611
+ ---
612
+
613
+ #### subscribe()
614
+
615
+ ```typescript
616
+ subscribe(handler: (state: OverallState) => void): () => void
617
+ ```
618
+
619
+ Subscribe to the overall state of the manager. Svelte store compatible - immediately calls handler with current state, then on changes.
620
+
621
+ **Parameters:**
622
+
623
+ | Name | Type | Description |
624
+ |------|------|-------------|
625
+ | handler | `function` | Callback receiving overall state |
626
+
627
+ **Overall State Object:**
628
+
629
+ ```typescript
630
+ {
631
+ state: WebRtcState;
632
+ localStream: MediaStream | null;
633
+ remoteStream: MediaStream | null;
634
+ dataChannels: ReadonlyMap<string, RTCDataChannel>;
635
+ peerConnection: RTCPeerConnection | null;
636
+ }
637
+ ```
638
+
639
+ **Returns:** `() => void` - Unsubscribe function
640
+
641
+ **Example (Svelte):**
642
+
643
+ ```svelte
644
+ <script>
645
+ const manager = new WebRtcManager(factory, config);
646
+ </script>
647
+
648
+ <p>State: {$manager.state}</p>
649
+ ```
650
+
651
+ **Example (Vanilla):**
652
+
653
+ ```typescript
654
+ const unsub = manager.subscribe((state) => {
655
+ console.log('Overall state:', state);
656
+ });
657
+ ```
658
+
659
+ ---
660
+
661
+ ### Utility Methods
662
+
663
+ #### toMermaid()
664
+
665
+ ```typescript
666
+ toMermaid(): string
667
+ ```
668
+
669
+ Returns a Mermaid diagram representation of the FSM state machine.
670
+
671
+ **Returns:** `string` - Mermaid diagram source
672
+
673
+ **Example:**
674
+
675
+ ```typescript
676
+ console.log(manager.toMermaid());
677
+ // Outputs Mermaid state diagram
678
+ ```
679
+
680
+ ---
681
+
682
+ ## Types
683
+
684
+ ### WebRtcFactory
685
+
686
+ Interface for dependency injection of WebRTC primitives.
687
+
688
+ ```typescript
689
+ interface WebRtcFactory {
690
+ createPeerConnection(config?: RTCConfiguration): RTCPeerConnection;
691
+ getUserMedia(constraints: MediaStreamConstraints): Promise<MediaStream>;
692
+ enumerateDevices(): Promise<MediaDeviceInfo[]>;
693
+ }
694
+ ```
695
+
696
+ **Browser Implementation:**
697
+
698
+ ```typescript
699
+ const factory: WebRtcFactory = {
700
+ createPeerConnection: (config) => new RTCPeerConnection(config),
701
+ getUserMedia: (constraints) => navigator.mediaDevices.getUserMedia(constraints),
702
+ enumerateDevices: () => navigator.mediaDevices.enumerateDevices(),
703
+ };
704
+ ```
705
+
706
+ ---
707
+
708
+ ### WebRtcManagerConfig
709
+
710
+ Configuration options for WebRtcManager.
711
+
712
+ ```typescript
713
+ interface WebRtcManagerConfig {
714
+ /** RTCConfiguration for ICE servers, certificates, etc. */
715
+ peerConfig?: RTCConfiguration;
716
+
717
+ /** Enable microphone on initialization. Default: false */
718
+ enableMicrophone?: boolean;
719
+
720
+ /** Create a data channel with this label on connect */
721
+ dataChannelLabel?: string;
722
+
723
+ /** Enable automatic reconnection on failure. Default: false */
724
+ autoReconnect?: boolean;
725
+
726
+ /** Maximum reconnection attempts. Default: 5 */
727
+ maxReconnectAttempts?: number;
728
+
729
+ /** Initial reconnection delay in ms (doubles each attempt). Default: 1000 */
730
+ reconnectDelay?: number;
731
+
732
+ /** Enable debug logging. Default: false */
733
+ debug?: boolean;
734
+ }
735
+ ```
736
+
737
+ ---
738
+
739
+ ### WebRtcState
740
+
741
+ Enum of possible connection states.
742
+
743
+ ```typescript
744
+ enum WebRtcState {
745
+ IDLE = "IDLE",
746
+ INITIALIZING = "INITIALIZING",
747
+ CONNECTING = "CONNECTING",
748
+ CONNECTED = "CONNECTED",
749
+ RECONNECTING = "RECONNECTING",
750
+ DISCONNECTED = "DISCONNECTED",
751
+ ERROR = "ERROR"
752
+ }
753
+ ```
754
+
755
+ | State | Description |
756
+ |-------|-------------|
757
+ | IDLE | Initial state, no resources allocated |
758
+ | INITIALIZING | Creating peer connection and setting up |
759
+ | CONNECTING | Performing offer/answer exchange |
760
+ | CONNECTED | Connection established, ready for communication |
761
+ | RECONNECTING | Attempting to restore failed connection |
762
+ | DISCONNECTED | Connection closed, can reconnect |
763
+ | ERROR | Error occurred, must call `reset()` |
764
+
765
+ ---
766
+
767
+ ### WebRtcFsmEvent
768
+
769
+ Internal FSM events (for reference).
770
+
771
+ ```typescript
772
+ enum WebRtcFsmEvent {
773
+ INIT = "initialize",
774
+ CONNECT = "connect",
775
+ CONNECTED = "connected",
776
+ RECONNECTING = "reconnecting",
777
+ DISCONNECT = "disconnect",
778
+ ERROR = "error",
779
+ RESET = "reset"
780
+ }
781
+ ```
782
+
783
+ ---
784
+
785
+ ### WebRtcEvents
786
+
787
+ Type definition for all events and their payloads.
788
+
789
+ ```typescript
790
+ interface WebRtcEvents {
791
+ state_change: WebRtcState;
792
+ local_stream: MediaStream | null;
793
+ remote_stream: MediaStream | null;
794
+ data_channel_open: RTCDataChannel;
795
+ data_channel_message: { channel: RTCDataChannel; data: any };
796
+ data_channel_close: RTCDataChannel;
797
+ ice_candidate: RTCIceCandidate | null;
798
+ reconnecting: { attempt: number; strategy: "ice-restart" | "full" };
799
+ reconnect_failed: { attempts: number };
800
+ device_changed: MediaDeviceInfo[];
801
+ microphone_failed: { error?: any; reason?: string };
802
+ error: Error;
803
+ }
804
+ ```
805
+
806
+ ---
807
+
808
+ ## Event Constants
809
+
810
+ Static event name constants on `WebRtcManager`.
811
+
812
+ | Constant | Value | Payload |
813
+ |----------|-------|---------|
814
+ | `EVENT_STATE_CHANGE` | `"state_change"` | `WebRtcState` |
815
+ | `EVENT_LOCAL_STREAM` | `"local_stream"` | `MediaStream \| null` |
816
+ | `EVENT_REMOTE_STREAM` | `"remote_stream"` | `MediaStream \| null` |
817
+ | `EVENT_DATA_CHANNEL_OPEN` | `"data_channel_open"` | `RTCDataChannel` |
818
+ | `EVENT_DATA_CHANNEL_MESSAGE` | `"data_channel_message"` | `{ channel, data }` |
819
+ | `EVENT_DATA_CHANNEL_CLOSE` | `"data_channel_close"` | `RTCDataChannel` |
820
+ | `EVENT_ICE_CANDIDATE` | `"ice_candidate"` | `RTCIceCandidate \| null` |
821
+ | `EVENT_RECONNECTING` | `"reconnecting"` | `{ attempt, strategy }` |
822
+ | `EVENT_RECONNECT_FAILED` | `"reconnect_failed"` | `{ attempts }` |
823
+ | `EVENT_DEVICE_CHANGED` | `"device_changed"` | `MediaDeviceInfo[]` |
824
+ | `EVENT_MICROPHONE_FAILED` | `"microphone_failed"` | `{ error?, reason? }` |
825
+ | `EVENT_ERROR` | `"error"` | `Error` |
826
+
827
+ **Usage:**
828
+
829
+ ```typescript
830
+ manager.on(WebRtcManager.EVENT_ICE_CANDIDATE, (candidate) => {
831
+ // Send to remote peer
832
+ });
833
+ ```
834
+
835
+ ---
836
+
837
+ ## State Machine
838
+
839
+ ### State Transition Diagram
840
+
841
+ ```
842
+ ┌─────────────────────────────────────────────────────────┐
843
+ │ │
844
+ ▼ │
845
+ ┌──────┐ INIT ┌──────────────┐ CONNECT ┌────────────┐ CONNECTED │
846
+ │ IDLE │────────▶│ INITIALIZING │──────────▶│ CONNECTING │─────────────┤
847
+ └──────┘ └──────────────┘ └────────────┘ │
848
+ ▲ │ │ │
849
+ │ │ ERROR │ ERROR │
850
+ │ ▼ ▼ ▼
851
+ │ ┌─────────┐◀──────────────────────────────────┌───────────┐
852
+ │ │ ERROR │ │ CONNECTED │
853
+ │ └─────────┘ └───────────┘
854
+ │ │ │
855
+ │ RESET DISCONNECT
856
+ │ │ │
857
+ │ ▼ ▼
858
+ │ ┌──────┐◀─────────────────────────────────┌──────────────┐
859
+ └──────────────│ IDLE │ RESET │ DISCONNECTED │
860
+ └──────┘◀────────────────────┬────────────└──────────────┘
861
+ │ │
862
+ │ RECONNECTING
863
+ │ │
864
+ RESET/ ▼
865
+ DISCONNECT ┌──────────────┐
866
+ │ │ RECONNECTING │
867
+ └─────────────└──────────────┘
868
+
869
+ CONNECT
870
+
871
+
872
+ ┌────────────┐
873
+ │ CONNECTING │
874
+ └────────────┘
875
+ ```
876
+
877
+ ### Valid Transitions
878
+
879
+ | From State | Event | To State |
880
+ |------------|-------|----------|
881
+ | IDLE | INIT | INITIALIZING |
882
+ | INITIALIZING | CONNECT | CONNECTING |
883
+ | INITIALIZING | ERROR | ERROR |
884
+ | CONNECTING | CONNECTED | CONNECTED |
885
+ | CONNECTING | DISCONNECT | DISCONNECTED |
886
+ | CONNECTING | ERROR | ERROR |
887
+ | CONNECTED | DISCONNECT | DISCONNECTED |
888
+ | CONNECTED | ERROR | ERROR |
889
+ | RECONNECTING | CONNECT | CONNECTING |
890
+ | RECONNECTING | DISCONNECT | DISCONNECTED |
891
+ | RECONNECTING | RESET | IDLE |
892
+ | DISCONNECTED | CONNECT | CONNECTING |
893
+ | DISCONNECTED | RECONNECTING | RECONNECTING |
894
+ | DISCONNECTED | RESET | IDLE |
895
+ | ERROR | RESET | IDLE |
896
+
897
+ ### Reconnection Strategy
898
+
899
+ When `autoReconnect: true`:
900
+
901
+ 1. **Attempts 1-2:** ICE restart (quick, preserves connection)
902
+ 2. **Attempts 3+:** Full reconnection (new peer connection)
903
+ 3. **Backoff:** `reconnectDelay * 2^(attempt-1)` milliseconds
904
+
905
+ For "full" strategy reconnections, listen for the `reconnecting` event and re-perform signaling:
906
+
907
+ ```typescript
908
+ manager.on('reconnecting', ({ attempt, strategy }) => {
909
+ if (strategy === 'full') {
910
+ // Re-do offer/answer exchange
911
+ const offer = await manager.createOffer();
912
+ await manager.setLocalDescription(offer);
913
+ sendToRemote(offer);
914
+ }
915
+ });
916
+ ```