pigeonrtc
Version:
Pluggable cross-browser compatible WebRTC library for PeerPigeon
3 lines (2 loc) • 14.9 kB
JavaScript
var PigeonRTC=(()=>{var g=Object.create;var p=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var _=Object.getOwnPropertyNames;var R=Object.getPrototypeOf,y=Object.prototype.hasOwnProperty;var m=(n=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(n,{get:(e,t)=>(typeof require<"u"?require:e)[t]}):n)(function(n){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+n+'" is not supported')});var T=(n,e)=>{for(var t in e)p(n,t,{get:e[t],enumerable:!0})},C=(n,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of _(e))!y.call(n,s)&&s!==t&&p(n,s,{get:()=>e[s],enumerable:!(i=v(e,s))||i.enumerable});return n};var f=(n,e,t)=>(t=n!=null?g(R(n)):{},C(e||!n||!n.__esModule?p(t,"default",{value:n,enumerable:!0}):t,n)),E=n=>C(p({},"__esModule",{value:!0}),n);var z={};T(z,{BrowserRTCAdapter:()=>d,MDNSResolver:()=>l,NodeRTCAdapter:()=>c,PeerConnection:()=>o,PigeonRTC:()=>h,RTCAdapter:()=>r,SignalingClient:()=>a,createPigeonRTC:()=>w,default:()=>S});var r=class{getRTCPeerConnection(){throw new Error("getRTCPeerConnection must be implemented by adapter")}getRTCSessionDescription(){throw new Error("getRTCSessionDescription must be implemented by adapter")}getRTCIceCandidate(){throw new Error("getRTCIceCandidate must be implemented by adapter")}getMediaStream(){return null}isSupported(){throw new Error("isSupported must be implemented by adapter")}getName(){throw new Error("getName must be implemented by adapter")}async initialize(){}async getUserMedia(e){throw new Error("getUserMedia not supported by this adapter")}async getDisplayMedia(e){throw new Error("getDisplayMedia not supported by this adapter")}};var d=class extends r{constructor(){super(),this._checkSupport()}_checkSupport(){typeof window>"u"||typeof navigator>"u"||(this.hasRTCPeerConnection=!!(window.RTCPeerConnection||window.webkitRTCPeerConnection||window.mozRTCPeerConnection),this.hasGetUserMedia=!!(navigator.mediaDevices?.getUserMedia||navigator.getUserMedia||navigator.webkitGetUserMedia||navigator.mozGetUserMedia),this.hasGetDisplayMedia=!!navigator.mediaDevices?.getDisplayMedia)}getRTCPeerConnection(){if(typeof window>"u")throw new Error("BrowserRTCAdapter requires a browser environment");return window.RTCPeerConnection||window.webkitRTCPeerConnection||window.mozRTCPeerConnection}getRTCSessionDescription(){if(typeof window>"u")throw new Error("BrowserRTCAdapter requires a browser environment");return window.RTCSessionDescription||window.mozRTCSessionDescription}getRTCIceCandidate(){if(typeof window>"u")throw new Error("BrowserRTCAdapter requires a browser environment");return window.RTCIceCandidate||window.mozRTCIceCandidate}getMediaStream(){return typeof window>"u"?null:window.MediaStream||window.webkitMediaStream}isSupported(){return typeof window<"u"&&this.hasRTCPeerConnection}getName(){return"BrowserRTCAdapter"}async getUserMedia(e){if(typeof navigator>"u")throw new Error("getUserMedia requires a browser environment");if(navigator.mediaDevices?.getUserMedia)return await navigator.mediaDevices.getUserMedia(e);let t=navigator.getUserMedia||navigator.webkitGetUserMedia||navigator.mozGetUserMedia;if(!t)throw new Error("getUserMedia is not supported in this browser");return new Promise((i,s)=>{t.call(navigator,e,i,s)})}async getDisplayMedia(e){if(typeof navigator>"u")throw new Error("getDisplayMedia requires a browser environment");if(!navigator.mediaDevices?.getDisplayMedia)throw new Error("getDisplayMedia is not supported in this browser");return await navigator.mediaDevices.getDisplayMedia(e)}};var c=class extends r{constructor(){super(),this._wrtc=null,this._initialized=!1}async initialize(){if(!this._initialized)try{let e=await import("@koush/wrtc");this._wrtc=e.default||e,this._initialized=!0}catch{throw new Error("NodeRTCAdapter requires @koush/wrtc to be installed. Install it with: npm install @koush/wrtc")}}_ensureInitialized(){if(!this._initialized||!this._wrtc)throw new Error("NodeRTCAdapter not initialized. Call initialize() first.")}getRTCPeerConnection(){return this._ensureInitialized(),this._wrtc.RTCPeerConnection}getRTCSessionDescription(){return this._ensureInitialized(),this._wrtc.RTCSessionDescription}getRTCIceCandidate(){return this._ensureInitialized(),this._wrtc.RTCIceCandidate}getMediaStream(){return this._ensureInitialized(),this._wrtc.MediaStream||null}isSupported(){return typeof process<"u"&&process.versions!=null&&process.versions.node!=null&&typeof window>"u"}getName(){return"NodeRTCAdapter"}async getUserMedia(e){throw new Error("getUserMedia is not supported in Node.js environment")}async getDisplayMedia(e){throw new Error("getDisplayMedia is not supported in Node.js environment")}};var a=class extends EventTarget{constructor(e){super(),this.serverUrl=e,this.ws=null,this.clientId=null,this.connected=!1,this.reconnectAttempts=0,this.maxReconnectAttempts=5,this.reconnectDelay=1e3}connect(){return new Promise((e,t)=>{try{this.ws=new WebSocket(this.serverUrl),this.ws.onopen=()=>{this.connected=!0,this.reconnectAttempts=0,this.dispatchEvent(new CustomEvent("connected")),e()},this.ws.onmessage=i=>{try{let s=JSON.parse(i.data);this.handleMessage(s)}catch(s){console.error("Error parsing message:",s)}},this.ws.onerror=i=>{this.dispatchEvent(new CustomEvent("error",{detail:i})),t(i)},this.ws.onclose=()=>{this.connected=!1,this.dispatchEvent(new CustomEvent("disconnected")),this.attemptReconnect()}}catch(i){t(i)}})}handleMessage(e){switch(e.type){case"id":this.clientId=e.id,this.dispatchEvent(new CustomEvent("id",{detail:{id:e.id}}));break;case"clients":this.dispatchEvent(new CustomEvent("clients",{detail:{clients:e.clients}}));break;case"offer":case"answer":case"ice-candidate":this.dispatchEvent(new CustomEvent("signal",{detail:e}));break;default:console.warn("Unknown message type:",e.type)}}send(e){if(this.ws&&this.ws.readyState===WebSocket.OPEN)this.ws.send(JSON.stringify(e));else throw new Error("WebSocket not connected")}sendOffer(e,t){this.send({type:"offer",to:e,offer:t})}sendAnswer(e,t){this.send({type:"answer",to:e,answer:t})}sendIceCandidate(e,t){this.send({type:"ice-candidate",to:e,candidate:t})}attemptReconnect(){this.reconnectAttempts<this.maxReconnectAttempts&&(this.reconnectAttempts++,setTimeout(()=>{console.log(`Reconnecting... attempt ${this.reconnectAttempts}`),this.connect().catch(e=>{console.error("Reconnect failed:",e)})},this.reconnectDelay*this.reconnectAttempts))}disconnect(){this.ws&&(this.ws.close(),this.ws=null,this.connected=!1,this.clientId=null)}isConnected(){return this.connected&&this.ws&&this.ws.readyState===WebSocket.OPEN}getClientId(){return this.clientId}};var l=class{constructor(e={}){this._resolver=null,this._initialized=!1,this._cache=new Map,this._cacheTimeout=6e4,this._mode=null,this._serverUrl=e.serverUrl||"http://localhost:5380"}async initialize(){if(this._initialized)return;if(typeof process<"u"&&process.versions!=null&&process.versions.node!=null)try{let t=await import("pigeonns"),i=t.default||t;this._resolver=new i({timeout:5e3,ttl:60,cacheSize:1e3}),this._resolver.start(),this._mode="node",this._initialized=!0,console.log("\u2713 mDNS resolver initialized (Node.js mode)")}catch(t){console.warn("Failed to initialize Node.js mDNS resolver:",t.message),this._initialized=!1}else try{(await fetch(`${this._serverUrl}/health`,{method:"GET",signal:AbortSignal.timeout(2e3)})).ok?(this._mode="browser",this._initialized=!0,console.log(`\u2713 mDNS resolver initialized (Browser mode) - Server: ${this._serverUrl}`)):(console.warn(`pigeonns server not responding at ${this._serverUrl}`),this._initialized=!1)}catch(t){console.warn(`Failed to connect to pigeonns server at ${this._serverUrl}:`,t.message),console.warn("mDNS resolution will be disabled. Start pigeonns server with: npx pigeonns serve"),this._initialized=!1}}isAvailable(){return this._initialized&&this._resolver!==null}isLocalCandidate(e){return!e||!e.candidate?!1:e.candidate.includes(".local")}_extractHostname(e){let t=e.split(" ");for(let i=0;i<t.length;i++)if(t[i].endsWith(".local"))return t[i];return null}_getCachedIP(e){let t=this._cache.get(e);return t&&Date.now()-t.timestamp<this._cacheTimeout?t.ip:null}_setCachedIP(e,t){this._cache.set(e,{ip:t,timestamp:Date.now()})}async resolve(e){if(!this.isAvailable())return console.warn("mDNS resolver not available"),null;let t=this._getCachedIP(e);if(t)return t;try{let i=null;if(this._mode==="node")i=await this._resolver.resolve(e,"A");else if(this._mode==="browser"){let s=await fetch(`${this._serverUrl}/resolve?name=${encodeURIComponent(e)}&type=A`,{method:"GET",signal:AbortSignal.timeout(5e3)});s.ok?i=(await s.json()).address:console.warn(`Failed to resolve ${e}: HTTP ${s.status}`)}return i?(this._setCachedIP(e,i),i):null}catch(i){return console.warn(`Failed to resolve ${e}:`,i.message),null}}async resolveCandidate(e){if(!e||!e.candidate)return null;if(!this.isLocalCandidate(e))return e;let t=this._extractHostname(e.candidate);if(!t)return console.warn("Could not extract hostname from candidate:",e.candidate),null;let i=await this.resolve(t);if(!i)return console.warn(`Could not resolve ${t} to IP address`),null;let s=e.candidate.replace(t,i);return{...e,candidate:s,address:i}}clearCache(){this._cache.clear()}dispose(){if(this.clearCache(),this._mode==="node"&&this._resolver)try{this._resolver.stop()}catch(e){console.warn("Error stopping mDNS resolver:",e.message)}this._resolver=null,this._initialized=!1,this._mode=null}};var o=class extends EventTarget{constructor(e,t,i={}){super(),this.rtc=e,this.signaling=t,this.config=i,this.pc=null,this.dataChannels=new Map,this.remoteId=null,this.isInitiator=!1,this.mdnsResolver=new l({serverUrl:i.mdnsServerUrl||"http://localhost:5380"}),this._mdnsEnabled=i.enableMDNS!==!1}async _init(){this._mdnsEnabled&&await this.mdnsResolver.initialize(),this.pc=this.rtc.createPeerConnection(this.config),this.pc.onicecandidate=async e=>{if(e.candidate&&this.remoteId){let t=e.candidate;if(this._mdnsEnabled&&this.mdnsResolver.isAvailable()&&this.mdnsResolver.isLocalCandidate(e.candidate)){let i=await this.mdnsResolver.resolveCandidate(e.candidate);i&&(t=i,console.log("Resolved .local ICE candidate:",e.candidate.candidate,"->",i.candidate))}this.signaling.sendIceCandidate(this.remoteId,t)}},this.pc.onconnectionstatechange=()=>{this.dispatchEvent(new CustomEvent("connectionstatechange",{detail:this.pc.connectionState})),this.pc.connectionState==="connected"?this.dispatchEvent(new CustomEvent("connected")):this.pc.connectionState==="failed"&&this.dispatchEvent(new CustomEvent("failed"))},this.pc.oniceconnectionstatechange=()=>{this.dispatchEvent(new CustomEvent("iceconnectionstatechange",{detail:this.pc.iceConnectionState}))},this.pc.ontrack=e=>{this.dispatchEvent(new CustomEvent("track",{detail:{track:e.track,streams:e.streams}}))},this.pc.ondatachannel=e=>{let t=e.channel;this.dataChannels.set(t.label,t),this._setupDataChannel(t),this.dispatchEvent(new CustomEvent("datachannel",{detail:t}))}}async connect(e,t=null){this.remoteId=e,this.isInitiator=!0,await this._init(),t&&t.getTracks().forEach(s=>{this.pc.addTrack(s,t)});let i=await this.pc.createOffer();await this.pc.setLocalDescription(i),this.signaling.sendOffer(e,i)}async handleOffer(e,t,i=null){this.remoteId=e,this.isInitiator=!1,await this._init(),i&&i.getTracks().forEach(u=>{this.pc.addTrack(u,i)}),await this.pc.setRemoteDescription(t);let s=await this.pc.createAnswer();await this.pc.setLocalDescription(s),this.signaling.sendAnswer(e,s)}async handleAnswer(e){await this.pc.setRemoteDescription(e)}async handleIceCandidate(e){if(this.pc){let t=e;if(this._mdnsEnabled&&this.mdnsResolver.isAvailable()&&this.mdnsResolver.isLocalCandidate(e)){let i=await this.mdnsResolver.resolveCandidate(e);i&&(t=i,console.log("Resolved incoming .local ICE candidate:",e.candidate,"->",i.candidate))}await this.pc.addIceCandidate(t)}}createDataChannel(e,t={}){if(!this.pc)throw new Error("Peer connection not initialized");let i=this.pc.createDataChannel(e,t);return this.dataChannels.set(e,i),this._setupDataChannel(i),i}_setupDataChannel(e){e.onopen=()=>{this.dispatchEvent(new CustomEvent("channelopen",{detail:e}))},e.onmessage=t=>{this.dispatchEvent(new CustomEvent("message",{detail:{channel:e.label,data:t.data}}))},e.onclose=()=>{this.dataChannels.delete(e.label),this.dispatchEvent(new CustomEvent("channelclose",{detail:e}))}}send(e,t){let i=this.dataChannels.get(e);if(i&&i.readyState==="open")i.send(t);else throw new Error(`Channel ${e} not open`)}getDataChannel(e){return this.dataChannels.get(e)}getRTCPeerConnection(){return this.pc}close(){this.pc&&(this.pc.close(),this.pc=null),this.dataChannels.clear(),this.remoteId=null,this.mdnsResolver&&this.mdnsResolver.dispose()}};var h=class{constructor(e={}){this.adapter=e.adapter||null,this.initialized=!1}async initialize(e={}){this.initialized||(e.adapter&&(this.adapter=e.adapter),this.adapter||(this.adapter=await this._detectAdapter(e)),await this.adapter.initialize(),this.initialized=!0)}async _detectAdapter(e={}){if(e.preferNode||typeof window>"u"&&typeof process<"u"){let i=new c;if(i.isSupported())try{return await i.initialize(),i}catch(s){console.warn("Node adapter initialization failed, trying browser adapter:",s.message)}}let t=new d;if(t.isSupported())return t;throw new Error("No supported WebRTC adapter found. Make sure you are running in a browser with WebRTC support or have @koush/wrtc installed for Node.js.")}_ensureInitialized(){if(!this.initialized||!this.adapter)throw new Error("PigeonRTC not initialized. Call initialize() first.")}getRTCPeerConnection(){return this._ensureInitialized(),this.adapter.getRTCPeerConnection()}getRTCSessionDescription(){return this._ensureInitialized(),this.adapter.getRTCSessionDescription()}getRTCIceCandidate(){return this._ensureInitialized(),this.adapter.getRTCIceCandidate()}getMediaStream(){return this._ensureInitialized(),this.adapter.getMediaStream()}createPeerConnection(e){this._ensureInitialized();let t=this.adapter.getRTCPeerConnection();return new t(e)}createSessionDescription(e){this._ensureInitialized();let t=this.adapter.getRTCSessionDescription();return new t(e)}createIceCandidate(e){this._ensureInitialized();let t=this.adapter.getRTCIceCandidate();return new t(e)}async getUserMedia(e){return this._ensureInitialized(),await this.adapter.getUserMedia(e)}async getDisplayMedia(e){return this._ensureInitialized(),await this.adapter.getDisplayMedia(e)}isSupported(){return this.adapter?this.adapter.isSupported():!1}getAdapterName(){return this.adapter?this.adapter.getName():"None"}createSignalingClient(e){return new a(e)}createManagedPeerConnection(e,t={}){return this._ensureInitialized(),new o(this,e,t)}};async function w(n={}){let e=new h(n);return await e.initialize(n),e}var S=w;return E(z);})();
//# sourceMappingURL=browser.min.js.map