@xtr-dev/rondevu-client 0.13.0 → 0.17.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/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@xtr-dev/rondevu-client)](https://www.npmjs.com/package/@xtr-dev/rondevu-client)
4
4
 
5
- 🌐 **Simple, high-level WebRTC peer-to-peer connections**
5
+ 🌐 **Simple WebRTC signaling client with username-based discovery**
6
6
 
7
- TypeScript/JavaScript client for Rondevu, providing easy-to-use WebRTC connections with automatic signaling, username-based discovery, and built-in reconnection support.
7
+ TypeScript/JavaScript client for Rondevu, providing WebRTC signaling with username claiming, service publishing/discovery, and efficient batch polling.
8
8
 
9
9
  **Related repositories:**
10
10
  - [@xtr-dev/rondevu-client](https://github.com/xtr-dev/rondevu-client) - TypeScript client library ([npm](https://www.npmjs.com/package/@xtr-dev/rondevu-client))
@@ -15,18 +15,17 @@ TypeScript/JavaScript client for Rondevu, providing easy-to-use WebRTC connectio
15
15
 
16
16
  ## Features
17
17
 
18
- - **High-Level Wrappers**: ServiceHost and ServiceClient eliminate WebRTC boilerplate
19
- - **Username-Based Discovery**: Connect to peers by username, not complex offer/answer exchange
20
- - **Semver-Compatible Matching**: Requesting chat@1.0.0 matches any compatible 1.x.x version
21
- - **Privacy-First Design**: Services are hidden by default - no enumeration possible
22
- - **Automatic Reconnection**: Built-in retry logic with exponential backoff
23
- - **Message Queuing**: Messages sent while disconnected are queued and flushed on reconnect
24
- - **Cryptographic Username Claiming**: Secure ownership with Ed25519 signatures
25
- - **Service Publishing**: Package-style naming (chat.app@1.0.0) with multiple simultaneous offers
18
+ - **Username Claiming**: Secure ownership with Ed25519 signatures
19
+ - **Anonymous Users**: Auto-generated anonymous usernames for quick testing
20
+ - **Service Publishing**: Publish services with multiple offers for connection pooling
21
+ - **Service Discovery**: Direct lookup, random discovery, or paginated search
22
+ - **Efficient Batch Polling**: Single endpoint for answers and ICE candidates (50% fewer requests)
23
+ - **Semantic Version Matching**: Compatible version resolution (chat:1.0.0 matches any 1.x.x)
26
24
  - **TypeScript**: Full type safety and autocomplete
27
- - **Configurable Polling**: Exponential backoff with jitter to reduce server load
25
+ - **Keypair Management**: Generate or reuse Ed25519 keypairs
26
+ - **Automatic Signatures**: All authenticated requests signed automatically
28
27
 
29
- ## Install
28
+ ## Installation
30
29
 
31
30
  ```bash
32
31
  npm install @xtr-dev/rondevu-client
@@ -34,424 +33,144 @@ npm install @xtr-dev/rondevu-client
34
33
 
35
34
  ## Quick Start
36
35
 
37
- ### Hosting a Service (Alice)
36
+ ### Publishing a Service (Offerer)
38
37
 
39
38
  ```typescript
40
- import { RondevuService, ServiceHost } from '@xtr-dev/rondevu-client'
41
-
42
- // Step 1: Create and initialize service
43
- const service = new RondevuService({
44
- apiUrl: 'https://api.ronde.vu',
45
- username: 'alice'
46
- })
47
-
48
- await service.initialize() // Generates keypair
49
- await service.claimUsername() // Claims username with signature
50
-
51
- // Step 2: Create ServiceHost
52
- const host = new ServiceHost({
53
- service: 'chat.app@1.0.0',
54
- rondevuService: service,
55
- maxPeers: 5, // Accept up to 5 connections
56
- ttl: 300000 // 5 minutes
57
- })
58
-
59
- // Step 3: Listen for incoming connections
60
- host.events.on('connection', (connection) => {
61
- console.log('✅ New connection!')
62
-
63
- connection.events.on('message', (msg) => {
64
- console.log('📨 Received:', msg)
65
- connection.sendMessage('Hello from Alice!')
39
+ import { Rondevu } from '@xtr-dev/rondevu-client'
40
+
41
+ // 1. Connect to Rondevu
42
+ const rondevu = await Rondevu.connect({
43
+ apiUrl: 'https://api.ronde.vu',
44
+ username: 'alice', // Or omit for anonymous username
45
+ iceServers: 'ipv4-turn' // Preset: 'ipv4-turn', 'hostname-turns', 'google-stun', 'relay-only'
46
+ })
47
+
48
+ // 2. Publish service with automatic offer management
49
+ await rondevu.publishService({
50
+ service: 'chat:1.0.0',
51
+ maxOffers: 5, // Maintain up to 5 concurrent offers
52
+ offerFactory: async (rtcConfig) => {
53
+ const pc = new RTCPeerConnection(rtcConfig)
54
+ const dc = pc.createDataChannel('chat')
55
+
56
+ dc.addEventListener('open', () => {
57
+ console.log('Connection opened!')
58
+ dc.send('Hello from Alice!')
66
59
  })
67
60
 
68
- connection.events.on('state-change', (state) => {
69
- console.log('Connection state:', state)
61
+ dc.addEventListener('message', (e) => {
62
+ console.log('Received:', e.data)
70
63
  })
71
- })
72
64
 
73
- host.events.on('error', (error) => {
74
- console.error('Host error:', error)
65
+ const offer = await pc.createOffer()
66
+ await pc.setLocalDescription(offer)
67
+ return { pc, dc, offer }
68
+ }
75
69
  })
76
70
 
77
- // Step 4: Start hosting
78
- await host.start()
79
- console.log('Service is now live! Others can connect to @alice')
80
-
81
- // Later: stop hosting
82
- host.dispose()
71
+ // 3. Start accepting connections
72
+ await rondevu.startFilling()
83
73
  ```
84
74
 
85
- ### Connecting to a Service (Bob)
75
+ ### Connecting to a Service (Answerer)
86
76
 
87
77
  ```typescript
88
- import { RondevuService, ServiceClient } from '@xtr-dev/rondevu-client'
89
-
90
- // Step 1: Create and initialize service
91
- const service = new RondevuService({
92
- apiUrl: 'https://api.ronde.vu',
93
- username: 'bob'
94
- })
95
-
96
- await service.initialize()
97
- await service.claimUsername()
78
+ import { Rondevu } from '@xtr-dev/rondevu-client'
98
79
 
99
- // Step 2: Create ServiceClient
100
- const client = new ServiceClient({
101
- username: 'alice', // Connect to Alice
102
- serviceFqn: 'chat.app@1.0.0',
103
- rondevuService: service,
104
- autoReconnect: true,
105
- maxReconnectAttempts: 5
80
+ // 1. Connect to Rondevu
81
+ const rondevu = await Rondevu.connect({
82
+ apiUrl: 'https://api.ronde.vu',
83
+ username: 'bob',
84
+ iceServers: 'ipv4-turn'
106
85
  })
107
86
 
108
- // Step 3: Listen for connection events
109
- client.events.on('connected', (connection) => {
110
- console.log('✅ Connected to Alice!')
87
+ // 2. Connect to service (automatic WebRTC setup)
88
+ const connection = await rondevu.connectToService({
89
+ serviceFqn: 'chat:1.0.0@alice',
90
+ onConnection: ({ dc, peerUsername }) => {
91
+ console.log('Connected to', peerUsername)
111
92
 
112
- connection.events.on('message', (msg) => {
113
- console.log('📨 Received:', msg)
93
+ dc.addEventListener('message', (e) => {
94
+ console.log('Received:', e.data)
114
95
  })
115
96
 
116
- // Send a message
117
- connection.sendMessage('Hello from Bob!')
118
- })
119
-
120
- client.events.on('disconnected', () => {
121
- console.log('🔌 Disconnected')
122
- })
123
-
124
- client.events.on('reconnecting', ({ attempt, maxAttempts }) => {
125
- console.log(`🔄 Reconnecting (${attempt}/${maxAttempts})...`)
126
- })
127
-
128
- client.events.on('error', (error) => {
129
- console.error('❌ Error:', error)
97
+ dc.addEventListener('open', () => {
98
+ dc.send('Hello from Bob!')
99
+ })
100
+ }
130
101
  })
131
102
 
132
- // Step 4: Connect
133
- await client.connect()
134
-
135
- // Later: disconnect
136
- client.dispose()
103
+ // Access connection
104
+ connection.dc.send('Another message')
105
+ connection.pc.close() // Close when done
137
106
  ```
138
107
 
139
- ## Core Concepts
140
-
141
- ### RondevuService
142
-
143
- Handles authentication and username management:
144
- - Generates Ed25519 keypair for signing
145
- - Claims usernames with cryptographic proof
146
- - Provides API client for signaling server
147
-
148
- ### ServiceHost
149
-
150
- High-level wrapper for hosting a WebRTC service:
151
- - Automatically creates and publishes offers
152
- - Handles incoming connections
153
- - Manages ICE candidate exchange
154
- - Supports multiple simultaneous peers
155
-
156
- ### ServiceClient
108
+ ## Core API
157
109
 
158
- High-level wrapper for connecting to services:
159
- - Discovers services by username
160
- - Handles offer/answer exchange automatically
161
- - Built-in auto-reconnection with exponential backoff
162
- - Event-driven API
163
-
164
- ### RTCDurableConnection
165
-
166
- Low-level connection wrapper (used internally):
167
- - Manages WebRTC PeerConnection lifecycle
168
- - Handles ICE candidate polling
169
- - Provides message queue for reliability
170
- - State management and events
171
-
172
- ## API Reference
173
-
174
- ### RondevuService
175
-
176
- ```typescript
177
- const service = new RondevuService({
178
- apiUrl: string, // Signaling server URL
179
- username: string, // Your username
180
- keypair?: Keypair // Optional: reuse existing keypair
181
- })
182
-
183
- // Initialize service (generates keypair if not provided)
184
- await service.initialize(): Promise<void>
185
-
186
- // Claim username with cryptographic signature
187
- await service.claimUsername(): Promise<void>
188
-
189
- // Check if username is claimed
190
- service.isUsernameClaimed(): boolean
191
-
192
- // Get current username
193
- service.getUsername(): string
194
-
195
- // Get keypair
196
- service.getKeypair(): Keypair
197
-
198
- // Get API client
199
- service.getAPI(): RondevuAPI
200
- ```
201
-
202
- ### ServiceHost
110
+ ### Rondevu.connect()
203
111
 
204
112
  ```typescript
205
- const host = new ServiceHost({
206
- service: string, // Service FQN (e.g., 'chat.app@1.0.0')
207
- rondevuService: RondevuService,
208
- maxPeers?: number, // Default: 5
209
- ttl?: number, // Default: 300000 (5 minutes)
210
- isPublic?: boolean, // Default: true
211
- rtcConfiguration?: RTCConfiguration
113
+ const rondevu = await Rondevu.connect({
114
+ apiUrl: string, // Required: Signaling server URL
115
+ username?: string, // Optional: your username (auto-generates anonymous if omitted)
116
+ keypair?: Keypair, // Optional: reuse existing keypair
117
+ iceServers?: IceServerPreset | RTCIceServer[], // Optional: preset or custom config
118
+ debug?: boolean // Optional: enable debug logging (default: false)
212
119
  })
213
-
214
- // Start hosting
215
- await host.start(): Promise<void>
216
-
217
- // Stop hosting and cleanup
218
- host.dispose(): void
219
-
220
- // Get all active connections
221
- host.getConnections(): RTCDurableConnection[]
222
-
223
- // Events
224
- host.events.on('connection', (conn: RTCDurableConnection) => {})
225
- host.events.on('error', (error: Error) => {})
226
120
  ```
227
121
 
228
- ### ServiceClient
122
+ ### Service Publishing
229
123
 
230
124
  ```typescript
231
- const client = new ServiceClient({
232
- username: string, // Host username to connect to
233
- serviceFqn: string, // Service FQN (e.g., 'chat.app@1.0.0')
234
- rondevuService: RondevuService,
235
- autoReconnect?: boolean, // Default: true
236
- maxReconnectAttempts?: number, // Default: 5
237
- rtcConfiguration?: RTCConfiguration
125
+ await rondevu.publishService({
126
+ service: string, // e.g., 'chat:1.0.0' (username auto-appended)
127
+ maxOffers: number, // Maximum concurrent offers to maintain
128
+ offerFactory?: OfferFactory, // Optional: custom offer creation
129
+ ttl?: number // Optional: offer lifetime in ms (default: 300000)
238
130
  })
239
131
 
240
- // Connect to service
241
- await client.connect(): Promise<RTCDurableConnection>
242
-
243
- // Disconnect and cleanup
244
- client.dispose(): void
245
-
246
- // Get current connection
247
- client.getConnection(): RTCDurableConnection | null
248
-
249
- // Events
250
- client.events.on('connected', (conn: RTCDurableConnection) => {})
251
- client.events.on('disconnected', () => {})
252
- client.events.on('reconnecting', (info: { attempt: number, maxAttempts: number }) => {})
253
- client.events.on('error', (error: Error) => {})
132
+ await rondevu.startFilling() // Start accepting connections
133
+ rondevu.stopFilling() // Stop and close all connections
254
134
  ```
255
135
 
256
- ### RTCDurableConnection
136
+ ### Service Discovery
257
137
 
258
138
  ```typescript
259
- // Connection state
260
- connection.state: 'connected' | 'connecting' | 'disconnected'
261
-
262
- // Send message (returns true if sent, false if queued)
263
- await connection.sendMessage(message: string): Promise<boolean>
264
-
265
- // Queue message for sending when connected
266
- await connection.queueMessage(message: string, options?: QueueMessageOptions): Promise<void>
139
+ // Direct lookup (with username)
140
+ await rondevu.getService('chat:1.0.0@alice')
267
141
 
268
- // Disconnect
269
- connection.disconnect(): void
142
+ // Random discovery (without username)
143
+ await rondevu.discoverService('chat:1.0.0')
270
144
 
271
- // Events
272
- connection.events.on('message', (msg: string) => {})
273
- connection.events.on('state-change', (state: ConnectionStates) => {})
145
+ // Paginated discovery
146
+ await rondevu.discoverServices('chat:1.0.0', limit, offset)
274
147
  ```
275
148
 
276
- ## Configuration
277
-
278
- ### Polling Configuration
279
-
280
- The signaling uses configurable polling with exponential backoff:
149
+ ### Connecting to Services
281
150
 
282
151
  ```typescript
283
- // Default polling config
284
- {
285
- initialInterval: 500, // Start at 500ms
286
- maxInterval: 5000, // Max 5 seconds
287
- backoffMultiplier: 1.5, // Increase by 1.5x each time
288
- maxRetries: 50, // Max 50 attempts
289
- jitter: true // Add random 0-100ms to prevent thundering herd
290
- }
291
- ```
292
-
293
- This is handled automatically - no configuration needed.
294
-
295
- ### WebRTC Configuration
296
-
297
- Provide custom STUN/TURN servers:
298
-
299
- ```typescript
300
- const host = new ServiceHost({
301
- service: 'chat.app@1.0.0',
302
- rondevuService: service,
303
- rtcConfiguration: {
304
- iceServers: [
305
- { urls: 'stun:stun.l.google.com:19302' },
306
- {
307
- urls: 'turn:turn.example.com:3478',
308
- username: 'user',
309
- credential: 'pass'
310
- }
311
- ]
312
- }
152
+ const connection = await rondevu.connectToService({
153
+ serviceFqn?: string, // Full FQN like 'chat:1.0.0@alice'
154
+ service?: string, // Service without username (for discovery)
155
+ username?: string, // Target username (combined with service)
156
+ onConnection?: (context) => void, // Called when data channel opens
157
+ rtcConfig?: RTCConfiguration // Optional: override ICE servers
313
158
  })
314
159
  ```
315
160
 
316
- ## Username Rules
161
+ ## Documentation
317
162
 
318
- - **Format**: Lowercase alphanumeric + dash (`a-z`, `0-9`, `-`)
319
- - **Length**: 3-32 characters
320
- - **Pattern**: `^[a-z0-9][a-z0-9-]*[a-z0-9]$`
321
- - **Validity**: 365 days from claim/last use
322
- - **Ownership**: Secured by Ed25519 public key signature
163
+ 📚 **[ADVANCED.md](./ADVANCED.md)** - Comprehensive guide including:
164
+ - Detailed API reference for all methods
165
+ - Type definitions and interfaces
166
+ - Platform support (Browser & Node.js)
167
+ - Advanced usage patterns
168
+ - Username rules and service FQN format
169
+ - Examples and migration guides
323
170
 
324
171
  ## Examples
325
172
 
326
- ### Chat Application
327
-
328
- See [demo/demo.js](./demo/demo.js) for a complete working example.
329
-
330
- ### Persistent Keypair
331
-
332
- ```typescript
333
- // Save keypair to localStorage
334
- const service = new RondevuService({
335
- apiUrl: 'https://api.ronde.vu',
336
- username: 'alice'
337
- })
338
-
339
- await service.initialize()
340
- await service.claimUsername()
341
-
342
- // Save for later
343
- localStorage.setItem('rondevu-keypair', JSON.stringify(service.getKeypair()))
344
- localStorage.setItem('rondevu-username', service.getUsername())
345
-
346
- // Load on next session
347
- const savedKeypair = JSON.parse(localStorage.getItem('rondevu-keypair'))
348
- const savedUsername = localStorage.getItem('rondevu-username')
349
-
350
- const service2 = new RondevuService({
351
- apiUrl: 'https://api.ronde.vu',
352
- username: savedUsername,
353
- keypair: savedKeypair
354
- })
355
-
356
- await service2.initialize() // Reuses keypair
357
- ```
358
-
359
- ### Message Queue Example
360
-
361
- ```typescript
362
- // Messages are automatically queued if not connected yet
363
- client.events.on('connected', (connection) => {
364
- // Send immediately
365
- connection.sendMessage('Hello!')
366
- })
367
-
368
- // Or queue for later
369
- await client.connect()
370
- const conn = client.getConnection()
371
- await conn.queueMessage('This will be sent when connected', {
372
- expiresAt: Date.now() + 60000 // Expire after 1 minute
373
- })
374
- ```
375
-
376
- ## Migration from v0.9.x
377
-
378
- v0.11.0+ introduces high-level wrappers, RESTful API changes, and semver-compatible discovery:
379
-
380
- **API Changes:**
381
- - Server endpoints restructured (`/usernames/*` → `/users/*`)
382
- - Added `ServiceHost` and `ServiceClient` wrappers
383
- - Message queue fully implemented
384
- - Configurable polling with exponential backoff
385
- - Removed deprecated `cleanup()` methods (use `dispose()`)
386
- - **v0.11.0+**: Services use `offers` array instead of single `sdp`
387
- - **v0.11.0+**: Semver-compatible service discovery (chat@1.0.0 matches 1.x.x)
388
- - **v0.11.0+**: All services are hidden - no listing endpoint
389
- - **v0.11.0+**: Services support multiple simultaneous offers for connection pooling
390
-
391
- **Migration Guide:**
392
-
393
- ```typescript
394
- // Before (v0.9.x) - Manual WebRTC setup
395
- const signaler = new RondevuSignaler(service, 'chat@1.0.0')
396
- const context = new WebRTCContext()
397
- const pc = context.createPeerConnection()
398
- // ... 50+ lines of boilerplate
399
-
400
- // After (v0.11.0) - ServiceHost wrapper
401
- const host = new ServiceHost({
402
- service: 'chat@1.0.0',
403
- rondevuService: service
404
- })
405
- await host.start()
406
- // Done!
407
- ```
408
-
409
- ## Platform Support
410
-
411
- ### Modern Browsers
412
- Works out of the box - no additional setup needed.
413
-
414
- ### Node.js 18+
415
- Native fetch is available, but WebRTC requires polyfills:
416
-
417
- ```bash
418
- npm install wrtc
419
- ```
420
-
421
- ```typescript
422
- import { WebRTCContext } from '@xtr-dev/rondevu-client'
423
- import { RTCPeerConnection, RTCSessionDescription, RTCIceCandidate } from 'wrtc'
424
-
425
- // Configure WebRTC context
426
- const context = new WebRTCContext({
427
- RTCPeerConnection,
428
- RTCSessionDescription,
429
- RTCIceCandidate
430
- } as any)
431
- ```
432
-
433
- ## TypeScript
434
-
435
- All types are exported:
436
-
437
- ```typescript
438
- import type {
439
- RondevuServiceOptions,
440
- ServiceHostOptions,
441
- ServiceHostEvents,
442
- ServiceClientOptions,
443
- ServiceClientEvents,
444
- ConnectionInterface,
445
- ConnectionEvents,
446
- ConnectionStates,
447
- Message,
448
- QueueMessageOptions,
449
- Signaler,
450
- PollingConfig,
451
- Credentials,
452
- Keypair
453
- } from '@xtr-dev/rondevu-client'
454
- ```
173
+ - [React Demo](https://github.com/xtr-dev/rondevu-demo) - Full browser UI ([live](https://ronde.vu))
455
174
 
456
175
  ## License
457
176