2 Commits

Author SHA1 Message Date
50d49d80d3 v0.18.6 - Fix duplicate answer bug (timestamp update) 2025-12-14 14:29:06 +01:00
b3b1751f63 Fix duplicate answer bug - update timestamp before processing
Root cause: When setRemoteDescription failed, lastPollTimestamp was
never updated, causing the server to return the same answer repeatedly.

Solution:
1. Update lastPollTimestamp BEFORE processing answers
2. Calculate max timestamp from all received answers upfront
3. Don't throw on setRemoteDescription errors - just log and continue
4. This ensures we advance the timestamp even if processing fails

This prevents the infinite loop of:
- Poll returns answer
- Processing fails
- Timestamp not updated
- Next poll returns same answer
- Repeat

Now the timestamp advances regardless of processing success,
preventing duplicate answer fetches from the server.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-14 14:28:57 +01:00
3 changed files with 11 additions and 5 deletions

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "@xtr-dev/rondevu-client", "name": "@xtr-dev/rondevu-client",
"version": "0.18.5", "version": "0.18.6",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@xtr-dev/rondevu-client", "name": "@xtr-dev/rondevu-client",
"version": "0.18.5", "version": "0.18.6",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@noble/ed25519": "^3.0.0", "@noble/ed25519": "^3.0.0",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@xtr-dev/rondevu-client", "name": "@xtr-dev/rondevu-client",
"version": "0.18.5", "version": "0.18.6",
"description": "TypeScript client for Rondevu with durable WebRTC connections, automatic reconnection, and message queuing", "description": "TypeScript client for Rondevu with durable WebRTC connections, automatic reconnection, and message queuing",
"type": "module", "type": "module",
"main": "dist/index.js", "main": "dist/index.js",

View File

@@ -645,6 +645,12 @@ export class Rondevu extends EventEmitter {
try { try {
const result = await this.api.poll(this.lastPollTimestamp) const result = await this.api.poll(this.lastPollTimestamp)
// Update timestamp FIRST to prevent re-fetching same answers if processing fails
if (result.answers.length > 0) {
const maxAnswerTimestamp = Math.max(...result.answers.map(a => a.answeredAt))
this.lastPollTimestamp = Math.max(this.lastPollTimestamp, maxAnswerTimestamp)
}
// Process answers // Process answers
for (const answer of result.answers) { for (const answer of result.answers) {
const activeOffer = this.activeOffers.get(answer.offerId) const activeOffer = this.activeOffers.get(answer.offerId)
@@ -660,7 +666,6 @@ export class Rondevu extends EventEmitter {
sdp: answer.sdp sdp: answer.sdp
}) })
this.lastPollTimestamp = answer.answeredAt
this.emit('offer:answered', answer.offerId, answer.answererId) this.emit('offer:answered', answer.offerId, answer.answererId)
// Create replacement offer // Create replacement offer
@@ -668,7 +673,8 @@ export class Rondevu extends EventEmitter {
} catch (err) { } catch (err) {
// If setRemoteDescription fails, reset the answered flag // If setRemoteDescription fails, reset the answered flag
activeOffer.answered = false activeOffer.answered = false
throw err this.debug(`Failed to set remote description for offer ${answer.offerId}:`, err)
// Don't throw - continue processing other answers
} }
} }
} }