@xtr-dev/rondevu-client 0.20.1 → 0.21.3

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.
Files changed (43) hide show
  1. package/README.md +83 -385
  2. package/dist/api/batcher.d.ts +60 -38
  3. package/dist/api/batcher.js +121 -77
  4. package/dist/api/client.d.ts +104 -61
  5. package/dist/api/client.js +273 -185
  6. package/dist/connections/answerer.d.ts +15 -6
  7. package/dist/connections/answerer.js +56 -19
  8. package/dist/connections/base.d.ts +6 -4
  9. package/dist/connections/base.js +26 -16
  10. package/dist/connections/config.d.ts +30 -0
  11. package/dist/connections/config.js +20 -0
  12. package/dist/connections/events.d.ts +6 -6
  13. package/dist/connections/offerer.d.ts +37 -8
  14. package/dist/connections/offerer.js +92 -24
  15. package/dist/core/ice-config.d.ts +35 -0
  16. package/dist/core/ice-config.js +111 -0
  17. package/dist/core/index.d.ts +18 -18
  18. package/dist/core/index.js +18 -13
  19. package/dist/core/offer-pool.d.ts +30 -11
  20. package/dist/core/offer-pool.js +90 -76
  21. package/dist/core/peer.d.ts +158 -0
  22. package/dist/core/peer.js +254 -0
  23. package/dist/core/polling-manager.d.ts +71 -0
  24. package/dist/core/polling-manager.js +122 -0
  25. package/dist/core/rondevu-errors.d.ts +59 -0
  26. package/dist/core/rondevu-errors.js +75 -0
  27. package/dist/core/rondevu-types.d.ts +125 -0
  28. package/dist/core/rondevu-types.js +6 -0
  29. package/dist/core/rondevu.d.ts +106 -209
  30. package/dist/core/rondevu.js +222 -349
  31. package/dist/crypto/adapter.d.ts +25 -9
  32. package/dist/crypto/node.d.ts +27 -5
  33. package/dist/crypto/node.js +96 -25
  34. package/dist/crypto/web.d.ts +26 -4
  35. package/dist/crypto/web.js +102 -25
  36. package/dist/utils/message-buffer.js +4 -4
  37. package/dist/webrtc/adapter.d.ts +22 -0
  38. package/dist/webrtc/adapter.js +5 -0
  39. package/dist/webrtc/browser.d.ts +12 -0
  40. package/dist/webrtc/browser.js +15 -0
  41. package/dist/webrtc/node.d.ts +32 -0
  42. package/dist/webrtc/node.js +32 -0
  43. package/package.json +17 -6
package/README.md CHANGED
@@ -2,37 +2,16 @@
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
- 🌐 **WebRTC signaling client with durable connections**
5
+ **WebRTC signaling client with durable connections**
6
6
 
