UNPKG

@rtco/peer

Version:
2 lines (1 loc) 5.19 kB
import{EventEmitter as J}from"eventemitter3";class A{#j=0;#P;constructor(P="[artico]",j=1){this.#P=P,this.#j=j}get logLevel(){return this.#j}set logLevel(P){this.#j=P}debug(...P){if(this.#j>=4)this.#q(4,...P)}log(...P){if(this.#j>=3)this.#q(4,...P)}warn(...P){if(this.#j>=2)this.#q(2,...P)}error(...P){if(this.#j>=1)this.#q(1,...P)}#q(P,...j){let q=[this.#P,...j];if(q.forEach((z,H)=>{if(z instanceof Error)q[H]=`(${z.name}) ${z.message}`}),P>=4)console.debug(...q);else if(P>=3)console.log(...q);else if(P>=2)console.warn("WARNING",...q);else if(P>=1)console.error("ERROR",...q)}}var B=()=>{return Math.random().toString(36).slice(2)};class F extends J{#j;#P;#q;#z=!1;#A=!1;#B;#F={iceServers:[{urls:"stun:stun.l.google.com:19302"},{urls:"stun:stun1.l.google.com:19302"}]};#H;#J;constructor(P){super();this.#j=new A("[peer]",P?.debug??1),this.#j.debug("new Peer:",P),this.#B=P?.initiator??!1,this.#F={...this.#F,...P?.config},this.#H=P?.channelName??`dc_${B()}`,this.#J=P?.channelConfig??{};try{this.#P=new RTCPeerConnection(this.#F),this.#Q()}catch{throw new Error("WebRTC is not supported by this browser")}if(this.#B)this.#q=this.#P.createDataChannel(this.#H,this.#J),this.#K()}get ready(){return this.#q?.readyState==="open"}get#M(){return!this.#B}destroy=()=>{if(this.#j.debug("destroy()"),this.#q)this.#S(),this.#q.close(),this.#q=void 0;this.#R(),this.#P.close(),this.emit("close"),this.removeAllListeners()};signal=async(P)=>{this.#j.debug(`signal(${P.type})`);try{if(P.type==="candidate")try{await this.#P.addIceCandidate(P.data)}catch(j){if(!this.#A)throw j}else if(P.type==="sdp"){let j=P.data,q=j.type==="offer"&&(this.#z||this.#P.signalingState!=="stable");if(this.#A=!this.#M&&q,this.#A){this.#j.debug("ignoring offer");return}if(await this.#P.setRemoteDescription(j),j.type==="offer")await this.#P.setLocalDescription(),this.emit("signal",{type:"sdp",data:this.#P.localDescription})}}catch(j){this.emit("error",j)}};send(P){if(this.#j.debug(`send(${P})`),!this.#q||!this.ready)throw new Error("Connection is not established yet.");this.#q.send(P)}addStream=(P)=>{this.#j.debug(`addStream(${P.id})`);for(let j of P.getTracks())this.#P.addTrack(j,P)};removeStream=(P)=>{this.#j.debug(`removeStream(${P.id})`);for(let j of P.getTracks())this.removeTrack(j)};addTrack=(P,j)=>{this.#j.debug(`addTrack(${P.id}, ${j.id})`),this.#P.addTrack(P,j)};removeTrack=(P)=>{this.#j.debug(`removeTrack(${P.id})`);let j=this.#P.getSenders().find((q)=>q.track===P);if(j)this.#j.debug("removeTrack(sender)"),this.#P.removeTrack(j)};replaceTrack=async(P,j)=>{this.#j.debug(`replaceTrack(${P.id}, ${j.id})`);let q=this.#P.getSenders().find((z)=>z.track===P);if(q){this.#j.debug("replaceTrack(sender)");try{await q.replaceTrack(j),this.emit("replacetrack",P,j)}catch(z){throw new Error(`Failed to replace track: ${z}`)}}else throw new Error("Failed to replace track: sender not found")};#Q=()=>{this.#P.onnegotiationneeded=this.#U,this.#P.onicecandidate=this.#V,this.#P.onicecandidateerror=(P)=>{this.#j.debug("onicecandidateerror:",P)},this.#P.oniceconnectionstatechange=this.#W,this.#P.ontrack=this.#Y,this.#P.ondatachannel=this.#Z,this.#P.onicegatheringstatechange=this.#X};#R=()=>{this.#P.onnegotiationneeded=null,this.#P.onicecandidate=null,this.#P.onicecandidateerror=null,this.#P.oniceconnectionstatechange=null,this.#P.ontrack=null,this.#P.ondatachannel=null,this.#P.onicegatheringstatechange=null};#K=()=>{if(!this.#q)return this.emit("error",new Error("Tried to setup undefined data channel.")),this.destroy();this.#H=this.#q.label,this.#q.onopen=()=>{this.#_()},this.#q.onclose=()=>{this.#$()},this.#q.onerror=(P)=>{let j=P,q=j.error.message,z=j.error instanceof Error?j.error:new Error(`Datachannel error: ${q}`);this.emit("error",z),this.destroy()},this.#q.onmessage=this.#E};#S=()=>{if(!this.#q)return;this.#q.onopen=null,this.#q.onclose=null,this.#q.onerror=null,this.#q.onmessage=null};#U=async()=>{this.#j.debug("onNegotiationNeeded()");try{this.#z=!0,await this.#P.setLocalDescription(),this.emit("signal",{type:"sdp",data:this.#P.localDescription})}catch(P){this.emit("error",new Error(`Failed to create offer: ${P}`))}finally{this.#z=!1}};#V=(P)=>{if(this.#j.debug(`onIceCandidate(${P.candidate?.candidate})`),P.candidate)this.emit("signal",{type:"candidate",data:P.candidate})};#W=()=>{switch(this.#j.debug(`onIceConnectionStateChange(${this.#P.iceConnectionState})`),this.#P.iceConnectionState){case"disconnected":this.destroy();break;case"failed":this.#P.restartIce();break;default:break}};#X=()=>{this.#j.debug(`onIceGatheringStateChange(${this.#P.iceGatheringState})`)};#Y=(P)=>{this.#j.debug("onTrack",P);let j=P.streams[0]??new MediaStream;if(j.onremovetrack=(q)=>{if(this.emit("removetrack",q.track,j),j.getTracks().length===0)this.emit("removestream",j)},P.streams.length)this.emit("stream",j);else j.addTrack(P.track),this.emit("stream",j);this.emit("track",P.track,j)};#Z=(P)=>{this.#j.debug("onDataChannel",P),this.#q=P.channel,this.#K()};#_=()=>{this.#j.debug("onChannelOpen"),this.emit("connect")};#$=()=>{this.#j.debug("onChannelClose"),this.destroy()};#E=(P)=>{this.#j.debug("onChannelMessage",P.data);let{data:j}=P;this.emit("data",j)}}export{F as default};