@xtr-dev/rondevu-client 0.9.1 → 0.9.2

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.
@@ -245,6 +245,7 @@ export class ServicePool {
245
245
  try {
246
246
  // Create peer connections and generate offers
247
247
  const offerRequests = [];
248
+ const pendingCandidates = []; // Store candidates before we have offer IDs
248
249
  for (let i = 0; i < batchSize; i++) {
249
250
  const pc = new RTCPeerConnection(this.options.rtcConfig || {
250
251
  iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
@@ -252,9 +253,27 @@ export class ServicePool {
252
253
  // Create data channel (required for offers) and save reference
253
254
  const channel = pc.createDataChannel('rondevu-service');
254
255
  dataChannels.push(channel);
256
+ // Set up temporary candidate collector BEFORE setLocalDescription
257
+ const candidatesForThisOffer = [];
258
+ pendingCandidates.push(candidatesForThisOffer);
259
+ pc.onicecandidate = (event) => {
260
+ if (event.candidate) {
261
+ const candidateData = event.candidate.toJSON();
262
+ if (candidateData.candidate && candidateData.candidate !== '') {
263
+ const type = candidateData.candidate.includes('typ host') ? 'host' :
264
+ candidateData.candidate.includes('typ srflx') ? 'srflx' :
265
+ candidateData.candidate.includes('typ relay') ? 'relay' : 'unknown';
266
+ console.log(`🧊 Service pool generated ${type} ICE candidate:`, candidateData.candidate);
267
+ candidatesForThisOffer.push(candidateData);
268
+ }
269
+ }
270
+ else {
271
+ console.log('🧊 Service pool ICE gathering complete');
272
+ }
273
+ };
255
274
  // Create offer
256
275
  const offer = await pc.createOffer();
257
- await pc.setLocalDescription(offer);
276
+ await pc.setLocalDescription(offer); // ICE gathering starts here, candidates go to collector
258
277
  if (!offer.sdp) {
259
278
  pc.close();
260
279
  throw new Error('Failed to generate SDP');
@@ -270,19 +289,37 @@ export class ServicePool {
270
289
  // Batch create offers
271
290
  const createdOffers = await this.offersApi.create(offerRequests);
272
291
  offers.push(...createdOffers);
273
- // Set up ICE candidate handlers AFTER we have offer IDs
292
+ // Now send all pending candidates and set up handlers for future ones
274
293
  for (let i = 0; i < peerConnections.length; i++) {
275
294
  const pc = peerConnections[i];
276
295
  const offerId = createdOffers[i].id;
296
+ const candidates = pendingCandidates[i];
297
+ // Send any candidates that were collected while waiting for offer ID
298
+ if (candidates.length > 0) {
299
+ console.log(`📤 Sending ${candidates.length} pending ICE candidate(s) for offer ${offerId}`);
300
+ try {
301
+ await this.offersApi.addIceCandidates(offerId, candidates);
302
+ console.log(`✅ Sent ${candidates.length} pending ICE candidate(s)`);
303
+ }
304
+ catch (err) {
305
+ console.error('❌ Error sending pending ICE candidates:', err);
306
+ }
307
+ }
308
+ // Replace temporary handler with permanent one for any future candidates
277
309
  pc.onicecandidate = async (event) => {
278
310
  if (event.candidate) {
279
311
  const candidateData = event.candidate.toJSON();
280
312
  if (candidateData.candidate && candidateData.candidate !== '') {
313
+ const type = candidateData.candidate.includes('typ host') ? 'host' :
314
+ candidateData.candidate.includes('typ srflx') ? 'srflx' :
315
+ candidateData.candidate.includes('typ relay') ? 'relay' : 'unknown';
316
+ console.log(`🧊 Service pool generated late ${type} ICE candidate:`, candidateData.candidate);
281
317
  try {
282
318
  await this.offersApi.addIceCandidates(offerId, [candidateData]);
319
+ console.log(`✅ Sent late ${type} ICE candidate`);
283
320
  }
284
321
  catch (err) {
285
- console.error('Error sending ICE candidate:', err);
322
+ console.error(`❌ Error sending ${type} ICE candidate:`, err);
286
323
  }
287
324
  }
288
325
  }
@@ -308,9 +345,27 @@ export class ServicePool {
308
345
  iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
309
346
  });
310
347
  const dataChannel = pc.createDataChannel('rondevu-service');
348
+ // Collect candidates before we have offer ID
349
+ const pendingCandidates = [];
350
+ // Set up temporary candidate collector BEFORE setLocalDescription
351
+ pc.onicecandidate = (event) => {
352
+ if (event.candidate) {
353
+ const candidateData = event.candidate.toJSON();
354
+ if (candidateData.candidate && candidateData.candidate !== '') {
355
+ const type = candidateData.candidate.includes('typ host') ? 'host' :
356
+ candidateData.candidate.includes('typ srflx') ? 'srflx' :
357
+ candidateData.candidate.includes('typ relay') ? 'relay' : 'unknown';
358
+ console.log(`🧊 Initial service generated ${type} ICE candidate:`, candidateData.candidate);
359
+ pendingCandidates.push(candidateData);
360
+ }
361
+ }
362
+ else {
363
+ console.log('🧊 Initial service ICE gathering complete');
364
+ }
365
+ };
311
366
  // Create offer
312
367
  const offer = await pc.createOffer();
313
- await pc.setLocalDescription(offer);
368
+ await pc.setLocalDescription(offer); // ICE gathering starts here
314
369
  if (!offer.sdp) {
315
370
  pc.close();
316
371
  throw new Error('Failed to generate SDP');
@@ -345,16 +400,32 @@ export class ServicePool {
345
400
  throw new Error(error.error || 'Failed to publish service');
346
401
  }
347
402
  const data = await response.json();
348
- // Set up ICE candidate handler now that we have the offer ID
403
+ // Send any pending candidates
404
+ if (pendingCandidates.length > 0) {
405
+ console.log(`📤 Sending ${pendingCandidates.length} pending ICE candidate(s) for initial service`);
406
+ try {
407
+ await this.offersApi.addIceCandidates(data.offerId, pendingCandidates);
408
+ console.log(`✅ Sent ${pendingCandidates.length} pending ICE candidate(s)`);
409
+ }
410
+ catch (err) {
411
+ console.error('❌ Error sending pending ICE candidates:', err);
412
+ }
413
+ }
414
+ // Set up handler for any future candidates
349
415
  pc.onicecandidate = async (event) => {
350
416
  if (event.candidate) {
351
417
  const candidateData = event.candidate.toJSON();
352
418
  if (candidateData.candidate && candidateData.candidate !== '') {
419
+ const type = candidateData.candidate.includes('typ host') ? 'host' :
420
+ candidateData.candidate.includes('typ srflx') ? 'srflx' :
421
+ candidateData.candidate.includes('typ relay') ? 'relay' : 'unknown';
422
+ console.log(`🧊 Initial service generated late ${type} ICE candidate:`, candidateData.candidate);
353
423
  try {
354
424
  await this.offersApi.addIceCandidates(data.offerId, [candidateData]);
425
+ console.log(`✅ Sent late ${type} ICE candidate`);
355
426
  }
356
427
  catch (err) {
357
- console.error('Error sending ICE candidate:', err);
428
+ console.error(`❌ Error sending ${type} ICE candidate:`, err);
358
429
  }
359
430
  }
360
431
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xtr-dev/rondevu-client",
3
- "version": "0.9.1",
3
+ "version": "0.9.2",
4
4
  "description": "TypeScript client for Rondevu with durable WebRTC connections, automatic reconnection, and message queuing",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",