@xtr-dev/rondevu-client 0.21.9 → 0.21.11
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/dist/api/client.d.ts +14 -0
- package/dist/api/client.js +17 -0
- package/dist/connections/base.js +3 -0
- package/dist/connections/config.d.ts +1 -1
- package/dist/connections/config.js +1 -1
- package/dist/core/offer-pool.js +17 -2
- package/dist/core/polling-manager.js +6 -0
- package/dist/core/rondevu.d.ts +20 -0
- package/dist/core/rondevu.js +20 -0
- package/package.json +1 -1
package/dist/api/client.d.ts
CHANGED
|
@@ -18,6 +18,13 @@ export interface DiscoverRequest {
|
|
|
18
18
|
limit?: number;
|
|
19
19
|
offset?: number;
|
|
20
20
|
}
|
|
21
|
+
export interface CountOffersByTagsRequest {
|
|
22
|
+
tags: string[];
|
|
23
|
+
unique?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface CountOffersByTagsResponse {
|
|
26
|
+
counts: Record<string, number>;
|
|
27
|
+
}
|
|
21
28
|
export interface TaggedOffer {
|
|
22
29
|
offerId: string;
|
|
23
30
|
publicKey: string;
|
|
@@ -127,6 +134,13 @@ export declare class RondevuAPI {
|
|
|
127
134
|
* @returns Paginated response if limit provided, single offer if not
|
|
128
135
|
*/
|
|
129
136
|
discover(request: DiscoverRequest): Promise<DiscoverResponse | TaggedOffer>;
|
|
137
|
+
/**
|
|
138
|
+
* Count available offers by tags
|
|
139
|
+
* Returns the count of available (unanswered, non-expired) offers for each tag
|
|
140
|
+
* @param request - Request with tags to count and optional unique flag
|
|
141
|
+
* @returns Object mapping each tag to its count
|
|
142
|
+
*/
|
|
143
|
+
countOffersByTags(request: CountOffersByTagsRequest): Promise<CountOffersByTagsResponse>;
|
|
130
144
|
/**
|
|
131
145
|
* Delete an offer by ID
|
|
132
146
|
*/
|
package/dist/api/client.js
CHANGED
|
@@ -211,6 +211,23 @@ export class RondevuAPI {
|
|
|
211
211
|
const authHeaders = await this.generateAuthHeaders(rpcRequest);
|
|
212
212
|
return await this.rpc(rpcRequest, authHeaders);
|
|
213
213
|
}
|
|
214
|
+
/**
|
|
215
|
+
* Count available offers by tags
|
|
216
|
+
* Returns the count of available (unanswered, non-expired) offers for each tag
|
|
217
|
+
* @param request - Request with tags to count and optional unique flag
|
|
218
|
+
* @returns Object mapping each tag to its count
|
|
219
|
+
*/
|
|
220
|
+
async countOffersByTags(request) {
|
|
221
|
+
const rpcRequest = {
|
|
222
|
+
method: 'countOffersByTags',
|
|
223
|
+
params: {
|
|
224
|
+
tags: request.tags,
|
|
225
|
+
...(request.unique !== undefined && { unique: request.unique }),
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
const authHeaders = await this.generateAuthHeaders(rpcRequest);
|
|
229
|
+
return await this.rpc(rpcRequest, authHeaders);
|
|
230
|
+
}
|
|
214
231
|
/**
|
|
215
232
|
* Delete an offer by ID
|
|
216
233
|
*/
|
package/dist/connections/base.js
CHANGED
|
@@ -123,6 +123,9 @@ export class RondevuConnection extends EventEmitter {
|
|
|
123
123
|
if (this.state === ConnectionState.SIGNALING) {
|
|
124
124
|
this.transitionTo(ConnectionState.CHECKING, 'ICE checking started');
|
|
125
125
|
}
|
|
126
|
+
// ICE is progressing - clear the connection timeout to give ICE time to complete
|
|
127
|
+
// The timeout was for signaling, ICE checking shows progress is being made
|
|
128
|
+
this.clearConnectionTimeout();
|
|
126
129
|
// Note: ICE candidate polling is handled by PollingManager
|
|
127
130
|
// Candidates are received via handleRemoteIceCandidates()
|
|
128
131
|
break;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Advanced options use sensible defaults.
|
|
7
7
|
*/
|
|
8
8
|
export interface ConnectionOptions {
|
|
9
|
-
/** Maximum time to wait for connection (ms). Default:
|
|
9
|
+
/** Maximum time to wait for connection (ms). Default: 30000 */
|
|
10
10
|
timeout?: number;
|
|
11
11
|
/** Enable automatic reconnection on failures. Default: true */
|
|
12
12
|
reconnect?: boolean;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const DEFAULT_CONNECTION_CONFIG = {
|
|
5
5
|
// Timeouts
|
|
6
|
-
connectionTimeout:
|
|
6
|
+
connectionTimeout: 30000, // 30 seconds (should be less than server offer TTL)
|
|
7
7
|
iceGatheringTimeout: 10000, // 10 seconds
|
|
8
8
|
// Reconnection
|
|
9
9
|
reconnectEnabled: true,
|
package/dist/core/offer-pool.js
CHANGED
|
@@ -233,11 +233,15 @@ export class OfferPool extends EventEmitter {
|
|
|
233
233
|
return;
|
|
234
234
|
}
|
|
235
235
|
this.debug(`Proceeding with rotation for offer ${currentOfferId}`);
|
|
236
|
+
// Track new RTCPeerConnection for cleanup if rotation fails
|
|
237
|
+
let newPcForCleanup = null;
|
|
236
238
|
try {
|
|
237
239
|
// Create new offer and rebind existing connection
|
|
238
240
|
const { newOfferId, pc, dc } = await this.createNewOfferForRotation();
|
|
239
|
-
//
|
|
241
|
+
newPcForCleanup = pc; // Track for cleanup if rebind fails
|
|
242
|
+
// Rebind the connection to new offer (this closes the old pc)
|
|
240
243
|
await connection.rebindToOffer(newOfferId, pc, dc);
|
|
244
|
+
newPcForCleanup = null; // Rebind succeeded, pc is now managed by connection
|
|
241
245
|
// Update map: remove old offerId, add new offerId with same connection
|
|
242
246
|
this.activeConnections.delete(currentOfferId);
|
|
243
247
|
this.activeConnections.set(newOfferId, connection);
|
|
@@ -247,8 +251,19 @@ export class OfferPool extends EventEmitter {
|
|
|
247
251
|
this.debug(`Connection rotated: ${currentOfferId} → ${newOfferId}`);
|
|
248
252
|
}
|
|
249
253
|
catch (rotationError) {
|
|
250
|
-
// If rotation fails,
|
|
254
|
+
// If rotation fails, clean up all RTCPeerConnections to prevent resource leak
|
|
251
255
|
this.debug(`Rotation failed for ${currentOfferId}:`, rotationError);
|
|
256
|
+
// Close the new pc if it was created but rebind failed
|
|
257
|
+
if (newPcForCleanup) {
|
|
258
|
+
try {
|
|
259
|
+
newPcForCleanup.close();
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
// Ignore close errors
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// Close the old connection (closes its RTCPeerConnection)
|
|
266
|
+
connection.close();
|
|
252
267
|
this.activeConnections.delete(currentOfferId);
|
|
253
268
|
this.emit('offer:failed', currentOfferId, error);
|
|
254
269
|
this.fillOffers(); // Create replacement
|
|
@@ -30,6 +30,7 @@ export class PollingManager extends EventEmitter {
|
|
|
30
30
|
this.debug('Already running');
|
|
31
31
|
return;
|
|
32
32
|
}
|
|
33
|
+
console.log('[PollingManager] Starting polling manager');
|
|
33
34
|
this.debug('Starting polling manager');
|
|
34
35
|
this.running = true;
|
|
35
36
|
// Poll immediately
|
|
@@ -74,6 +75,11 @@ export class PollingManager extends EventEmitter {
|
|
|
74
75
|
return;
|
|
75
76
|
try {
|
|
76
77
|
const result = await this.api.poll(this.lastPollTimestamp);
|
|
78
|
+
// Log poll results for debugging (only when there are results)
|
|
79
|
+
const iceCount = Object.values(result.iceCandidates).reduce((sum, candidates) => sum + candidates.length, 0);
|
|
80
|
+
if (result.answers.length > 0 || iceCount > 0) {
|
|
81
|
+
console.log(`[PollingManager] Poll: ${result.answers.length} answers, ${iceCount} ICE (since: ${this.lastPollTimestamp})`);
|
|
82
|
+
}
|
|
77
83
|
// Emit answer events
|
|
78
84
|
for (const answer of result.answers) {
|
|
79
85
|
this.debug(`Poll: answer for ${answer.offerId}`);
|
package/dist/core/rondevu.d.ts
CHANGED
|
@@ -255,6 +255,26 @@ export declare class Rondevu extends EventEmitter {
|
|
|
255
255
|
* ```
|
|
256
256
|
*/
|
|
257
257
|
discover(tags: string[], options?: DiscoverOptions): Promise<DiscoverResult>;
|
|
258
|
+
/**
|
|
259
|
+
* Count available offers by tags
|
|
260
|
+
* Returns the count of available (unanswered, non-expired) offers for each tag
|
|
261
|
+
*
|
|
262
|
+
* @param tags - Tags to count offers for
|
|
263
|
+
* @param unique - If true, count unique public keys instead of total offers
|
|
264
|
+
* @returns Object mapping each tag to its count
|
|
265
|
+
*
|
|
266
|
+
* @example
|
|
267
|
+
* ```typescript
|
|
268
|
+
* const counts = await rondevu.countOffersByTags(['chat', 'video'])
|
|
269
|
+
* console.log(counts.counts) // { chat: 5, video: 3 }
|
|
270
|
+
*
|
|
271
|
+
* // Count unique peers instead of total offers
|
|
272
|
+
* const uniquePeers = await rondevu.countOffersByTags(['chat'], true)
|
|
273
|
+
* ```
|
|
274
|
+
*/
|
|
275
|
+
countOffersByTags(tags: string[], unique?: boolean): Promise<{
|
|
276
|
+
counts: Record<string, number>;
|
|
277
|
+
}>;
|
|
258
278
|
/**
|
|
259
279
|
* Post answer SDP to specific offer
|
|
260
280
|
*/
|
package/dist/core/rondevu.js
CHANGED
|
@@ -446,6 +446,26 @@ export class Rondevu extends EventEmitter {
|
|
|
446
446
|
// Always pass limit to ensure we get DiscoverResponse (paginated mode)
|
|
447
447
|
return (await this.api.discover({ tags, limit, offset }));
|
|
448
448
|
}
|
|
449
|
+
/**
|
|
450
|
+
* Count available offers by tags
|
|
451
|
+
* Returns the count of available (unanswered, non-expired) offers for each tag
|
|
452
|
+
*
|
|
453
|
+
* @param tags - Tags to count offers for
|
|
454
|
+
* @param unique - If true, count unique public keys instead of total offers
|
|
455
|
+
* @returns Object mapping each tag to its count
|
|
456
|
+
*
|
|
457
|
+
* @example
|
|
458
|
+
* ```typescript
|
|
459
|
+
* const counts = await rondevu.countOffersByTags(['chat', 'video'])
|
|
460
|
+
* console.log(counts.counts) // { chat: 5, video: 3 }
|
|
461
|
+
*
|
|
462
|
+
* // Count unique peers instead of total offers
|
|
463
|
+
* const uniquePeers = await rondevu.countOffersByTags(['chat'], true)
|
|
464
|
+
* ```
|
|
465
|
+
*/
|
|
466
|
+
async countOffersByTags(tags, unique) {
|
|
467
|
+
return await this.api.countOffersByTags({ tags, unique });
|
|
468
|
+
}
|
|
449
469
|
// ============================================
|
|
450
470
|
// WebRTC Signaling
|
|
451
471
|
// ============================================
|
package/package.json
CHANGED