Add debug mode and wrap all console.log statements

Implements opt-in debug logging system:
- Add debug?: boolean option to RondevuOptions
- Add private debug() method that only logs when debug mode is enabled
- Replace all 25+ console.log statements with this.debug() calls
- Static connect() method checks options.debug before logging

Benefits:
- Clean production console output by default
- Users can enable debug logging when needed: debug: true
- All debug messages prefixed with [Rondevu]
- Error logging (console.error) preserved for important failures

Usage:
```typescript
const rondevu = await Rondevu.connect({
  apiUrl: 'https://api.ronde.vu',
  username: 'alice',
  debug: true  // Enable debug logging
})
```

🤖 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-12 23:05:13 +01:00
parent bd16798a2f
commit 9262043e97

View File

@@ -56,6 +56,7 @@ export interface RondevuOptions {
cryptoAdapter?: CryptoAdapter // Optional, defaults to WebCryptoAdapter cryptoAdapter?: CryptoAdapter // Optional, defaults to WebCryptoAdapter
batching?: BatcherOptions | false // Optional, defaults to enabled with default options batching?: BatcherOptions | false // Optional, defaults to enabled with default options
iceServers?: IceServerPreset | RTCIceServer[] // Optional: preset name or custom STUN/TURN servers iceServers?: IceServerPreset | RTCIceServer[] // Optional: preset name or custom STUN/TURN servers
debug?: boolean // Optional: enable debug logging (default: false)
} }
export interface OfferContext { export interface OfferContext {
@@ -165,6 +166,7 @@ export class Rondevu {
private cryptoAdapter?: CryptoAdapter private cryptoAdapter?: CryptoAdapter
private batchingOptions?: BatcherOptions | false private batchingOptions?: BatcherOptions | false
private iceServers: RTCIceServer[] private iceServers: RTCIceServer[]
private debugEnabled: boolean
// Service management // Service management
private currentService: string | null = null private currentService: string | null = null
@@ -185,7 +187,8 @@ export class Rondevu {
api: RondevuAPI, api: RondevuAPI,
iceServers: RTCIceServer[], iceServers: RTCIceServer[],
cryptoAdapter?: CryptoAdapter, cryptoAdapter?: CryptoAdapter,
batchingOptions?: BatcherOptions | false batchingOptions?: BatcherOptions | false,
debugEnabled = false
) { ) {
this.apiUrl = apiUrl this.apiUrl = apiUrl
this.username = username this.username = username
@@ -194,8 +197,9 @@ export class Rondevu {
this.iceServers = iceServers this.iceServers = iceServers
this.cryptoAdapter = cryptoAdapter this.cryptoAdapter = cryptoAdapter
this.batchingOptions = batchingOptions this.batchingOptions = batchingOptions
this.debugEnabled = debugEnabled
console.log('[Rondevu] Instance created:', { this.debug('Instance created:', {
username: this.username, username: this.username,
publicKey: this.keypair.publicKey, publicKey: this.keypair.publicKey,
hasIceServers: iceServers.length > 0, hasIceServers: iceServers.length > 0,
@@ -203,6 +207,15 @@ export class Rondevu {
}) })
} }
/**
* Internal debug logging - only logs if debug mode is enabled
*/
private debug(message: string, ...args: any[]): void {
if (this.debugEnabled) {
console.log(`[Rondevu] ${message}`, ...args)
}
}
/** /**
* Create and initialize a Rondevu client * Create and initialize a Rondevu client
* *
@@ -227,21 +240,23 @@ export class Rondevu {
] ]
} }
console.log('[Rondevu] Connecting:', { if (options.debug) {
username, console.log('[Rondevu] Connecting:', {
hasKeypair: !!options.keypair, username,
iceServers: iceServers.length, hasKeypair: !!options.keypair,
batchingEnabled: options.batching !== false iceServers: iceServers.length,
}) batchingEnabled: options.batching !== false
})
}
// Generate keypair if not provided // Generate keypair if not provided
let keypair = options.keypair let keypair = options.keypair
if (!keypair) { if (!keypair) {
console.log('[Rondevu] Generating new keypair...') if (options.debug) console.log('[Rondevu] Generating new keypair...')
keypair = await RondevuAPI.generateKeypair(options.cryptoAdapter) keypair = await RondevuAPI.generateKeypair(options.cryptoAdapter)
console.log('[Rondevu] Generated keypair, publicKey:', keypair.publicKey) if (options.debug) console.log('[Rondevu] Generated keypair, publicKey:', keypair.publicKey)
} else { } else {
console.log('[Rondevu] Using existing keypair, publicKey:', keypair.publicKey) if (options.debug) console.log('[Rondevu] Using existing keypair, publicKey:', keypair.publicKey)
} }
// Create API instance // Create API instance
@@ -252,7 +267,7 @@ export class Rondevu {
options.cryptoAdapter, options.cryptoAdapter,
options.batching options.batching
) )
console.log('[Rondevu] Created API instance') if (options.debug) console.log('[Rondevu] Created API instance')
return new Rondevu( return new Rondevu(
options.apiUrl, options.apiUrl,
@@ -261,7 +276,8 @@ export class Rondevu {
api, api,
iceServers, iceServers,
options.cryptoAdapter, options.cryptoAdapter,
options.batching options.batching,
options.debug || false
) )
} }
@@ -334,7 +350,7 @@ export class Rondevu {
this.offerFactory = offerFactory || this.defaultOfferFactory.bind(this) this.offerFactory = offerFactory || this.defaultOfferFactory.bind(this)
this.ttl = ttl || Rondevu.DEFAULT_TTL_MS this.ttl = ttl || Rondevu.DEFAULT_TTL_MS
console.log(`[Rondevu] Publishing service: ${service} with maxOffers: ${maxOffers}`) this.debug(`Publishing service: ${service} with maxOffers: ${maxOffers}`)
this.usernameClaimed = true this.usernameClaimed = true
} }
@@ -350,7 +366,7 @@ export class Rondevu {
iceServers: this.iceServers iceServers: this.iceServers
} }
console.log('[Rondevu] Creating new offer...') this.debug('Creating new offer...')
// Create the offer using the factory // Create the offer using the factory
const { pc, dc, offer } = await this.offerFactory(rtcConfig) const { pc, dc, offer } = await this.offerFactory(rtcConfig)
@@ -379,7 +395,7 @@ export class Rondevu {
createdAt: Date.now() createdAt: Date.now()
}) })
console.log(`[Rondevu] Offer created: ${offerId}`) this.debug(`Offer created: ${offerId}`)
// Set up ICE candidate handler // Set up ICE candidate handler
pc.onicecandidate = async (event) => { pc.onicecandidate = async (event) => {
@@ -398,7 +414,7 @@ export class Rondevu {
// Monitor connection state // Monitor connection state
pc.onconnectionstatechange = () => { pc.onconnectionstatechange = () => {
console.log(`[Rondevu] Offer ${offerId} connection state: ${pc.connectionState}`) this.debug(`Offer ${offerId} connection state: ${pc.connectionState}`)
if (pc.connectionState === 'failed' || pc.connectionState === 'closed') { if (pc.connectionState === 'failed' || pc.connectionState === 'closed') {
this.activeOffers.delete(offerId) this.activeOffers.delete(offerId)
@@ -416,7 +432,7 @@ export class Rondevu {
const currentCount = this.activeOffers.size const currentCount = this.activeOffers.size
const needed = this.maxOffers - currentCount const needed = this.maxOffers - currentCount
console.log(`[Rondevu] Filling offers: current=${currentCount}, needed=${needed}`) this.debug(`Filling offers: current=${currentCount}, needed=${needed}`)
for (let i = 0; i < needed; i++) { for (let i = 0; i < needed; i++) {
try { try {
@@ -440,7 +456,7 @@ export class Rondevu {
for (const answer of result.answers) { for (const answer of result.answers) {
const activeOffer = this.activeOffers.get(answer.offerId) const activeOffer = this.activeOffers.get(answer.offerId)
if (activeOffer && !activeOffer.answered) { if (activeOffer && !activeOffer.answered) {
console.log(`[Rondevu] Received answer for offer ${answer.offerId}`) this.debug(`Received answer for offer ${answer.offerId}`)
await activeOffer.pc.setRemoteDescription({ await activeOffer.pc.setRemoteDescription({
type: 'answer', type: 'answer',
@@ -480,7 +496,7 @@ export class Rondevu {
*/ */
async startFilling(): Promise<void> { async startFilling(): Promise<void> {
if (this.filling) { if (this.filling) {
console.log('[Rondevu] Already filling') this.debug('Already filling')
return return
} }
@@ -488,7 +504,7 @@ export class Rondevu {
throw new Error('No service published. Call publishService() first.') throw new Error('No service published. Call publishService() first.')
} }
console.log('[Rondevu] Starting offer filling and polling') this.debug('Starting offer filling and polling')
this.filling = true this.filling = true
// Fill initial offers // Fill initial offers
@@ -505,7 +521,7 @@ export class Rondevu {
* Closes all active peer connections * Closes all active peer connections
*/ */
stopFilling(): void { stopFilling(): void {
console.log('[Rondevu] Stopping offer filling and polling') this.debug('Stopping offer filling and polling')
this.filling = false this.filling = false
// Stop polling // Stop polling
@@ -516,7 +532,7 @@ export class Rondevu {
// Close all active connections // Close all active connections
for (const [offerId, offer] of this.activeOffers.entries()) { for (const [offerId, offer] of this.activeOffers.entries()) {
console.log(`[Rondevu] Closing offer ${offerId}`) this.debug(`Closing offer ${offerId}`)
offer.dc?.close() offer.dc?.close()
offer.pc.close() offer.pc.close()
} }
@@ -571,18 +587,18 @@ export class Rondevu {
fqn = `${service}@${username}` fqn = `${service}@${username}`
} else if (service) { } else if (service) {
// Discovery mode - get random service // Discovery mode - get random service
console.log(`[Rondevu] Discovering service: ${service}`) this.debug(`Discovering service: ${service}`)
const discovered = await this.discoverService(service) const discovered = await this.discoverService(service)
fqn = discovered.serviceFqn fqn = discovered.serviceFqn
} else { } else {
throw new Error('Either serviceFqn or service must be provided') throw new Error('Either serviceFqn or service must be provided')
} }
console.log(`[Rondevu] Connecting to service: ${fqn}`) this.debug(`Connecting to service: ${fqn}`)
// 1. Get service offer // 1. Get service offer
const serviceData = await this.api.getService(fqn) const serviceData = await this.api.getService(fqn)
console.log(`[Rondevu] Found service from @${serviceData.username}`) this.debug(`Found service from @${serviceData.username}`)
// 2. Create RTCPeerConnection // 2. Create RTCPeerConnection
const rtcConfiguration = rtcConfig || { const rtcConfiguration = rtcConfig || {
@@ -594,7 +610,7 @@ export class Rondevu {
let dc: RTCDataChannel | null = null let dc: RTCDataChannel | null = null
const dataChannelPromise = new Promise<RTCDataChannel>((resolve) => { const dataChannelPromise = new Promise<RTCDataChannel>((resolve) => {
pc.ondatachannel = (event) => { pc.ondatachannel = (event) => {
console.log('[Rondevu] Data channel received from offerer') this.debug('Data channel received from offerer')
dc = event.channel dc = event.channel
resolve(dc) resolve(dc)
} }
@@ -664,7 +680,7 @@ export class Rondevu {
// 9. Set up connection state monitoring // 9. Set up connection state monitoring
pc.onconnectionstatechange = () => { pc.onconnectionstatechange = () => {
console.log(`[Rondevu] Connection state: ${pc.connectionState}`) this.debug(`Connection state: ${pc.connectionState}`)
if (pc.connectionState === 'failed' || pc.connectionState === 'closed') { if (pc.connectionState === 'failed' || pc.connectionState === 'closed') {
clearInterval(icePollInterval) clearInterval(icePollInterval)
} }
@@ -672,14 +688,14 @@ export class Rondevu {
// 10. Wait for data channel to open and call onConnection // 10. Wait for data channel to open and call onConnection
if (dc.readyState === 'open') { if (dc.readyState === 'open') {
console.log('[Rondevu] Data channel already open') this.debug('Data channel already open')
if (onConnection) { if (onConnection) {
await onConnection(context) await onConnection(context)
} }
} else { } else {
await new Promise<void>((resolve) => { await new Promise<void>((resolve) => {
dc!.addEventListener('open', async () => { dc!.addEventListener('open', async () => {
console.log('[Rondevu] Data channel opened') this.debug('Data channel opened')
if (onConnection) { if (onConnection) {
await onConnection(context) await onConnection(context)
} }