mirror of
https://github.com/xtr-dev/rondevu-client.git
synced 2025-12-11 11:23:24 +00:00
Add ServiceHost, ServiceClient, and RondevuService for high-level service management
- Add RondevuService: High-level API for username claiming and service publishing with Ed25519 signatures - Add ServiceHost: Manages offer pool for hosting services with auto-replacement - Add ServiceClient: Connects to hosted services with automatic reconnection - Add NoOpSignaler: Placeholder signaler for connection setup - Integrate Ed25519 signature functionality from @noble/ed25519 - Add ESLint and Prettier configuration with 4-space indentation - Add demo with local signaling test - Version bump to 0.10.0 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
304
demo/demo.js
Normal file
304
demo/demo.js
Normal file
@@ -0,0 +1,304 @@
|
||||
import { WebRTCRondevuConnection } from '../src/index.js'
|
||||
import { WebRTCContext } from '../src/webrtc-context.js'
|
||||
|
||||
// Local signaling implementation for testing
|
||||
class LocalSignaler {
|
||||
constructor(name, remoteName) {
|
||||
this.name = name
|
||||
this.remoteName = remoteName
|
||||
this.iceCandidates = []
|
||||
this.iceListeners = []
|
||||
this.remote = null
|
||||
this.remoteIceCandidates = []
|
||||
this.offerCallbacks = []
|
||||
this.answerCallbacks = []
|
||||
}
|
||||
|
||||
// Link two signalers together
|
||||
linkTo(remoteSignaler) {
|
||||
this.remote = remoteSignaler
|
||||
this.remoteIceCandidates = remoteSignaler.iceCandidates
|
||||
}
|
||||
|
||||
// Set local offer (called when offer is created)
|
||||
setOffer(offer) {
|
||||
console.log(`[${this.name}] Setting offer`)
|
||||
// Notify remote peer about the offer
|
||||
if (this.remote) {
|
||||
this.remote.offerCallbacks.forEach(callback => callback(offer))
|
||||
}
|
||||
}
|
||||
|
||||
// Set local answer (called when answer is created)
|
||||
setAnswer(answer) {
|
||||
console.log(`[${this.name}] Setting answer`)
|
||||
// Notify remote peer about the answer
|
||||
if (this.remote) {
|
||||
this.remote.answerCallbacks.forEach(callback => callback(answer))
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for offers from remote peer
|
||||
addOfferListener(callback) {
|
||||
this.offerCallbacks.push(callback)
|
||||
return () => {
|
||||
const index = this.offerCallbacks.indexOf(callback)
|
||||
if (index > -1) {
|
||||
this.offerCallbacks.splice(index, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for answers from remote peer
|
||||
addAnswerListener(callback) {
|
||||
this.answerCallbacks.push(callback)
|
||||
return () => {
|
||||
const index = this.answerCallbacks.indexOf(callback)
|
||||
if (index > -1) {
|
||||
this.answerCallbacks.splice(index, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add local ICE candidate (called by local connection)
|
||||
addIceCandidate(candidate) {
|
||||
console.log(`[${this.name}] Adding ICE candidate:`, candidate.candidate)
|
||||
this.iceCandidates.push(candidate)
|
||||
|
||||
// Immediately send to remote peer if linked
|
||||
if (this.remote) {
|
||||
setTimeout(() => {
|
||||
this.remote.iceListeners.forEach(listener => {
|
||||
console.log(`[${this.name}] Sending ICE to ${this.remoteName}`)
|
||||
listener(candidate)
|
||||
})
|
||||
}, 10)
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for remote ICE candidates
|
||||
addListener(callback) {
|
||||
console.log(`[${this.name}] Adding ICE listener`)
|
||||
this.iceListeners.push(callback)
|
||||
|
||||
// Send any existing remote candidates
|
||||
this.remoteIceCandidates.forEach(candidate => {
|
||||
setTimeout(() => callback(candidate), 10)
|
||||
})
|
||||
|
||||
return () => {
|
||||
const index = this.iceListeners.indexOf(callback)
|
||||
if (index > -1) {
|
||||
this.iceListeners.splice(index, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create signalers for host and client
|
||||
const hostSignaler = new LocalSignaler('HOST', 'CLIENT')
|
||||
const clientSignaler = new LocalSignaler('CLIENT', 'HOST')
|
||||
|
||||
// Link them together for bidirectional communication
|
||||
hostSignaler.linkTo(clientSignaler)
|
||||
clientSignaler.linkTo(hostSignaler)
|
||||
|
||||
// Store connections
|
||||
let hostConnection = null
|
||||
let clientConnection = null
|
||||
|
||||
// UI Update functions
|
||||
function updateStatus(peer, state) {
|
||||
const statusEl = document.getElementById(`status-${peer}`)
|
||||
if (statusEl) {
|
||||
statusEl.className = `status ${state}`
|
||||
statusEl.textContent = state.charAt(0).toUpperCase() + state.slice(1)
|
||||
}
|
||||
}
|
||||
|
||||
function addLog(peer, message) {
|
||||
const logEl = document.getElementById(`log-${peer}`)
|
||||
if (logEl) {
|
||||
const time = new Date().toLocaleTimeString()
|
||||
logEl.innerHTML += `<div class="log-entry">[${time}] ${message}</div>`
|
||||
logEl.scrollTop = logEl.scrollHeight
|
||||
}
|
||||
}
|
||||
|
||||
// Create Host (Offerer)
|
||||
async function createHost() {
|
||||
try {
|
||||
addLog('a', 'Creating host connection (offerer)...')
|
||||
|
||||
const hostContext = new WebRTCContext(hostSignaler)
|
||||
|
||||
hostConnection = new WebRTCRondevuConnection({
|
||||
id: 'test-connection',
|
||||
host: 'client-peer',
|
||||
service: 'test.demo@1.0.0',
|
||||
offer: null,
|
||||
context: hostContext,
|
||||
})
|
||||
|
||||
// Listen for state changes
|
||||
hostConnection.events.on('state-change', state => {
|
||||
console.log('[HOST] State changed:', state)
|
||||
updateStatus('a', state)
|
||||
addLog('a', `State changed to: ${state}`)
|
||||
})
|
||||
|
||||
// Listen for messages
|
||||
hostConnection.events.on('message', message => {
|
||||
console.log('[HOST] Received message:', message)
|
||||
addLog('a', `📨 Received: ${message}`)
|
||||
})
|
||||
|
||||
addLog('a', '✅ Host connection created')
|
||||
updateStatus('a', 'connecting')
|
||||
|
||||
// Wait for host to be ready (offer created and set)
|
||||
await hostConnection.ready
|
||||
addLog('a', '✅ Host offer created')
|
||||
|
||||
// Get the offer
|
||||
const offer = hostConnection.connection.localDescription
|
||||
document.getElementById('offer-a').value = JSON.stringify(offer, null, 2)
|
||||
|
||||
addLog('a', 'Offer ready to send to client')
|
||||
} catch (error) {
|
||||
console.error('[HOST] Error:', error)
|
||||
addLog('a', `❌ Error: ${error.message}`)
|
||||
updateStatus('a', 'disconnected')
|
||||
}
|
||||
}
|
||||
|
||||
// Create Client (Answerer)
|
||||
async function createClient() {
|
||||
try {
|
||||
addLog('b', 'Creating client connection (answerer)...')
|
||||
|
||||
// Get offer from host
|
||||
if (!hostConnection) {
|
||||
alert('Please create host first!')
|
||||
return
|
||||
}
|
||||
|
||||
const offer = hostConnection.connection.localDescription
|
||||
if (!offer) {
|
||||
alert('Host offer not ready yet!')
|
||||
return
|
||||
}
|
||||
|
||||
addLog('b', 'Got offer from host')
|
||||
|
||||
const clientContext = new WebRTCContext(clientSignaler)
|
||||
|
||||
clientConnection = new WebRTCRondevuConnection({
|
||||
id: 'test-connection',
|
||||
host: 'host-peer',
|
||||
service: 'test.demo@1.0.0',
|
||||
offer: offer,
|
||||
context: clientContext,
|
||||
})
|
||||
|
||||
// Listen for state changes
|
||||
clientConnection.events.on('state-change', state => {
|
||||
console.log('[CLIENT] State changed:', state)
|
||||
updateStatus('b', state)
|
||||
addLog('b', `State changed to: ${state}`)
|
||||
})
|
||||
|
||||
// Listen for messages
|
||||
clientConnection.events.on('message', message => {
|
||||
console.log('[CLIENT] Received message:', message)
|
||||
addLog('b', `📨 Received: ${message}`)
|
||||
})
|
||||
|
||||
addLog('b', '✅ Client connection created')
|
||||
updateStatus('b', 'connecting')
|
||||
|
||||
// Wait for client to be ready
|
||||
await clientConnection.ready
|
||||
addLog('b', '✅ Client answer created')
|
||||
|
||||
// Get the answer
|
||||
const answer = clientConnection.connection.localDescription
|
||||
document.getElementById('answer-b').value = JSON.stringify(answer, null, 2)
|
||||
|
||||
// Set answer on host
|
||||
addLog('b', 'Setting answer on host...')
|
||||
await hostConnection.connection.setRemoteDescription(answer)
|
||||
addLog('b', '✅ Answer set on host')
|
||||
addLog('a', '✅ Answer received from client')
|
||||
} catch (error) {
|
||||
console.error('[CLIENT] Error:', error)
|
||||
addLog('b', `❌ Error: ${error.message}`)
|
||||
updateStatus('b', 'disconnected')
|
||||
}
|
||||
}
|
||||
|
||||
// Send test message from host to client
|
||||
function sendFromHost() {
|
||||
if (!hostConnection) {
|
||||
alert('Please create host first!')
|
||||
return
|
||||
}
|
||||
|
||||
const message = document.getElementById('message-a').value || 'Hello from Host!'
|
||||
addLog('a', `📤 Sending: ${message}`)
|
||||
hostConnection
|
||||
.sendMessage(message)
|
||||
.then(success => {
|
||||
if (success) {
|
||||
addLog('a', '✅ Message sent successfully')
|
||||
} else {
|
||||
addLog('a', '⚠️ Message queued (not connected)')
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
addLog('a', `❌ Error sending: ${error.message}`)
|
||||
})
|
||||
}
|
||||
|
||||
// Send test message from client to host
|
||||
function sendFromClient() {
|
||||
if (!clientConnection) {
|
||||
alert('Please create client first!')
|
||||
return
|
||||
}
|
||||
|
||||
const message = document.getElementById('message-b').value || 'Hello from Client!'
|
||||
addLog('b', `📤 Sending: ${message}`)
|
||||
clientConnection
|
||||
.sendMessage(message)
|
||||
.then(success => {
|
||||
if (success) {
|
||||
addLog('b', '✅ Message sent successfully')
|
||||
} else {
|
||||
addLog('b', '⚠️ Message queued (not connected)')
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
addLog('b', `❌ Error sending: ${error.message}`)
|
||||
})
|
||||
}
|
||||
|
||||
// Attach event listeners when DOM is ready
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Clear all textareas on load
|
||||
document.getElementById('offer-a').value = ''
|
||||
document.getElementById('answer-b').value = ''
|
||||
|
||||
// Make functions globally available (for console testing)
|
||||
window.createHost = createHost
|
||||
window.createClient = createClient
|
||||
window.sendFromHost = sendFromHost
|
||||
window.sendFromClient = sendFromClient
|
||||
|
||||
console.log('🚀 Local signaling test loaded')
|
||||
console.log('Steps:')
|
||||
console.log('1. Click "Create Host" (Peer A)')
|
||||
console.log('2. Click "Create Client" (Peer B)')
|
||||
console.log('3. Wait for connection to establish')
|
||||
console.log('4. Send messages between peers')
|
||||
})
|
||||
Reference in New Issue
Block a user