@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/AGENTS.md +357 -0
- package/API.md +916 -0
- package/README.md +6 -1
- package/dist/webrtc-manager.d.ts +45 -0
- package/dist/webrtc-manager.js +45 -1
- package/llm.txt +364 -0
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# @marianmeres/webrtc
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@marianmeres/webrtc)
|
|
4
|
+
[](https://jsr.io/@marianmeres/webrtc)
|
|
4
5
|
|
|
5
6
|
A lightweight, framework-agnostic WebRTC manager with state machine-based lifecycle management and event-driven architecture.
|
|
6
7
|
|
|
@@ -366,6 +367,10 @@ await connection.createOffer();
|
|
|
366
367
|
connection.sendMessage('Hello!');
|
|
367
368
|
```
|
|
368
369
|
|
|
370
|
+
## API Reference
|
|
371
|
+
|
|
372
|
+
For complete API documentation, see [API.md](API.md).
|
|
373
|
+
|
|
369
374
|
## State Machine
|
|
370
375
|
|
|
371
376
|
The manager uses a finite state machine with the following states:
|
package/dist/webrtc-manager.d.ts
CHANGED
|
@@ -1,18 +1,61 @@
|
|
|
1
1
|
import { type WebRtcFactory, type WebRtcManagerConfig, WebRtcState, type WebRtcEvents } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* WebRTC connection manager with FSM-based lifecycle and event-driven architecture.
|
|
4
|
+
*
|
|
5
|
+
* Provides a high-level API for managing WebRTC peer connections, audio streams,
|
|
6
|
+
* and data channels. The manager uses a finite state machine to handle connection
|
|
7
|
+
* lifecycle and emits events for all state changes and important occurrences.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const factory = {
|
|
12
|
+
* createPeerConnection: (config) => new RTCPeerConnection(config),
|
|
13
|
+
* getUserMedia: (constraints) => navigator.mediaDevices.getUserMedia(constraints),
|
|
14
|
+
* enumerateDevices: () => navigator.mediaDevices.enumerateDevices(),
|
|
15
|
+
* };
|
|
16
|
+
*
|
|
17
|
+
* const manager = new WebRtcManager(factory, {
|
|
18
|
+
* peerConfig: { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] },
|
|
19
|
+
* enableMicrophone: true,
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* await manager.initialize();
|
|
23
|
+
* await manager.connect();
|
|
24
|
+
* const offer = await manager.createOffer();
|
|
25
|
+
* await manager.setLocalDescription(offer);
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
2
28
|
export declare class WebRtcManager {
|
|
3
29
|
#private;
|
|
30
|
+
/** Event emitted when connection state changes. Payload: {@link WebRtcState} */
|
|
4
31
|
static readonly EVENT_STATE_CHANGE = "state_change";
|
|
32
|
+
/** Event emitted when local media stream changes. Payload: `MediaStream | null` */
|
|
5
33
|
static readonly EVENT_LOCAL_STREAM = "local_stream";
|
|
34
|
+
/** Event emitted when remote media stream is received. Payload: `MediaStream | null` */
|
|
6
35
|
static readonly EVENT_REMOTE_STREAM = "remote_stream";
|
|
36
|
+
/** Event emitted when a data channel opens. Payload: `RTCDataChannel` */
|
|
7
37
|
static readonly EVENT_DATA_CHANNEL_OPEN = "data_channel_open";
|
|
38
|
+
/** Event emitted when a data channel receives a message. Payload: `{ channel: RTCDataChannel; data: any }` */
|
|
8
39
|
static readonly EVENT_DATA_CHANNEL_MESSAGE = "data_channel_message";
|
|
40
|
+
/** Event emitted when a data channel closes. Payload: `RTCDataChannel` */
|
|
9
41
|
static readonly EVENT_DATA_CHANNEL_CLOSE = "data_channel_close";
|
|
42
|
+
/** Event emitted when an ICE candidate is generated. Payload: `RTCIceCandidate | null` */
|
|
10
43
|
static readonly EVENT_ICE_CANDIDATE = "ice_candidate";
|
|
44
|
+
/** Event emitted when reconnection is attempted. Payload: `{ attempt: number; strategy: "ice-restart" | "full" }` */
|
|
11
45
|
static readonly EVENT_RECONNECTING = "reconnecting";
|
|
46
|
+
/** Event emitted when all reconnection attempts fail. Payload: `{ attempts: number }` */
|
|
12
47
|
static readonly EVENT_RECONNECT_FAILED = "reconnect_failed";
|
|
48
|
+
/** Event emitted when audio devices change. Payload: `MediaDeviceInfo[]` */
|
|
13
49
|
static readonly EVENT_DEVICE_CHANGED = "device_changed";
|
|
50
|
+
/** Event emitted when microphone access fails. Payload: `{ error?: any; reason?: string }` */
|
|
14
51
|
static readonly EVENT_MICROPHONE_FAILED = "microphone_failed";
|
|
52
|
+
/** Event emitted when an error occurs. Payload: `Error` */
|
|
15
53
|
static readonly EVENT_ERROR = "error";
|
|
54
|
+
/**
|
|
55
|
+
* Creates a new WebRtcManager instance.
|
|
56
|
+
* @param factory - Factory object providing WebRTC primitives (peer connection, media, devices).
|
|
57
|
+
* @param config - Optional configuration for the manager.
|
|
58
|
+
*/
|
|
16
59
|
constructor(factory: WebRtcFactory, config?: WebRtcManagerConfig);
|
|
17
60
|
/** Returns the current state of the WebRTC connection. */
|
|
18
61
|
get state(): WebRtcState;
|
|
@@ -28,6 +71,8 @@ export declare class WebRtcManager {
|
|
|
28
71
|
toMermaid(): string;
|
|
29
72
|
/**
|
|
30
73
|
* Subscribe to a specific WebRTC event.
|
|
74
|
+
* @param event - The event name to subscribe to (e.g., "state_change", "ice_candidate").
|
|
75
|
+
* @param handler - Callback function that receives the event data.
|
|
31
76
|
* @returns Unsubscribe function to remove the event listener.
|
|
32
77
|
*/
|
|
33
78
|
on(event: keyof WebRtcEvents, handler: (data: any) => void): () => void;
|
package/dist/webrtc-manager.js
CHANGED
|
@@ -1,19 +1,56 @@
|
|
|
1
1
|
import { FSM } from "@marianmeres/fsm";
|
|
2
2
|
import { PubSub } from "@marianmeres/pubsub";
|
|
3
3
|
import { WebRtcState, WebRtcFsmEvent, } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* WebRTC connection manager with FSM-based lifecycle and event-driven architecture.
|
|
6
|
+
*
|
|
7
|
+
* Provides a high-level API for managing WebRTC peer connections, audio streams,
|
|
8
|
+
* and data channels. The manager uses a finite state machine to handle connection
|
|
9
|
+
* lifecycle and emits events for all state changes and important occurrences.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const factory = {
|
|
14
|
+
* createPeerConnection: (config) => new RTCPeerConnection(config),
|
|
15
|
+
* getUserMedia: (constraints) => navigator.mediaDevices.getUserMedia(constraints),
|
|
16
|
+
* enumerateDevices: () => navigator.mediaDevices.enumerateDevices(),
|
|
17
|
+
* };
|
|
18
|
+
*
|
|
19
|
+
* const manager = new WebRtcManager(factory, {
|
|
20
|
+
* peerConfig: { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] },
|
|
21
|
+
* enableMicrophone: true,
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* await manager.initialize();
|
|
25
|
+
* await manager.connect();
|
|
26
|
+
* const offer = await manager.createOffer();
|
|
27
|
+
* await manager.setLocalDescription(offer);
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
4
30
|
export class WebRtcManager {
|
|
5
|
-
|
|
31
|
+
/** Event emitted when connection state changes. Payload: {@link WebRtcState} */
|
|
6
32
|
static EVENT_STATE_CHANGE = "state_change";
|
|
33
|
+
/** Event emitted when local media stream changes. Payload: `MediaStream | null` */
|
|
7
34
|
static EVENT_LOCAL_STREAM = "local_stream";
|
|
35
|
+
/** Event emitted when remote media stream is received. Payload: `MediaStream | null` */
|
|
8
36
|
static EVENT_REMOTE_STREAM = "remote_stream";
|
|
37
|
+
/** Event emitted when a data channel opens. Payload: `RTCDataChannel` */
|
|
9
38
|
static EVENT_DATA_CHANNEL_OPEN = "data_channel_open";
|
|
39
|
+
/** Event emitted when a data channel receives a message. Payload: `{ channel: RTCDataChannel; data: any }` */
|
|
10
40
|
static EVENT_DATA_CHANNEL_MESSAGE = "data_channel_message";
|
|
41
|
+
/** Event emitted when a data channel closes. Payload: `RTCDataChannel` */
|
|
11
42
|
static EVENT_DATA_CHANNEL_CLOSE = "data_channel_close";
|
|
43
|
+
/** Event emitted when an ICE candidate is generated. Payload: `RTCIceCandidate | null` */
|
|
12
44
|
static EVENT_ICE_CANDIDATE = "ice_candidate";
|
|
45
|
+
/** Event emitted when reconnection is attempted. Payload: `{ attempt: number; strategy: "ice-restart" | "full" }` */
|
|
13
46
|
static EVENT_RECONNECTING = "reconnecting";
|
|
47
|
+
/** Event emitted when all reconnection attempts fail. Payload: `{ attempts: number }` */
|
|
14
48
|
static EVENT_RECONNECT_FAILED = "reconnect_failed";
|
|
49
|
+
/** Event emitted when audio devices change. Payload: `MediaDeviceInfo[]` */
|
|
15
50
|
static EVENT_DEVICE_CHANGED = "device_changed";
|
|
51
|
+
/** Event emitted when microphone access fails. Payload: `{ error?: any; reason?: string }` */
|
|
16
52
|
static EVENT_MICROPHONE_FAILED = "microphone_failed";
|
|
53
|
+
/** Event emitted when an error occurs. Payload: `Error` */
|
|
17
54
|
static EVENT_ERROR = "error";
|
|
18
55
|
#fsm;
|
|
19
56
|
#pubsub;
|
|
@@ -26,6 +63,11 @@ export class WebRtcManager {
|
|
|
26
63
|
#reconnectAttempts = 0;
|
|
27
64
|
#reconnectTimer = null;
|
|
28
65
|
#deviceChangeHandler = null;
|
|
66
|
+
/**
|
|
67
|
+
* Creates a new WebRtcManager instance.
|
|
68
|
+
* @param factory - Factory object providing WebRTC primitives (peer connection, media, devices).
|
|
69
|
+
* @param config - Optional configuration for the manager.
|
|
70
|
+
*/
|
|
29
71
|
constructor(factory, config = {}) {
|
|
30
72
|
this.#factory = factory;
|
|
31
73
|
this.#config = config;
|
|
@@ -103,6 +145,8 @@ export class WebRtcManager {
|
|
|
103
145
|
}
|
|
104
146
|
/**
|
|
105
147
|
* Subscribe to a specific WebRTC event.
|
|
148
|
+
* @param event - The event name to subscribe to (e.g., "state_change", "ice_candidate").
|
|
149
|
+
* @param handler - Callback function that receives the event data.
|
|
106
150
|
* @returns Unsubscribe function to remove the event listener.
|
|
107
151
|
*/
|
|
108
152
|
on(event, handler) {
|
package/llm.txt
ADDED
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
# @marianmeres/webrtc - LLM Knowledge Base
|
|
2
|
+
|
|
3
|
+
## Package Identity
|
|
4
|
+
|
|
5
|
+
name: @marianmeres/webrtc
|
|
6
|
+
version: 0.0.2
|
|
7
|
+
license: MIT
|
|
8
|
+
author: Marian Meres
|
|
9
|
+
repository: https://github.com/marianmeres/webrtc
|
|
10
|
+
runtime: Deno (source), Node.js/Browser (distribution)
|
|
11
|
+
type: WebRTC connection management library
|
|
12
|
+
|
|
13
|
+
## Purpose
|
|
14
|
+
|
|
15
|
+
A lightweight, framework-agnostic WebRTC manager providing:
|
|
16
|
+
- Finite State Machine (FSM) based lifecycle management
|
|
17
|
+
- Event-driven architecture with PubSub pattern
|
|
18
|
+
- Svelte store compatibility
|
|
19
|
+
- Audio device management (microphone switching)
|
|
20
|
+
- Data channel support
|
|
21
|
+
- Auto-reconnection with exponential backoff
|
|
22
|
+
- Full TypeScript type safety
|
|
23
|
+
|
|
24
|
+
## Architecture Overview
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
WebRtcManager
|
|
28
|
+
├── FSM (@marianmeres/fsm) - State transitions
|
|
29
|
+
├── PubSub (@marianmeres/pubsub) - Event subscriptions
|
|
30
|
+
├── RTCPeerConnection - WebRTC connection (via factory)
|
|
31
|
+
├── MediaStream (local/remote) - Audio streams
|
|
32
|
+
└── DataChannels Map - RTCDataChannel instances
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Dependencies
|
|
36
|
+
|
|
37
|
+
Production:
|
|
38
|
+
- @marianmeres/fsm: ^2.3.0 (state machine)
|
|
39
|
+
- @marianmeres/pubsub: ^2.3.0 (event system)
|
|
40
|
+
|
|
41
|
+
Development (Deno):
|
|
42
|
+
- @std/assert: testing
|
|
43
|
+
- @std/fs: file operations
|
|
44
|
+
- @std/path: path utilities
|
|
45
|
+
|
|
46
|
+
## File Structure
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
src/
|
|
50
|
+
├── mod.ts # Entry point, re-exports all public APIs
|
|
51
|
+
├── types.ts # Type definitions (interfaces, enums)
|
|
52
|
+
└── webrtc-manager.ts # Main WebRtcManager class (839 lines)
|
|
53
|
+
|
|
54
|
+
tests/
|
|
55
|
+
├── mocks.ts # Mock WebRtcFactory for testing
|
|
56
|
+
├── webrtc-manager.test.ts # Deno unit tests
|
|
57
|
+
└── browser/
|
|
58
|
+
├── p2p-tests.ts # Browser integration tests
|
|
59
|
+
└── README.md # Browser test documentation
|
|
60
|
+
|
|
61
|
+
example/
|
|
62
|
+
├── peer.ts # Two-peer example with localStorage signaling
|
|
63
|
+
├── p2p.ts # Single-page P2P example
|
|
64
|
+
├── audio-peer.ts # Audio testing implementation
|
|
65
|
+
└── main.ts # Signaling server example
|
|
66
|
+
|
|
67
|
+
scripts/
|
|
68
|
+
├── build-npm.ts # npm distribution build
|
|
69
|
+
├── build-example.ts # Example bundling
|
|
70
|
+
├── build-browser-tests.ts
|
|
71
|
+
├── serve-browser-tests.ts
|
|
72
|
+
└── signaling-server.ts
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## State Machine
|
|
76
|
+
|
|
77
|
+
### States (WebRtcState enum)
|
|
78
|
+
|
|
79
|
+
| State | Description | Valid Transitions |
|
|
80
|
+
|-------|-------------|-------------------|
|
|
81
|
+
| IDLE | Initial state, no resources | → INITIALIZING |
|
|
82
|
+
| INITIALIZING | Creating peer connection | → CONNECTING, ERROR |
|
|
83
|
+
| CONNECTING | Performing SDP exchange | → CONNECTED, DISCONNECTED, ERROR |
|
|
84
|
+
| CONNECTED | Connection established | → DISCONNECTED, ERROR |
|
|
85
|
+
| RECONNECTING | Auto-reconnection in progress | → CONNECTING, DISCONNECTED, IDLE |
|
|
86
|
+
| DISCONNECTED | Closed but recoverable | → CONNECTING, RECONNECTING, IDLE |
|
|
87
|
+
| ERROR | Error occurred, must reset | → IDLE |
|
|
88
|
+
|
|
89
|
+
### Events (WebRtcFsmEvent enum)
|
|
90
|
+
|
|
91
|
+
| Event | Description |
|
|
92
|
+
|-------|-------------|
|
|
93
|
+
| INIT ("initialize") | Start initialization |
|
|
94
|
+
| CONNECT ("connect") | Begin connection |
|
|
95
|
+
| CONNECTED ("connected") | Connection succeeded |
|
|
96
|
+
| RECONNECTING ("reconnecting") | Start reconnection |
|
|
97
|
+
| DISCONNECT ("disconnect") | Close connection |
|
|
98
|
+
| ERROR ("error") | Error occurred |
|
|
99
|
+
| RESET ("reset") | Return to IDLE |
|
|
100
|
+
|
|
101
|
+
### State Transition Diagram
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
IDLE --INIT--> INITIALIZING --CONNECT--> CONNECTING --CONNECTED--> CONNECTED
|
|
105
|
+
| | |
|
|
106
|
+
v v v
|
|
107
|
+
ERROR <-----------------ERROR<----------------------ERROR
|
|
108
|
+
|
|
|
109
|
+
v
|
|
110
|
+
IDLE (via RESET)
|
|
111
|
+
|
|
112
|
+
CONNECTED --DISCONNECT--> DISCONNECTED --RESET--> IDLE
|
|
113
|
+
|
|
|
114
|
+
v
|
|
115
|
+
RECONNECTING --CONNECT--> CONNECTING
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Public API
|
|
119
|
+
|
|
120
|
+
### Constructor
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
new WebRtcManager(factory: WebRtcFactory, config?: WebRtcManagerConfig)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### WebRtcFactory Interface
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
interface WebRtcFactory {
|
|
130
|
+
createPeerConnection(config?: RTCConfiguration): RTCPeerConnection;
|
|
131
|
+
getUserMedia(constraints: MediaStreamConstraints): Promise<MediaStream>;
|
|
132
|
+
enumerateDevices(): Promise<MediaDeviceInfo[]>;
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Browser implementation:
|
|
137
|
+
```typescript
|
|
138
|
+
const factory = {
|
|
139
|
+
createPeerConnection: (config) => new RTCPeerConnection(config),
|
|
140
|
+
getUserMedia: (constraints) => navigator.mediaDevices.getUserMedia(constraints),
|
|
141
|
+
enumerateDevices: () => navigator.mediaDevices.enumerateDevices(),
|
|
142
|
+
};
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### WebRtcManagerConfig Interface
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
interface WebRtcManagerConfig {
|
|
149
|
+
peerConfig?: RTCConfiguration; // ICE servers, certificates
|
|
150
|
+
enableMicrophone?: boolean; // Enable mic on init (default: false)
|
|
151
|
+
dataChannelLabel?: string; // Auto-create data channel
|
|
152
|
+
autoReconnect?: boolean; // Enable auto-reconnect (default: false)
|
|
153
|
+
maxReconnectAttempts?: number; // Max attempts (default: 5)
|
|
154
|
+
reconnectDelay?: number; // Initial delay ms (default: 1000)
|
|
155
|
+
debug?: boolean; // Enable logging (default: false)
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Properties (Getters)
|
|
160
|
+
|
|
161
|
+
| Property | Type | Description |
|
|
162
|
+
|----------|------|-------------|
|
|
163
|
+
| state | WebRtcState | Current FSM state |
|
|
164
|
+
| localStream | MediaStream \| null | Local audio stream |
|
|
165
|
+
| remoteStream | MediaStream \| null | Remote audio stream |
|
|
166
|
+
| dataChannels | ReadonlyMap<string, RTCDataChannel> | Active data channels |
|
|
167
|
+
| peerConnection | RTCPeerConnection \| null | Underlying connection |
|
|
168
|
+
|
|
169
|
+
### Lifecycle Methods
|
|
170
|
+
|
|
171
|
+
| Method | Signature | Description |
|
|
172
|
+
|--------|-----------|-------------|
|
|
173
|
+
| initialize | `(): Promise<void>` | Create peer connection, setup tracks |
|
|
174
|
+
| connect | `(): Promise<void>` | Transition to CONNECTING (auto-initializes) |
|
|
175
|
+
| disconnect | `(): void` | Close connection, cleanup resources |
|
|
176
|
+
| reset | `(): void` | Reset to IDLE from any state |
|
|
177
|
+
|
|
178
|
+
### Audio Methods
|
|
179
|
+
|
|
180
|
+
| Method | Signature | Returns | Description |
|
|
181
|
+
|--------|-----------|---------|-------------|
|
|
182
|
+
| enableMicrophone | `(enable: boolean): Promise<boolean>` | success | Enable/disable microphone |
|
|
183
|
+
| switchMicrophone | `(deviceId: string): Promise<boolean>` | success | Switch audio input device |
|
|
184
|
+
| getAudioInputDevices | `(): Promise<MediaDeviceInfo[]>` | devices | List audio inputs |
|
|
185
|
+
|
|
186
|
+
### Data Channel Methods
|
|
187
|
+
|
|
188
|
+
| Method | Signature | Returns | Description |
|
|
189
|
+
|--------|-----------|---------|-------------|
|
|
190
|
+
| createDataChannel | `(label: string, options?: RTCDataChannelInit): RTCDataChannel \| null` | channel | Create/get data channel |
|
|
191
|
+
| getDataChannel | `(label: string): RTCDataChannel \| undefined` | channel | Get existing channel |
|
|
192
|
+
| sendData | `(label: string, data: string \| Blob \| ArrayBuffer \| ArrayBufferView): boolean` | success | Send through channel |
|
|
193
|
+
|
|
194
|
+
### Signaling Methods
|
|
195
|
+
|
|
196
|
+
| Method | Signature | Returns | Description |
|
|
197
|
+
|--------|-----------|---------|-------------|
|
|
198
|
+
| createOffer | `(options?: RTCOfferOptions): Promise<RTCSessionDescriptionInit \| null>` | offer | Create SDP offer |
|
|
199
|
+
| createAnswer | `(options?: RTCAnswerOptions): Promise<RTCSessionDescriptionInit \| null>` | answer | Create SDP answer |
|
|
200
|
+
| setLocalDescription | `(description: RTCSessionDescriptionInit): Promise<boolean>` | success | Set local SDP |
|
|
201
|
+
| setRemoteDescription | `(description: RTCSessionDescriptionInit): Promise<boolean>` | success | Set remote SDP |
|
|
202
|
+
| addIceCandidate | `(candidate: RTCIceCandidateInit \| null): Promise<boolean>` | success | Add ICE candidate |
|
|
203
|
+
| iceRestart | `(): Promise<boolean>` | success | Perform ICE restart |
|
|
204
|
+
| getLocalDescription | `(): RTCSessionDescription \| null` | description | Get local SDP |
|
|
205
|
+
| getRemoteDescription | `(): RTCSessionDescription \| null` | description | Get remote SDP |
|
|
206
|
+
| getStats | `(): Promise<RTCStatsReport \| null>` | stats | Get connection statistics |
|
|
207
|
+
|
|
208
|
+
### Event Subscription Methods
|
|
209
|
+
|
|
210
|
+
| Method | Signature | Returns | Description |
|
|
211
|
+
|--------|-----------|---------|-------------|
|
|
212
|
+
| on | `<K extends keyof WebRtcEvents>(event: K, handler: (data: WebRtcEvents[K]) => void): () => void` | unsubscribe | Subscribe to specific event |
|
|
213
|
+
| subscribe | `(handler: (state: OverallState) => void): () => void` | unsubscribe | Subscribe to overall state (Svelte compatible) |
|
|
214
|
+
|
|
215
|
+
### Utility Methods
|
|
216
|
+
|
|
217
|
+
| Method | Signature | Returns | Description |
|
|
218
|
+
|--------|-----------|---------|-------------|
|
|
219
|
+
| toMermaid | `(): string` | mermaid | Get FSM as Mermaid diagram |
|
|
220
|
+
|
|
221
|
+
## Events
|
|
222
|
+
|
|
223
|
+
### Event Constants (Static)
|
|
224
|
+
|
|
225
|
+
| Constant | Value | Payload Type |
|
|
226
|
+
|----------|-------|--------------|
|
|
227
|
+
| EVENT_STATE_CHANGE | "state_change" | WebRtcState |
|
|
228
|
+
| EVENT_LOCAL_STREAM | "local_stream" | MediaStream \| null |
|
|
229
|
+
| EVENT_REMOTE_STREAM | "remote_stream" | MediaStream \| null |
|
|
230
|
+
| EVENT_DATA_CHANNEL_OPEN | "data_channel_open" | RTCDataChannel |
|
|
231
|
+
| EVENT_DATA_CHANNEL_MESSAGE | "data_channel_message" | { channel: RTCDataChannel; data: any } |
|
|
232
|
+
| EVENT_DATA_CHANNEL_CLOSE | "data_channel_close" | RTCDataChannel |
|
|
233
|
+
| EVENT_ICE_CANDIDATE | "ice_candidate" | RTCIceCandidate \| null |
|
|
234
|
+
| EVENT_RECONNECTING | "reconnecting" | { attempt: number; strategy: "ice-restart" \| "full" } |
|
|
235
|
+
| EVENT_RECONNECT_FAILED | "reconnect_failed" | { attempts: number } |
|
|
236
|
+
| EVENT_DEVICE_CHANGED | "device_changed" | MediaDeviceInfo[] |
|
|
237
|
+
| EVENT_MICROPHONE_FAILED | "microphone_failed" | { error?: any; reason?: string } |
|
|
238
|
+
| EVENT_ERROR | "error" | Error |
|
|
239
|
+
|
|
240
|
+
### WebRtcEvents Interface
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
interface WebRtcEvents {
|
|
244
|
+
state_change: WebRtcState;
|
|
245
|
+
local_stream: MediaStream | null;
|
|
246
|
+
remote_stream: MediaStream | null;
|
|
247
|
+
data_channel_open: RTCDataChannel;
|
|
248
|
+
data_channel_message: { channel: RTCDataChannel; data: any };
|
|
249
|
+
data_channel_close: RTCDataChannel;
|
|
250
|
+
ice_candidate: RTCIceCandidate | null;
|
|
251
|
+
reconnecting: { attempt: number; strategy: "ice-restart" | "full" };
|
|
252
|
+
reconnect_failed: { attempts: number };
|
|
253
|
+
device_changed: MediaDeviceInfo[];
|
|
254
|
+
microphone_failed: { error?: any; reason?: string };
|
|
255
|
+
error: Error;
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Signaling Flow (User Responsibility)
|
|
260
|
+
|
|
261
|
+
The library does NOT handle signaling transport. Users must:
|
|
262
|
+
|
|
263
|
+
1. Create signaling channel (WebSocket, HTTP, localStorage, etc.)
|
|
264
|
+
2. Listen for `ice_candidate` events and send to remote peer
|
|
265
|
+
3. Send offers/answers through signaling channel
|
|
266
|
+
4. Receive remote offers/answers and call setRemoteDescription
|
|
267
|
+
5. Receive remote ICE candidates and call addIceCandidate
|
|
268
|
+
|
|
269
|
+
### Offer/Answer Flow
|
|
270
|
+
|
|
271
|
+
```
|
|
272
|
+
Initiator: Responder:
|
|
273
|
+
1. initialize()
|
|
274
|
+
2. connect()
|
|
275
|
+
3. createOffer()
|
|
276
|
+
4. setLocalDescription(offer)
|
|
277
|
+
5. [send offer via signaling] ───→ 6. initialize()
|
|
278
|
+
7. setRemoteDescription(offer)
|
|
279
|
+
8. createAnswer()
|
|
280
|
+
9. setLocalDescription(answer)
|
|
281
|
+
10. setRemoteDescription(answer) ←── [send answer via signaling]
|
|
282
|
+
11. [exchange ICE candidates] ←───→ [exchange ICE candidates]
|
|
283
|
+
12. CONNECTED 12. CONNECTED
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Reconnection Strategy
|
|
287
|
+
|
|
288
|
+
When autoReconnect is enabled:
|
|
289
|
+
|
|
290
|
+
1. Attempts 1-2: ICE restart (preserves connection, quick recovery)
|
|
291
|
+
2. Attempts 3+: Full reconnection (new peer connection)
|
|
292
|
+
3. Exponential backoff: delay * 2^(attempt-1)
|
|
293
|
+
4. Max attempts configurable (default: 5)
|
|
294
|
+
|
|
295
|
+
For "full" strategy reconnections, consumers MUST:
|
|
296
|
+
- Listen for `reconnecting` event with strategy="full"
|
|
297
|
+
- Re-perform signaling handshake (create new offer/answer)
|
|
298
|
+
|
|
299
|
+
## Error Handling Patterns
|
|
300
|
+
|
|
301
|
+
1. Methods return boolean for success/failure
|
|
302
|
+
2. Critical errors transition to ERROR state
|
|
303
|
+
3. ERROR state requires reset() to recover
|
|
304
|
+
4. EVENT_ERROR emitted for all errors
|
|
305
|
+
5. Specific events: EVENT_MICROPHONE_FAILED, EVENT_RECONNECT_FAILED
|
|
306
|
+
|
|
307
|
+
## Testing
|
|
308
|
+
|
|
309
|
+
### Unit Tests (Deno)
|
|
310
|
+
```bash
|
|
311
|
+
deno task test
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Browser Integration Tests
|
|
315
|
+
```bash
|
|
316
|
+
deno task test:browser
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## Build Commands
|
|
320
|
+
|
|
321
|
+
```bash
|
|
322
|
+
deno task npm:build # Build npm distribution
|
|
323
|
+
deno task npm:publish # Build and publish to npm
|
|
324
|
+
deno task build:example # Build examples
|
|
325
|
+
deno task serve:example # Run signaling server
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## Important Implementation Details
|
|
329
|
+
|
|
330
|
+
1. subscribe() is Svelte store compatible (immediate callback + updates)
|
|
331
|
+
2. Data channels auto-cleanup on close
|
|
332
|
+
3. Device change listener auto-setup on initialize
|
|
333
|
+
4. "User-Initiated Abort" errors from intentional close() are ignored
|
|
334
|
+
5. recvonly transceiver added when microphone disabled (ensures audio SDP)
|
|
335
|
+
6. Private fields use # (true private, not accessible externally)
|
|
336
|
+
|
|
337
|
+
## Common Usage Patterns
|
|
338
|
+
|
|
339
|
+
### Minimal P2P Setup
|
|
340
|
+
```typescript
|
|
341
|
+
const manager = new WebRtcManager(factory, { enableMicrophone: true });
|
|
342
|
+
await manager.initialize();
|
|
343
|
+
await manager.connect();
|
|
344
|
+
const offer = await manager.createOffer();
|
|
345
|
+
await manager.setLocalDescription(offer);
|
|
346
|
+
// Send offer, receive answer, exchange ICE candidates...
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### With Data Channel
|
|
350
|
+
```typescript
|
|
351
|
+
const manager = new WebRtcManager(factory, { dataChannelLabel: "chat" });
|
|
352
|
+
manager.on("data_channel_message", ({ data }) => console.log(data));
|
|
353
|
+
// After connection...
|
|
354
|
+
manager.sendData("chat", "Hello!");
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Svelte Integration
|
|
358
|
+
```svelte
|
|
359
|
+
<script>
|
|
360
|
+
const manager = new WebRtcManager(factory, config);
|
|
361
|
+
// $manager reactive access to state
|
|
362
|
+
</script>
|
|
363
|
+
{$manager.state}
|
|
364
|
+
```
|
package/package.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marianmeres/webrtc",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/mod.js",
|
|
6
6
|
"types": "dist/mod.d.ts",
|
|
7
7
|
"author": "Marian Meres",
|
|
8
8
|
"license": "MIT",
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@marianmeres/fsm": "^2.5.4",
|
|
11
|
+
"@marianmeres/pubsub": "^2.4.2"
|
|
12
|
+
},
|
|
9
13
|
"repository": {
|
|
10
14
|
"type": "git",
|
|
11
15
|
"url": "git+https://github.com/marianmeres/webrtc.git"
|
|
12
16
|
},
|
|
13
17
|
"bugs": {
|
|
14
18
|
"url": "https://github.com/marianmeres/webrtc/issues"
|
|
15
|
-
},
|
|
16
|
-
"dependencies": {
|
|
17
|
-
"@marianmeres/fsm": "^2.3.0",
|
|
18
|
-
"@marianmeres/pubsub": "^2.3.0"
|
|
19
19
|
}
|
|
20
|
-
}
|
|
20
|
+
}
|