peerpigeon
Version:
WebRTC-based peer-to-peer mesh networking library with intelligent routing and signaling server
235 lines (195 loc) • 9.49 kB
JavaScript
/**
* PeerPigeon MINIMAL Browser Implementation
* Stripped down to absolute essentials: peer connections, data channels, encryption
*/
// Load PeerPigeon from the global bundle
let PeerPigeon;
// Wait for PeerPigeon to be available
async function waitForPeerPigeon() {
return new Promise((resolve) => {
const checkPeerPigeon = () => {
if (window.PeerPigeon) {
PeerPigeon = window.PeerPigeon;
resolve();
} else {
setTimeout(checkPeerPigeon, 100);
}
};
checkPeerPigeon();
});
}
class MinimalPeerPigeon {
constructor() {
this.mesh = null;
this.connected = false;
this.peerCount = 0;
this.init();
}
async init() {
// Wait for PeerPigeon to be loaded
await waitForPeerPigeon();
this.log('🚀 Initializing minimal PeerPigeon...');
try {
// Create mesh with ONLY essential features
this.mesh = new PeerPigeon.PeerPigeonMesh({
enableWebDHT: false, // DISABLED - not needed for basic connectivity
enableCrypto: true, // ENABLED - needed for key exchange
enableDistributedStorage: false, // DISABLED - not needed for basic connectivity
maxPeers: 2, // MINIMAL - only 2 peers max
minPeers: 1, // MINIMAL - only 1 peer min
autoDiscovery: true, // ENABLED - needed for peer discovery
evictionStrategy: false, // DISABLED - not needed for basic connectivity
xorRouting: false // DISABLED - not needed for basic connectivity
});
await this.mesh.init();
this.setupDetailedEventListeners();
document.getElementById('peer-id').textContent = this.mesh.peerId.substring(0, 8) + '...';
document.getElementById('status').textContent = 'Ready';
this.log('✅ Minimal PeerPigeon initialized');
this.log(`🆔 Peer ID: ${this.mesh.peerId}`);
} catch (error) {
this.log(`❌ Initialization failed: ${error.message}`, 'error');
}
}
setupDetailedEventListeners() {
// Connection events with detailed debugging
this.mesh.addEventListener('peerConnected', (data) => {
this.peerCount++;
this.log(`🤝 PEER CONNECTED: ${data.peerId.substring(0, 8)}...`, 'success');
this.log(`🔍 Connection details: reason=${data.reason || 'unknown'}, total peers=${this.peerCount}`);
document.getElementById('peer-count').textContent = this.peerCount;
});
this.mesh.addEventListener('peerDisconnected', (data) => {
this.peerCount--;
this.log(`👋 PEER DISCONNECTED: ${data.peerId.substring(0, 8)}... (${data.reason})`, 'warning');
document.getElementById('peer-count').textContent = this.peerCount;
});
this.mesh.addEventListener('peerDiscovered', (data) => {
this.log(`🔍 PEER DISCOVERED: ${data.peerId.substring(0, 8)}...`);
this.log(`🔍 Discovery details: method=${data.method || 'signaling'}, timestamp=${Date.now()}`);
});
// Message events
this.mesh.addEventListener('messageReceived', (data) => {
this.log(`📨 MESSAGE FROM ${data.fromPeerId.substring(0, 8)}...: ${JSON.stringify(data.message)}`, 'success');
});
// Crypto events
this.mesh.addEventListener('cryptoReady', () => {
this.log('🔐 CRYPTO READY');
});
this.mesh.addEventListener('peerKeyAdded', (data) => {
this.log(`🔐 KEY EXCHANGE COMPLETED: ${data.peerId.substring(0, 8)}...`, 'success');
});
// Enhanced status events with detailed logging
this.mesh.addEventListener('statusChanged', (data) => {
this.log(`📊 STATUS: ${data.type} - ${data.message || 'No message'}`);
if (data.type === 'connected') {
this.connected = true;
document.getElementById('status').textContent = 'Connected to signaling';
this.log('🟢 CONNECTED to signaling server', 'success');
} else if (data.type === 'disconnected') {
this.connected = false;
document.getElementById('status').textContent = 'Disconnected';
this.log('🔴 DISCONNECTED from signaling server', 'error');
}
});
// Error events with enhanced debugging
this.mesh.addEventListener('error', (data) => {
this.log(`❌ ERROR: ${data.error.message}`, 'error');
this.log(`🔍 Error details: ${JSON.stringify(data)}`, 'error');
});
// REAL DATA CHANNEL EVENTS - these should exist
this.mesh.connectionManager?.addEventListener('dataChannelOpen', (data) => {
this.log(`� DATA CHANNEL OPENED: ${data.peerId.substring(0, 8)}...`, 'success');
});
// Check for peer connection events from ConnectionManager
if (this.mesh.connectionManager) {
this.log('🔍 ConnectionManager found, setting up peer connection listeners');
// Monitor new peer connections
this.mesh.connectionManager.addEventListener('peerConnected', (data) => {
this.log(`� PEER CONNECTION ESTABLISHED: ${data.peerId.substring(0, 8)}...`, 'success');
});
}
// Add a debug function to check connection states
setInterval(() => {
if (this.mesh && this.mesh.connectionManager) {
const peers = this.mesh.connectionManager.peers || new Map();
const connectedPeers = [];
peers.forEach((peerConnection, peerId) => {
const state = peerConnection.connection?.connectionState || 'unknown';
const iceState = peerConnection.connection?.iceConnectionState || 'unknown';
const signalingState = peerConnection.connection?.signalingState || 'unknown';
const dataChannelState = peerConnection.dataChannel?.readyState || 'no-channel';
const isInitiator = peerConnection.isInitiator;
if (state !== 'new' || iceState !== 'new') {
connectedPeers.push({
id: peerId.substring(0, 8),
connection: state,
ice: iceState,
signaling: signalingState,
dataChannel: dataChannelState,
initiator: isInitiator
});
}
});
if (connectedPeers.length > 0) {
connectedPeers.forEach(peer => {
this.log(`🔍 PEER STATUS: ${peer.id}... | conn:${peer.connection} | ice:${peer.ice} | sig:${peer.signaling} | data:${peer.dataChannel} | init:${peer.initiator}`);
});
}
}
}, 5000); // Check every 5 seconds
}
async connect() {
if (!this.mesh) {
this.log('❌ Mesh not initialized', 'error');
return;
}
try {
this.log('🔌 Connecting to signaling server...');
await this.mesh.connect('ws://localhost:3000');
this.log('✅ Connection initiated', 'success');
} catch (error) {
this.log(`❌ Connection failed: ${error.message}`, 'error');
}
}
sendMessage() {
if (!this.mesh || this.peerCount === 0) {
this.log('❌ No connected peers', 'error');
return;
}
const testMessage = {
type: 'test',
content: 'Hello from minimal browser!',
timestamp: Date.now()
};
try {
this.mesh.broadcast(testMessage);
this.log(`📤 BROADCAST: ${JSON.stringify(testMessage)}`);
} catch (error) {
this.log(`❌ Broadcast failed: ${error.message}`, 'error');
}
}
log(message, type = 'info') {
const logElement = document.getElementById('log');
const timestamp = new Date().toLocaleTimeString();
const className = type === 'error' ? 'error' : type === 'success' ? 'success' : type === 'warning' ? 'warning' : '';
const logEntry = document.createElement('div');
logEntry.className = className;
logEntry.textContent = `[${timestamp}] ${message}`;
logElement.appendChild(logEntry);
logElement.scrollTop = logElement.scrollHeight;
// Also log to console for debugging
console.log(`[MINIMAL] ${message}`);
}
clearLog() {
document.getElementById('log').innerHTML = '';
}
}
// Global functions for HTML buttons
const minimalPeer = new MinimalPeerPigeon();
window.minimalPeer = minimalPeer;
window.connect = () => minimalPeer.connect();
window.sendMessage = () => minimalPeer.sendMessage();
window.clearLog = () => minimalPeer.clearLog();
// Debug: Expose mesh for console inspection
window.mesh = () => minimalPeer.mesh;