6 Commits

Author SHA1 Message Date
fcd0f8ead0 0.18.4 2025-12-14 14:06:57 +01:00
8fd4b249de Fix EventEmitter for cross-platform compatibility (v0.18.3)
Replace Node.js 'events' module with 'eventemitter3' package
to ensure compatibility in both browser and Node.js environments.

Changes:
- Replace import from 'events' to 'eventemitter3'
- Add eventemitter3 as dependency
- Remove @types/node (no longer needed)

Fixes browser bundling error where 'events' module was not available.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-14 14:05:36 +01:00
275c156c64 0.18.3 2025-12-14 13:35:45 +01:00
c60a5f332a Merge remote-tracking branch 'origin/main' 2025-12-14 13:35:38 +01:00
Bas
ecd6be7f8a Merge pull request #7 from xtr-dev/claude/fix-issue-6-CTKj9
Fix issue #6
2025-12-14 13:35:19 +01:00
Claude
e652fdc130 Fix duplicate answer processing race condition (#6)
Add polling guard to prevent concurrent pollInternal() execution.
The setInterval callback doesn't await the async pollInternal(),
which could cause multiple polls to process the same answer before
lastPollTimestamp is updated, resulting in "Called in wrong state:
stable" errors from setRemoteDescription().
2025-12-14 11:59:43 +00:00
3 changed files with 30 additions and 9 deletions

20
package-lock.json generated
View File

@@ -1,19 +1,19 @@
{
"name": "@xtr-dev/rondevu-client",
"version": "0.18.2",
"version": "0.18.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@xtr-dev/rondevu-client",
"version": "0.18.2",
"version": "0.18.4",
"license": "MIT",
"dependencies": {
"@noble/ed25519": "^3.0.0"
"@noble/ed25519": "^3.0.0",
"eventemitter3": "^5.0.1"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@types/node": "^25.0.2",
"@typescript-eslint/eslint-plugin": "^8.48.1",
"@typescript-eslint/parser": "^8.48.1",
"eslint": "^9.39.1",
@@ -1082,6 +1082,8 @@
"integrity": "sha512-gWEkeiyYE4vqjON/+Obqcoeffmk0NF15WSBwSs7zwVA2bAbTaE0SJ7P0WNGoJn8uE7fiaV5a7dKYIJriEqOrmA==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"undici-types": "~7.16.0"
}
@@ -2001,6 +2003,12 @@
"node": ">=0.10.0"
}
},
"node_modules/eventemitter3": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
"license": "MIT"
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -2844,7 +2852,9 @@
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
"dev": true,
"license": "MIT"
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/update-browserslist-db": {
"version": "1.2.2",

View File

@@ -1,6 +1,6 @@
{
"name": "@xtr-dev/rondevu-client",
"version": "0.18.2",
"version": "0.18.4",
"description": "TypeScript client for Rondevu with durable WebRTC connections, automatic reconnection, and message queuing",
"type": "module",
"main": "dist/index.js",
@@ -25,7 +25,6 @@
"license": "MIT",
"devDependencies": {
"@eslint/js": "^9.39.1",
"@types/node": "^25.0.2",
"@typescript-eslint/eslint-plugin": "^8.48.1",
"@typescript-eslint/parser": "^8.48.1",
"eslint": "^9.39.1",
@@ -42,6 +41,7 @@
"README.md"
],
"dependencies": {
"@noble/ed25519": "^3.0.0"
"@noble/ed25519": "^3.0.0",
"eventemitter3": "^5.0.1"
}
}

View File

@@ -1,6 +1,6 @@
import { RondevuAPI, Keypair, IceCandidate, BatcherOptions } from './api.js'
import { CryptoAdapter } from './crypto-adapter.js'
import { EventEmitter } from 'events'
import { EventEmitter } from 'eventemitter3'
// ICE server preset names
export type IceServerPreset = 'ipv4-turn' | 'hostname-turns' | 'google-stun' | 'relay-only'
@@ -259,6 +259,7 @@ export class Rondevu extends EventEmitter {
private filling = false
private pollingInterval: ReturnType<typeof setInterval> | null = null
private lastPollTimestamp = 0
private isPolling = false // Guard against concurrent poll execution
private constructor(
apiUrl: string,
@@ -634,6 +635,13 @@ export class Rondevu extends EventEmitter {
private async pollInternal(): Promise<void> {
if (!this.filling) return
// Prevent concurrent poll execution to avoid duplicate answer processing
if (this.isPolling) {
this.debug('Poll already in progress, skipping')
return
}
this.isPolling = true
try {
const result = await this.api.poll(this.lastPollTimestamp)
@@ -674,6 +682,8 @@ export class Rondevu extends EventEmitter {
}
} catch (err) {
console.error('[Rondevu] Polling error:', err)
} finally {
this.isPolling = false
}
}
@@ -710,6 +720,7 @@ export class Rondevu extends EventEmitter {
stopFilling(): void {
this.debug('Stopping offer filling and polling')
this.filling = false
this.isPolling = false // Reset polling guard
// Stop polling
if (this.pollingInterval) {