From 135eda73cfa77f304c2d8b43e61a487e9b47c359 Mon Sep 17 00:00:00 2001 From: Bas van den Aakster Date: Sun, 16 Nov 2025 18:00:15 +0100 Subject: [PATCH] Update README to reflect current RondevuPeer API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replaced all references to removed RondevuConnection class - Updated to use RondevuPeer with state machine lifecycle - Documented state transitions (idle → creating-offer → waiting-for-answer → exchanging-ice → connected) - Added trickle ICE documentation - Updated all code examples to use addEventListener - Added timeout configuration examples - Documented peer properties (stateName, connectionState, offerId, role) - Updated TypeScript types in API reference 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- README.md | 201 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 145 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index d039e4b..80f3e6b 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,10 @@ 🌐 **Topic-based peer discovery and WebRTC signaling client** -TypeScript/JavaScript client for Rondevu, providing topic-based peer discovery, stateless authentication, and complete WebRTC signaling. +TypeScript/JavaScript client for Rondevu, providing topic-based peer discovery, stateless authentication, and complete WebRTC signaling with trickle ICE support. **Related repositories:** -- [rondevu-server](https://github.com/xtr-dev/rondevu) - HTTP signaling server +- [rondevu-server](https://github.com/xtr-dev/rondevu-server) - HTTP signaling server - [rondevu-demo](https://rondevu-demo.pages.dev) - Interactive demo --- @@ -19,6 +19,8 @@ TypeScript/JavaScript client for Rondevu, providing topic-based peer discovery, - **Bloom Filters**: Efficient peer exclusion for repeated discoveries - **Multi-Offer Management**: Create and manage multiple offers per peer - **Complete WebRTC Signaling**: Full offer/answer and ICE candidate exchange +- **Trickle ICE**: Send ICE candidates as they're discovered (faster connections) +- **State Machine**: Clean state-based connection lifecycle - **TypeScript**: Full type safety and autocomplete ## Install @@ -29,36 +31,42 @@ npm install @xtr-dev/rondevu-client ## Quick Start -The easiest way to use Rondevu is with the high-level `RondevuConnection` class, which handles all WebRTC connection complexity including offer/answer exchange, ICE candidates, and connection lifecycle. - ### Creating an Offer (Peer A) ```typescript -import { Rondevu } from '@xtr-dev/rondevu-client'; +import { Rondevu, RondevuPeer } from '@xtr-dev/rondevu-client'; +// Initialize client and register const client = new Rondevu({ baseUrl: 'https://api.ronde.vu' }); await client.register(); -// Create a connection -const conn = client.createConnection(); +// Create peer connection +const peer = new RondevuPeer(client.offers); // Set up event listeners -conn.on('connected', () => { - console.log('Connected to peer!'); +peer.on('state', (state) => { + console.log('Peer state:', state); + // States: idle → creating-offer → waiting-for-answer → exchanging-ice → connected }); -conn.on('datachannel', (channel) => { - console.log('Data channel ready'); +peer.on('connected', () => { + console.log('✅ Connected to peer!'); +}); - channel.onmessage = (event) => { - console.log('Received:', event.data); - }; +peer.on('datachannel', (channel) => { + console.log('📡 Data channel ready'); - channel.send('Hello from peer A!'); + channel.addEventListener('message', (event) => { + console.log('📥 Received:', event.data); + }); + + channel.addEventListener('open', () => { + channel.send('Hello from peer A!'); + }); }); // Create offer and advertise on topics -const offerId = await conn.createOffer({ +const offerId = await peer.createOffer({ topics: ['my-app', 'room-123'], ttl: 300000 // 5 minutes }); @@ -70,8 +78,9 @@ console.log('Share these topics with peers:', ['my-app', 'room-123']); ### Answering an Offer (Peer B) ```typescript -import { Rondevu } from '@xtr-dev/rondevu-client'; +import { Rondevu, RondevuPeer } from '@xtr-dev/rondevu-client'; +// Initialize client and register const client = new Rondevu({ baseUrl: 'https://api.ronde.vu' }); await client.register(); @@ -81,65 +90,109 @@ const offers = await client.offers.findByTopic('my-app', { limit: 10 }); if (offers.length > 0) { const offer = offers[0]; - // Create connection - const conn = client.createConnection(); + // Create peer connection + const peer = new RondevuPeer(client.offers); // Set up event listeners - conn.on('connecting', () => { - console.log('Connecting...'); + peer.on('state', (state) => { + console.log('Peer state:', state); + // States: idle → answering → exchanging-ice → connected }); - conn.on('connected', () => { - console.log('Connected!'); + peer.on('connected', () => { + console.log('✅ Connected!'); }); - conn.on('datachannel', (channel) => { - console.log('Data channel ready'); + peer.on('datachannel', (channel) => { + console.log('📡 Data channel ready'); - channel.onmessage = (event) => { - console.log('Received:', event.data); - }; + channel.addEventListener('message', (event) => { + console.log('📥 Received:', event.data); + }); - channel.send('Hello from peer B!'); + channel.addEventListener('open', () => { + channel.send('Hello from peer B!'); + }); + }); + + peer.on('failed', (error) => { + console.error('❌ Connection failed:', error); }); // Answer the offer - await conn.answer(offer.id, offer.sdp); + await peer.answer(offer.id, offer.sdp, { + topics: offer.topics, + rtcConfig: { + iceServers: [ + { urls: 'stun:stun.l.google.com:19302' } + ] + } + }); } ``` -### Connection Events +## Connection Lifecycle + +The `RondevuPeer` uses a state machine for connection management: + +### Offerer States +1. **idle** - Initial state +2. **creating-offer** - Creating WebRTC offer +3. **waiting-for-answer** - Polling for answer from peer +4. **exchanging-ice** - Exchanging ICE candidates +5. **connected** - Successfully connected +6. **failed** - Connection failed +7. **closed** - Connection closed + +### Answerer States +1. **idle** - Initial state +2. **answering** - Creating WebRTC answer +3. **exchanging-ice** - Exchanging ICE candidates +4. **connected** - Successfully connected +5. **failed** - Connection failed +6. **closed** - Connection closed + +### State Events ```typescript -conn.on('connecting', () => { - // Connection is being established +peer.on('state', (stateName) => { + console.log('Current state:', stateName); }); -conn.on('connected', () => { +peer.on('connected', () => { // Connection established successfully }); -conn.on('disconnected', () => { +peer.on('disconnected', () => { // Connection lost or closed }); -conn.on('error', (error) => { - // An error occurred +peer.on('failed', (error) => { + // Connection failed console.error('Connection error:', error); }); -conn.on('datachannel', (channel) => { - // Data channel is ready to use +peer.on('datachannel', (channel) => { + // Data channel is ready (use channel.addEventListener) }); -conn.on('track', (event) => { +peer.on('track', (event) => { // Media track received (for audio/video streaming) const stream = event.streams[0]; videoElement.srcObject = stream; }); ``` -### Adding Media Tracks +## Trickle ICE + +This library implements **trickle ICE** for faster connection establishment: + +- ICE candidates are sent to the server as they're discovered +- No waiting for all candidates before sending offer/answer +- Connections establish much faster (milliseconds vs seconds) +- Proper event listener cleanup to prevent memory leaks + +## Adding Media Tracks ```typescript // Get user's camera/microphone @@ -148,29 +201,64 @@ const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); -// Add tracks to connection +// Add tracks to peer connection stream.getTracks().forEach(track => { - conn.addTrack(track, stream); + peer.addTrack(track, stream); }); ``` -### Connection Properties +## Peer Properties ```typescript +// Get current state name +console.log(peer.stateName); // 'idle', 'creating-offer', 'connected', etc. + // Get connection state -console.log(conn.connectionState); // 'connecting', 'connected', 'disconnected', etc. +console.log(peer.connectionState); // RTCPeerConnectionState -// Get offer ID -console.log(conn.id); +// Get offer ID (after creating offer or answering) +console.log(peer.offerId); -// Get data channel -console.log(conn.channel); +// Get role +console.log(peer.role); // 'offerer' or 'answerer' ``` -### Closing a Connection +## Closing a Connection ```typescript -conn.close(); +await peer.close(); +``` + +## Custom RTCConfiguration + +```typescript +const peer = new RondevuPeer(client.offers, { + iceServers: [ + { urls: 'stun:stun.l.google.com:19302' }, + { + urls: 'turn:turn.example.com:3478', + username: 'user', + credential: 'pass' + } + ], + iceTransportPolicy: 'relay' // Force TURN relay (useful for testing) +}); +``` + +## Timeouts + +Configure connection timeouts: + +```typescript +await peer.createOffer({ + topics: ['my-topic'], + timeouts: { + iceGathering: 10000, // ICE gathering timeout (10s) + waitingForAnswer: 30000, // Waiting for answer timeout (30s) + creatingAnswer: 10000, // Creating answer timeout (10s) + iceConnection: 30000 // ICE connection timeout (30s) + } +}); ``` ## Platform-Specific Setup @@ -230,7 +318,7 @@ export default { ## Low-Level API Usage -For advanced use cases where you need direct control over the signaling process, you can use the low-level API: +For direct control over the signaling process without WebRTC: ```typescript import { Rondevu, BloomFilter } from '@xtr-dev/rondevu-client'; @@ -370,7 +458,7 @@ Post ICE candidates for an offer. ```typescript await client.offers.addIceCandidates(offerId, [ - 'candidate:1 1 UDP...' + { candidate: 'candidate:1 1 UDP...', sdpMid: '0', sdpMLineIndex: 0 } ]); ``` @@ -378,7 +466,7 @@ await client.offers.addIceCandidates(offerId, [ Get ICE candidates from the other peer. ```typescript -const candidates = await client.offers.getIceCandidates(offerId); +const candidates = await client.offers.getIceCandidates(offerId, since); ``` ### Bloom Filter @@ -414,8 +502,9 @@ import type { IceCandidate, FetchFunction, RondevuOptions, - ConnectionOptions, - RondevuConnectionEvents + PeerOptions, + PeerEvents, + PeerTimeouts } from '@xtr-dev/rondevu-client'; ```