UNPKG

@rtco/peer

Version:
2 lines (1 loc) 4.83 kB
import{EventEmitter as H}from"eventemitter3";class z{#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((w,F)=>{if(w instanceof Error)q[F]=`(${w.name}) ${w.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 A=()=>{return Math.random().toString(36).slice(2)};class B extends H{#j;#P;#q;#w=!1;#z=!1;#A;#B={iceServers:[{urls:"stun:stun.l.google.com:19302"},{urls:"stun:stun1.l.google.com:19302"}]};#F;#H;constructor(P){super();this.#j=new z("[peer]",P?.debug??1),this.#j.debug("new Peer:",P),this.#A=P?.initiator??!1,this.#B={...this.#B,...P?.config},this.#F=P?.channelName??`dc_${A()}`,this.#H=P?.channelConfig??{};try{this.#P=new RTCPeerConnection(this.#B),this.#M()}catch{throw new Error("WebRTC is not supported by this browser")}if(this.#A)this.#q=this.#P.createDataChannel(this.#F,this.#H),this.#J()}get ready(){return this.#q?.readyState==="open"}get#K(){return!this.#A}destroy=()=>{if(this.#j.debug("destroy()"),this.#q)this.#R(),this.#q.close(),this.#q=void 0;this.#Q(),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.#z)throw j}else if(P.type==="sdp"){let j=P.data,q=j.type==="offer"&&(this.#w||this.#P.signalingState!=="stable");if(this.#z=!this.#K&&q,this.#z){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)};#M=()=>{this.#P.onnegotiationneeded=this.#S,this.#P.onicecandidate=this.#U,this.#P.onicecandidateerror=(P)=>{this.#j.debug("onicecandidateerror:",P)},this.#P.oniceconnectionstatechange=this.#V,this.#P.ontrack=this.#X,this.#P.ondatachannel=this.#Y,this.#P.onicegatheringstatechange=this.#W};#Q=()=>{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};#J=()=>{if(!this.#q)return this.emit("error",new Error("Tried to setup undefined data channel.")),this.destroy();this.#F=this.#q.label,this.#q.onopen=()=>{this.#Z()},this.#q.onclose=()=>{this.#_()},this.#q.onerror=(P)=>{let j=P,q=j.error.message,w=j.error instanceof Error?j.error:new Error(`Datachannel error: ${q}`);this.emit("error",w),this.destroy()},this.#q.onmessage=this.#$};#R=()=>{if(!this.#q)return;this.#q.onopen=null,this.#q.onclose=null,this.#q.onerror=null,this.#q.onmessage=null};#S=async()=>{this.#j.debug("onNegotiationNeeded()");try{this.#w=!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.#w=!1}};#U=(P)=>{if(this.#j.debug(`onIceCandidate(${P.candidate?.candidate})`),P.candidate)this.emit("signal",{type:"candidate",data:P.candidate})};#V=()=>{switch(this.#j.debug(`onIceConnectionStateChange(${this.#P.iceConnectionState})`),this.#P.iceConnectionState){case"disconnected":this.destroy();break;case"failed":this.#P.restartIce();break;default:break}};#W=()=>{this.#j.debug(`onIceGatheringStateChange(${this.#P.iceGatheringState})`)};#X=(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)};#Y=(P)=>{this.#j.debug("onDataChannel",P),this.#q=P.channel,this.#J()};#Z=()=>{this.#j.debug("onChannelOpen"),this.emit("connect")};#_=()=>{this.#j.debug("onChannelClose"),this.destroy()};#$=(P)=>{this.#j.debug("onChannelMessage",P.data);let{data:j}=P;this.emit("data",j)}}export{B as default};