@xtr-dev/rondevu-client 0.18.9 → 0.20.1
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 +324 -47
- package/dist/{api.d.ts → api/client.d.ts} +17 -8
- package/dist/{api.js → api/client.js} +114 -81
- package/dist/{answerer-connection.d.ts → connections/answerer.d.ts} +13 -5
- package/dist/{answerer-connection.js → connections/answerer.js} +17 -32
- package/dist/{connection.d.ts → connections/base.d.ts} +26 -5
- package/dist/{connection.js → connections/base.js} +45 -4
- package/dist/{offerer-connection.d.ts → connections/offerer.d.ts} +30 -5
- package/dist/{offerer-connection.js → connections/offerer.js} +93 -32
- package/dist/core/index.d.ts +22 -0
- package/dist/core/index.js +17 -0
- package/dist/core/offer-pool.d.ts +94 -0
- package/dist/core/offer-pool.js +267 -0
- package/dist/{rondevu.d.ts → core/rondevu.d.ts} +7 -28
- package/dist/{rondevu.js → core/rondevu.js} +32 -175
- package/dist/{node-crypto-adapter.d.ts → crypto/node.d.ts} +1 -1
- package/dist/{web-crypto-adapter.d.ts → crypto/web.d.ts} +1 -1
- package/dist/utils/async-lock.d.ts +42 -0
- package/dist/utils/async-lock.js +75 -0
- package/dist/{message-buffer.d.ts → utils/message-buffer.d.ts} +1 -1
- package/package.json +3 -3
- package/dist/index.d.ts +0 -22
- package/dist/index.js +0 -17
- package/dist/rondevu-signaler.d.ts +0 -112
- package/dist/rondevu-signaler.js +0 -401
- /package/dist/{rpc-batcher.d.ts → api/batcher.d.ts} +0 -0
- /package/dist/{rpc-batcher.js → api/batcher.js} +0 -0
- /package/dist/{connection-config.d.ts → connections/config.d.ts} +0 -0
- /package/dist/{connection-config.js → connections/config.js} +0 -0
- /package/dist/{connection-events.d.ts → connections/events.d.ts} +0 -0
- /package/dist/{connection-events.js → connections/events.js} +0 -0
- /package/dist/{types.d.ts → core/types.d.ts} +0 -0
- /package/dist/{types.js → core/types.js} +0 -0
- /package/dist/{crypto-adapter.d.ts → crypto/adapter.d.ts} +0 -0
- /package/dist/{crypto-adapter.js → crypto/adapter.js} +0 -0
- /package/dist/{node-crypto-adapter.js → crypto/node.js} +0 -0
- /package/dist/{web-crypto-adapter.js → crypto/web.js} +0 -0
- /package/dist/{exponential-backoff.d.ts → utils/exponential-backoff.d.ts} +0 -0
- /package/dist/{exponential-backoff.js → utils/exponential-backoff.js} +0 -0
- /package/dist/{message-buffer.js → utils/message-buffer.js} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { RondevuAPI } from '
|
|
1
|
+
import { RondevuAPI } from '../api/client.js';
|
|
2
2
|
import { EventEmitter } from 'eventemitter3';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { AnswererConnection } from '../connections/answerer.js';
|
|
4
|
+
import { OfferPool } from './offer-pool.js';
|
|
5
5
|
// ICE server presets
|
|
6
6
|
export const ICE_SERVER_PRESETS = {
|
|
7
7
|
'ipv4-turn': [
|
|
@@ -146,15 +146,7 @@ export class Rondevu extends EventEmitter {
|
|
|
146
146
|
this.usernameClaimed = false;
|
|
147
147
|
// Service management
|
|
148
148
|
this.currentService = null;
|
|
149
|
-
this.
|
|
150
|
-
this.offerFactory = null;
|
|
151
|
-
this.ttl = Rondevu.DEFAULT_TTL_MS;
|
|
152
|
-
this.activeConnections = new Map();
|
|
153
|
-
// Polling
|
|
154
|
-
this.filling = false;
|
|
155
|
-
this.fillingSemaphore = false; // Semaphore to prevent concurrent fillOffers calls
|
|
156
|
-
this.pollingInterval = null;
|
|
157
|
-
this.lastPollTimestamp = 0;
|
|
149
|
+
this.offerPool = null;
|
|
158
150
|
this.apiUrl = apiUrl;
|
|
159
151
|
this.username = username;
|
|
160
152
|
this.keypair = keypair;
|
|
@@ -297,160 +289,43 @@ export class Rondevu extends EventEmitter {
|
|
|
297
289
|
async publishService(options) {
|
|
298
290
|
const { service, maxOffers, offerFactory, ttl, connectionConfig } = options;
|
|
299
291
|
this.currentService = service;
|
|
300
|
-
this.maxOffers = maxOffers;
|
|
301
|
-
this.offerFactory = offerFactory || this.defaultOfferFactory.bind(this);
|
|
302
|
-
this.ttl = ttl || Rondevu.DEFAULT_TTL_MS;
|
|
303
292
|
this.connectionConfig = connectionConfig;
|
|
304
|
-
this.debug(`Publishing service: ${service} with maxOffers: ${maxOffers}`);
|
|
305
|
-
this.usernameClaimed = true;
|
|
306
|
-
}
|
|
307
|
-
/**
|
|
308
|
-
* Create a single offer and publish it to the server using OffererConnection
|
|
309
|
-
*/
|
|
310
|
-
async createOffer() {
|
|
311
|
-
if (!this.currentService || !this.offerFactory) {
|
|
312
|
-
throw new Error('Service not published. Call publishService() first.');
|
|
313
|
-
}
|
|
314
|
-
const rtcConfig = {
|
|
315
|
-
iceServers: this.iceServers
|
|
316
|
-
};
|
|
317
293
|
// Auto-append username to service
|
|
318
|
-
const serviceFqn = `${
|
|
319
|
-
this.debug(
|
|
320
|
-
//
|
|
321
|
-
|
|
322
|
-
// 2. Call the factory to create offer
|
|
323
|
-
let dc;
|
|
324
|
-
let offer;
|
|
325
|
-
try {
|
|
326
|
-
const factoryResult = await this.offerFactory(pc);
|
|
327
|
-
dc = factoryResult.dc;
|
|
328
|
-
offer = factoryResult.offer;
|
|
329
|
-
}
|
|
330
|
-
catch (err) {
|
|
331
|
-
pc.close();
|
|
332
|
-
throw err;
|
|
333
|
-
}
|
|
334
|
-
// 3. Publish to server to get offerId
|
|
335
|
-
const result = await this.api.publishService({
|
|
336
|
-
serviceFqn,
|
|
337
|
-
offers: [{ sdp: offer.sdp }],
|
|
338
|
-
ttl: this.ttl,
|
|
339
|
-
signature: '',
|
|
340
|
-
message: '',
|
|
341
|
-
});
|
|
342
|
-
const offerId = result.offers[0].offerId;
|
|
343
|
-
// 4. Create OffererConnection instance with already-created PC and DC
|
|
344
|
-
const connection = new OffererConnection({
|
|
294
|
+
const serviceFqn = `${service}@${this.username}`;
|
|
295
|
+
this.debug(`Publishing service: ${service} with maxOffers: ${maxOffers}`);
|
|
296
|
+
// Create OfferPool (but don't start it yet - call startFilling() to begin)
|
|
297
|
+
this.offerPool = new OfferPool({
|
|
345
298
|
api: this.api,
|
|
346
299
|
serviceFqn,
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
},
|
|
300
|
+
maxOffers,
|
|
301
|
+
offerFactory: offerFactory || this.defaultOfferFactory.bind(this),
|
|
302
|
+
ttl: ttl || Rondevu.DEFAULT_TTL_MS,
|
|
303
|
+
iceServers: this.iceServers,
|
|
304
|
+
connectionConfig,
|
|
305
|
+
debugEnabled: this.debugEnabled,
|
|
354
306
|
});
|
|
355
|
-
//
|
|
356
|
-
|
|
357
|
-
this.debug(`Connection established for offer ${offerId}`);
|
|
307
|
+
// Forward events from OfferPool
|
|
308
|
+
this.offerPool.on('connection:opened', (offerId, connection) => {
|
|
358
309
|
this.emit('connection:opened', offerId, connection);
|
|
359
310
|
});
|
|
360
|
-
|
|
361
|
-
this.
|
|
362
|
-
this.activeConnections.delete(offerId);
|
|
363
|
-
this.fillOffers(); // Replace failed offer
|
|
311
|
+
this.offerPool.on('offer:created', (offerId, serviceFqn) => {
|
|
312
|
+
this.emit('offer:created', offerId, serviceFqn);
|
|
364
313
|
});
|
|
365
|
-
|
|
366
|
-
this.
|
|
367
|
-
this.activeConnections.delete(offerId);
|
|
368
|
-
this.fillOffers(); // Replace closed offer
|
|
314
|
+
this.offerPool.on('connection:rotated', (oldOfferId, newOfferId, connection) => {
|
|
315
|
+
this.emit('connection:rotated', oldOfferId, newOfferId, connection);
|
|
369
316
|
});
|
|
370
|
-
|
|
371
|
-
this.activeConnections.set(offerId, connection);
|
|
372
|
-
// Initialize the connection
|
|
373
|
-
await connection.initialize();
|
|
374
|
-
this.debug(`Offer created: ${offerId}`);
|
|
375
|
-
this.emit('offer:created', offerId, serviceFqn);
|
|
376
|
-
}
|
|
377
|
-
/**
|
|
378
|
-
* Fill offers to reach maxOffers count with semaphore protection
|
|
379
|
-
*/
|
|
380
|
-
async fillOffers() {
|
|
381
|
-
if (!this.filling || !this.currentService)
|
|
382
|
-
return;
|
|
383
|
-
// Semaphore to prevent concurrent fills
|
|
384
|
-
if (this.fillingSemaphore) {
|
|
385
|
-
this.debug('fillOffers already in progress, skipping');
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
388
|
-
this.fillingSemaphore = true;
|
|
389
|
-
try {
|
|
390
|
-
const currentCount = this.activeConnections.size;
|
|
391
|
-
const needed = this.maxOffers - currentCount;
|
|
392
|
-
this.debug(`Filling offers: current=${currentCount}, needed=${needed}`);
|
|
393
|
-
for (let i = 0; i < needed; i++) {
|
|
394
|
-
try {
|
|
395
|
-
await this.createOffer();
|
|
396
|
-
}
|
|
397
|
-
catch (err) {
|
|
398
|
-
console.error('[Rondevu] Failed to create offer:', err);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
finally {
|
|
403
|
-
this.fillingSemaphore = false;
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
/**
|
|
407
|
-
* Poll for answers and ICE candidates (internal use for automatic offer management)
|
|
408
|
-
*/
|
|
409
|
-
async pollInternal() {
|
|
410
|
-
if (!this.filling)
|
|
411
|
-
return;
|
|
412
|
-
try {
|
|
413
|
-
const result = await this.api.poll(this.lastPollTimestamp);
|
|
414
|
-
// Process answers - delegate to OffererConnections
|
|
415
|
-
for (const answer of result.answers) {
|
|
416
|
-
const connection = this.activeConnections.get(answer.offerId);
|
|
417
|
-
if (connection) {
|
|
418
|
-
try {
|
|
419
|
-
await connection.processAnswer(answer.sdp, answer.answererId);
|
|
420
|
-
this.lastPollTimestamp = Math.max(this.lastPollTimestamp, answer.answeredAt);
|
|
421
|
-
// Create replacement offer
|
|
422
|
-
this.fillOffers();
|
|
423
|
-
}
|
|
424
|
-
catch (err) {
|
|
425
|
-
this.debug(`Failed to process answer for offer ${answer.offerId}:`, err);
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
catch (err) {
|
|
431
|
-
console.error('[Rondevu] Polling error:', err);
|
|
432
|
-
}
|
|
317
|
+
this.usernameClaimed = true;
|
|
433
318
|
}
|
|
434
319
|
/**
|
|
435
320
|
* Start filling offers and polling for answers/ICE
|
|
436
321
|
* Call this after publishService() to begin accepting connections
|
|
437
322
|
*/
|
|
438
323
|
async startFilling() {
|
|
439
|
-
if (this.
|
|
440
|
-
this.debug('Already filling');
|
|
441
|
-
return;
|
|
442
|
-
}
|
|
443
|
-
if (!this.currentService) {
|
|
324
|
+
if (!this.offerPool) {
|
|
444
325
|
throw new Error('No service published. Call publishService() first.');
|
|
445
326
|
}
|
|
446
327
|
this.debug('Starting offer filling and polling');
|
|
447
|
-
this.
|
|
448
|
-
// Fill initial offers
|
|
449
|
-
await this.fillOffers();
|
|
450
|
-
// Start polling
|
|
451
|
-
this.pollingInterval = setInterval(() => {
|
|
452
|
-
this.pollInternal();
|
|
453
|
-
}, Rondevu.POLLING_INTERVAL_MS);
|
|
328
|
+
await this.offerPool.start();
|
|
454
329
|
}
|
|
455
330
|
/**
|
|
456
331
|
* Stop filling offers and polling
|
|
@@ -458,26 +333,14 @@ export class Rondevu extends EventEmitter {
|
|
|
458
333
|
*/
|
|
459
334
|
stopFilling() {
|
|
460
335
|
this.debug('Stopping offer filling and polling');
|
|
461
|
-
this.
|
|
462
|
-
this.fillingSemaphore = false;
|
|
463
|
-
// Stop polling
|
|
464
|
-
if (this.pollingInterval) {
|
|
465
|
-
clearInterval(this.pollingInterval);
|
|
466
|
-
this.pollingInterval = null;
|
|
467
|
-
}
|
|
468
|
-
// Close all active connections
|
|
469
|
-
for (const [offerId, connection] of this.activeConnections.entries()) {
|
|
470
|
-
this.debug(`Closing connection ${offerId}`);
|
|
471
|
-
connection.close();
|
|
472
|
-
}
|
|
473
|
-
this.activeConnections.clear();
|
|
336
|
+
this.offerPool?.stop();
|
|
474
337
|
}
|
|
475
338
|
/**
|
|
476
339
|
* Get the count of active offers
|
|
477
340
|
* @returns Number of active offers
|
|
478
341
|
*/
|
|
479
342
|
getOfferCount() {
|
|
480
|
-
return this.
|
|
343
|
+
return this.offerPool?.getOfferCount() ?? 0;
|
|
481
344
|
}
|
|
482
345
|
/**
|
|
483
346
|
* Check if an offer is currently connected
|
|
@@ -485,20 +348,15 @@ export class Rondevu extends EventEmitter {
|
|
|
485
348
|
* @returns True if the offer exists and is connected
|
|
486
349
|
*/
|
|
487
350
|
isConnected(offerId) {
|
|
488
|
-
|
|
489
|
-
return connection ? connection.getState() === 'connected' : false;
|
|
351
|
+
return this.offerPool?.isConnected(offerId) ?? false;
|
|
490
352
|
}
|
|
491
353
|
/**
|
|
492
354
|
* Disconnect all active offers
|
|
493
355
|
* Similar to stopFilling() but doesn't stop the polling/filling process
|
|
494
356
|
*/
|
|
495
|
-
|
|
357
|
+
disconnectAll() {
|
|
496
358
|
this.debug('Disconnecting all offers');
|
|
497
|
-
|
|
498
|
-
this.debug(`Closing connection ${offerId}`);
|
|
499
|
-
connection.close();
|
|
500
|
-
}
|
|
501
|
-
this.activeConnections.clear();
|
|
359
|
+
this.offerPool?.disconnectAll();
|
|
502
360
|
}
|
|
503
361
|
/**
|
|
504
362
|
* Get the current service status
|
|
@@ -507,9 +365,7 @@ export class Rondevu extends EventEmitter {
|
|
|
507
365
|
getServiceStatus() {
|
|
508
366
|
return {
|
|
509
367
|
active: this.currentService !== null,
|
|
510
|
-
offerCount: this.
|
|
511
|
-
maxOffers: this.maxOffers,
|
|
512
|
-
filling: this.filling
|
|
368
|
+
offerCount: this.offerPool?.getOfferCount() ?? 0
|
|
513
369
|
};
|
|
514
370
|
}
|
|
515
371
|
/**
|
|
@@ -706,7 +562,7 @@ export class Rondevu extends EventEmitter {
|
|
|
706
562
|
* Get active connections (for offerer side)
|
|
707
563
|
*/
|
|
708
564
|
getActiveConnections() {
|
|
709
|
-
return this.
|
|
565
|
+
return this.offerPool?.getActiveConnections() ?? new Map();
|
|
710
566
|
}
|
|
711
567
|
/**
|
|
712
568
|
* Get all active offers (legacy compatibility)
|
|
@@ -714,7 +570,8 @@ export class Rondevu extends EventEmitter {
|
|
|
714
570
|
*/
|
|
715
571
|
getActiveOffers() {
|
|
716
572
|
const offers = [];
|
|
717
|
-
|
|
573
|
+
const connections = this.offerPool?.getActiveConnections() ?? new Map();
|
|
574
|
+
for (const [offerId, connection] of connections.entries()) {
|
|
718
575
|
const pc = connection.getPeerConnection();
|
|
719
576
|
const dc = connection.getDataChannel();
|
|
720
577
|
if (pc) {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Node.js Crypto adapter for Node.js environments
|
|
3
3
|
* Requires Node.js 19+ or Node.js 18 with --experimental-global-webcrypto flag
|
|
4
4
|
*/
|
|
5
|
-
import { CryptoAdapter, Keypair } from './
|
|
5
|
+
import { CryptoAdapter, Keypair } from './adapter.js';
|
|
6
6
|
/**
|
|
7
7
|
* Node.js Crypto implementation using Node.js built-in APIs
|
|
8
8
|
* Uses Buffer for base64 encoding and crypto.randomBytes for random generation
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Web Crypto adapter for browser environments
|
|
3
3
|
*/
|
|
4
|
-
import { CryptoAdapter, Keypair } from './
|
|
4
|
+
import { CryptoAdapter, Keypair } from './adapter.js';
|
|
5
5
|
/**
|
|
6
6
|
* Web Crypto implementation using browser APIs
|
|
7
7
|
* Uses btoa/atob for base64 encoding and crypto.getRandomValues for random bytes
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AsyncLock provides a mutual exclusion primitive for asynchronous operations.
|
|
3
|
+
* Ensures only one async operation can proceed at a time while queuing others.
|
|
4
|
+
*/
|
|
5
|
+
export declare class AsyncLock {
|
|
6
|
+
private locked;
|
|
7
|
+
private queue;
|
|
8
|
+
/**
|
|
9
|
+
* Acquire the lock. If already locked, waits until released.
|
|
10
|
+
* @returns Promise that resolves when lock is acquired
|
|
11
|
+
*/
|
|
12
|
+
acquire(): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Release the lock. If others are waiting, grants lock to next in queue.
|
|
15
|
+
*/
|
|
16
|
+
release(): void;
|
|
17
|
+
/**
|
|
18
|
+
* Run a function with the lock acquired, automatically releasing after.
|
|
19
|
+
* This is the recommended way to use AsyncLock to prevent forgetting to release.
|
|
20
|
+
*
|
|
21
|
+
* @param fn - Async function to run with lock held
|
|
22
|
+
* @returns Promise resolving to the function's return value
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const lock = new AsyncLock()
|
|
27
|
+
* const result = await lock.run(async () => {
|
|
28
|
+
* // Critical section - only one caller at a time
|
|
29
|
+
* return await doSomething()
|
|
30
|
+
* })
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
run<T>(fn: () => Promise<T>): Promise<T>;
|
|
34
|
+
/**
|
|
35
|
+
* Check if lock is currently held
|
|
36
|
+
*/
|
|
37
|
+
isLocked(): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Get number of operations waiting for the lock
|
|
40
|
+
*/
|
|
41
|
+
getQueueLength(): number;
|
|
42
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AsyncLock provides a mutual exclusion primitive for asynchronous operations.
|
|
3
|
+
* Ensures only one async operation can proceed at a time while queuing others.
|
|
4
|
+
*/
|
|
5
|
+
export class AsyncLock {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.locked = false;
|
|
8
|
+
this.queue = [];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Acquire the lock. If already locked, waits until released.
|
|
12
|
+
* @returns Promise that resolves when lock is acquired
|
|
13
|
+
*/
|
|
14
|
+
async acquire() {
|
|
15
|
+
if (!this.locked) {
|
|
16
|
+
this.locked = true;
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
// Lock is held, wait in queue
|
|
20
|
+
return new Promise(resolve => {
|
|
21
|
+
this.queue.push(resolve);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Release the lock. If others are waiting, grants lock to next in queue.
|
|
26
|
+
*/
|
|
27
|
+
release() {
|
|
28
|
+
const next = this.queue.shift();
|
|
29
|
+
if (next) {
|
|
30
|
+
// Grant lock to next waiter
|
|
31
|
+
next();
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
// No waiters, mark as unlocked
|
|
35
|
+
this.locked = false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Run a function with the lock acquired, automatically releasing after.
|
|
40
|
+
* This is the recommended way to use AsyncLock to prevent forgetting to release.
|
|
41
|
+
*
|
|
42
|
+
* @param fn - Async function to run with lock held
|
|
43
|
+
* @returns Promise resolving to the function's return value
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* const lock = new AsyncLock()
|
|
48
|
+
* const result = await lock.run(async () => {
|
|
49
|
+
* // Critical section - only one caller at a time
|
|
50
|
+
* return await doSomething()
|
|
51
|
+
* })
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
async run(fn) {
|
|
55
|
+
await this.acquire();
|
|
56
|
+
try {
|
|
57
|
+
return await fn();
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
this.release();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Check if lock is currently held
|
|
65
|
+
*/
|
|
66
|
+
isLocked() {
|
|
67
|
+
return this.locked;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Get number of operations waiting for the lock
|
|
71
|
+
*/
|
|
72
|
+
getQueueLength() {
|
|
73
|
+
return this.queue.length;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Message buffering system for storing messages during disconnections
|
|
3
3
|
*/
|
|
4
|
-
import { BufferedMessage } from '
|
|
4
|
+
import { BufferedMessage } from '../connections/events.js';
|
|
5
5
|
export interface MessageBufferConfig {
|
|
6
6
|
maxSize: number;
|
|
7
7
|
maxAge: number;
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xtr-dev/rondevu-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.1",
|
|
4
4
|
"description": "TypeScript client for Rondevu with durable WebRTC connections, automatic reconnection, and message queuing",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "dist/index.js",
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
6
|
+
"main": "dist/core/index.js",
|
|
7
|
+
"types": "dist/core/index.d.ts",
|
|
8
8
|
"scripts": {
|
|
9
9
|
"build": "tsc",
|
|
10
10
|
"typecheck": "tsc --noEmit",
|
package/dist/index.d.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @xtr-dev/rondevu-client
|
|
3
|
-
* WebRTC peer signaling client
|
|
4
|
-
*/
|
|
5
|
-
export { Rondevu, RondevuError, NetworkError, ValidationError, ConnectionError } from './rondevu.js';
|
|
6
|
-
export { RondevuAPI } from './api.js';
|
|
7
|
-
export { RpcBatcher } from './rpc-batcher.js';
|
|
8
|
-
export { RondevuConnection } from './connection.js';
|
|
9
|
-
export { OffererConnection } from './offerer-connection.js';
|
|
10
|
-
export { AnswererConnection } from './answerer-connection.js';
|
|
11
|
-
export { ExponentialBackoff } from './exponential-backoff.js';
|
|
12
|
-
export { MessageBuffer } from './message-buffer.js';
|
|
13
|
-
export { WebCryptoAdapter } from './web-crypto-adapter.js';
|
|
14
|
-
export { NodeCryptoAdapter } from './node-crypto-adapter.js';
|
|
15
|
-
export type { Signaler, Binnable, } from './types.js';
|
|
16
|
-
export type { Keypair, OfferRequest, ServiceRequest, Service, ServiceOffer, IceCandidate, } from './api.js';
|
|
17
|
-
export type { RondevuOptions, PublishServiceOptions, ConnectToServiceOptions, ConnectionContext, OfferContext, OfferFactory, ActiveOffer, FindServiceOptions, ServiceResult, PaginatedServiceResult } from './rondevu.js';
|
|
18
|
-
export type { CryptoAdapter } from './crypto-adapter.js';
|
|
19
|
-
export type { ConnectionConfig, } from './connection-config.js';
|
|
20
|
-
export type { ConnectionState, BufferedMessage, ReconnectInfo, StateChangeInfo, ConnectionEventMap, ConnectionEventName, ConnectionEventArgs, } from './connection-events.js';
|
|
21
|
-
export type { OffererOptions, } from './offerer-connection.js';
|
|
22
|
-
export type { AnswererOptions, } from './answerer-connection.js';
|
package/dist/index.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @xtr-dev/rondevu-client
|
|
3
|
-
* WebRTC peer signaling client
|
|
4
|
-
*/
|
|
5
|
-
export { Rondevu, RondevuError, NetworkError, ValidationError, ConnectionError } from './rondevu.js';
|
|
6
|
-
export { RondevuAPI } from './api.js';
|
|
7
|
-
export { RpcBatcher } from './rpc-batcher.js';
|
|
8
|
-
// Export connection classes
|
|
9
|
-
export { RondevuConnection } from './connection.js';
|
|
10
|
-
export { OffererConnection } from './offerer-connection.js';
|
|
11
|
-
export { AnswererConnection } from './answerer-connection.js';
|
|
12
|
-
// Export utilities
|
|
13
|
-
export { ExponentialBackoff } from './exponential-backoff.js';
|
|
14
|
-
export { MessageBuffer } from './message-buffer.js';
|
|
15
|
-
// Export crypto adapters
|
|
16
|
-
export { WebCryptoAdapter } from './web-crypto-adapter.js';
|
|
17
|
-
export { NodeCryptoAdapter } from './node-crypto-adapter.js';
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { Signaler, Binnable } from './types.js';
|
|
2
|
-
import { Rondevu } from './rondevu.js';
|
|
3
|
-
export interface PollingConfig {
|
|
4
|
-
initialInterval?: number;
|
|
5
|
-
maxInterval?: number;
|
|
6
|
-
backoffMultiplier?: number;
|
|
7
|
-
maxRetries?: number;
|
|
8
|
-
jitter?: boolean;
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* RondevuSignaler - Handles WebRTC signaling via Rondevu service
|
|
12
|
-
*
|
|
13
|
-
* Manages offer/answer exchange and ICE candidate polling for establishing
|
|
14
|
-
* WebRTC connections through the Rondevu signaling server.
|
|
15
|
-
*
|
|
16
|
-
* Supports configurable polling with exponential backoff and jitter to reduce
|
|
17
|
-
* server load and prevent thundering herd issues.
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```typescript
|
|
21
|
-
* const signaler = new RondevuSignaler(
|
|
22
|
-
* rondevuService,
|
|
23
|
-
* 'chat.app@1.0.0',
|
|
24
|
-
* 'peer-username',
|
|
25
|
-
* { initialInterval: 500, maxInterval: 5000, jitter: true }
|
|
26
|
-
* )
|
|
27
|
-
*
|
|
28
|
-
* // For offerer:
|
|
29
|
-
* await signaler.setOffer(offer)
|
|
30
|
-
* signaler.addAnswerListener(answer => {
|
|
31
|
-
* // Handle remote answer
|
|
32
|
-
* })
|
|
33
|
-
*
|
|
34
|
-
* // For answerer:
|
|
35
|
-
* signaler.addOfferListener(offer => {
|
|
36
|
-
* // Handle remote offer
|
|
37
|
-
* })
|
|
38
|
-
* await signaler.setAnswer(answer)
|
|
39
|
-
* ```
|
|
40
|
-
*/
|
|
41
|
-
export declare class RondevuSignaler implements Signaler {
|
|
42
|
-
private readonly rondevu;
|
|
43
|
-
private readonly service;
|
|
44
|
-
private readonly host?;
|
|
45
|
-
private offerId;
|
|
46
|
-
private serviceFqn;
|
|
47
|
-
private offerListeners;
|
|
48
|
-
private answerListeners;
|
|
49
|
-
private iceListeners;
|
|
50
|
-
private pollingTimeout;
|
|
51
|
-
private icePollingTimeout;
|
|
52
|
-
private lastPollTimestamp;
|
|
53
|
-
private isPolling;
|
|
54
|
-
private isOfferer;
|
|
55
|
-
private pollingConfig;
|
|
56
|
-
constructor(rondevu: Rondevu, service: string, host?: string | undefined, pollingConfig?: PollingConfig);
|
|
57
|
-
/**
|
|
58
|
-
* Publish an offer as a service
|
|
59
|
-
* Used by the offerer to make their offer available
|
|
60
|
-
*/
|
|
61
|
-
setOffer(offer: RTCSessionDescriptionInit): Promise<void>;
|
|
62
|
-
/**
|
|
63
|
-
* Send an answer to the offerer
|
|
64
|
-
* Used by the answerer to respond to an offer
|
|
65
|
-
*/
|
|
66
|
-
setAnswer(answer: RTCSessionDescriptionInit): Promise<void>;
|
|
67
|
-
/**
|
|
68
|
-
* Listen for incoming offers
|
|
69
|
-
* Used by the answerer to receive offers from the offerer
|
|
70
|
-
*/
|
|
71
|
-
addOfferListener(callback: (offer: RTCSessionDescriptionInit) => void): Binnable;
|
|
72
|
-
/**
|
|
73
|
-
* Listen for incoming answers
|
|
74
|
-
* Used by the offerer to receive the answer from the answerer
|
|
75
|
-
*/
|
|
76
|
-
addAnswerListener(callback: (answer: RTCSessionDescriptionInit) => void): Binnable;
|
|
77
|
-
/**
|
|
78
|
-
* Send an ICE candidate to the remote peer
|
|
79
|
-
*/
|
|
80
|
-
addIceCandidate(candidate: RTCIceCandidate): Promise<void>;
|
|
81
|
-
/**
|
|
82
|
-
* Listen for ICE candidates from the remote peer
|
|
83
|
-
*/
|
|
84
|
-
addListener(callback: (candidate: RTCIceCandidate) => void): Binnable;
|
|
85
|
-
/**
|
|
86
|
-
* Search for an offer from the host
|
|
87
|
-
* Used by the answerer to find the offerer's service
|
|
88
|
-
*/
|
|
89
|
-
private searchForOffer;
|
|
90
|
-
/**
|
|
91
|
-
* Start combined polling for answers and ICE candidates (offerer side)
|
|
92
|
-
* Uses poll() for efficient batch polling
|
|
93
|
-
*/
|
|
94
|
-
private startPolling;
|
|
95
|
-
/**
|
|
96
|
-
* Stop combined polling
|
|
97
|
-
*/
|
|
98
|
-
private stopPolling;
|
|
99
|
-
/**
|
|
100
|
-
* Start polling for ICE candidates (answerer side only)
|
|
101
|
-
* Answerers use the separate endpoint since they don't have offers to poll
|
|
102
|
-
*/
|
|
103
|
-
private startIcePolling;
|
|
104
|
-
/**
|
|
105
|
-
* Stop polling for ICE candidates
|
|
106
|
-
*/
|
|
107
|
-
private stopIcePolling;
|
|
108
|
-
/**
|
|
109
|
-
* Stop all polling and cleanup
|
|
110
|
-
*/
|
|
111
|
-
dispose(): void;
|
|
112
|
-
}
|