- Add secret field to PeerOptions interface
- Pass secret when creating offers in CreatingOfferState
- Pass secret when answering offers in AnsweringState
- Bump version to 0.7.7
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added optional polyfill parameters to RondevuOptions to support Node.js:
- RTCPeerConnection: Custom peer connection implementation
- RTCSessionDescription: Custom session description implementation
- RTCIceCandidate: Custom ICE candidate implementation
This allows users to plug in wrtc or node-webrtc packages for full
WebRTC support in Node.js environments. Updated documentation with
usage examples and environment compatibility matrix.
Version bumped to 0.7.4
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The answerer was getting 403 Forbidden when sending ICE candidates because
the server didn't know who the answerer was yet. ICE gathering starts when
setLocalDescription is called, but we were calling /answer AFTER that.
Fixed by sending the answer to the server BEFORE setLocalDescription:
1. Create answer SDP
2. Send answer to server (registers answererPeerId)
3. Set up ICE handler
4. Set local description (ICE gathering starts)
This ensures the server has answererPeerId set before ICE candidates arrive,
so they're properly authorized.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
ICE candidate handler was being set up AFTER setLocalDescription, but ICE
gathering starts when setLocalDescription is called. This meant candidates
were generated before the handler was attached, so they were never sent to
the server, causing connection failures.
Fixed by:
- Setting up ICE handler BEFORE setLocalDescription in both offer and answer flows
- Changed setupIceCandidateHandler() to use this.peer.offerId instead of parameter
- Handler now checks this.peer.offerId before sending (waits for it to be set)
Order of operations now:
1. Set up ICE candidate handler
2. Call setLocalDescription (ICE gathering starts)
3. Set this.peer.offerId (handler can now send candidates)
This ensures all ICE candidates are captured and sent to the server.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Refactored common ICE candidate handling logic to reduce code duplication:
- Added setupIceCandidateHandler() method to base PeerState class
- Moved iceCandidateHandler property to base class
- Updated cleanup() in base class to remove ICE candidate handler
- Removed duplicate handler code from CreatingOfferState and AnsweringState
- Both states now call this.setupIceCandidateHandler(offerId)
This eliminates ~15 lines of duplicated code per state and ensures consistent ICE candidate handling across all states that need it.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Updated all event handler assignments to use addEventListener instead of .on* properties:
- peer/index.ts: Replaced onconnectionstatechange, ondatachannel, ontrack, onicecandidateerror
- creating-offer-state.ts: Replaced onicecandidate
- answering-state.ts: Replaced onicecandidate
Benefits:
- Proper cleanup with removeEventListener
- Prevents memory leaks by removing listeners when states/peer close
- Allows multiple listeners for the same event
- More modern and explicit event handling approach
All event handlers are now stored as class properties and properly cleaned up in cleanup()/close() methods.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Split the monolithic peer.ts file into a modular state-based architecture:
- Created separate files for each state class (idle, creating-offer, waiting-for-answer, answering, exchanging-ice, connected, failed, closed)
- Extracted shared types into types.ts
- Extracted base PeerState class into state.ts
- Updated peer/index.ts to import state classes instead of defining them inline
- Made close() method async to support dynamic imports and avoid circular dependencies
- Used dynamic imports in state transitions to prevent circular dependency issues
This improves code organization, maintainability, and makes each state's logic easier to understand and test.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Major improvements to connection establishment:
**Trickle ICE Implementation:**
- Send offer/answer to server IMMEDIATELY after creating SDP
- Don't wait for ICE gathering before sending offer/answer
- ICE candidates are now sent as they're discovered (true trickle ICE)
- Connection attempts can start with first candidates while more gather
**Removed Delays:**
- CreatingOfferState: No longer waits 10-15s for ICE before sending offer
- AnsweringState: No longer waits 10-15s for ICE before sending answer
- Answering state now takes ~50-200ms instead of 15+ seconds
**Code Organization:**
- Moved peer.ts to peer/index.ts directory structure
- Removed unused pendingCandidates buffering
- Removed unused waitForIceGathering methods
- Cleaned up timeout handling
**Breaking Changes:**
- "answering" state now transitions much faster to "exchanging-ice"
- ICE candidates start trickling immediately instead of in batches
This dramatically improves connection speed and follows WebRTC best practices.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>