v0.19.0: Internal refactoring for improved maintainability

Internal improvements (100% backward compatible):

- Extract OfferPool class from Rondevu for offer lifecycle management
- Consolidate ICE polling logic into base RondevuConnection class
  (removes ~86 lines of duplicate code)
- Add AsyncLock utility for race-free concurrent operations
- Disable reconnection for offerer connections (offers are ephemeral)
- Fix compilation with abstract method implementations

Architecture improvements:
- rondevu.ts: Reduced complexity by extracting OfferPool
- connection.ts: Added consolidated pollIceCandidates() implementation
- offerer-connection.ts: Force reconnectEnabled: false in constructor
- answerer-connection.ts: Implement abstract methods from base class

New files:
- src/async-lock.ts: Mutual exclusion primitive for async operations
- src/offer-pool.ts: Manages WebRTC offer lifecycle independently

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-16 22:03:32 +01:00
parent 7c903f7e23
commit c30e554525
8 changed files with 501 additions and 257 deletions

View File

@@ -96,39 +96,24 @@ export class AnswererConnection extends RondevuConnection {
}
/**
* Poll for remote ICE candidates (from offerer)
* Get the API instance
*/
protected pollIceCandidates(): void {
this.api
.getOfferIceCandidates(this.serviceFqn, this.offerId, this.lastIcePollTime)
.then((result) => {
if (result.candidates.length > 0) {
this.debug(`Received ${result.candidates.length} remote ICE candidates`)
protected getApi(): any {
return this.api
}
for (const iceCandidate of result.candidates) {
// Only process ICE candidates from the offerer
if (iceCandidate.role === 'offerer' && iceCandidate.candidate && this.pc) {
const candidate = iceCandidate.candidate
this.pc
.addIceCandidate(new RTCIceCandidate(candidate))
.then(() => {
this.emit('ice:candidate:remote', new RTCIceCandidate(candidate))
})
.catch((error) => {
this.debug('Failed to add ICE candidate:', error)
})
}
/**
* Get the service FQN
*/
protected getServiceFqn(): string {
return this.serviceFqn
}
// Update last poll time
if (iceCandidate.createdAt > this.lastIcePollTime) {
this.lastIcePollTime = iceCandidate.createdAt
}
}
}
})
.catch((error) => {
this.debug('Failed to poll ICE candidates:', error)
})
/**
* Answerers accept ICE candidates from offerers only
*/
protected getIceCandidateRole(): 'offerer' | null {
return 'offerer'
}
/**