@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 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
+ ```