@xtr-dev/rondevu-client 0.21.13 → 0.21.16
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/batcher.d.ts +24 -22
- package/dist/api/batcher.js +33 -55
- package/dist/api/client.d.ts +13 -3
- package/dist/api/client.js +40 -26
- package/dist/connections/answerer.d.ts +3 -0
- package/dist/connections/answerer.js +6 -0
- package/dist/core/offer-pool.d.ts +3 -3
- package/dist/core/offer-pool.js +13 -3
- package/dist/core/peer.d.ts +7 -0
- package/dist/core/peer.js +6 -1
- package/dist/core/polling-manager.js +0 -5
- package/package.json +2 -2
package/dist/api/batcher.d.ts
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* RPC Request Batcher with throttling
|
|
3
3
|
*
|
|
4
|
-
* Collects RPC requests over a short time window and sends them
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* authenticated requests are sent individually while unauthenticated
|
|
8
|
-
* requests can be truly batched together.
|
|
4
|
+
* Collects RPC requests over a short time window and sends them in a single
|
|
5
|
+
* HTTP request. Each request includes its own auth credentials in the JSON body,
|
|
6
|
+
* allowing true batching of authenticated requests.
|
|
9
7
|
*/
|
|
10
8
|
export interface RpcRequest {
|
|
11
9
|
method: string;
|
|
12
10
|
params?: any;
|
|
13
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Per-request authentication credentials
|
|
14
|
+
*/
|
|
15
|
+
export interface RequestAuth {
|
|
16
|
+
publicKey: string;
|
|
17
|
+
timestamp: number;
|
|
18
|
+
nonce: string;
|
|
19
|
+
signature: string;
|
|
20
|
+
}
|
|
14
21
|
export interface RpcResponse {
|
|
15
22
|
success: boolean;
|
|
16
23
|
result?: any;
|
|
@@ -20,12 +27,16 @@ export interface RpcResponse {
|
|
|
20
27
|
export interface BatcherOptions {
|
|
21
28
|
/** Delay in ms before flushing queued requests (default: 10) */
|
|
22
29
|
delay?: number;
|
|
23
|
-
/** Maximum batch size
|
|
30
|
+
/** Maximum batch size (default: 50) */
|
|
24
31
|
maxBatchSize?: number;
|
|
25
32
|
}
|
|
26
33
|
/**
|
|
27
34
|
* RpcBatcher - Batches RPC requests with throttling
|
|
28
35
|
*
|
|
36
|
+
* All requests (authenticated and unauthenticated) are batched together
|
|
37
|
+
* into a single HTTP request. Auth credentials are included per-request
|
|
38
|
+
* in the JSON body.
|
|
39
|
+
*
|
|
29
40
|
* @example
|
|
30
41
|
* ```typescript
|
|
31
42
|
* const batcher = new RpcBatcher('https://api.example.com', {
|
|
@@ -33,10 +44,10 @@ export interface BatcherOptions {
|
|
|
33
44
|
* maxBatchSize: 50
|
|
34
45
|
* })
|
|
35
46
|
*
|
|
36
|
-
* // Requests made within the delay window are batched
|
|
47
|
+
* // Requests made within the delay window are batched into ONE HTTP call
|
|
37
48
|
* const [result1, result2] = await Promise.all([
|
|
38
|
-
* batcher.add({ method: '
|
|
39
|
-
* batcher.add({ method: '
|
|
49
|
+
* batcher.add({ method: 'publish', params: {...} }, auth1),
|
|
50
|
+
* batcher.add({ method: 'discover', params: {...} }, auth2)
|
|
40
51
|
* ])
|
|
41
52
|
* ```
|
|
42
53
|
*/
|
|
@@ -50,10 +61,10 @@ export declare class RpcBatcher {
|
|
|
50
61
|
/**
|
|
51
62
|
* Add a request to the batch queue
|
|
52
63
|
* @param request - The RPC request
|
|
53
|
-
* @param
|
|
64
|
+
* @param auth - Per-request auth credentials, null for unauthenticated
|
|
54
65
|
* @returns Promise that resolves with the request result
|
|
55
66
|
*/
|
|
56
|
-
add(request: RpcRequest,
|
|
67
|
+
add(request: RpcRequest, auth: RequestAuth | null): Promise<any>;
|
|
57
68
|
/**
|
|
58
69
|
* Schedule a flush after the delay
|
|
59
70
|
*/
|
|
@@ -63,17 +74,8 @@ export declare class RpcBatcher {
|
|
|
63
74
|
*/
|
|
64
75
|
private flush;
|
|
65
76
|
/**
|
|
66
|
-
*
|
|
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
|
+
* Send a batch of requests in a single HTTP call
|
|
78
|
+
* Each request includes its own auth credentials in the JSON body
|
|
77
79
|
*/
|
|
78
80
|
private sendBatch;
|
|
79
81
|
/**
|
package/dist/api/batcher.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* RPC Request Batcher with throttling
|
|
3
3
|
*
|
|
4
|
-
* Collects RPC requests over a short time window and sends them
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* authenticated requests are sent individually while unauthenticated
|
|
8
|
-
* requests can be truly batched together.
|
|
4
|
+
* Collects RPC requests over a short time window and sends them in a single
|
|
5
|
+
* HTTP request. Each request includes its own auth credentials in the JSON body,
|
|
6
|
+
* allowing true batching of authenticated requests.
|
|
9
7
|
*/
|
|
10
8
|
/**
|
|
11
9
|
* RpcBatcher - Batches RPC requests with throttling
|
|
12
10
|
*
|
|
11
|
+
* All requests (authenticated and unauthenticated) are batched together
|
|
12
|
+
* into a single HTTP request. Auth credentials are included per-request
|
|
13
|
+
* in the JSON body.
|
|
14
|
+
*
|
|
13
15
|
* @example
|
|
14
16
|
* ```typescript
|
|
15
17
|
* const batcher = new RpcBatcher('https://api.example.com', {
|
|
@@ -17,10 +19,10 @@
|
|
|
17
19
|
* maxBatchSize: 50
|
|
18
20
|
* })
|
|
19
21
|
*
|
|
20
|
-
* // Requests made within the delay window are batched
|
|
22
|
+
* // Requests made within the delay window are batched into ONE HTTP call
|
|
21
23
|
* const [result1, result2] = await Promise.all([
|
|
22
|
-
* batcher.add({ method: '
|
|
23
|
-
* batcher.add({ method: '
|
|
24
|
+
* batcher.add({ method: 'publish', params: {...} }, auth1),
|
|
25
|
+
* batcher.add({ method: 'discover', params: {...} }, auth2)
|
|
24
26
|
* ])
|
|
25
27
|
* ```
|
|
26
28
|
*/
|
|
@@ -35,12 +37,12 @@ export class RpcBatcher {
|
|
|
35
37
|
/**
|
|
36
38
|
* Add a request to the batch queue
|
|
37
39
|
* @param request - The RPC request
|
|
38
|
-
* @param
|
|
40
|
+
* @param auth - Per-request auth credentials, null for unauthenticated
|
|
39
41
|
* @returns Promise that resolves with the request result
|
|
40
42
|
*/
|
|
41
|
-
add(request,
|
|
43
|
+
add(request, auth) {
|
|
42
44
|
return new Promise((resolve, reject) => {
|
|
43
|
-
this.queue.push({ request,
|
|
45
|
+
this.queue.push({ request, auth, resolve, reject });
|
|
44
46
|
this.scheduleFlush();
|
|
45
47
|
});
|
|
46
48
|
}
|
|
@@ -63,59 +65,35 @@ export class RpcBatcher {
|
|
|
63
65
|
return;
|
|
64
66
|
const items = this.queue;
|
|
65
67
|
this.queue = [];
|
|
66
|
-
//
|
|
67
|
-
const unauthenticated = [];
|
|
68
|
-
const authenticated = [];
|
|
69
|
-
for (const item of items) {
|
|
70
|
-
if (item.authHeaders) {
|
|
71
|
-
authenticated.push(item);
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
unauthenticated.push(item);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
// Process unauthenticated requests in batches
|
|
78
|
-
await this.processUnauthenticatedBatches(unauthenticated);
|
|
79
|
-
// Process authenticated requests individually (each needs unique signature)
|
|
80
|
-
await this.processAuthenticatedRequests(authenticated);
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Process unauthenticated requests in batches
|
|
84
|
-
*/
|
|
85
|
-
async processUnauthenticatedBatches(items) {
|
|
86
|
-
if (items.length === 0)
|
|
87
|
-
return;
|
|
88
|
-
// Split into chunks of maxBatchSize
|
|
68
|
+
// Split into chunks of maxBatchSize and send each chunk
|
|
89
69
|
for (let i = 0; i < items.length; i += this.maxBatchSize) {
|
|
90
70
|
const chunk = items.slice(i, i + this.maxBatchSize);
|
|
91
|
-
await this.sendBatch(chunk
|
|
71
|
+
await this.sendBatch(chunk);
|
|
92
72
|
}
|
|
93
73
|
}
|
|
94
74
|
/**
|
|
95
|
-
*
|
|
96
|
-
* Each
|
|
97
|
-
* the signature covers the specific method+params
|
|
98
|
-
*/
|
|
99
|
-
async processAuthenticatedRequests(items) {
|
|
100
|
-
// Send all authenticated requests in parallel, each as its own batch of 1
|
|
101
|
-
await Promise.all(items.map(item => this.sendBatch([item], item.authHeaders)));
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Send a batch of requests
|
|
75
|
+
* Send a batch of requests in a single HTTP call
|
|
76
|
+
* Each request includes its own auth credentials in the JSON body
|
|
105
77
|
*/
|
|
106
|
-
async sendBatch(items
|
|
78
|
+
async sendBatch(items) {
|
|
107
79
|
try {
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
80
|
+
// Build wire requests with per-request auth
|
|
81
|
+
const wireRequests = items.map(item => {
|
|
82
|
+
const wireReq = {
|
|
83
|
+
method: item.request.method,
|
|
84
|
+
params: item.request.params,
|
|
85
|
+
};
|
|
86
|
+
if (item.auth) {
|
|
87
|
+
wireReq.auth = item.auth;
|
|
88
|
+
}
|
|
89
|
+
return wireReq;
|
|
90
|
+
});
|
|
115
91
|
const response = await fetch(`${this.baseUrl}/rpc`, {
|
|
116
92
|
method: 'POST',
|
|
117
|
-
headers
|
|
118
|
-
|
|
93
|
+
headers: {
|
|
94
|
+
'Content-Type': 'application/json',
|
|
95
|
+
},
|
|
96
|
+
body: JSON.stringify(wireRequests),
|
|
119
97
|
});
|
|
120
98
|
if (!response.ok) {
|
|
121
99
|
const error = new Error(`HTTP ${response.status}: ${response.statusText}`);
|
package/dist/api/client.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { CryptoAdapter, KeyPair } from '../crypto/adapter.js';
|
|
5
5
|
import { BatcherOptions } from './batcher.js';
|
|
6
6
|
export type { KeyPair } from '../crypto/adapter.js';
|
|
7
|
-
export type { BatcherOptions } from './batcher.js';
|
|
7
|
+
export type { BatcherOptions, RequestAuth } from './batcher.js';
|
|
8
8
|
export interface OfferRequest {
|
|
9
9
|
sdp: string;
|
|
10
10
|
}
|
|
@@ -96,7 +96,7 @@ export declare class RondevuAPI {
|
|
|
96
96
|
*/
|
|
97
97
|
private generateNonce;
|
|
98
98
|
/**
|
|
99
|
-
* Generate authentication
|
|
99
|
+
* Generate per-request authentication credentials
|
|
100
100
|
* Uses Ed25519 signature with nonce for replay protection
|
|
101
101
|
*
|
|
102
102
|
* Security notes:
|
|
@@ -110,10 +110,11 @@ export declare class RondevuAPI {
|
|
|
110
110
|
* - Each nonce can only be used once within the timestamp validity window
|
|
111
111
|
* - Server maintains nonce cache with expiration matching timestamp window
|
|
112
112
|
*/
|
|
113
|
-
private
|
|
113
|
+
private generateAuth;
|
|
114
114
|
/**
|
|
115
115
|
* Execute RPC call via batcher
|
|
116
116
|
* Requests are batched with throttling for efficiency
|
|
117
|
+
* All requests within the batch delay window are sent in a single HTTP call
|
|
117
118
|
*/
|
|
118
119
|
private rpc;
|
|
119
120
|
/**
|
|
@@ -147,6 +148,15 @@ export declare class RondevuAPI {
|
|
|
147
148
|
deleteOffer(offerId: string): Promise<{
|
|
148
149
|
success: boolean;
|
|
149
150
|
}>;
|
|
151
|
+
/**
|
|
152
|
+
* Update tags for all offers owned by this identity
|
|
153
|
+
* @param tags New tags to set on all offers
|
|
154
|
+
* @returns Number of offers updated
|
|
155
|
+
*/
|
|
156
|
+
updateOfferTags(tags: string[]): Promise<{
|
|
157
|
+
success: boolean;
|
|
158
|
+
count: number;
|
|
159
|
+
}>;
|
|
150
160
|
/**
|
|
151
161
|
* Answer an offer
|
|
152
162
|
* @param offerId The offer ID to answer
|
package/dist/api/client.js
CHANGED
|
@@ -128,7 +128,7 @@ export class RondevuAPI {
|
|
|
128
128
|
return this.crypto.bytesToHex(randomBytes);
|
|
129
129
|
}
|
|
130
130
|
/**
|
|
131
|
-
* Generate authentication
|
|
131
|
+
* Generate per-request authentication credentials
|
|
132
132
|
* Uses Ed25519 signature with nonce for replay protection
|
|
133
133
|
*
|
|
134
134
|
* Security notes:
|
|
@@ -142,25 +142,26 @@ export class RondevuAPI {
|
|
|
142
142
|
* - Each nonce can only be used once within the timestamp validity window
|
|
143
143
|
* - Server maintains nonce cache with expiration matching timestamp window
|
|
144
144
|
*/
|
|
145
|
-
async
|
|
145
|
+
async generateAuth(request) {
|
|
146
146
|
const timestamp = Date.now();
|
|
147
147
|
const nonce = this.generateNonce();
|
|
148
148
|
// Build message and generate Ed25519 signature
|
|
149
149
|
const message = this.buildSignatureMessage(timestamp, nonce, request.method, request.params);
|
|
150
150
|
const signature = await this.crypto.signMessage(this.keyPair.privateKey, message);
|
|
151
151
|
return {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
152
|
+
publicKey: this.keyPair.publicKey,
|
|
153
|
+
timestamp,
|
|
154
|
+
nonce,
|
|
155
|
+
signature,
|
|
156
156
|
};
|
|
157
157
|
}
|
|
158
158
|
/**
|
|
159
159
|
* Execute RPC call via batcher
|
|
160
160
|
* Requests are batched with throttling for efficiency
|
|
161
|
+
* All requests within the batch delay window are sent in a single HTTP call
|
|
161
162
|
*/
|
|
162
|
-
async rpc(request,
|
|
163
|
-
return this.batcher.add(request,
|
|
163
|
+
async rpc(request, auth) {
|
|
164
|
+
return this.batcher.add(request, auth);
|
|
164
165
|
}
|
|
165
166
|
// ============================================
|
|
166
167
|
// Identity Management (Ed25519 Public Key)
|
|
@@ -191,8 +192,8 @@ export class RondevuAPI {
|
|
|
191
192
|
ttl: request.ttl,
|
|
192
193
|
},
|
|
193
194
|
};
|
|
194
|
-
const
|
|
195
|
-
return await this.rpc(rpcRequest,
|
|
195
|
+
const auth = await this.generateAuth(rpcRequest);
|
|
196
|
+
return await this.rpc(rpcRequest, auth);
|
|
196
197
|
}
|
|
197
198
|
/**
|
|
198
199
|
* Discover offers by tags
|
|
@@ -208,8 +209,8 @@ export class RondevuAPI {
|
|
|
208
209
|
offset: request.offset,
|
|
209
210
|
},
|
|
210
211
|
};
|
|
211
|
-
const
|
|
212
|
-
return await this.rpc(rpcRequest,
|
|
212
|
+
const auth = await this.generateAuth(rpcRequest);
|
|
213
|
+
return await this.rpc(rpcRequest, auth);
|
|
213
214
|
}
|
|
214
215
|
/**
|
|
215
216
|
* Count available offers by tags
|
|
@@ -225,8 +226,8 @@ export class RondevuAPI {
|
|
|
225
226
|
...(request.unique !== undefined && { unique: request.unique }),
|
|
226
227
|
},
|
|
227
228
|
};
|
|
228
|
-
const
|
|
229
|
-
return await this.rpc(rpcRequest,
|
|
229
|
+
const auth = await this.generateAuth(rpcRequest);
|
|
230
|
+
return await this.rpc(rpcRequest, auth);
|
|
230
231
|
}
|
|
231
232
|
/**
|
|
232
233
|
* Delete an offer by ID
|
|
@@ -236,8 +237,21 @@ export class RondevuAPI {
|
|
|
236
237
|
method: 'deleteOffer',
|
|
237
238
|
params: { offerId },
|
|
238
239
|
};
|
|
239
|
-
const
|
|
240
|
-
return await this.rpc(request,
|
|
240
|
+
const auth = await this.generateAuth(request);
|
|
241
|
+
return await this.rpc(request, auth);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Update tags for all offers owned by this identity
|
|
245
|
+
* @param tags New tags to set on all offers
|
|
246
|
+
* @returns Number of offers updated
|
|
247
|
+
*/
|
|
248
|
+
async updateOfferTags(tags) {
|
|
249
|
+
const request = {
|
|
250
|
+
method: 'updateOfferTags',
|
|
251
|
+
params: { tags },
|
|
252
|
+
};
|
|
253
|
+
const auth = await this.generateAuth(request);
|
|
254
|
+
return await this.rpc(request, auth);
|
|
241
255
|
}
|
|
242
256
|
// ============================================
|
|
243
257
|
// WebRTC Signaling
|
|
@@ -253,8 +267,8 @@ export class RondevuAPI {
|
|
|
253
267
|
method: 'answerOffer',
|
|
254
268
|
params: { offerId, sdp, matchedTags },
|
|
255
269
|
};
|
|
256
|
-
const
|
|
257
|
-
await this.rpc(request,
|
|
270
|
+
const auth = await this.generateAuth(request);
|
|
271
|
+
await this.rpc(request, auth);
|
|
258
272
|
}
|
|
259
273
|
/**
|
|
260
274
|
* Get answer for a specific offer (offerer polls this)
|
|
@@ -265,8 +279,8 @@ export class RondevuAPI {
|
|
|
265
279
|
method: 'getOfferAnswer',
|
|
266
280
|
params: { offerId },
|
|
267
281
|
};
|
|
268
|
-
const
|
|
269
|
-
return await this.rpc(request,
|
|
282
|
+
const auth = await this.generateAuth(request);
|
|
283
|
+
return await this.rpc(request, auth);
|
|
270
284
|
}
|
|
271
285
|
catch (err) {
|
|
272
286
|
if (err.message.includes('not yet answered')) {
|
|
@@ -283,8 +297,8 @@ export class RondevuAPI {
|
|
|
283
297
|
method: 'poll',
|
|
284
298
|
params: { since },
|
|
285
299
|
};
|
|
286
|
-
const
|
|
287
|
-
return await this.rpc(request,
|
|
300
|
+
const auth = await this.generateAuth(request);
|
|
301
|
+
return await this.rpc(request, auth);
|
|
288
302
|
}
|
|
289
303
|
/**
|
|
290
304
|
* Add ICE candidates to a specific offer
|
|
@@ -294,8 +308,8 @@ export class RondevuAPI {
|
|
|
294
308
|
method: 'addIceCandidates',
|
|
295
309
|
params: { offerId, candidates },
|
|
296
310
|
};
|
|
297
|
-
const
|
|
298
|
-
return await this.rpc(request,
|
|
311
|
+
const auth = await this.generateAuth(request);
|
|
312
|
+
return await this.rpc(request, auth);
|
|
299
313
|
}
|
|
300
314
|
/**
|
|
301
315
|
* Get ICE candidates for a specific offer
|
|
@@ -305,8 +319,8 @@ export class RondevuAPI {
|
|
|
305
319
|
method: 'getIceCandidates',
|
|
306
320
|
params: { offerId, since },
|
|
307
321
|
};
|
|
308
|
-
const
|
|
309
|
-
const result = await this.rpc(request,
|
|
322
|
+
const auth = await this.generateAuth(request);
|
|
323
|
+
const result = await this.rpc(request, auth);
|
|
310
324
|
return {
|
|
311
325
|
candidates: result.candidates || [],
|
|
312
326
|
offerId: result.offerId,
|
|
@@ -15,6 +15,8 @@ export interface AnswererOptions {
|
|
|
15
15
|
webrtcAdapter?: WebRTCAdapter;
|
|
16
16
|
config?: Partial<ConnectionConfig>;
|
|
17
17
|
matchedTags?: string[];
|
|
18
|
+
/** Callback invoked when RTCPeerConnection is created, before signaling starts */
|
|
19
|
+
onPeerConnectionCreated?: (pc: RTCPeerConnection) => void;
|
|
18
20
|
}
|
|
19
21
|
/**
|
|
20
22
|
* Answerer connection - processes offers and creates answers
|
|
@@ -26,6 +28,7 @@ export declare class AnswererConnection extends RondevuConnection {
|
|
|
26
28
|
private offerId;
|
|
27
29
|
private offerSdp;
|
|
28
30
|
private matchedTags?;
|
|
31
|
+
private onPeerConnectionCreated?;
|
|
29
32
|
constructor(options: AnswererOptions);
|
|
30
33
|
/**
|
|
31
34
|
* Initialize the connection by processing offer and creating answer
|
|
@@ -15,6 +15,7 @@ export class AnswererConnection extends RondevuConnection {
|
|
|
15
15
|
this.offerId = options.offerId;
|
|
16
16
|
this.offerSdp = options.offerSdp;
|
|
17
17
|
this.matchedTags = options.matchedTags;
|
|
18
|
+
this.onPeerConnectionCreated = options.onPeerConnectionCreated;
|
|
18
19
|
}
|
|
19
20
|
/**
|
|
20
21
|
* Initialize the connection by processing offer and creating answer
|
|
@@ -25,6 +26,11 @@ export class AnswererConnection extends RondevuConnection {
|
|
|
25
26
|
this.createPeerConnection();
|
|
26
27
|
if (!this.pc)
|
|
27
28
|
throw new Error('Peer connection not created');
|
|
29
|
+
// Call the callback to allow creating negotiated data channels
|
|
30
|
+
// This must happen BEFORE signaling starts so channels exist on both sides
|
|
31
|
+
if (this.onPeerConnectionCreated) {
|
|
32
|
+
this.onPeerConnectionCreated(this.pc);
|
|
33
|
+
}
|
|
28
34
|
// Setup ondatachannel handler BEFORE setting remote description
|
|
29
35
|
// This is critical to avoid race conditions
|
|
30
36
|
this.pc.ondatachannel = event => {
|
|
@@ -72,9 +72,9 @@ export declare class OfferPool extends EventEmitter<OfferPoolEvents> {
|
|
|
72
72
|
*/
|
|
73
73
|
getOfferCount(): number;
|
|
74
74
|
/**
|
|
75
|
-
* Update tags for
|
|
76
|
-
*
|
|
77
|
-
* New offers created during fill will use the updated tags
|
|
75
|
+
* Update tags for all offers (local and server-side)
|
|
76
|
+
* Updates existing offers on the server immediately for discoverability
|
|
77
|
+
* New offers created during fill will also use the updated tags
|
|
78
78
|
*/
|
|
79
79
|
updateTags(newTags: string[]): void;
|
|
80
80
|
/**
|
package/dist/core/offer-pool.js
CHANGED
|
@@ -68,13 +68,23 @@ export class OfferPool extends EventEmitter {
|
|
|
68
68
|
return this.activeConnections.size;
|
|
69
69
|
}
|
|
70
70
|
/**
|
|
71
|
-
* Update tags for
|
|
72
|
-
*
|
|
73
|
-
* New offers created during fill will use the updated tags
|
|
71
|
+
* Update tags for all offers (local and server-side)
|
|
72
|
+
* Updates existing offers on the server immediately for discoverability
|
|
73
|
+
* New offers created during fill will also use the updated tags
|
|
74
74
|
*/
|
|
75
75
|
updateTags(newTags) {
|
|
76
76
|
this.debug(`Updating tags: ${newTags.join(', ')}`);
|
|
77
77
|
this.tags = newTags;
|
|
78
|
+
// Update tags on existing offers via server API
|
|
79
|
+
// Fire and forget - errors are logged but don't block
|
|
80
|
+
this.api
|
|
81
|
+
.updateOfferTags(newTags)
|
|
82
|
+
.then(result => {
|
|
83
|
+
this.debug(`Server updated ${result.count} offers with new tags`);
|
|
84
|
+
})
|
|
85
|
+
.catch(err => {
|
|
86
|
+
this.debug('Failed to update offer tags on server:', err);
|
|
87
|
+
});
|
|
78
88
|
}
|
|
79
89
|
/**
|
|
80
90
|
* Get current tags
|
package/dist/core/peer.d.ts
CHANGED
|
@@ -43,6 +43,12 @@ export interface PeerOptions {
|
|
|
43
43
|
rtcConfig?: RTCConfiguration;
|
|
44
44
|
/** Optional: connection behavior configuration */
|
|
45
45
|
config?: Partial<ConnectionConfig>;
|
|
46
|
+
/**
|
|
47
|
+
* Optional callback invoked when RTCPeerConnection is created.
|
|
48
|
+
* Use this to create negotiated data channels that must exist
|
|
49
|
+
* before the connection opens (e.g., control channels).
|
|
50
|
+
*/
|
|
51
|
+
onPeerConnectionCreated?: (pc: RTCPeerConnection) => void;
|
|
46
52
|
}
|
|
47
53
|
/**
|
|
48
54
|
* Internal options passed from Rondevu
|
|
@@ -92,6 +98,7 @@ export declare class Peer extends EventEmitter<PeerEventMap> {
|
|
|
92
98
|
private webrtcAdapter?;
|
|
93
99
|
private connectionConfig?;
|
|
94
100
|
private debugEnabled;
|
|
101
|
+
private onPeerConnectionCreated?;
|
|
95
102
|
private _state;
|
|
96
103
|
private _peerPublicKey;
|
|
97
104
|
private _offerId;
|
package/dist/core/peer.js
CHANGED
|
@@ -50,6 +50,7 @@ export class Peer extends EventEmitter {
|
|
|
50
50
|
this.webrtcAdapter = options.webrtcAdapter;
|
|
51
51
|
this.connectionConfig = options.config;
|
|
52
52
|
this.debugEnabled = options.debug || false;
|
|
53
|
+
this.onPeerConnectionCreated = options.onPeerConnectionCreated;
|
|
53
54
|
}
|
|
54
55
|
/**
|
|
55
56
|
* Initialize the peer connection (called internally by Rondevu.peer())
|
|
@@ -78,6 +79,9 @@ export class Peer extends EventEmitter {
|
|
|
78
79
|
this._peerPublicKey = offer.publicKey;
|
|
79
80
|
this._offerId = offer.offerId;
|
|
80
81
|
this.debug(`Selected offer ${offer.offerId} from ${offer.publicKey}`);
|
|
82
|
+
// Find which of our search tags actually exist on the offer (exact match)
|
|
83
|
+
const actualMatchedTags = this.tags.filter(searchTag => offer.tags.includes(searchTag));
|
|
84
|
+
this.debug(`Matched tags: ${actualMatchedTags.join(', ')} (from search: ${this.tags.join(', ')})`);
|
|
81
85
|
// Create the underlying AnswererConnection
|
|
82
86
|
this.connection = new AnswererConnection({
|
|
83
87
|
api: this.api,
|
|
@@ -94,7 +98,8 @@ export class Peer extends EventEmitter {
|
|
|
94
98
|
...this.connectionConfig,
|
|
95
99
|
debug: this.debugEnabled,
|
|
96
100
|
},
|
|
97
|
-
matchedTags:
|
|
101
|
+
matchedTags: actualMatchedTags.length > 0 ? actualMatchedTags : undefined,
|
|
102
|
+
onPeerConnectionCreated: this.onPeerConnectionCreated,
|
|
98
103
|
});
|
|
99
104
|
// Wire up events
|
|
100
105
|
this.setupEventHandlers();
|
|
@@ -75,11 +75,6 @@ export class PollingManager extends EventEmitter {
|
|
|
75
75
|
return;
|
|
76
76
|
try {
|
|
77
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
|
-
}
|
|
83
78
|
// Emit answer events
|
|
84
79
|
for (const answer of result.answers) {
|
|
85
80
|
this.debug(`Poll: answer for ${answer.offerId}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xtr-dev/rondevu-client",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.16",
|
|
4
4
|
"description": "TypeScript client for Rondevu with durable WebRTC connections, automatic reconnection, and message queuing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/core/index.js",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"lint:fix": "eslint src test --ext .ts,.tsx,.js --fix",
|
|
14
14
|
"format": "prettier --write \"src/**/*.{ts,tsx,js}\" \"test/**/*.{ts,tsx,js}\"",
|
|
15
15
|
"prepublishOnly": "npm run build",
|
|
16
|
-
"prepare": "husky"
|
|
16
|
+
"prepare": "npm run build && husky || true"
|
|
17
17
|
},
|
|
18
18
|
"keywords": [
|
|
19
19
|
"webrtc",
|