2 Commits

Author SHA1 Message Date
9a4fbb63f8 v0.18.7 - Revert to v0.18.3 answer processing logic 2025-12-14 14:39:26 +01:00
f8fb842935 Revert to v0.18.3 answer processing logic
Reverted pollInternal to exactly match v0.18.3 which was the last
working version. The changes in v0.18.5 and v0.18.6 that moved the
answered flag and timestamp updates were causing issues.

v0.18.3 working logic restored:
- Check !activeOffer.answered
- Call setRemoteDescription (no try/catch)
- Set answered = true AFTER
- Update lastPollTimestamp AFTER
- No pre-emptive timestamp updates

The only difference from v0.18.3 is the eventemitter3 import which
should not affect answer processing behavior.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-14 14:39:15 +01:00
3 changed files with 12 additions and 26 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.6", "version": "0.18.7",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@xtr-dev/rondevu-client", "name": "@xtr-dev/rondevu-client",
"version": "0.18.6", "version": "0.18.7",
"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.6", "version": "0.18.7",
"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,37 +645,23 @@ 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)
if (activeOffer && !activeOffer.answered) { if (activeOffer && !activeOffer.answered) {
this.debug(`Received answer for offer ${answer.offerId}`) this.debug(`Received answer for offer ${answer.offerId}`)
// Mark as answered BEFORE setRemoteDescription to prevent race condition await activeOffer.pc.setRemoteDescription({
type: 'answer',
sdp: answer.sdp
})
activeOffer.answered = true activeOffer.answered = true
this.lastPollTimestamp = answer.answeredAt
this.emit('offer:answered', answer.offerId, answer.answererId)
try { // Create replacement offer
await activeOffer.pc.setRemoteDescription({ this.fillOffers()
type: 'answer',
sdp: answer.sdp
})
this.emit('offer:answered', answer.offerId, answer.answererId)
// Create replacement offer
this.fillOffers()
} catch (err) {
// If setRemoteDescription fails, reset the answered flag
activeOffer.answered = false
this.debug(`Failed to set remote description for offer ${answer.offerId}:`, err)
// Don't throw - continue processing other answers
}
} }
} }