@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/AGENTS.md
ADDED
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
# AGENTS.md - Machine-Readable Package Documentation
|
|
2
|
+
|
|
3
|
+
## Package Metadata
|
|
4
|
+
|
|
5
|
+
```yaml
|
|
6
|
+
name: "@marianmeres/webrtc"
|
|
7
|
+
version: "0.0.2"
|
|
8
|
+
license: MIT
|
|
9
|
+
author: Marian Meres
|
|
10
|
+
repository: https://github.com/marianmeres/webrtc
|
|
11
|
+
runtime: Deno (source), Node.js/Browser (distribution)
|
|
12
|
+
type: WebRTC connection management library
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Purpose
|
|
16
|
+
|
|
17
|
+
A lightweight, framework-agnostic WebRTC manager providing:
|
|
18
|
+
- FSM-based connection lifecycle management
|
|
19
|
+
- Event-driven architecture with PubSub pattern
|
|
20
|
+
- Svelte store compatibility
|
|
21
|
+
- Audio device management (microphone switching)
|
|
22
|
+
- Data channel support
|
|
23
|
+
- Auto-reconnection with exponential backoff
|
|
24
|
+
- Full TypeScript type safety
|
|
25
|
+
|
|
26
|
+
## Dependencies
|
|
27
|
+
|
|
28
|
+
```yaml
|
|
29
|
+
production:
|
|
30
|
+
- "@marianmeres/fsm": "^2.3.0"
|
|
31
|
+
- "@marianmeres/pubsub": "^2.4.0"
|
|
32
|
+
development:
|
|
33
|
+
- "@std/assert": testing
|
|
34
|
+
- "@std/fs": file operations
|
|
35
|
+
- "@std/path": path utilities
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## File Structure
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
src/
|
|
42
|
+
mod.ts # Entry point, re-exports all public APIs
|
|
43
|
+
types.ts # Type definitions (interfaces, enums)
|
|
44
|
+
webrtc-manager.ts # Main WebRtcManager class
|
|
45
|
+
|
|
46
|
+
tests/
|
|
47
|
+
mocks.ts # Mock WebRtcFactory for testing
|
|
48
|
+
webrtc-manager.test.ts # Deno unit tests
|
|
49
|
+
browser/
|
|
50
|
+
p2p-tests.ts # Browser integration tests
|
|
51
|
+
|
|
52
|
+
example/
|
|
53
|
+
peer.ts # Two-peer example with localStorage signaling
|
|
54
|
+
p2p.ts # Single-page P2P example
|
|
55
|
+
audio-peer.ts # Audio testing implementation
|
|
56
|
+
main.ts # Signaling server example
|
|
57
|
+
|
|
58
|
+
scripts/
|
|
59
|
+
build-npm.ts # npm distribution build
|
|
60
|
+
build-example.ts # Example bundling
|
|
61
|
+
build-browser-tests.ts
|
|
62
|
+
serve-browser-tests.ts
|
|
63
|
+
signaling-server.ts
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## State Machine
|
|
67
|
+
|
|
68
|
+
### States (WebRtcState)
|
|
69
|
+
|
|
70
|
+
| State | Description | Valid Outgoing Transitions |
|
|
71
|
+
|-------|-------------|---------------------------|
|
|
72
|
+
| IDLE | Initial state, no resources allocated | INITIALIZING |
|
|
73
|
+
| INITIALIZING | Creating peer connection and setting up | CONNECTING, ERROR |
|
|
74
|
+
| CONNECTING | Performing SDP offer/answer exchange | CONNECTED, DISCONNECTED, ERROR |
|
|
75
|
+
| CONNECTED | Connection established, communication active | DISCONNECTED, ERROR |
|
|
76
|
+
| RECONNECTING | Auto-reconnection in progress | CONNECTING, DISCONNECTED, IDLE |
|
|
77
|
+
| DISCONNECTED | Connection closed, resources may be cleaned up | CONNECTING, RECONNECTING, IDLE |
|
|
78
|
+
| ERROR | Error state, requires reset() to recover | IDLE |
|
|
79
|
+
|
|
80
|
+
### Events (WebRtcFsmEvent)
|
|
81
|
+
|
|
82
|
+
| Event | Value | Description |
|
|
83
|
+
|-------|-------|-------------|
|
|
84
|
+
| INIT | "initialize" | Start initialization |
|
|
85
|
+
| CONNECT | "connect" | Begin connection |
|
|
86
|
+
| CONNECTED | "connected" | Connection succeeded |
|
|
87
|
+
| RECONNECTING | "reconnecting" | Start reconnection |
|
|
88
|
+
| DISCONNECT | "disconnect" | Close connection |
|
|
89
|
+
| ERROR | "error" | Error occurred |
|
|
90
|
+
| RESET | "reset" | Return to IDLE |
|
|
91
|
+
|
|
92
|
+
### Transition Matrix
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
IDLE --INIT--> INITIALIZING
|
|
96
|
+
INITIALIZING --CONNECT--> CONNECTING
|
|
97
|
+
INITIALIZING --ERROR--> ERROR
|
|
98
|
+
CONNECTING --CONNECTED--> CONNECTED
|
|
99
|
+
CONNECTING --DISCONNECT--> DISCONNECTED
|
|
100
|
+
CONNECTING --ERROR--> ERROR
|
|
101
|
+
CONNECTED --DISCONNECT--> DISCONNECTED
|
|
102
|
+
CONNECTED --ERROR--> ERROR
|
|
103
|
+
RECONNECTING --CONNECT--> CONNECTING
|
|
104
|
+
RECONNECTING --DISCONNECT--> DISCONNECTED
|
|
105
|
+
RECONNECTING --RESET--> IDLE
|
|
106
|
+
DISCONNECTED --CONNECT--> CONNECTING
|
|
107
|
+
DISCONNECTED --RECONNECTING-->RECONNECTING
|
|
108
|
+
DISCONNECTED --RESET--> IDLE
|
|
109
|
+
ERROR --RESET--> IDLE
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Public API Reference
|
|
113
|
+
|
|
114
|
+
### Constructor
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
new WebRtcManager(factory: WebRtcFactory, config?: WebRtcManagerConfig)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### WebRtcFactory Interface
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
interface WebRtcFactory {
|
|
124
|
+
createPeerConnection(config?: RTCConfiguration): RTCPeerConnection;
|
|
125
|
+
getUserMedia(constraints: MediaStreamConstraints): Promise<MediaStream>;
|
|
126
|
+
enumerateDevices(): Promise<MediaDeviceInfo[]>;
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### WebRtcManagerConfig Interface
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
interface WebRtcManagerConfig {
|
|
134
|
+
peerConfig?: RTCConfiguration; // ICE servers, certificates
|
|
135
|
+
enableMicrophone?: boolean; // Default: false
|
|
136
|
+
dataChannelLabel?: string; // Auto-create data channel
|
|
137
|
+
autoReconnect?: boolean; // Default: false
|
|
138
|
+
maxReconnectAttempts?: number; // Default: 5
|
|
139
|
+
reconnectDelay?: number; // Default: 1000ms
|
|
140
|
+
debug?: boolean; // Default: false
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Properties (Getters)
|
|
145
|
+
|
|
146
|
+
| Property | Type | Description |
|
|
147
|
+
|----------|------|-------------|
|
|
148
|
+
| state | WebRtcState | Current FSM state |
|
|
149
|
+
| localStream | MediaStream \| null | Local audio stream |
|
|
150
|
+
| remoteStream | MediaStream \| null | Remote audio stream |
|
|
151
|
+
| dataChannels | ReadonlyMap<string, RTCDataChannel> | Active data channels |
|
|
152
|
+
| peerConnection | RTCPeerConnection \| null | Underlying connection |
|
|
153
|
+
|
|
154
|
+
### Lifecycle Methods
|
|
155
|
+
|
|
156
|
+
| Method | Signature | Description |
|
|
157
|
+
|--------|-----------|-------------|
|
|
158
|
+
| initialize | `(): Promise<void>` | Create peer connection, setup tracks |
|
|
159
|
+
| connect | `(): Promise<void>` | Transition to CONNECTING (auto-initializes if IDLE) |
|
|
160
|
+
| disconnect | `(): void` | Close connection, cleanup resources |
|
|
161
|
+
| reset | `(): void` | Reset to IDLE from any state |
|
|
162
|
+
|
|
163
|
+
### Audio Methods
|
|
164
|
+
|
|
165
|
+
| Method | Signature | Description |
|
|
166
|
+
|--------|-----------|-------------|
|
|
167
|
+
| enableMicrophone | `(enable: boolean): Promise<boolean>` | Enable/disable microphone |
|
|
168
|
+
| switchMicrophone | `(deviceId: string): Promise<boolean>` | Switch audio input device |
|
|
169
|
+
| getAudioInputDevices | `(): Promise<MediaDeviceInfo[]>` | List available audio inputs |
|
|
170
|
+
|
|
171
|
+
### Data Channel Methods
|
|
172
|
+
|
|
173
|
+
| Method | Signature | Description |
|
|
174
|
+
|--------|-----------|-------------|
|
|
175
|
+
| createDataChannel | `(label: string, options?: RTCDataChannelInit): RTCDataChannel \| null` | Create/get data channel |
|
|
176
|
+
| getDataChannel | `(label: string): RTCDataChannel \| undefined` | Get existing channel |
|
|
177
|
+
| sendData | `(label: string, data: string \| Blob \| ArrayBuffer \| ArrayBufferView): boolean` | Send through channel |
|
|
178
|
+
|
|
179
|
+
### Signaling Methods
|
|
180
|
+
|
|
181
|
+
| Method | Signature | Description |
|
|
182
|
+
|--------|-----------|-------------|
|
|
183
|
+
| createOffer | `(options?: RTCOfferOptions): Promise<RTCSessionDescriptionInit \| null>` | Create SDP offer |
|
|
184
|
+
| createAnswer | `(options?: RTCAnswerOptions): Promise<RTCSessionDescriptionInit \| null>` | Create SDP answer |
|
|
185
|
+
| setLocalDescription | `(description: RTCSessionDescriptionInit): Promise<boolean>` | Set local SDP |
|
|
186
|
+
| setRemoteDescription | `(description: RTCSessionDescriptionInit): Promise<boolean>` | Set remote SDP |
|
|
187
|
+
| addIceCandidate | `(candidate: RTCIceCandidateInit \| null): Promise<boolean>` | Add ICE candidate |
|
|
188
|
+
| iceRestart | `(): Promise<boolean>` | Perform ICE restart |
|
|
189
|
+
| getLocalDescription | `(): RTCSessionDescription \| null` | Get local SDP |
|
|
190
|
+
| getRemoteDescription | `(): RTCSessionDescription \| null` | Get remote SDP |
|
|
191
|
+
| getStats | `(): Promise<RTCStatsReport \| null>` | Get connection statistics |
|
|
192
|
+
|
|
193
|
+
### Event Methods
|
|
194
|
+
|
|
195
|
+
| Method | Signature | Description |
|
|
196
|
+
|--------|-----------|-------------|
|
|
197
|
+
| on | `(event: keyof WebRtcEvents, handler: (data: any) => void): () => void` | Subscribe to specific event |
|
|
198
|
+
| subscribe | `(handler: (state: OverallState) => void): () => void` | Subscribe to overall state (Svelte compatible) |
|
|
199
|
+
|
|
200
|
+
### Utility Methods
|
|
201
|
+
|
|
202
|
+
| Method | Signature | Description |
|
|
203
|
+
|--------|-----------|-------------|
|
|
204
|
+
| toMermaid | `(): string` | Get FSM as Mermaid diagram |
|
|
205
|
+
|
|
206
|
+
## Event Constants
|
|
207
|
+
|
|
208
|
+
| Constant | Value | Payload Type |
|
|
209
|
+
|----------|-------|--------------|
|
|
210
|
+
| EVENT_STATE_CHANGE | "state_change" | WebRtcState |
|
|
211
|
+
| EVENT_LOCAL_STREAM | "local_stream" | MediaStream \| null |
|
|
212
|
+
| EVENT_REMOTE_STREAM | "remote_stream" | MediaStream \| null |
|
|
213
|
+
| EVENT_DATA_CHANNEL_OPEN | "data_channel_open" | RTCDataChannel |
|
|
214
|
+
| EVENT_DATA_CHANNEL_MESSAGE | "data_channel_message" | { channel: RTCDataChannel; data: any } |
|
|
215
|
+
| EVENT_DATA_CHANNEL_CLOSE | "data_channel_close" | RTCDataChannel |
|
|
216
|
+
| EVENT_ICE_CANDIDATE | "ice_candidate" | RTCIceCandidate \| null |
|
|
217
|
+
| EVENT_RECONNECTING | "reconnecting" | { attempt: number; strategy: "ice-restart" \| "full" } |
|
|
218
|
+
| EVENT_RECONNECT_FAILED | "reconnect_failed" | { attempts: number } |
|
|
219
|
+
| EVENT_DEVICE_CHANGED | "device_changed" | MediaDeviceInfo[] |
|
|
220
|
+
| EVENT_MICROPHONE_FAILED | "microphone_failed" | { error?: any; reason?: string } |
|
|
221
|
+
| EVENT_ERROR | "error" | Error |
|
|
222
|
+
|
|
223
|
+
## Signaling Flow (User Responsibility)
|
|
224
|
+
|
|
225
|
+
The library does NOT handle signaling transport. Users must implement:
|
|
226
|
+
|
|
227
|
+
1. Create signaling channel (WebSocket, HTTP, localStorage, etc.)
|
|
228
|
+
2. Listen for `ice_candidate` events and send to remote peer
|
|
229
|
+
3. Send offers/answers through signaling channel
|
|
230
|
+
4. Receive remote offers/answers and call setRemoteDescription
|
|
231
|
+
5. Receive remote ICE candidates and call addIceCandidate
|
|
232
|
+
|
|
233
|
+
### Initiator Flow
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
1. initialize()
|
|
237
|
+
2. connect()
|
|
238
|
+
3. createOffer()
|
|
239
|
+
4. setLocalDescription(offer)
|
|
240
|
+
5. [send offer via signaling]
|
|
241
|
+
6. [receive answer]
|
|
242
|
+
7. setRemoteDescription(answer)
|
|
243
|
+
8. [exchange ICE candidates via addIceCandidate]
|
|
244
|
+
9. CONNECTED
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Responder Flow
|
|
248
|
+
|
|
249
|
+
```
|
|
250
|
+
1. initialize()
|
|
251
|
+
2. [receive offer]
|
|
252
|
+
3. setRemoteDescription(offer)
|
|
253
|
+
4. createAnswer()
|
|
254
|
+
5. setLocalDescription(answer)
|
|
255
|
+
6. [send answer via signaling]
|
|
256
|
+
7. [exchange ICE candidates via addIceCandidate]
|
|
257
|
+
8. CONNECTED
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Reconnection Strategy
|
|
261
|
+
|
|
262
|
+
When `autoReconnect: true`:
|
|
263
|
+
|
|
264
|
+
| Attempt | Strategy | Description |
|
|
265
|
+
|---------|----------|-------------|
|
|
266
|
+
| 1-2 | ice-restart | Quick recovery, preserves connection |
|
|
267
|
+
| 3+ | full | New peer connection required |
|
|
268
|
+
|
|
269
|
+
Backoff formula: `reconnectDelay * 2^(attempt-1)` milliseconds
|
|
270
|
+
|
|
271
|
+
For "full" strategy reconnections, consumers MUST:
|
|
272
|
+
1. Listen for `reconnecting` event with `strategy: "full"`
|
|
273
|
+
2. Re-perform signaling handshake (create new offer/answer)
|
|
274
|
+
|
|
275
|
+
## Error Handling
|
|
276
|
+
|
|
277
|
+
| Pattern | Description |
|
|
278
|
+
|---------|-------------|
|
|
279
|
+
| Boolean returns | Methods return `true` for success, `false` for failure |
|
|
280
|
+
| ERROR state | Critical errors transition to ERROR state |
|
|
281
|
+
| Recovery | ERROR state requires `reset()` to recover |
|
|
282
|
+
| Events | EVENT_ERROR emitted for all errors |
|
|
283
|
+
| Specific events | EVENT_MICROPHONE_FAILED, EVENT_RECONNECT_FAILED |
|
|
284
|
+
|
|
285
|
+
## Build Commands
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
deno task test # Run unit tests
|
|
289
|
+
deno task test:browser # Run browser integration tests
|
|
290
|
+
deno task npm:build # Build npm distribution
|
|
291
|
+
deno task npm:publish # Build and publish to npm
|
|
292
|
+
deno task build:example # Build examples
|
|
293
|
+
deno task serve:example # Run signaling server
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## Implementation Notes
|
|
297
|
+
|
|
298
|
+
1. `subscribe()` is Svelte store compatible (immediate callback + updates)
|
|
299
|
+
2. Data channels auto-cleanup on close
|
|
300
|
+
3. Device change listener auto-setup on initialize
|
|
301
|
+
4. "User-Initiated Abort" errors from intentional `close()` are ignored
|
|
302
|
+
5. `recvonly` transceiver added when microphone disabled (ensures audio SDP)
|
|
303
|
+
6. Private fields use `#` syntax (true ES2022 private fields)
|
|
304
|
+
7. Signaling transport NOT included - users implement their own
|
|
305
|
+
|
|
306
|
+
## Common Usage Patterns
|
|
307
|
+
|
|
308
|
+
### Minimal P2P Setup
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
const manager = new WebRtcManager(factory, { enableMicrophone: true });
|
|
312
|
+
await manager.initialize();
|
|
313
|
+
await manager.connect();
|
|
314
|
+
const offer = await manager.createOffer();
|
|
315
|
+
await manager.setLocalDescription(offer);
|
|
316
|
+
// Send offer, receive answer, exchange ICE candidates...
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### With Data Channel
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
const manager = new WebRtcManager(factory, { dataChannelLabel: "chat" });
|
|
323
|
+
manager.on("data_channel_message", ({ data }) => console.log(data));
|
|
324
|
+
// After connection...
|
|
325
|
+
manager.sendData("chat", "Hello!");
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Svelte Integration
|
|
329
|
+
|
|
330
|
+
```svelte
|
|
331
|
+
<script>
|
|
332
|
+
const manager = new WebRtcManager(factory, config);
|
|
333
|
+
// $manager reactive access to state
|
|
334
|
+
</script>
|
|
335
|
+
{$manager.state}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Auto-reconnection Handling
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
const manager = new WebRtcManager(factory, {
|
|
342
|
+
autoReconnect: true,
|
|
343
|
+
maxReconnectAttempts: 5,
|
|
344
|
+
reconnectDelay: 1000,
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
manager.on("reconnecting", ({ attempt, strategy }) => {
|
|
348
|
+
console.log(`Reconnecting: attempt ${attempt}, strategy ${strategy}`);
|
|
349
|
+
if (strategy === "full") {
|
|
350
|
+
// Re-do signaling handshake
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
manager.on("reconnect_failed", ({ attempts }) => {
|
|
355
|
+
console.log(`Reconnection failed after ${attempts} attempts`);
|
|
356
|
+
});
|
|
357
|
+
```
|