@xtr-dev/rondevu-client 0.0.3 → 0.0.5
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/README.md +35 -615
- package/dist/rondevu.d.ts +6 -3
- package/dist/rondevu.js +19 -8
- package/dist/types.d.ts +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,640 +1,60 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Rondevu
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
🎯 Meet WebRTC peers by topic, by peer ID, or by connection ID.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## @xtr-dev/rondevu-client
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- 🔄 **Automatic Everything** - Handles signaling, ICE candidates, and connection lifecycle
|
|
9
|
-
- 📡 **Event-Driven** - Clean event-based API for connection state and data
|
|
10
|
-
- 🎯 **Type-Safe** - Full TypeScript support with comprehensive type definitions
|
|
11
|
-
- 🌐 **Universal** - Works in browsers and Node.js
|
|
12
|
-
- 🚀 **Zero Dependencies** - Lightweight with no runtime dependencies
|
|
7
|
+
[](https://www.npmjs.com/package/@xtr-dev/rondevu-client)
|
|
13
8
|
|
|
14
|
-
|
|
9
|
+
TypeScript Rondevu HTTP and WebRTC client, for simple peer discovery and connection.
|
|
10
|
+
|
|
11
|
+
### Install
|
|
15
12
|
|
|
16
13
|
```bash
|
|
17
14
|
npm install @xtr-dev/rondevu-client
|
|
18
15
|
```
|
|
19
16
|
|
|
20
|
-
|
|
17
|
+
### Usage
|
|
21
18
|
|
|
22
19
|
```typescript
|
|
23
20
|
import { Rondevu } from '@xtr-dev/rondevu-client';
|
|
24
21
|
|
|
25
|
-
|
|
26
|
-
const rdv = new Rondevu({
|
|
27
|
-
baseUrl: 'https://your-rondevu-server.com',
|
|
28
|
-
rtcConfig: {
|
|
29
|
-
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
// Peer A: Create a connection
|
|
34
|
-
const connA = await rdv.create('meeting-123', 'meetings');
|
|
35
|
-
connA.on('connect', () => {
|
|
36
|
-
const channel = connA.dataChannel('chat');
|
|
37
|
-
channel.send('Hello!');
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
// Peer B: Connect to the connection
|
|
41
|
-
const connB = await rdv.connect('meeting-123');
|
|
42
|
-
connB.on('datachannel', (channel) => {
|
|
43
|
-
channel.onmessage = (e) => console.log('Received:', e.data);
|
|
44
|
-
});
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## API Documentation
|
|
48
|
-
|
|
49
|
-
### Rondevu Class
|
|
50
|
-
|
|
51
|
-
The main class for WebRTC connection management with automatic signaling.
|
|
52
|
-
|
|
53
|
-
#### Constructor
|
|
54
|
-
|
|
55
|
-
```typescript
|
|
56
|
-
const rdv = new Rondevu(options: RondevuOptions);
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
**Options:**
|
|
60
|
-
```typescript
|
|
61
|
-
interface RondevuOptions {
|
|
62
|
-
baseUrl: string; // Rondevu server URL (required)
|
|
63
|
-
peerId?: string; // Custom peer ID (auto-generated if not provided)
|
|
64
|
-
origin?: string; // Origin for session isolation
|
|
65
|
-
fetch?: typeof fetch; // Custom fetch (for Node.js)
|
|
66
|
-
rtcConfig?: RTCConfiguration; // WebRTC configuration (ICE servers, etc.)
|
|
67
|
-
pollingInterval?: number; // Polling interval in ms (default: 1000)
|
|
68
|
-
connectionTimeout?: number; // Connection timeout in ms (default: 30000)
|
|
69
|
-
}
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
#### Properties
|
|
73
|
-
|
|
74
|
-
- `peerId: string` - The current peer identifier (read-only)
|
|
75
|
-
|
|
76
|
-
#### Methods
|
|
77
|
-
|
|
78
|
-
##### `create(id: string, topic: string): Promise<RondevuConnection>`
|
|
79
|
-
|
|
80
|
-
Creates a new connection (offerer role) and waits for a peer to join.
|
|
81
|
-
|
|
82
|
-
```typescript
|
|
83
|
-
const connection = await rdv.create('my-connection-id', 'my-topic');
|
|
84
|
-
```
|
|
22
|
+
const rdv = new Rondevu({ baseUrl: 'https://server.com' });
|
|
85
23
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
- `topic` - Topic name for grouping connections (max 1024 chars)
|
|
89
|
-
|
|
90
|
-
**Returns:** `RondevuConnection` object
|
|
91
|
-
|
|
92
|
-
**Use case:** When you want to create a new connection and share its ID with others.
|
|
93
|
-
|
|
94
|
-
---
|
|
95
|
-
|
|
96
|
-
##### `connect(id: string): Promise<RondevuConnection>`
|
|
97
|
-
|
|
98
|
-
Connects to an existing connection by ID (answerer role).
|
|
99
|
-
|
|
100
|
-
```typescript
|
|
101
|
-
const connection = await rdv.connect('my-connection-id');
|
|
102
|
-
```
|
|
24
|
+
// Connect by topic
|
|
25
|
+
const conn = await rdv.join('room');
|
|
103
26
|
|
|
104
|
-
|
|
105
|
-
|
|
27
|
+
// Or connect by ID
|
|
28
|
+
const conn = await rdv.connect('meeting-123');
|
|
106
29
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
---
|
|
112
|
-
|
|
113
|
-
##### `join(topic: string, options?: JoinOptions): Promise<RondevuConnection>`
|
|
114
|
-
|
|
115
|
-
Joins a topic and automatically connects to the first available peer (answerer role).
|
|
116
|
-
|
|
117
|
-
```typescript
|
|
118
|
-
const connection = await rdv.join('my-topic');
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
**Parameters:**
|
|
122
|
-
- `topic` - Topic name to join
|
|
123
|
-
- `options` - Optional join options
|
|
124
|
-
|
|
125
|
-
```typescript
|
|
126
|
-
interface JoinOptions {
|
|
127
|
-
filter?: (session: { code: string; peerId: string }) => boolean;
|
|
128
|
-
select?: 'first' | 'newest' | 'oldest' | 'random';
|
|
129
|
-
}
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
**Returns:** `RondevuConnection` object
|
|
133
|
-
|
|
134
|
-
**Use case:** When you want automatic peer discovery and connection.
|
|
135
|
-
|
|
136
|
-
**Example with options:**
|
|
137
|
-
```typescript
|
|
138
|
-
const connection = await rdv.join('game-room', {
|
|
139
|
-
filter: (session) => session.peerId.startsWith('player-'),
|
|
140
|
-
select: 'random'
|
|
141
|
-
});
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
---
|
|
145
|
-
|
|
146
|
-
##### `updatePeerId(newPeerId: string): void`
|
|
147
|
-
|
|
148
|
-
Updates the peer ID (useful when user identity changes).
|
|
149
|
-
|
|
150
|
-
```typescript
|
|
151
|
-
rdv.updatePeerId('new-peer-id');
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
---
|
|
155
|
-
|
|
156
|
-
### RondevuConnection Class
|
|
157
|
-
|
|
158
|
-
Represents a WebRTC connection with event-driven API.
|
|
159
|
-
|
|
160
|
-
#### Properties
|
|
161
|
-
|
|
162
|
-
- `id: string` - Connection identifier (read-only)
|
|
163
|
-
- `topic: string` - Topic name (read-only)
|
|
164
|
-
- `role: 'offerer' | 'answerer'` - Connection role (read-only)
|
|
165
|
-
- `remotePeerId: string` - Remote peer's ID (read-only)
|
|
166
|
-
|
|
167
|
-
#### Methods
|
|
168
|
-
|
|
169
|
-
##### `dataChannel(label: string, options?: RTCDataChannelInit): RTCDataChannel`
|
|
170
|
-
|
|
171
|
-
Gets or creates a data channel.
|
|
172
|
-
|
|
173
|
-
```typescript
|
|
174
|
-
const channel = connection.dataChannel('chat');
|
|
175
|
-
channel.onopen = () => channel.send('Hello!');
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
**Parameters:**
|
|
179
|
-
- `label` - Data channel label/name
|
|
180
|
-
- `options` - Optional RTCDataChannel configuration
|
|
181
|
-
|
|
182
|
-
**Returns:** `RTCDataChannel` object
|
|
183
|
-
|
|
184
|
-
---
|
|
185
|
-
|
|
186
|
-
##### `addStream(stream: MediaStream): void`
|
|
187
|
-
|
|
188
|
-
Adds a local media stream (audio/video) to the connection.
|
|
189
|
-
|
|
190
|
-
```typescript
|
|
191
|
-
const stream = await navigator.mediaDevices.getUserMedia({
|
|
192
|
-
video: true,
|
|
193
|
-
audio: true
|
|
194
|
-
});
|
|
195
|
-
connection.addStream(stream);
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
---
|
|
199
|
-
|
|
200
|
-
##### `getPeerConnection(): RTCPeerConnection`
|
|
201
|
-
|
|
202
|
-
Gets the underlying `RTCPeerConnection` for advanced use cases.
|
|
203
|
-
|
|
204
|
-
```typescript
|
|
205
|
-
const pc = connection.getPeerConnection();
|
|
206
|
-
const stats = await pc.getStats();
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
**Use case:** Accessing WebRTC stats, adding custom tracks, or other advanced WebRTC features.
|
|
210
|
-
|
|
211
|
-
---
|
|
212
|
-
|
|
213
|
-
##### `close(): void`
|
|
214
|
-
|
|
215
|
-
Closes the connection and cleans up resources.
|
|
216
|
-
|
|
217
|
-
```typescript
|
|
218
|
-
connection.close();
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
---
|
|
222
|
-
|
|
223
|
-
#### Events
|
|
224
|
-
|
|
225
|
-
Listen to connection events using the `.on()` method:
|
|
226
|
-
|
|
227
|
-
##### `'connect'`
|
|
228
|
-
|
|
229
|
-
Emitted when the WebRTC connection is established.
|
|
230
|
-
|
|
231
|
-
```typescript
|
|
232
|
-
connection.on('connect', () => {
|
|
233
|
-
console.log('Connected!');
|
|
234
|
-
});
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
---
|
|
238
|
-
|
|
239
|
-
##### `'disconnect'`
|
|
240
|
-
|
|
241
|
-
Emitted when the connection is closed or lost.
|
|
242
|
-
|
|
243
|
-
```typescript
|
|
244
|
-
connection.on('disconnect', () => {
|
|
245
|
-
console.log('Disconnected');
|
|
246
|
-
});
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
---
|
|
250
|
-
|
|
251
|
-
##### `'error'`
|
|
252
|
-
|
|
253
|
-
Emitted when an error occurs.
|
|
254
|
-
|
|
255
|
-
```typescript
|
|
256
|
-
connection.on('error', (error: Error) => {
|
|
257
|
-
console.error('Connection error:', error.message);
|
|
258
|
-
});
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
---
|
|
262
|
-
|
|
263
|
-
##### `'datachannel'`
|
|
264
|
-
|
|
265
|
-
Emitted when a remote peer creates a data channel.
|
|
266
|
-
|
|
267
|
-
```typescript
|
|
268
|
-
connection.on('datachannel', (channel: RTCDataChannel) => {
|
|
269
|
-
console.log('Received channel:', channel.label);
|
|
270
|
-
channel.onmessage = (e) => console.log(e.data);
|
|
271
|
-
});
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
---
|
|
275
|
-
|
|
276
|
-
##### `'stream'`
|
|
277
|
-
|
|
278
|
-
Emitted when a remote media stream is received.
|
|
279
|
-
|
|
280
|
-
```typescript
|
|
281
|
-
connection.on('stream', (stream: MediaStream) => {
|
|
282
|
-
videoElement.srcObject = stream;
|
|
283
|
-
});
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
---
|
|
287
|
-
|
|
288
|
-
## Usage Examples
|
|
289
|
-
|
|
290
|
-
### Example 1: Simple Chat Application
|
|
291
|
-
|
|
292
|
-
```typescript
|
|
293
|
-
import { Rondevu } from '@xtr-dev/rondevu-client';
|
|
294
|
-
|
|
295
|
-
const rdv = new Rondevu({
|
|
296
|
-
baseUrl: 'https://your-server.com',
|
|
297
|
-
rtcConfig: {
|
|
298
|
-
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
|
|
299
|
-
}
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
// Peer A: Create connection
|
|
303
|
-
const connectionA = await rdv.create('chat-room-123', 'chat-rooms');
|
|
304
|
-
|
|
305
|
-
connectionA.on('connect', () => {
|
|
306
|
-
const chat = connectionA.dataChannel('messages');
|
|
307
|
-
chat.send(JSON.stringify({ user: 'Alice', text: 'Hello!' }));
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
connectionA.on('datachannel', (channel) => {
|
|
311
|
-
if (channel.label === 'messages') {
|
|
312
|
-
channel.onmessage = (e) => {
|
|
313
|
-
const msg = JSON.parse(e.data);
|
|
314
|
-
console.log(`${msg.user}: ${msg.text}`);
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
// Share 'chat-room-123' with Peer B
|
|
320
|
-
|
|
321
|
-
// Peer B: Join connection
|
|
322
|
-
const connectionB = await rdv.connect('chat-room-123');
|
|
323
|
-
|
|
324
|
-
connectionB.on('connect', () => {
|
|
325
|
-
const chat = connectionB.dataChannel('messages');
|
|
326
|
-
chat.send(JSON.stringify({ user: 'Bob', text: 'Hi Alice!' }));
|
|
327
|
-
});
|
|
328
|
-
```
|
|
329
|
-
|
|
330
|
-
---
|
|
331
|
-
|
|
332
|
-
### Example 2: Video Chat
|
|
333
|
-
|
|
334
|
-
```typescript
|
|
335
|
-
import { Rondevu } from '@xtr-dev/rondevu-client';
|
|
336
|
-
|
|
337
|
-
const rdv = new Rondevu({
|
|
338
|
-
baseUrl: 'https://your-server.com',
|
|
339
|
-
rtcConfig: {
|
|
340
|
-
iceServers: [
|
|
341
|
-
{ urls: 'stun:stun.l.google.com:19302' },
|
|
342
|
-
{
|
|
343
|
-
urls: 'turn:your-turn-server.com:3478',
|
|
344
|
-
username: 'user',
|
|
345
|
-
credential: 'pass'
|
|
346
|
-
}
|
|
347
|
-
]
|
|
348
|
-
}
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
// Get local video stream
|
|
352
|
-
const localStream = await navigator.mediaDevices.getUserMedia({
|
|
353
|
-
video: true,
|
|
354
|
-
audio: true
|
|
355
|
-
});
|
|
356
|
-
document.getElementById('local-video').srcObject = localStream;
|
|
357
|
-
|
|
358
|
-
// Create connection and add stream
|
|
359
|
-
const connection = await rdv.create('video-call-456', 'video-calls');
|
|
360
|
-
connection.addStream(localStream);
|
|
361
|
-
|
|
362
|
-
// Handle remote stream
|
|
363
|
-
connection.on('stream', (remoteStream) => {
|
|
364
|
-
document.getElementById('remote-video').srcObject = remoteStream;
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
connection.on('connect', () => {
|
|
368
|
-
console.log('Video call connected!');
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
// Other peer joins
|
|
372
|
-
// const connection = await rdv.connect('video-call-456');
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
---
|
|
376
|
-
|
|
377
|
-
### Example 3: Topic-Based Discovery
|
|
378
|
-
|
|
379
|
-
```typescript
|
|
380
|
-
import { Rondevu } from '@xtr-dev/rondevu-client';
|
|
381
|
-
|
|
382
|
-
const rdv = new Rondevu({
|
|
383
|
-
baseUrl: 'https://your-server.com',
|
|
384
|
-
peerId: 'player-123' // Custom peer ID
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
// Create a game session
|
|
388
|
-
const host = await rdv.create('game-session-1', 'multiplayer-games');
|
|
389
|
-
|
|
390
|
-
host.on('connect', () => {
|
|
391
|
-
console.log('Player joined:', host.remotePeerId);
|
|
392
|
-
|
|
393
|
-
const game = host.dataChannel('game-state');
|
|
394
|
-
game.send(JSON.stringify({ type: 'start', level: 1 }));
|
|
395
|
-
});
|
|
396
|
-
|
|
397
|
-
// Another player discovers and joins
|
|
398
|
-
const player = await rdv.join('multiplayer-games', {
|
|
399
|
-
filter: (session) => session.peerId !== rdv.peerId, // Avoid self
|
|
400
|
-
select: 'first' // Join first available game
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
player.on('connect', () => {
|
|
404
|
-
console.log('Joined game with:', player.remotePeerId);
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
player.on('datachannel', (channel) => {
|
|
408
|
-
channel.onmessage = (e) => {
|
|
409
|
-
const data = JSON.parse(e.data);
|
|
410
|
-
console.log('Game event:', data);
|
|
411
|
-
};
|
|
412
|
-
});
|
|
413
|
-
```
|
|
414
|
-
|
|
415
|
-
---
|
|
416
|
-
|
|
417
|
-
### Example 4: Connection Stats Monitoring
|
|
418
|
-
|
|
419
|
-
```typescript
|
|
420
|
-
import { Rondevu } from '@xtr-dev/rondevu-client';
|
|
421
|
-
|
|
422
|
-
const rdv = new Rondevu({ baseUrl: 'https://your-server.com' });
|
|
423
|
-
const connection = await rdv.connect('meeting-123');
|
|
424
|
-
|
|
425
|
-
connection.on('connect', () => {
|
|
426
|
-
// Access underlying RTCPeerConnection for stats
|
|
427
|
-
const pc = connection.getPeerConnection();
|
|
428
|
-
|
|
429
|
-
setInterval(async () => {
|
|
430
|
-
const stats = await pc.getStats();
|
|
431
|
-
|
|
432
|
-
stats.forEach(report => {
|
|
433
|
-
if (report.type === 'candidate-pair' && report.state === 'succeeded') {
|
|
434
|
-
console.log('RTT:', report.currentRoundTripTime * 1000, 'ms');
|
|
435
|
-
console.log('Bytes sent:', report.bytesSent);
|
|
436
|
-
console.log('Bytes received:', report.bytesReceived);
|
|
437
|
-
}
|
|
438
|
-
});
|
|
439
|
-
}, 2000);
|
|
440
|
-
});
|
|
441
|
-
```
|
|
442
|
-
|
|
443
|
-
---
|
|
444
|
-
|
|
445
|
-
## Low-Level API (Advanced)
|
|
446
|
-
|
|
447
|
-
For advanced use cases, you can use the low-level `RondevuClient` for manual signaling:
|
|
448
|
-
|
|
449
|
-
```typescript
|
|
450
|
-
import { RondevuClient } from '@xtr-dev/rondevu-client';
|
|
451
|
-
|
|
452
|
-
const client = new RondevuClient({
|
|
453
|
-
baseUrl: 'https://your-server.com'
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
// List topics
|
|
457
|
-
const { topics } = await client.listTopics();
|
|
458
|
-
|
|
459
|
-
// List sessions in a topic
|
|
460
|
-
const { sessions } = await client.listSessions('my-topic');
|
|
461
|
-
|
|
462
|
-
// Create offer manually
|
|
463
|
-
const { code } = await client.createOffer('my-topic', {
|
|
464
|
-
peerId: 'my-peer-id',
|
|
465
|
-
offer: sdpOffer,
|
|
466
|
-
code: 'custom-session-id' // Optional
|
|
467
|
-
});
|
|
468
|
-
|
|
469
|
-
// Send answer
|
|
470
|
-
await client.sendAnswer({
|
|
471
|
-
code: sessionCode,
|
|
472
|
-
answer: sdpAnswer,
|
|
473
|
-
side: 'answerer'
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
// Poll for remote data
|
|
477
|
-
const data = await client.poll(sessionCode, 'offerer');
|
|
478
|
-
```
|
|
479
|
-
|
|
480
|
-
See the original README for full low-level API documentation.
|
|
481
|
-
|
|
482
|
-
---
|
|
483
|
-
|
|
484
|
-
## Configuration
|
|
485
|
-
|
|
486
|
-
### ICE Servers
|
|
487
|
-
|
|
488
|
-
Configure STUN/TURN servers for NAT traversal:
|
|
489
|
-
|
|
490
|
-
```typescript
|
|
491
|
-
const rdv = new Rondevu({
|
|
492
|
-
baseUrl: 'https://your-server.com',
|
|
493
|
-
rtcConfig: {
|
|
494
|
-
iceServers: [
|
|
495
|
-
// Public STUN servers
|
|
496
|
-
{ urls: 'stun:stun.l.google.com:19302' },
|
|
497
|
-
{ urls: 'stun:stun1.l.google.com:19302' },
|
|
498
|
-
|
|
499
|
-
// Private TURN server (recommended for production)
|
|
500
|
-
{
|
|
501
|
-
urls: 'turn:your-turn-server.com:3478',
|
|
502
|
-
username: 'username',
|
|
503
|
-
credential: 'password'
|
|
504
|
-
}
|
|
505
|
-
],
|
|
506
|
-
iceTransportPolicy: 'all' // 'relay' to force TURN
|
|
507
|
-
}
|
|
508
|
-
});
|
|
509
|
-
```
|
|
510
|
-
|
|
511
|
-
### Polling and Timeouts
|
|
512
|
-
|
|
513
|
-
Customize polling interval and connection timeout:
|
|
514
|
-
|
|
515
|
-
```typescript
|
|
516
|
-
const rdv = new Rondevu({
|
|
517
|
-
baseUrl: 'https://your-server.com',
|
|
518
|
-
pollingInterval: 500, // Poll every 500ms (default: 1000)
|
|
519
|
-
connectionTimeout: 60000 // 60 second timeout (default: 30000)
|
|
520
|
-
});
|
|
521
|
-
```
|
|
522
|
-
|
|
523
|
-
### Custom Peer ID
|
|
524
|
-
|
|
525
|
-
Provide a custom peer identifier:
|
|
526
|
-
|
|
527
|
-
```typescript
|
|
528
|
-
const rdv = new Rondevu({
|
|
529
|
-
baseUrl: 'https://your-server.com',
|
|
530
|
-
peerId: 'user-alice-session-xyz'
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
console.log(rdv.peerId); // 'user-alice-session-xyz'
|
|
534
|
-
|
|
535
|
-
// Update later if needed
|
|
536
|
-
rdv.updatePeerId('user-alice-new-session');
|
|
537
|
-
```
|
|
538
|
-
|
|
539
|
-
---
|
|
540
|
-
|
|
541
|
-
## Error Handling
|
|
542
|
-
|
|
543
|
-
Always handle connection errors:
|
|
544
|
-
|
|
545
|
-
```typescript
|
|
546
|
-
const connection = await rdv.create('session-1', 'topic-1');
|
|
547
|
-
|
|
548
|
-
connection.on('error', (error) => {
|
|
549
|
-
console.error('Connection error:', error.message);
|
|
550
|
-
|
|
551
|
-
if (error.message.includes('timeout')) {
|
|
552
|
-
// Handle timeout
|
|
553
|
-
} else if (error.message.includes('not found')) {
|
|
554
|
-
// Handle session not found
|
|
555
|
-
}
|
|
556
|
-
});
|
|
557
|
-
|
|
558
|
-
connection.on('disconnect', () => {
|
|
559
|
-
console.log('Connection closed');
|
|
560
|
-
// Cleanup UI, attempt reconnection, etc.
|
|
561
|
-
});
|
|
562
|
-
```
|
|
563
|
-
|
|
564
|
-
---
|
|
565
|
-
|
|
566
|
-
## TypeScript Support
|
|
567
|
-
|
|
568
|
-
The library is written in TypeScript and includes comprehensive type definitions:
|
|
569
|
-
|
|
570
|
-
```typescript
|
|
571
|
-
import {
|
|
572
|
-
Rondevu,
|
|
573
|
-
RondevuConnection,
|
|
574
|
-
RondevuOptions,
|
|
575
|
-
JoinOptions,
|
|
576
|
-
ConnectionRole
|
|
577
|
-
} from '@xtr-dev/rondevu-client';
|
|
578
|
-
|
|
579
|
-
const options: RondevuOptions = {
|
|
580
|
-
baseUrl: 'https://your-server.com',
|
|
581
|
-
rtcConfig: {
|
|
582
|
-
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
|
|
583
|
-
}
|
|
584
|
-
};
|
|
585
|
-
|
|
586
|
-
const rdv: Rondevu = new Rondevu(options);
|
|
587
|
-
const connection: RondevuConnection = await rdv.create('id', 'topic');
|
|
588
|
-
|
|
589
|
-
console.log(connection.role); // Type: 'offerer' | 'answerer'
|
|
590
|
-
```
|
|
591
|
-
|
|
592
|
-
---
|
|
593
|
-
|
|
594
|
-
## Node.js Support
|
|
595
|
-
|
|
596
|
-
The library works in Node.js with a WebRTC polyfill:
|
|
597
|
-
|
|
598
|
-
```bash
|
|
599
|
-
npm install wrtc
|
|
600
|
-
```
|
|
601
|
-
|
|
602
|
-
```typescript
|
|
603
|
-
import { Rondevu } from '@xtr-dev/rondevu-client';
|
|
604
|
-
import wrtc from 'wrtc';
|
|
605
|
-
|
|
606
|
-
// Polyfill WebRTC globals
|
|
607
|
-
global.RTCPeerConnection = wrtc.RTCPeerConnection;
|
|
608
|
-
global.RTCSessionDescription = wrtc.RTCSessionDescription;
|
|
609
|
-
global.RTCIceCandidate = wrtc.RTCIceCandidate;
|
|
610
|
-
|
|
611
|
-
const rdv = new Rondevu({
|
|
612
|
-
baseUrl: 'https://your-server.com',
|
|
613
|
-
fetch: fetch // Use node-fetch if needed
|
|
30
|
+
// Use the connection
|
|
31
|
+
conn.on('connect', () => {
|
|
32
|
+
const channel = conn.dataChannel('chat');
|
|
33
|
+
channel.send('Hello!');
|
|
614
34
|
});
|
|
615
35
|
```
|
|
616
36
|
|
|
617
|
-
|
|
37
|
+
### API
|
|
618
38
|
|
|
619
|
-
|
|
39
|
+
**Main Methods:**
|
|
40
|
+
- `rdv.join(topic)` - Auto-connect to first peer in topic
|
|
41
|
+
- `rdv.join(topic, {filter})` - Connect to specific peer by ID
|
|
42
|
+
- `rdv.create(id, topic)` - Create connection for others to join
|
|
43
|
+
- `rdv.connect(id)` - Join connection by ID
|
|
620
44
|
|
|
621
|
-
|
|
622
|
-
-
|
|
623
|
-
-
|
|
624
|
-
-
|
|
45
|
+
**Connection Events:**
|
|
46
|
+
- `connect` - Connection established
|
|
47
|
+
- `disconnect` - Connection closed
|
|
48
|
+
- `datachannel` - Remote peer created data channel
|
|
49
|
+
- `stream` - Remote media stream received
|
|
50
|
+
- `error` - Error occurred
|
|
625
51
|
|
|
626
|
-
|
|
52
|
+
**Connection Methods:**
|
|
53
|
+
- `conn.dataChannel(label)` - Get or create data channel
|
|
54
|
+
- `conn.addStream(stream)` - Add media stream
|
|
55
|
+
- `conn.getPeerConnection()` - Get underlying RTCPeerConnection
|
|
56
|
+
- `conn.close()` - Close connection
|
|
627
57
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
## License
|
|
58
|
+
### License
|
|
631
59
|
|
|
632
60
|
MIT
|
|
633
|
-
|
|
634
|
-
---
|
|
635
|
-
|
|
636
|
-
## Links
|
|
637
|
-
|
|
638
|
-
- [GitHub Repository](https://github.com/xtr-dev/rondevu)
|
|
639
|
-
- [Server Documentation](https://github.com/xtr-dev/rondevu/tree/main/server)
|
|
640
|
-
- [Demo Application](https://github.com/xtr-dev/rondevu/tree/main/demo)
|
package/dist/rondevu.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ import { RondevuOptions, JoinOptions } from './types';
|
|
|
6
6
|
export declare class Rondevu {
|
|
7
7
|
readonly peerId: string;
|
|
8
8
|
private client;
|
|
9
|
+
private baseUrl;
|
|
10
|
+
private fetchImpl?;
|
|
9
11
|
private rtcConfig?;
|
|
10
12
|
private pollingInterval;
|
|
11
13
|
private connectionTimeout;
|
|
@@ -13,7 +15,7 @@ export declare class Rondevu {
|
|
|
13
15
|
* Creates a new Rondevu client instance
|
|
14
16
|
* @param options - Client configuration options
|
|
15
17
|
*/
|
|
16
|
-
constructor(options
|
|
18
|
+
constructor(options?: RondevuOptions);
|
|
17
19
|
/**
|
|
18
20
|
* Generate a unique peer ID
|
|
19
21
|
*/
|
|
@@ -32,9 +34,10 @@ export declare class Rondevu {
|
|
|
32
34
|
/**
|
|
33
35
|
* Connect to an existing connection by ID (answerer role)
|
|
34
36
|
* @param id - Connection identifier
|
|
37
|
+
* @param origin - Optional origin header override for this connection
|
|
35
38
|
* @returns Promise that resolves to RondevuConnection
|
|
36
39
|
*/
|
|
37
|
-
connect(id: string): Promise<RondevuConnection>;
|
|
40
|
+
connect(id: string, origin?: string): Promise<RondevuConnection>;
|
|
38
41
|
/**
|
|
39
42
|
* Join a topic and discover available peers (answerer role)
|
|
40
43
|
* @param topic - Topic name
|
|
@@ -54,5 +57,5 @@ export declare class Rondevu {
|
|
|
54
57
|
* Find a session by connection ID
|
|
55
58
|
* This requires polling since we don't know which topic it's in
|
|
56
59
|
*/
|
|
57
|
-
private
|
|
60
|
+
private findSessionByIdWithClient;
|
|
58
61
|
}
|
package/dist/rondevu.js
CHANGED
|
@@ -8,9 +8,11 @@ export class Rondevu {
|
|
|
8
8
|
* Creates a new Rondevu client instance
|
|
9
9
|
* @param options - Client configuration options
|
|
10
10
|
*/
|
|
11
|
-
constructor(options) {
|
|
11
|
+
constructor(options = {}) {
|
|
12
|
+
this.baseUrl = options.baseUrl || 'https://rondevu.xtrdev.workers.dev';
|
|
13
|
+
this.fetchImpl = options.fetch;
|
|
12
14
|
this.client = new RondevuClient({
|
|
13
|
-
baseUrl:
|
|
15
|
+
baseUrl: this.baseUrl,
|
|
14
16
|
origin: options.origin,
|
|
15
17
|
fetch: options.fetch,
|
|
16
18
|
});
|
|
@@ -73,11 +75,20 @@ export class Rondevu {
|
|
|
73
75
|
/**
|
|
74
76
|
* Connect to an existing connection by ID (answerer role)
|
|
75
77
|
* @param id - Connection identifier
|
|
78
|
+
* @param origin - Optional origin header override for this connection
|
|
76
79
|
* @returns Promise that resolves to RondevuConnection
|
|
77
80
|
*/
|
|
78
|
-
async connect(id) {
|
|
81
|
+
async connect(id, origin) {
|
|
82
|
+
// Create a client with overridden origin if specified
|
|
83
|
+
const client = origin
|
|
84
|
+
? new RondevuClient({
|
|
85
|
+
baseUrl: this.baseUrl,
|
|
86
|
+
origin,
|
|
87
|
+
fetch: this.fetchImpl,
|
|
88
|
+
})
|
|
89
|
+
: this.client;
|
|
79
90
|
// Poll server to get session by ID
|
|
80
|
-
const sessionData = await this.
|
|
91
|
+
const sessionData = await this.findSessionByIdWithClient(id, client);
|
|
81
92
|
if (!sessionData) {
|
|
82
93
|
throw new Error(`Connection ${id} not found or expired`);
|
|
83
94
|
}
|
|
@@ -94,7 +105,7 @@ export class Rondevu {
|
|
|
94
105
|
// Wait for ICE gathering
|
|
95
106
|
await this.waitForIceGathering(pc);
|
|
96
107
|
// Send answer to server
|
|
97
|
-
await
|
|
108
|
+
await client.sendAnswer({
|
|
98
109
|
code: id,
|
|
99
110
|
answer: pc.localDescription.sdp,
|
|
100
111
|
side: 'answerer',
|
|
@@ -110,7 +121,7 @@ export class Rondevu {
|
|
|
110
121
|
pollingInterval: this.pollingInterval,
|
|
111
122
|
connectionTimeout: this.connectionTimeout,
|
|
112
123
|
};
|
|
113
|
-
const connection = new RondevuConnection(connectionParams,
|
|
124
|
+
const connection = new RondevuConnection(connectionParams, client);
|
|
114
125
|
// Start polling for ICE candidates
|
|
115
126
|
connection.startPolling();
|
|
116
127
|
return connection;
|
|
@@ -181,11 +192,11 @@ export class Rondevu {
|
|
|
181
192
|
* Find a session by connection ID
|
|
182
193
|
* This requires polling since we don't know which topic it's in
|
|
183
194
|
*/
|
|
184
|
-
async
|
|
195
|
+
async findSessionByIdWithClient(id, client) {
|
|
185
196
|
try {
|
|
186
197
|
// Try to poll for the session directly
|
|
187
198
|
// The poll endpoint should return the session data
|
|
188
|
-
const response = await
|
|
199
|
+
const response = await client.poll(id, 'answerer');
|
|
189
200
|
const answererResponse = response;
|
|
190
201
|
if (answererResponse.offer) {
|
|
191
202
|
return {
|
package/dist/types.d.ts
CHANGED
|
@@ -150,8 +150,8 @@ export interface RondevuClientOptions {
|
|
|
150
150
|
* Configuration options for Rondevu WebRTC client
|
|
151
151
|
*/
|
|
152
152
|
export interface RondevuOptions {
|
|
153
|
-
/** Base URL of the Rondevu server (
|
|
154
|
-
baseUrl
|
|
153
|
+
/** Base URL of the Rondevu server (defaults to 'https://rondevu.xtrdev.workers.dev') */
|
|
154
|
+
baseUrl?: string;
|
|
155
155
|
/** Peer identifier (optional, auto-generated if not provided) */
|
|
156
156
|
peerId?: string;
|
|
157
157
|
/** Origin header value for session isolation (defaults to baseUrl origin) */
|