Files
rondevu-client/MIGRATION.md
Bas van den Aakster a480fa3ba4 Add durable WebRTC connections with auto-reconnect and message buffering (v0.18.8)
- Add connection state machine with proper lifecycle management
- Implement automatic reconnection with exponential backoff
- Add message buffering during disconnections
- Create RondevuConnection base class with state tracking
- Create OffererConnection and AnswererConnection classes
- Fix ICE polling lifecycle (now stops when connected)
- Add fillOffers() semaphore to prevent exceeding maxOffers
- Implement answer fingerprinting to prevent duplicate processing
- Add dual ICE state monitoring (iceConnectionState + connectionState)
- Fix data channel handler timing issues
- Add comprehensive event system (20+ events)
- Add connection timeouts and proper cleanup

Breaking changes:
- connectToService() now returns AnswererConnection instead of ConnectionContext
- connection:opened event signature changed: (offerId, dc) → (offerId, connection)
- Direct DataChannel access replaced with connection wrapper API

See MIGRATION.md for upgrade guide.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-14 16:52:57 +01:00

442 lines
10 KiB
Markdown

# Migration Guide: v0.18.x → v0.18.8
Version 0.18.8 introduces significant improvements to connection durability and reliability. While we've maintained backward compatibility where possible, there are some breaking changes to be aware of.
## Overview of Changes
### New Features
- **Automatic reconnection** with exponential backoff
- **Message buffering** during disconnections
- **Connection state machine** with proper lifecycle management
- **Rich event system** for connection monitoring
- **ICE polling lifecycle** (stops when connected, no more resource leaks)
### Breaking Changes
- `connectToService()` now returns `AnswererConnection` instead of `ConnectionContext`
- `connection:opened` event signature changed for offerer side
- Direct DataChannel access replaced with connection wrapper API
---
## Migration Steps
### 1. Answerer Side (connectToService)
#### Old API (v0.18.7 and earlier)
```typescript
const context = await rondevu.connectToService({
serviceFqn: 'chat:1.0.0@alice',
onConnection: ({ dc, pc, peerUsername }) => {
console.log('Connected to', peerUsername)
dc.addEventListener('message', (event) => {
console.log('Received:', event.data)
})
dc.addEventListener('open', () => {
dc.send('Hello!')
})
}
})
// Access peer connection
context.pc.getStats()
```
#### New API (v0.18.8)
```typescript
const connection = await rondevu.connectToService({
serviceFqn: 'chat:1.0.0@alice',
connectionConfig: {
reconnectEnabled: true, // Optional: enable auto-reconnect
bufferEnabled: true, // Optional: enable message buffering
connectionTimeout: 30000 // Optional: connection timeout (ms)
}
})
// Listen for connection events
connection.on('connected', () => {
console.log('Connected!')
connection.send('Hello!')
})
connection.on('message', (data) => {
console.log('Received:', data)
})
// Optional: monitor reconnection
connection.on('reconnecting', (attempt) => {
console.log(`Reconnecting, attempt ${attempt}`)
})
connection.on('reconnect:success', () => {
console.log('Reconnection successful!')
})
// Access peer connection if needed
const pc = connection.getPeerConnection()
const dc = connection.getDataChannel()
```
**Key Changes:**
- ❌ Removed `onConnection` callback
- ✅ Use event listeners instead: `connection.on('connected', ...)`
- ❌ Removed direct `dc.send()` access
- ✅ Use `connection.send()` for automatic buffering support
- ✅ Added automatic reconnection and message buffering
---
### 2. Offerer Side (publishService)
#### Old API (v0.18.7 and earlier)
```typescript
await rondevu.publishService({
service: 'chat:1.0.0',
maxOffers: 5
})
await rondevu.startFilling()
// Handle connections
rondevu.on('connection:opened', (offerId, dc) => {
console.log('New connection:', offerId)
dc.addEventListener('message', (event) => {
console.log('Received:', event.data)
})
dc.send('Welcome!')
})
```
#### New API (v0.18.8)
```typescript
await rondevu.publishService({
service: 'chat:1.0.0',
maxOffers: 5,
connectionConfig: {
reconnectEnabled: true,
bufferEnabled: true
}
})
await rondevu.startFilling()
// Handle connections - signature changed!
rondevu.on('connection:opened', (offerId, connection) => {
console.log('New connection:', offerId)
connection.on('message', (data) => {
console.log('Received:', data)
})
connection.on('disconnected', () => {
console.log('Connection lost, will auto-reconnect')
})
connection.send('Welcome!')
})
```
**Key Changes:**
- ⚠️ Event signature changed: `(offerId, dc)``(offerId, connection)`
- ❌ Removed direct DataChannel access
- ✅ Use `connection.send()` and `connection.on('message', ...)`
- ✅ Connection object provides lifecycle events
---
## New Connection Configuration
All connection-related options are now configured via `connectionConfig`:
```typescript
interface ConnectionConfig {
// Timeouts
connectionTimeout: number // Default: 30000ms (30s)
iceGatheringTimeout: number // Default: 10000ms (10s)
// Reconnection
reconnectEnabled: boolean // Default: true
maxReconnectAttempts: number // Default: 5
reconnectBackoffBase: number // Default: 1000ms
reconnectBackoffMax: number // Default: 30000ms (30s)
// Message buffering
bufferEnabled: boolean // Default: true
maxBufferSize: number // Default: 100 messages
maxBufferAge: number // Default: 60000ms (1 min)
// Debug
debug: boolean // Default: false
}
```
### Example Usage
```typescript
const connection = await rondevu.connectToService({
serviceFqn: 'chat:1.0.0@alice',
connectionConfig: {
// Disable auto-reconnect if you want manual control
reconnectEnabled: false,
// Disable buffering if messages are time-sensitive
bufferEnabled: false,
// Increase timeout for slow networks
connectionTimeout: 60000,
// Reduce retry attempts
maxReconnectAttempts: 3
}
})
```
---
## New Event System
### Connection Lifecycle Events
```typescript
connection.on('state:changed', ({ oldState, newState, reason }) => {})
connection.on('connecting', () => {})
connection.on('connected', () => {})
connection.on('disconnected', (reason) => {})
connection.on('failed', (error) => {})
connection.on('closed', (reason) => {})
```
### Reconnection Events
```typescript
connection.on('reconnect:scheduled', ({ attempt, delay, maxAttempts }) => {})
connection.on('reconnect:attempting', (attempt) => {})
connection.on('reconnect:success', () => {})
connection.on('reconnect:failed', (error) => {})
connection.on('reconnect:exhausted', (attempts) => {})
```
### Message Events
```typescript
connection.on('message', (data) => {})
connection.on('message:sent', (data, buffered) => {})
connection.on('message:buffered', (data) => {})
connection.on('message:replayed', (message) => {})
connection.on('message:buffer:overflow', (discardedMessage) => {})
```
### ICE Events
```typescript
connection.on('ice:candidate:local', (candidate) => {})
connection.on('ice:candidate:remote', (candidate) => {})
connection.on('ice:connection:state', (state) => {})
connection.on('ice:polling:started', () => {})
connection.on('ice:polling:stopped', () => {})
```
---
## Common Migration Patterns
### Pattern 1: Simple Message Handler
**Before:**
```typescript
dc.addEventListener('message', (event) => {
console.log(event.data)
})
dc.send('Hello')
```
**After:**
```typescript
connection.on('message', (data) => {
console.log(data)
})
connection.send('Hello')
```
---
### Pattern 2: Connection State Monitoring
**Before:**
```typescript
pc.oniceconnectionstatechange = () => {
console.log('ICE state:', pc.iceConnectionState)
}
```
**After:**
```typescript
connection.on('ice:connection:state', (state) => {
console.log('ICE state:', state)
})
// Or use higher-level events
connection.on('connected', () => console.log('Connected!'))
connection.on('disconnected', () => console.log('Disconnected!'))
```
---
### Pattern 3: Handling Connection Failures
**Before:**
```typescript
pc.oniceconnectionstatechange = () => {
if (pc.iceConnectionState === 'failed') {
// Manual reconnection logic
pc.close()
await setupNewConnection()
}
}
```
**After:**
```typescript
// Automatic reconnection built-in!
connection.on('reconnecting', (attempt) => {
console.log(`Reconnecting... attempt ${attempt}`)
})
connection.on('reconnect:success', () => {
console.log('Back online!')
})
connection.on('reconnect:exhausted', (attempts) => {
console.log(`Failed after ${attempts} attempts`)
// Fallback logic here
})
```
---
### Pattern 4: Accessing Raw RTCPeerConnection/DataChannel
If you need low-level access:
```typescript
const connection = await rondevu.connectToService({ ... })
// Get raw objects if needed
const pc = connection.getPeerConnection()
const dc = connection.getDataChannel()
// Use them directly (bypasses buffering/reconnection features)
if (dc) {
dc.addEventListener('message', (event) => {
console.log(event.data)
})
}
```
**Note:** Using raw DataChannel bypasses automatic buffering and reconnection features.
---
## Backward Compatibility Notes
### What Still Works
`publishService()` API (just add `connectionConfig` optionally)
`findService()` API (unchanged)
✅ All RondevuAPI methods (unchanged)
✅ ICE server presets (unchanged)
✅ Username and keypair management (unchanged)
### What Changed
⚠️ `connectToService()` return type: `ConnectionContext``AnswererConnection`
⚠️ `connection:opened` event signature: `(offerId, dc)``(offerId, connection)`
⚠️ Direct DataChannel access replaced with connection wrapper
### What's New
✨ Automatic reconnection with exponential backoff
✨ Message buffering during disconnections
✨ Rich event system (20+ events)
✨ Connection state machine
✨ ICE polling lifecycle management (no more resource leaks)
---
## Troubleshooting
### Issue: "connection.send is not a function"
You're trying to use the old `dc.send()` API. Update to:
```typescript
// Old
dc.send('Hello')
// New
connection.send('Hello')
```
---
### Issue: "Cannot read property 'addEventListener' of undefined"
You're trying to access `dc` directly. Update to event listeners:
```typescript
// Old
dc.addEventListener('message', (event) => {
console.log(event.data)
})
// New
connection.on('message', (data) => {
console.log(data)
})
```
---
### Issue: Messages not being delivered
Check if buffering is enabled and connection is established:
```typescript
connection.on('connected', () => {
// Only send after connected
connection.send('Hello')
})
// Monitor buffer
connection.on('message:buffered', (data) => {
console.log('Message buffered, will send when reconnected')
})
```
---
## Need Help?
- Check the updated README for full API documentation
- See examples in the `demo/` directory
- File issues at: https://github.com/xtr-dev/rondevu/issues
---
## Summary Checklist
When migrating from v0.18.7 to v0.18.8:
- [ ] Update `connectToService()` to use returned `AnswererConnection`
- [ ] Replace `dc.addEventListener('message', ...)` with `connection.on('message', ...)`
- [ ] Replace `dc.send()` with `connection.send()`
- [ ] Update `connection:opened` event handler signature
- [ ] Consider adding reconnection event handlers
- [ ] Optionally configure `connectionConfig` for your use case
- [ ] Test connection resilience (disconnect network, should auto-reconnect)
- [ ] Remove manual reconnection logic (now built-in)