7
- TypeScript/JavaScript client for Rondevu, providing WebRTC signaling with **automatic reconnection**, **message buffering**, username claiming, service publishing/discovery, and efficient batch polling.
8
-
9
- **Related repositories:**
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))
11
- - [@xtr-dev/rondevu-server](https://github.com/xtr-dev/rondevu-server) - HTTP signaling server ([npm](https://www.npmjs.com/package/@xtr-dev/rondevu-server), [live](https://api.ronde.vu))
12
- - [@xtr-dev/rondevu-demo](https://github.com/xtr-dev/rondevu-demo) - Interactive demo ([live](https://ronde.vu))
13
-
14
- ---
7
+ TypeScript client for [Rondevu](https://github.com/xtr-dev/rondevu-server), providing WebRTC signaling with automatic reconnection, message buffering, and tags-based discovery.
15
8
 
16
9
  ## Features
17
10
 
18
- ### New in v0.20.0
19
- - **🔄 Automatic Reconnection**: Built-in exponential backoff for failed connections
20
- - **📦 Message Buffering**: Queues messages during disconnections, replays on reconnect
21
- - **🔄 Connection Persistence**: OffererConnection objects persist across disconnections via offer rotation
22
- - **📊 Connection State Machine**: Explicit lifecycle tracking with native RTC events
23
- - **🎯 Rich Event System**: 20+ events for monitoring connection health including `connection:rotated`
24
- - **⚡ Improved Reliability**: ICE polling lifecycle management, proper cleanup, rotation fallback
25
- - **🏗️ Internal Refactoring**: Cleaner codebase with OfferPool extraction and consolidated ICE polling
26
-
27
- ### Core Features
28
- - **Username Claiming**: Secure ownership with Ed25519 signatures
29
- - **Anonymous Users**: Auto-generated anonymous usernames for quick testing
30
- - **Service Publishing**: Publish services with multiple offers for connection pooling
31
- - **Service Discovery**: Direct lookup, random discovery, or paginated search
32
- - **Efficient Batch Polling**: Single endpoint for answers and ICE candidates
33
- - **Semantic Version Matching**: Compatible version resolution (chat:1.0.0 matches any 1.x.x)
34
- - **TypeScript**: Full type safety and autocomplete
35
- - **Keypair Management**: Generate or reuse Ed25519 keypairs
11
+ - **Simple Peer API**: Connect with `rondevu.peer({ tags, username })`
12
+ - **Tags-Based Discovery**: Find peers using tags (e.g., `["chat", "video"]`)
13
+ - **Automatic Reconnection**: Built-in exponential backoff
14
+ - **Message Buffering**: Queues messages during disconnections
36
15
 
37
16
  ## Installation
38
17
 
@@ -42,412 +21,131 @@ npm install @xtr-dev/rondevu-client
42
21
 
43
22
  ## Quick Start
44
23
 
45
- ### Publishing a Service (Offerer)
46
-
47
24
  ```typescript
48
25
  import { Rondevu } from '@xtr-dev/rondevu-client'
49
26
 
50
- // 1. Connect to Rondevu
51
- const rondevu = await Rondevu.connect({
52
- apiUrl: 'https://api.ronde.vu',
53
- username: 'alice', // Or omit for anonymous username
54
- iceServers: 'ipv4-turn' // Preset: 'ipv4-turn', 'hostname-turns', 'google-stun', 'relay-only'
55
- })
56
-
57
- // 2. Publish service with automatic offer management
58
- await rondevu.publishService({
59
- service: 'chat:1.0.0',
60
- maxOffers: 5, // Maintain up to 5 concurrent offers
61
- connectionConfig: {
62
- reconnectEnabled: true, // Auto-reconnect on failures
63
- bufferEnabled: true, // Buffer messages during disconnections
64
- connectionTimeout: 30000 // 30 second timeout
65
- }
66
- })
67
-
68
- // 3. Start accepting connections
69
- await rondevu.startFilling()
70
-
71
- // 4. Handle incoming connections
72
- rondevu.on('connection:opened', (offerId, connection) => {
73
- console.log('New connection:', offerId)
74
-
75
- // Listen for messages
76
- connection.on('message', (data) => {
77
- console.log('Received:', data)
78
- })
79
-
80
- // Monitor connection state
81
- connection.on('connected', () => {
82
- console.log('Fully connected!')
83
- connection.send('Hello from Alice!')
84
- })
85
-
86
- connection.on('disconnected', () => {
87
- console.log('Connection lost, will auto-reconnect')
88
- })
89
- })
90
- ```
27
+ // ============================================
28
+ // ALICE: Host and wait for connections
29
+ // ============================================
30
+ const alice = await Rondevu.connect({ username: 'alice' })
91
31
 
92
- ### Connecting to a Service (Answerer)
93
-
94
- ```typescript
95
- import { Rondevu } from '@xtr-dev/rondevu-client'
96
-
97
- // 1. Connect to Rondevu
98
- const rondevu = await Rondevu.connect({
99
- apiUrl: 'https://api.ronde.vu',
100
- username: 'bob',
101
- iceServers: 'ipv4-turn'
102
- })
103
-
104
- // 2. Connect to service - returns AnswererConnection
105
- const connection = await rondevu.connectToService({
106
- serviceFqn: 'chat:1.0.0@alice',
107
- connectionConfig: {
108
- reconnectEnabled: true,
109
- bufferEnabled: true,
110
- maxReconnectAttempts: 5
111
- }
32
+ alice.on('connection:opened', (offerId, connection) => {
33
+ console.log('Connected to', connection.peerUsername)
34
+ connection.on('message', (data) => console.log('Received:', data))
35
+ connection.send('Hello!')
112
36
  })
113
37
 
114
- // 3. Setup event handlers
115
- connection.on('connected', () => {
116
- console.log('Connected to alice!')
117
- connection.send('Hello from Bob!')
118
- })
38
+ const offer = await alice.offer({ tags: ['chat'], maxOffers: 5 })
39
+ // Later: offer.cancel() to stop accepting connections
119
40
 
120
- connection.on('message', (data) => {
121
- console.log('Received:', data)
122
- })
41
+ // ============================================
42
+ // BOB: Connect to Alice
43
+ // ============================================
44
+ const bob = await Rondevu.connect()
123
45
 
124
- // 4. Monitor connection health
125
- connection.on('reconnecting', (attempt) => {
126
- console.log(`Reconnecting... attempt ${attempt}`)
46
+ const peer = await bob.peer({
47
+ username: 'alice',
48
+ tags: ['chat']
127
49
  })
128
50
 
129
- connection.on('reconnect:success', () => {
130
- console.log('Back online!')
131
- })
132
-
133
- connection.on('failed', (error) => {
134
- console.error('Connection failed:', error)
135
- })
51
+ peer.on('open', () => peer.send('Hello Alice!'))
52
+ peer.on('message', (data) => console.log('Received:', data))
136
53
  ```
137
54
 
138
- ## Core API
55
+ ## API Reference
139
56
 
140
57
  ### Rondevu.connect()
141
58
 
142
59
  ```typescript
143
60
  const rondevu = await Rondevu.connect({
144
- apiUrl: string, // Required: Signaling server URL
145
- username?: string, // Optional: your username (auto-generates anonymous if omitted)
146
- keypair?: Keypair, // Optional: reuse existing keypair
147
- iceServers?: IceServerPreset | RTCIceServer[], // Optional: preset or custom config
148
- debug?: boolean // Optional: enable debug logging (default: false)
61
+ apiUrl?: string, // Default: 'https://api.ronde.vu'
62
+ credential?: Credential, // Reuse existing credential
63
+ username?: string, // Claim username (4-32 chars)
64
+ iceServers?: IceServerPreset | RTCIceServer[], // Default: 'rondevu'
65
+ debug?: boolean
149
66
  })
150
- ```
151
-
152
- ### Service Publishing
153
67
 
154
- ```typescript
155
- await rondevu.publishService({
156
- service: string, // e.g., 'chat:1.0.0' (username auto-appended)
157
- maxOffers: number, // Maximum concurrent offers to maintain
158
- offerFactory?: OfferFactory, // Optional: custom offer creation
159
- ttl?: number, // Optional: offer lifetime in ms (default: 300000)
160
- connectionConfig?: Partial<ConnectionConfig> // Optional: durability settings
161
- })
162
-
163
- await rondevu.startFilling() // Start accepting connections
164
- rondevu.stopFilling() // Stop and close all connections
68
+ rondevu.getName() // Get username
69
+ rondevu.getCredential() // Get credential for reuse
165
70
  ```
166
71
 
167
- ### Connecting to Services
72
+ **ICE Presets**: `'rondevu'` (default), `'rondevu-relay'`, `'google-stun'`, `'public-stun'`
168
73
 
169
- **⚠️ Breaking Change in v0.18.9+:** `connectToService()` now returns `AnswererConnection` instead of `ConnectionContext`.
74
+ ### rondevu.peer()
170
75
 
171
76
  ```typescript
172
- // New API (v0.18.9/v0.18.11+)
173
- const connection = await rondevu.connectToService({
174
- serviceFqn?: string, // Full FQN like 'chat:1.0.0@alice'
175
- service?: string, // Service without username (for discovery)
176
- username?: string, // Target username (combined with service)
177
- connectionConfig?: Partial<ConnectionConfig>, // Durability settings
178
- rtcConfig?: RTCConfiguration // Optional: override ICE servers
179
- })
180
-
181
- // Setup event handlers
182
- connection.on('connected', () => {
183
- connection.send('Hello!')
184
- })
185
-
186
- connection.on('message', (data) => {
187
- console.log(data)
188
- })
189
- ```
190
-
191
- ### Connection Configuration
192
-
193
- ```typescript
194
- interface ConnectionConfig {
195
- // Timeouts
196
- connectionTimeout: number // Default: 30000ms (30s)
197
- iceGatheringTimeout: number // Default: 10000ms (10s)
198
-
199
- // Reconnection
200
- reconnectEnabled: boolean // Default: true
201
- maxReconnectAttempts: number // Default: 5 (0 = infinite)
202
- reconnectBackoffBase: number // Default: 1000ms
203
- reconnectBackoffMax: number // Default: 30000ms (30s)
204
-
205
- // Message buffering
206
- bufferEnabled: boolean // Default: true
207
- maxBufferSize: number // Default: 100 messages
208
- maxBufferAge: number // Default: 60000ms (1 min)
209
-
210
- // Debug
211
- debug: boolean // Default: false
212
- }
213
- ```
214
-
215
- ### Connection Events
216
-
217
- ```typescript
218
- // Lifecycle events
219
- connection.on('connecting', () => {})
220
- connection.on('connected', () => {})
221
- connection.on('disconnected', (reason) => {})
222
- connection.on('failed', (error) => {})
223
- connection.on('closed', (reason) => {})
224
-
225
- // Reconnection events
226
- connection.on('reconnecting', (attempt) => {})
227
- connection.on('reconnect:success', () => {})
228
- connection.on('reconnect:failed', (error) => {})
229
- connection.on('reconnect:exhausted', (attempts) => {})
230
-
231
- // Message events
232
- connection.on('message', (data) => {})
233
- connection.on('message:buffered', (data) => {})
234
- connection.on('message:replayed', (message) => {})
235
-
236
- // ICE events
237
- connection.on('ice:connection:state', (state) => {})
238
- connection.on('ice:polling:started', () => {})
239
- connection.on('ice:polling:stopped', () => {})
77
+ const peer = await rondevu.peer({
78
+ tags: string[],
79
+ username?: string,
80
+ rtcConfig?: RTCConfiguration
81
+ })
82
+
83
+ // Events
84
+ peer.on('open', () => {})
85
+ peer.on('close', (reason) => {})
86
+ peer.on('message', (data) => {})
87
+ peer.on('error', (error) => {})
88
+ peer.on('reconnecting', (attempt, max) => {})
89
+
90
+ // Properties & Methods
91
+ peer.state // 'connecting' | 'connected' | 'reconnecting' | ...
92
+ peer.peerUsername
93
+ peer.send(data)
94
+ peer.close()
240
95
  ```
241
96
 
242
- ### Service Discovery
243
-
244
- ```typescript
245
- // Unified discovery API
246
- const service = await rondevu.findService(
247
- 'chat:1.0.0@alice', // Direct lookup (with username)
248
- { mode: 'direct' }
249
- )
250
-
251
- const service = await rondevu.findService(
252
- 'chat:1.0.0', // Random discovery (without username)
253
- { mode: 'random' }
254
- )
255
-
256
- const result = await rondevu.findService(
257
- 'chat:1.0.0',
258
- {
259
- mode: 'paginated',
260
- limit: 20,
261
- offset: 0
262
- }
263
- )
264
- ```
265
-
266
- ## Migration Guide
267
-
268
- **Upgrading from v0.18.10 or earlier?** See [MIGRATION.md](./MIGRATION.md) for detailed upgrade instructions.
269
-
270
- ### Quick Migration Summary
271
-
272
- **Before (v0.18.7/v0.18.10):**
273
- ```typescript
274
- const context = await rondevu.connectToService({
275
- serviceFqn: 'chat:1.0.0@alice',
276
- onConnection: ({ dc }) => {
277
- dc.addEventListener('message', (e) => console.log(e.data))
278
- dc.send('Hello')
279
- }
280
- })
281
- ```
97
+ ### rondevu.offer()
282
98
 
283
- **After (v0.18.9/v0.18.11):**
284
99
  ```typescript
285
- const connection = await rondevu.connectToService({
286
- serviceFqn: 'chat:1.0.0@alice'
100
+ const offer = await rondevu.offer({
101
+ tags: string[],
102
+ maxOffers: number,
103
+ ttl?: number, // Offer lifetime in ms (default: 300000)
104
+ autoStart?: boolean // Auto-start filling (default: true)
287
105
  })
288
106
 
289
- connection.on('connected', () => {
290
- connection.send('Hello') // Use connection.send()
291
- })
107
+ offer.cancel() // Stop accepting connections
292
108
 
293
- connection.on('message', (data) => {
294
- console.log(data) // data is already extracted
109
+ rondevu.on('connection:opened', (offerId, connection) => {
110
+ connection.on('message', (data) => {})
111
+ connection.send('Hello!')
295
112
  })
296
113
  ```
297
114
 
298
- ## Advanced Usage
299
-
300
- ### Custom Offer Factory
115
+ ### rondevu.discover()
301
116
 
302
117
  ```typescript
303
- await rondevu.publishService({
304
- service: 'file-transfer:1.0.0',
305
- maxOffers: 3,
306
- offerFactory: async (pc) => {
307
- // Customize data channel settings
308
- const dc = pc.createDataChannel('files', {
309
- ordered: true,
310
- maxRetransmits: 10
311
- })
312
-
313
- // Add custom listeners
314
- dc.addEventListener('open', () => {
315
- console.log('Transfer channel ready')
316
- })
317
-
318
- const offer = await pc.createOffer()
319
- await pc.setLocalDescription(offer)
320
- return { dc, offer }
321
- }
322
- })
118
+ const result = await rondevu.discover(['chat'], { limit: 20 })
119
+ result.offers.forEach(o => console.log(o.username, o.tags))
323
120
  ```
324
121
 
325
- ### Accessing Raw RTCPeerConnection
122
+ ## Credentials
326
123
 
327
124
  ```typescript
328
- const connection = await rondevu.connectToService({ ... })
329
-
330
- // Get raw objects if needed
331
- const pc = connection.getPeerConnection()
332
- const dc = connection.getDataChannel()
333
-
334
- // Note: Using raw DataChannel bypasses buffering/reconnection features
335
- if (dc) {
336
- dc.addEventListener('message', (e) => {
337
- console.log('Raw message:', e.data)
338
- })
339
- }
340
- ```
125
+ // Auto-generated username
126
+ const rondevu = await Rondevu.connect()
127
+ // rondevu.getName() === 'friendly-panda-a1b2c3'
341
128
 
342
- ### Disabling Durability Features
129
+ // Claimed username
130
+ const rondevu = await Rondevu.connect({ username: 'alice' })
343
131
 
344
- ```typescript
345
- const connection = await rondevu.connectToService({
346
- serviceFqn: 'chat:1.0.0@alice',
347
- connectionConfig: {
348
- reconnectEnabled: false, // Disable auto-reconnect
349
- bufferEnabled: false, // Disable message buffering
350
- }
351
- })
352
- ```
132
+ // Save and restore credentials
133
+ const credential = rondevu.getCredential()
134
+ localStorage.setItem('cred', JSON.stringify(credential))
353
135
 
354
- ## Documentation
355
-
356
- 📚 **[MIGRATION.md](./MIGRATION.md)** - Upgrade guide from v0.18.7 to v0.18.9
136
+ const saved = JSON.parse(localStorage.getItem('cred'))
137
+ const rondevu = await Rondevu.connect({ credential: saved })
138
+ ```
357
139
 
358
- 📚 **[ADVANCED.md](./ADVANCED.md)** - Comprehensive guide including:
359
- - Detailed API reference for all methods
360
- - Type definitions and interfaces
361
- - Platform support (Browser & Node.js)
362
- - Advanced usage patterns
363
- - Username rules and service FQN format
140
+ ## Tag Validation
364
141
 
365
- ## Connection Persistence (v0.20.0+)
142
+ Tags: 1-64 chars, lowercase alphanumeric with dots/dashes.
366
143
 
367
- Connection objects now persist across disconnections via **"offer rotation"**. When a connection fails, the same connection object is rebound to a new offer instead of being destroyed:
144
+ Valid: `chat`, `video-call`, `com.example.service`
368
145
 
369
- ```typescript
370
- rondevu.on('connection:opened', (offerId, connection) => {
371
- console.log(`Connection ${offerId} opened`)
372
-
373
- // Listen for offer rotation
374
- rondevu.on('connection:rotated', (oldOfferId, newOfferId, conn) => {
375
- if (conn === connection) {
376
- console.log(`Connection rotated: ${oldOfferId} → ${newOfferId}`)
377
- // Same connection object! Event listeners still work
378
- // Message buffer preserved
379
- }
380
- })
381
-
382
- connection.on('message', (data) => {
383
- console.log('Received:', data)
384
- // This listener continues working even after rotation
385
- })
386
-
387
- connection.on('failed', () => {
388
- console.log('Connection failed, will auto-rotate to new offer')
389
- })
390
- })
391
- ```
146
+ ## Links
392
147
 
393
- **Benefits:**
394
- - ✅ Same connection object remains usable through disconnections
395
- - ✅ Message buffer preserved during temporary disconnections
396
- - ✅ Event listeners don't need to be re-registered
397
- - ✅ Seamless reconnection experience for offerer side
398
-
399
- ## Examples
400
-
401
- - [React Demo](https://github.com/xtr-dev/rondevu-demo) - Full browser UI ([live](https://ronde.vu))
402
-
403
- ## Changelog
404
-
405
- ### v0.20.0 (Latest)
406
- - **Connection Persistence** - OffererConnection objects now persist across disconnections
407
- - **Offer Rotation** - When connection fails, same object is rebound to new offer
408
- - **Message Buffering** - Now works seamlessly on offerer side through rotations
409
- - **New Event**: `connection:rotated` emitted when offer is rotated
410
- - **Internal**: Added `OffererConnection.rebindToOffer()` method
411
- - **Internal**: Modified OfferPool failure handler to rotate offers instead of destroying connections
412
- - **Internal**: Added rotation lock to prevent concurrent rotations
413
- - **Internal**: Added max rotation attempts limit (default: 5)
414
- - 100% backward compatible - no breaking changes
415
-
416
- ### v0.19.0
417
- - **Internal Refactoring** - Improved codebase maintainability (no API changes)
418
- - Extract OfferPool class for offer lifecycle management
419
- - Consolidate ICE polling logic (remove ~86 lines of duplicate code)
420
- - Add AsyncLock utility for race-free concurrent operations
421
- - Disable reconnection for offerer connections (offers are ephemeral)
422
- - 100% backward compatible - upgrade without code changes
423
-
424
- ### v0.18.11
425
- - Restore EventEmitter-based durable connections (same as v0.18.9)
426
- - Durable WebRTC connections with state machine
427
- - Automatic reconnection with exponential backoff
428
- - Message buffering during disconnections
429
- - ICE polling lifecycle management
430
- - **Breaking:** `connectToService()` returns `AnswererConnection` instead of `ConnectionContext`
431
- - See [MIGRATION.md](./MIGRATION.md) for upgrade guide
432
-
433
- ### v0.18.10
434
- - Temporary revert to callback-based API (reverted in v0.18.11)
435
-
436
- ### v0.18.9
437
- - Add durable WebRTC connections with state machine
438
- - Implement automatic reconnection with exponential backoff
439
- - Add message buffering during disconnections
440
- - Fix ICE polling lifecycle (stops when connected)
441
- - Add fillOffers() semaphore to prevent exceeding maxOffers
442
- - **Breaking:** `connectToService()` returns `AnswererConnection` instead of `ConnectionContext`
443
- - **Breaking:** `connection:opened` event signature changed
444
- - See [MIGRATION.md](./MIGRATION.md) for upgrade guide
445
-
446
- ### v0.18.8
447
- - Initial durable connections implementation
448
-
449
- ### v0.18.3
450
- - Fix EventEmitter cross-platform compatibility
148
+ - [Live Demo](https://ronde.vu) | [Server](https://github.com/xtr-dev/rondevu-server) | [API](https://api.ronde.vu)
451
149
 
452
150
  ## License
453
151
 
@@ -1,61 +1,83 @@
1
1
  /**
2
- * RPC Batcher - Throttles and batches RPC requests to reduce HTTP overhead
2
+ * RPC Request Batcher with throttling
3
+ *
4
+ * Collects RPC requests over a short time window and sends them efficiently.
5
+ *
6
+ * Due to server authentication design (signature covers method+params),
7
+ * authenticated requests are sent individually while unauthenticated
8
+ * requests can be truly batched together.
3
9
  */
10
+ export interface RpcRequest {
11
+ method: string;
12
+ params?: any;
13
+ }
14
+ export interface RpcResponse {
15
+ success: boolean;
16
+ result?: any;
17
+ error?: string;
18
+ errorCode?: string;
19
+ }
4
20
  export interface BatcherOptions {
5
- /**
6
- * Maximum number of requests to batch together
7
- * Default: 10
8
- */
21
+ /** Delay in ms before flushing queued requests (default: 10) */
22
+ delay?: number;
23
+ /** Maximum batch size for unauthenticated requests (default: 50) */
9
24
  maxBatchSize?: number;
10
- /**
11
- * Maximum time to wait before sending a batch (ms)
12
- * Default: 50ms
13
- */
14
- maxWaitTime?: number;
15
- /**
16
- * Minimum time between batches (ms)
17
- * Default: 10ms
18
- */
19
- throttleInterval?: number;
20
25
  }
21
26
  /**
22
- * Batches and throttles RPC requests to optimize network usage
27
+ * RpcBatcher - Batches RPC requests with throttling
23
28
  *
24
29
  * @example
25
30
  * ```typescript
26
- * const batcher = new RpcBatcher(
27
- * (requests) => api.rpcBatch(requests),
28
- * { maxBatchSize: 10, maxWaitTime: 50 }
29
- * )
31
+ * const batcher = new RpcBatcher('https://api.example.com', {
32
+ * delay: 10,
33
+ * maxBatchSize: 50
34
+ * })
30
35
  *
31
- * // These will be batched together if called within maxWaitTime
32
- * const result1 = await batcher.add(request1)
33
- * const result2 = await batcher.add(request2)
34
- * const result3 = await batcher.add(request3)
36
+ * // Requests made within the delay window are batched
37
+ * const [result1, result2] = await Promise.all([
38
+ * batcher.add({ method: 'getOffer', params: {...} }, null),
39
+ * batcher.add({ method: 'getOffer', params: {...} }, null)
40
+ * ])
35
41
  * ```
36
42
  */
37
43
  export declare class RpcBatcher {
44
+ private readonly baseUrl;
38
45
  private queue;
39
- private batchTimeout;
40
- private lastBatchTime;
41
- private options;
42
- private sendBatch;
43
- constructor(sendBatch: (requests: any[]) => Promise<any[]>, options?: BatcherOptions);
46
+ private flushTimer;
47
+ private readonly delay;
48
+ private readonly maxBatchSize;
49
+ constructor(baseUrl: string, options?: BatcherOptions);
44
50
  /**
45
- * Add an RPC request to the batch queue
46
- * Returns a promise that resolves when the request completes
51
+ * Add a request to the batch queue
52
+ * @param request - The RPC request
53
+ * @param authHeaders - Auth headers for authenticated requests, null for unauthenticated
54
+ * @returns Promise that resolves with the request result
47
55
  */
48
- add(request: any): Promise<any>;
56
+ add(request: RpcRequest, authHeaders: Record<string, string> | null): Promise<any>;
49
57
  /**
50
- * Flush the queue immediately
58
+ * Schedule a flush after the delay
51
59
  */
52
- flush(): Promise<void>;
60
+ private scheduleFlush;
53
61
  /**
54
- * Get current queue size
62
+ * Flush all queued requests
55
63
  */
56
- getQueueSize(): number;
64
+ private flush;
65
+ /**
66
+ * Process unauthenticated requests in batches
67
+ */
68
+ private processUnauthenticatedBatches;
69
+ /**
70
+ * Process authenticated requests individually
71
+ * Each authenticated request needs its own HTTP call because
72
+ * the signature covers the specific method+params
73
+ */
74
+ private processAuthenticatedRequests;
75
+ /**
76
+ * Send a batch of requests
77
+ */
78
+ private sendBatch;
57
79
  /**
58
- * Clear the queue without sending
80
+ * Flush immediately (useful for cleanup/testing)
59
81
  */
60
- clear(): void;
82
+ flushNow(): Promise<void>;
61
83
  }