fastrtc 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var R=Object.defineProperty;var N=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var I=Object.prototype.hasOwnProperty;var K=(i,e)=>{for(var t in e)R(i,t,{get:e[t],enumerable:!0})},q=(i,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of j(e))!I.call(i,r)&&r!==t&&R(i,r,{get:()=>e[r],enumerable:!(n=N(e,r))||n.enumerable});return i};var V=i=>q(R({},"__esModule",{value:!0}),i);var H={};K(H,{FastDataChannel:()=>w,RTCManager:()=>k});module.exports=V(H);var F={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},L=4;var p=class{#t=new Map;on(e,t){let n=t,r=this.#t.get(e)??[];return r.push(n),this.#t.set(e,r),()=>{let s=this.#t.get(e);if(!s)return;let a=s.indexOf(n);a>=0&&s.splice(a,1),s.length===0&&this.#t.delete(e)}}has(e){return(this.#t.get(e)?.length??0)>0}emit(e,...t){let n=this.#t.get(e);if(n)for(let r of n.slice())r(...t)}destroy(){this.#t.clear()}};function B(i,e){let t=new ArrayBuffer(2+e.byteLength),n=new DataView(t);return n.setUint8(0,252),n.setUint8(1,i),new Uint8Array(t,2).set(e),t}function U(i){if(i.byteLength<2)return null;let e=new DataView(i);return e.getUint8(0)!==252?null:{flags:e.getUint8(1),payload:new Uint8Array(i,2)}}function _(i){return i instanceof Blob||i instanceof ArrayBuffer||ArrayBuffer.isView(i)}function O(i){if(i instanceof Blob)return i.stream();if(i instanceof ArrayBuffer||ArrayBuffer.isView(i)){let t=T(i);return new ReadableStream({start(n){n.enqueue(t),n.close()}})}if(i instanceof ReadableStream)return i;let e=i[Symbol.asyncIterator]();return new ReadableStream({async pull(t){let{done:n,value:r}=await e.next();if(n){t.close();return}t.enqueue(T(r))},cancel:()=>{e.return?.()}})}function T(i){if(i instanceof Uint8Array)return i;if(i instanceof ArrayBuffer)return new Uint8Array(i);if(ArrayBuffer.isView(i))return new Uint8Array(i.buffer,i.byteOffset,i.byteLength);if(typeof i=="string")return new TextEncoder().encode(i);throw i instanceof Blob?new Error("Pass the Blob/File to send, not blob chunks"):new Error("Unsupported stream chunk")}var S=class{#t;#e=0;#s=[];#i=!1;constructor(e){this.#t=e}async acquire(){if(this.#i)throw new Error("Transfer limiter destroyed");if(this.#e<this.#t)this.#e++;else{let t=Promise.withResolvers();if(this.#s.push(t),await t.promise,this.#i)throw this.#e--,new Error("Transfer limiter destroyed")}let e=!1;return()=>{e||(e=!0,this.#n())}}destroy(){this.#i=!0;let e=new Error("Transfer limiter destroyed");for(let t of this.#s.splice(0))t.reject(e)}#n(){if(this.#i){this.#e--;return}let e=this.#s.shift();if(e){e.resolve();return}this.#e--}};function Z(i){let e=i?.maxMessageSize;return!e||!Number.isFinite(e)?65536:e}var w=class extends p{channel;#t=0;#e=null;#s=null;#i;#n=[];#r=null;#a=null;#o=!1;#l=!1;#u=Promise.resolve();#d=0;constructor(e,t,n=new S(1/0)){super(),this.channel=e,this.#i=n,t instanceof RTCPeerConnection?this.#e=t:this.#s=t,this.#f(),this.channel.binaryType="arraybuffer",this.channel.onopen=()=>this.#m(),this.channel.onclose=()=>this.destroy(),this.channel.onmessage=r=>this.#C(r.data),this.channel.onerror=r=>this.emit("error",r),this.channel.onbufferedamountlow=()=>{this.#c(),this.#p()},this.channel.readyState==="open"&&queueMicrotask(()=>this.#m())}async send(e,t={}){if(this.#l)throw new Error("Data channel closed");if(typeof e=="string"){this.#n.push(e),this.#c();return}await this.#v(async()=>{let n=await this.#i.acquire();try{await this.#b(e,t)}finally{n()}})}destroy(){if(this.#l)return;this.#l=!0,this.#n.length=0,this.#o=!1;let e=new Error("Data channel closed");this.#r?.reject(e),this.#r=null,this.#a?.error(e),this.#a=null,this.emit("close"),this.channel.onopen=null,this.channel.onclose=null,this.channel.onmessage=null,this.channel.onerror=null,this.channel.onbufferedamountlow=null,super.destroy();try{this.channel.close()}catch{}}#m(){this.#l||(this.#f(),this.#c(),this.emit("open"))}#f(){let e=Z(this.#s??this.#e?.sctp??null);this.#t=Math.max(1,e-2),this.channel.bufferedAmountLowThreshold=e*2}async#v(e){let t=this.#u,{promise:n,resolve:r}=Promise.withResolvers();this.#u=n,await t;try{await e()}finally{r()}}#S(e,t){if(e instanceof Blob)return e.size-t;if(e instanceof ArrayBuffer||ArrayBuffer.isView(e))return e.byteLength-t}#w(e,t,n){n?.(e,t),this.emit("progress",{direction:"send",bytes:e,total:t})}async#b(e,t){this.#f();let n=t.offset??0;if(n!==0&&!(e instanceof Blob))throw new Error("offset requires a Blob/File");let r=e;n!==0&&e instanceof Blob&&(r=e.slice(n));let s=this.#S(e,n),a=0,h=!1;if(t.meta!==void 0){let l=new TextEncoder().encode(JSON.stringify(t.meta));this.#h(5,l),h=!0}let m=O(r),u=m.getReader(),c=!h,d=null,b=()=>{if(t.signal?.aborted)throw t.signal.reason??new Error("Aborted");if(this.#l)throw new Error("Data channel closed")},y=l=>{if(!d)return;let g=d.payload;this.#h(d.flags|(l?2:0),g),d=null,a+=g.byteLength,this.#w(a,s,t.onProgress)},o=()=>{u.cancel(t.signal?.reason??new Error("Aborted"))};t.signal?.addEventListener("abort",o,{once:!0});try{for(;;){b(),await this.#g(t.signal);let{done:l,value:g}=await u.read();if(l){d?y(!0):c?this.#h(3,new Uint8Array(0)):h&&this.#h(2,new Uint8Array(0));break}let D=T(g),C=0;for(;C<D.byteLength;){b(),await this.#g(t.signal);let P=Math.min(C+this.#t,D.byteLength);y(!1);let G=c?1:0;c=!1,d={flags:G,payload:D.subarray(C,P)},C=P}}}catch(l){try{d?y(!0):c||this.#h(2,new Uint8Array(0))}catch{}try{await u.cancel(l)}catch{}throw l}finally{t.signal?.removeEventListener("abort",o),m.locked&&u.releaseLock()}}#C(e){let t=e instanceof ArrayBuffer?U(e):null;if(!t){this.emit("message",e);return}if(this.#o){t.flags&2&&(this.#o=!1);return}if(!this.#a){if(!(t.flags&1))return;if(!this.has("stream")){this.#o=!(t.flags&2);return}let r;if(t.flags&4)try{r=JSON.parse(new TextDecoder().decode(t.payload))}catch(h){this.emit("error",h),this.#o=!(t.flags&2);return}let s,a=new ReadableStream({start:h=>{s=h,this.#a=h},cancel:()=>{this.#a=null}});if(this.#d=0,this.emit("stream",a,r),t.flags&4){t.flags&2&&(s?.close(),this.#a=null);return}}let n=this.#a;if(n){if(n.enqueue(t.payload),this.#d+=t.payload.byteLength,this.emit("progress",{direction:"receive",bytes:this.#d}),(n.desiredSize??0)<-32){n.error(new Error("Receive stream backlog exceeded")),this.#a=null,this.#o=!(t.flags&2);return}t.flags&2&&(n.close(),this.#a=null)}}#h(e,t){this.#l||(this.#n.push(B(e,t)),this.#c())}#y(){return!this.#l&&this.channel.readyState==="open"&&this.#n.length===0&&this.channel.bufferedAmount<=this.channel.bufferedAmountLowThreshold}#T(){let e=this.#n.findIndex(t=>typeof t=="string");return e>=0?this.#n.splice(e,1)[0]:this.#n.shift()}#c(){if(!(this.#l||this.channel.readyState!=="open")){for(;this.#n.length>0&&!(this.channel.bufferedAmount>this.channel.bufferedAmountLowThreshold);){let e=this.#T();if(e===void 0)break;try{typeof e=="string"?this.channel.send(e):this.channel.send(e)}catch(t){this.emit("error",t),this.#n.unshift(e);break}}this.#p()}}#g(e){if(this.#l)return Promise.reject(new Error("Data channel closed"));if(e?.aborted)return Promise.reject(e.reason??new Error("Aborted"));if(this.#y())return Promise.resolve();this.#r||(this.#r=Promise.withResolvers());let{promise:t}=this.#r,n=()=>{this.#r?.reject(e?.reason??new Error("Aborted")),this.#r=null};return e?.addEventListener("abort",n,{once:!0}),t.finally(()=>{e?.removeEventListener("abort",n)})}#p(){this.#y()&&(this.#r?.resolve(),this.#r=null)}};var E=class{remoteId;channels=new Map;#t;#e;#s;#i=[];#n=!1;#r=!1;constructor(e){this.remoteId=e.remoteId,this.#t=e,this.#s=e.localId>e.remoteId,this.#e=new RTCPeerConnection(e.rtcConfig),this.#e.onicecandidate=t=>{t.candidate&&this.#t.onSignal({type:"candidate",candidate:t.candidate})},this.#e.onnegotiationneeded=async()=>{try{this.#n=!0,await this.#e.setLocalDescription(),this.#t.onSignal({type:"description",description:this.#e.localDescription})}catch{}finally{this.#n=!1}},this.#e.onconnectionstatechange=()=>{["disconnected","failed","closed"].includes(this.#e.connectionState)&&this.#t.onDisconnect()},this.#e.ontrack=t=>{let n=t.streams[0];t.track.onended=()=>this.#t.onTrackEnded(t.track,n),this.#t.onTrack(t.track,n)},this.#e.ondatachannel=t=>this.#a(t.channel)}addTrack(e,t){this.#e.addTrack(e,t)}removeTrack(e){let t=this.#e.getSenders().find(n=>n.track===e);if(t)try{this.#e.removeTrack(t)}catch{}}async receiveSignal(e){try{if(e.type==="description"){let t=e.description.type==="offer"&&(this.#n||this.#e.signalingState!=="stable");if(this.#r=!this.#s&&t,this.#r)return;await this.#e.setRemoteDescription(e.description);for(let n of this.#i)await this.#e.addIceCandidate(n);this.#i=[],e.description.type==="offer"&&(await this.#e.setLocalDescription(),this.#t.onSignal({type:"description",description:this.#e.localDescription}));return}this.#e.remoteDescription?await this.#e.addIceCandidate(e.candidate):this.#i.push(e.candidate)}catch{}}createDataChannel(e,t){this.#s||this.#a(this.#e.createDataChannel(e,t))}#a(e){this.channels.get(e.label)?.destroy();let n=new w(e,this.#e,this.#t.limiter);this.channels.set(e.label,n),this.#t.onChannel(n)}destroy(){this.#e.onicecandidate=null,this.#e.onnegotiationneeded=null,this.#e.onconnectionstatechange=null,this.#e.ontrack=null,this.#e.ondatachannel=null;for(let e of this.#e.getReceivers())e.track.onended=null;for(let e of this.channels.values())e.destroy();this.channels.clear();try{this.#e.close()}catch{}}};var k=class extends p{id;rtcConfig;#t=new Map;#e;#s=new Set;#i=new Map;#n;#r=new Set;constructor({id:e,signal:t,rtcConfig:n=F,maxTransfers:r=L}){super(),this.id=e,this.#e=t,this.rtcConfig=n,this.#n=new S(r)}add(e){this.#s.add(e);for(let t of e.getTracks())for(let n of this.#t.values())n.addTrack(t,e)}remove(e){this.#s.delete(e);for(let t of e.getTracks())for(let n of this.#t.values())n.removeTrack(t)}channel(e,t={}){this.#i.set(e,t);for(let n of this.#t.values())n.createDataChannel(e,t)}getChannel(e,t){return this.#t.get(e)?.channels.get(t)}whenOpen(e,t,n={}){let r=this.getChannel(e,t);if(r?.channel.readyState==="open")return Promise.resolve(r);let{promise:s,resolve:a,reject:h}=Promise.withResolvers(),m=[],u=!1,c=(o,l)=>{if(!u){u=!0;for(let g of m)g();n.signal?.removeEventListener("abort",d),l?a(l):h(o??new Error("Channel open failed"))}},d=()=>{c(n.signal?.reason??new Error("Aborted"))};if(n.signal?.aborted)return Promise.reject(n.signal.reason??new Error("Aborted"));n.signal?.addEventListener("abort",d,{once:!0});let b=o=>{if(o.channel.readyState==="open"){c(void 0,o);return}m.push(o.on("open",()=>c(void 0,o)),o.on("close",()=>c(new Error("Data channel closed"))))},y=this.getChannel(e,t);return y&&b(y),m.push(this.on("channel",(o,l)=>{o!==e||l.channel.label!==t||b(l)}),this.on("leave",o=>{o===e&&c(new Error(`Peer left: ${e}`))})),s}connect(e){if(this.#t.has(e))return;let t=new E({localId:this.id,remoteId:e,rtcConfig:this.rtcConfig,limiter:this.#n,onSignal:n=>this.#e(e,n),onTrack:(n,r)=>this.emit("track",e,n,r),onTrackEnded:(n,r)=>this.emit("track-ended",e,n,r),onChannel:n=>{this.emit("channel",e,n);let r=()=>this.#o(e);n.on("open",r),n.channel.readyState==="open"&&r()},onDisconnect:()=>{this.#t.delete(e)&&(this.#r.delete(e),t.destroy(),this.emit("leave",e))}});this.#i.forEach((n,r)=>t.createDataChannel(r,n));for(let n of this.#s)for(let r of n.getTracks())t.addTrack(r,n);this.#t.set(e,t),this.emit("join",e),this.#o(e)}receive(e,t){this.connect(e),this.#t.get(e).receiveSignal(t)}async sendTo(e,t,n,r={}){let s=this.#t.get(e);if(!s)throw new Error(`Unknown peer: ${e}`);let a=s.channels.get(t);if(!a)throw new Error(`No channel "${t}" for peer ${e}`);if(a.channel.readyState!=="open")throw new Error(`Channel "${t}" with ${e} is not open`);await a.send(n,r)}async send(e,t,n={}){let r=[];for(let s of this.#t.values()){let a=s.channels.get(e);a?.channel.readyState==="open"&&r.push(a)}if(r.length!==0){if(r.length>1&&typeof t!="string"&&!_(t))throw new Error("Pass a Blob/File/BufferSource to send to multiple peers");await Promise.all(r.map(s=>s.send(t,n)))}}close(e){let t=this.#t.get(e);t&&(this.#t.delete(e),this.#r.delete(e),t.destroy(),this.emit("leave",e))}destroy(){this.#n.destroy();for(let e of[...this.#t.keys()])this.close(e);this.#s.clear(),this.#i.clear(),this.#r.clear(),super.destroy()}#a(e){let t=this.#t.get(e);if(!t)return!1;for(let n of this.#i.keys()){let r=t.channels.get(n);if(!r||r.channel.readyState!=="open")return!1}return!0}#o(e){this.#r.has(e)||this.#a(e)&&(this.#r.add(e),this.emit("ready",e))}};0&&(module.exports={FastDataChannel,RTCManager});
1
+ "use strict";var R=Object.defineProperty;var N=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var K=Object.prototype.hasOwnProperty;var I=(i,e)=>{for(var t in e)R(i,t,{get:e[t],enumerable:!0})},q=(i,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of j(e))!K.call(i,r)&&r!==t&&R(i,r,{get:()=>e[r],enumerable:!(n=N(e,r))||n.enumerable});return i};var V=i=>q(R({},"__esModule",{value:!0}),i);var H={};I(H,{FastDataChannel:()=>w,RTCManager:()=>k});module.exports=V(H);var F={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},L=4;var p=class{#t=new Map;on(e,t){let n=t,r=this.#t.get(e)??[];return r.push(n),this.#t.set(e,r),()=>{let s=this.#t.get(e);if(!s)return;let a=s.indexOf(n);a>=0&&s.splice(a,1),s.length===0&&this.#t.delete(e)}}has(e){return(this.#t.get(e)?.length??0)>0}emit(e,...t){let n=this.#t.get(e);if(n)for(let r of n.slice())r(...t)}destroy(){this.#t.clear()}};function B(i,e){let t=new ArrayBuffer(2+e.byteLength),n=new DataView(t);return n.setUint8(0,252),n.setUint8(1,i),new Uint8Array(t,2).set(e),t}function U(i){if(i.byteLength<2)return null;let e=new DataView(i);return e.getUint8(0)!==252?null:{flags:e.getUint8(1),payload:new Uint8Array(i,2)}}function _(i){return i instanceof Blob||i instanceof ArrayBuffer||ArrayBuffer.isView(i)}function O(i){if(i instanceof Blob)return i.stream();if(i instanceof ArrayBuffer||ArrayBuffer.isView(i)){let t=T(i);return new ReadableStream({start(n){n.enqueue(t),n.close()}})}if(i instanceof ReadableStream)return i;let e=i[Symbol.asyncIterator]();return new ReadableStream({async pull(t){let{done:n,value:r}=await e.next();if(n){t.close();return}t.enqueue(T(r))},cancel:()=>{e.return?.()}})}function T(i){if(i instanceof Uint8Array)return i;if(i instanceof ArrayBuffer)return new Uint8Array(i);if(ArrayBuffer.isView(i))return new Uint8Array(i.buffer,i.byteOffset,i.byteLength);if(typeof i=="string")return new TextEncoder().encode(i);throw i instanceof Blob?new Error("Pass the Blob/File to send, not blob chunks"):new Error("Unsupported stream chunk")}var S=class{#t;#e=0;#s=[];#i=!1;constructor(e){this.#t=e}async acquire(){if(this.#i)throw new Error("Transfer limiter destroyed");if(this.#e<this.#t)this.#e++;else{let t=Promise.withResolvers();if(this.#s.push(t),await t.promise,this.#i)throw this.#e--,new Error("Transfer limiter destroyed")}let e=!1;return()=>{e||(e=!0,this.#n())}}destroy(){this.#i=!0;let e=new Error("Transfer limiter destroyed");for(let t of this.#s.splice(0))t.reject(e)}#n(){if(this.#i){this.#e--;return}let e=this.#s.shift();if(e){e.resolve();return}this.#e--}};function Z(i){let e=i?.maxMessageSize;return!e||!Number.isFinite(e)?65536:e}var w=class extends p{channel;#t=0;#e=null;#s=null;#i;#n=[];#r=null;#a=null;#o=!1;#l=!1;#u=Promise.resolve();#d=0;constructor(e,t,n=new S(1/0)){super(),this.channel=e,this.#i=n,t instanceof RTCPeerConnection?this.#e=t:this.#s=t,this.#f(),this.channel.binaryType="arraybuffer",this.channel.onopen=()=>this.#m(),this.channel.onclose=()=>this.destroy(),this.channel.onmessage=r=>this.#C(r.data),this.channel.onerror=r=>this.emit("error",r),this.channel.onbufferedamountlow=()=>{this.#h(),this.#p()},this.channel.readyState==="open"&&queueMicrotask(()=>this.#m())}async send(e,t={}){if(this.#l)throw new Error("Data channel closed");if(typeof e=="string"){this.#n.push(e),this.#h();return}await this.#v(async()=>{let n=await this.#i.acquire();try{await this.#b(e,t)}finally{n()}})}destroy(){if(this.#l)return;this.#l=!0,this.#n.length=0,this.#o=!1;let e=new Error("Data channel closed");this.#r?.reject(e),this.#r=null,this.#a?.error(e),this.#a=null,this.emit("close"),this.channel.onopen=null,this.channel.onclose=null,this.channel.onmessage=null,this.channel.onerror=null,this.channel.onbufferedamountlow=null,super.destroy();try{this.channel.close()}catch{}}#m(){this.#l||(this.#f(),this.#h(),this.emit("open"))}#f(){let e=Z(this.#s??this.#e?.sctp??null);this.#t=Math.max(1,e-2),this.channel.bufferedAmountLowThreshold=e*2}async#v(e){let t=this.#u,{promise:n,resolve:r}=Promise.withResolvers();this.#u=n,await t;try{await e()}finally{r()}}#S(e,t){if(e instanceof Blob)return e.size-t;if(e instanceof ArrayBuffer||ArrayBuffer.isView(e))return e.byteLength-t}#w(e,t,n){n?.(e,t),this.emit("progress",{direction:"send",bytes:e,total:t})}async#b(e,t){this.#f();let n=t.offset??0;if(n!==0&&!(e instanceof Blob))throw new Error("offset requires a Blob/File");let r=e;n!==0&&e instanceof Blob&&(r=e.slice(n));let s=this.#S(e,n),a=0,c=!1;if(t.meta!==void 0){let l=new TextEncoder().encode(JSON.stringify(t.meta));this.#c(5,l),c=!0}let m=O(r),u=m.getReader(),h=!c,d=null,b=()=>{if(t.signal?.aborted)throw t.signal.reason??new Error("Aborted");if(this.#l)throw new Error("Data channel closed")},y=l=>{if(!d)return;let g=d.payload;this.#c(d.flags|(l?2:0),g),d=null,a+=g.byteLength,this.#w(a,s,t.onProgress)},o=()=>{u.cancel(t.signal?.reason??new Error("Aborted"))};t.signal?.addEventListener("abort",o,{once:!0});try{for(;;){b(),await this.#g(t.signal);let{done:l,value:g}=await u.read();if(l){d?y(!0):h?this.#c(3,new Uint8Array(0)):c&&this.#c(2,new Uint8Array(0));break}let D=T(g),C=0;for(;C<D.byteLength;){b(),await this.#g(t.signal);let P=Math.min(C+this.#t,D.byteLength);y(!1);let G=h?1:0;h=!1,d={flags:G,payload:D.subarray(C,P)},C=P}}}catch(l){try{d?y(!0):h||this.#c(2,new Uint8Array(0))}catch{}try{await u.cancel(l)}catch{}throw l}finally{t.signal?.removeEventListener("abort",o),m.locked&&u.releaseLock()}}#C(e){let t=e instanceof ArrayBuffer?U(e):null;if(!t){this.emit("message",e);return}if(this.#o){t.flags&2&&(this.#o=!1);return}if(!this.#a){if(!(t.flags&1))return;if(!this.has("stream")){this.#o=!(t.flags&2);return}let r;if(t.flags&4)try{r=JSON.parse(new TextDecoder().decode(t.payload))}catch(c){this.emit("error",c),this.#o=!(t.flags&2);return}let s,a=new ReadableStream({start:c=>{s=c,this.#a=c},cancel:()=>{this.#a=null}});if(this.#d=0,this.emit("stream",a,r),t.flags&4){t.flags&2&&(s?.close(),this.#a=null);return}}let n=this.#a;if(n){if(n.enqueue(t.payload),this.#d+=t.payload.byteLength,this.emit("progress",{direction:"receive",bytes:this.#d}),(n.desiredSize??0)<-32){n.error(new Error("Receive stream backlog exceeded")),this.#a=null,this.#o=!(t.flags&2);return}t.flags&2&&(n.close(),this.#a=null)}}#c(e,t){this.#l||(this.#n.push(B(e,t)),this.#h())}#y(){return!this.#l&&this.channel.readyState==="open"&&this.#n.length===0&&this.channel.bufferedAmount<=this.channel.bufferedAmountLowThreshold}#T(){let e=this.#n.findIndex(t=>typeof t=="string");return e>=0?this.#n.splice(e,1)[0]:this.#n.shift()}#h(){if(!(this.#l||this.channel.readyState!=="open")){for(;this.#n.length>0&&!(this.channel.bufferedAmount>this.channel.bufferedAmountLowThreshold);){let e=this.#T();if(e===void 0)break;try{typeof e=="string"?this.channel.send(e):this.channel.send(e)}catch(t){this.emit("error",t),this.#n.unshift(e);break}}this.#p()}}#g(e){if(this.#l)return Promise.reject(new Error("Data channel closed"));if(e?.aborted)return Promise.reject(e.reason??new Error("Aborted"));if(this.#y())return Promise.resolve();this.#r||(this.#r=Promise.withResolvers());let{promise:t}=this.#r,n=()=>{this.#r?.reject(e?.reason??new Error("Aborted")),this.#r=null};return e?.addEventListener("abort",n,{once:!0}),t.finally(()=>{e?.removeEventListener("abort",n)})}#p(){this.#y()&&(this.#r?.resolve(),this.#r=null)}};var E=class{remoteId;channels=new Map;get isConnected(){return this.#e.connectionState==="connected"}#t;#e;#s;#i=[];#n=!1;#r=!1;constructor(e){this.remoteId=e.remoteId,this.#t=e,this.#s=e.localId>e.remoteId,this.#e=new RTCPeerConnection(e.rtcConfig),this.#e.onicecandidate=t=>{t.candidate&&this.#t.onSignal({type:"candidate",candidate:t.candidate})},this.#e.onnegotiationneeded=async()=>{try{this.#n=!0,await this.#e.setLocalDescription(),this.#t.onSignal({type:"description",description:this.#e.localDescription})}catch{}finally{this.#n=!1}},this.#e.onconnectionstatechange=()=>{this.#e.connectionState==="connected"&&this.#t.onConnected(),["disconnected","failed","closed"].includes(this.#e.connectionState)&&this.#t.onDisconnect()},this.#e.ontrack=t=>{let n=t.streams[0];t.track.onended=()=>this.#t.onTrackEnded(t.track,n),this.#t.onTrack(t.track,n)},this.#e.ondatachannel=t=>this.#a(t.channel)}addTrack(e,t){this.#e.addTrack(e,t)}removeTrack(e){let t=this.#e.getSenders().find(n=>n.track===e);if(t)try{this.#e.removeTrack(t)}catch{}}async receiveSignal(e){try{if(e.type==="description"){let t=e.description.type==="offer"&&(this.#n||this.#e.signalingState!=="stable");if(this.#r=!this.#s&&t,this.#r)return;await this.#e.setRemoteDescription(e.description);for(let n of this.#i)await this.#e.addIceCandidate(n);this.#i=[],e.description.type==="offer"&&(await this.#e.setLocalDescription(),this.#t.onSignal({type:"description",description:this.#e.localDescription}));return}this.#e.remoteDescription?await this.#e.addIceCandidate(e.candidate):this.#i.push(e.candidate)}catch{}}createDataChannel(e,t){this.#s||this.#a(this.#e.createDataChannel(e,t))}#a(e){this.channels.get(e.label)?.destroy();let n=new w(e,this.#e,this.#t.limiter);this.channels.set(e.label,n),this.#t.onChannel(n)}destroy(){this.#e.onicecandidate=null,this.#e.onnegotiationneeded=null,this.#e.onconnectionstatechange=null,this.#e.ontrack=null,this.#e.ondatachannel=null;for(let e of this.#e.getReceivers())e.track.onended=null;for(let e of this.channels.values())e.destroy();this.channels.clear();try{this.#e.close()}catch{}}};var k=class extends p{id;rtcConfig;#t=new Map;#e;#s=new Set;#i=new Map;#n;#r=new Set;constructor({id:e,signal:t,rtcConfig:n=F,maxTransfers:r=L}){super(),this.id=e,this.#e=t,this.rtcConfig=n,this.#n=new S(r)}add(e){this.#s.add(e);for(let t of e.getTracks())for(let n of this.#t.values())n.addTrack(t,e)}remove(e){this.#s.delete(e);for(let t of e.getTracks())for(let n of this.#t.values())n.removeTrack(t)}channel(e,t={}){this.#i.set(e,t);for(let n of this.#t.values())n.createDataChannel(e,t)}getChannel(e,t){return this.#t.get(e)?.channels.get(t)}whenOpen(e,t,n={}){let r=this.getChannel(e,t);if(r?.channel.readyState==="open")return Promise.resolve(r);let{promise:s,resolve:a,reject:c}=Promise.withResolvers(),m=[],u=!1,h=(o,l)=>{if(!u){u=!0;for(let g of m)g();n.signal?.removeEventListener("abort",d),l?a(l):c(o??new Error("Channel open failed"))}},d=()=>{h(n.signal?.reason??new Error("Aborted"))};if(n.signal?.aborted)return Promise.reject(n.signal.reason??new Error("Aborted"));n.signal?.addEventListener("abort",d,{once:!0});let b=o=>{if(o.channel.readyState==="open"){h(void 0,o);return}m.push(o.on("open",()=>h(void 0,o)),o.on("close",()=>h(new Error("Data channel closed"))))},y=this.getChannel(e,t);return y&&b(y),m.push(this.on("channel",(o,l)=>{o!==e||l.channel.label!==t||b(l)}),this.on("leave",o=>{o===e&&h(new Error(`Peer left: ${e}`))})),s}connect(e){if(this.#t.has(e))return;let t=new E({localId:this.id,remoteId:e,rtcConfig:this.rtcConfig,limiter:this.#n,onSignal:n=>this.#e(e,n),onTrack:(n,r)=>this.emit("track",e,n,r),onTrackEnded:(n,r)=>this.emit("track-ended",e,n,r),onChannel:n=>{this.emit("channel",e,n);let r=()=>this.#o(e);n.on("open",r),n.channel.readyState==="open"&&r()},onConnected:()=>this.#o(e),onDisconnect:()=>{this.#t.delete(e)&&(this.#r.delete(e),t.destroy(),this.emit("leave",e))}});this.#i.forEach((n,r)=>t.createDataChannel(r,n));for(let n of this.#s)for(let r of n.getTracks())t.addTrack(r,n);this.#t.set(e,t),this.emit("join",e),this.#o(e)}receive(e,t){this.connect(e),this.#t.get(e).receiveSignal(t)}async sendTo(e,t,n,r={}){let s=this.#t.get(e);if(!s)throw new Error(`Unknown peer: ${e}`);let a=s.channels.get(t);if(!a)throw new Error(`No channel "${t}" for peer ${e}`);if(a.channel.readyState!=="open")throw new Error(`Channel "${t}" with ${e} is not open`);await a.send(n,r)}async send(e,t,n={}){let r=[];for(let s of this.#t.values()){let a=s.channels.get(e);a?.channel.readyState==="open"&&r.push(a)}if(r.length!==0){if(r.length>1&&typeof t!="string"&&!_(t))throw new Error("Pass a Blob/File/BufferSource to send to multiple peers");await Promise.all(r.map(s=>s.send(t,n)))}}close(e){let t=this.#t.get(e);t&&(this.#t.delete(e),this.#r.delete(e),t.destroy(),this.emit("leave",e))}destroy(){this.#n.destroy();for(let e of[...this.#t.keys()])this.close(e);this.#s.clear(),this.#i.clear(),this.#r.clear(),super.destroy()}#a(e){let t=this.#t.get(e);if(!t)return!1;if(this.#i.size===0)return t.isConnected;for(let n of this.#i.keys()){let r=t.channels.get(n);if(!r||r.channel.readyState!=="open")return!1}return!0}#o(e){this.#r.has(e)||this.#a(e)&&(this.#r.add(e),this.emit("ready",e))}};0&&(module.exports={FastDataChannel,RTCManager});
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- var P={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},F=4;var p=class{#t=new Map;on(e,t){let n=t,r=this.#t.get(e)??[];return r.push(n),this.#t.set(e,r),()=>{let s=this.#t.get(e);if(!s)return;let a=s.indexOf(n);a>=0&&s.splice(a,1),s.length===0&&this.#t.delete(e)}}has(e){return(this.#t.get(e)?.length??0)>0}emit(e,...t){let n=this.#t.get(e);if(n)for(let r of n.slice())r(...t)}destroy(){this.#t.clear()}};function M(i,e){let t=new ArrayBuffer(2+e.byteLength),n=new DataView(t);return n.setUint8(0,252),n.setUint8(1,i),new Uint8Array(t,2).set(e),t}function B(i){if(i.byteLength<2)return null;let e=new DataView(i);return e.getUint8(0)!==252?null:{flags:e.getUint8(1),payload:new Uint8Array(i,2)}}function U(i){return i instanceof Blob||i instanceof ArrayBuffer||ArrayBuffer.isView(i)}function _(i){if(i instanceof Blob)return i.stream();if(i instanceof ArrayBuffer||ArrayBuffer.isView(i)){let t=T(i);return new ReadableStream({start(n){n.enqueue(t),n.close()}})}if(i instanceof ReadableStream)return i;let e=i[Symbol.asyncIterator]();return new ReadableStream({async pull(t){let{done:n,value:r}=await e.next();if(n){t.close();return}t.enqueue(T(r))},cancel:()=>{e.return?.()}})}function T(i){if(i instanceof Uint8Array)return i;if(i instanceof ArrayBuffer)return new Uint8Array(i);if(ArrayBuffer.isView(i))return new Uint8Array(i.buffer,i.byteOffset,i.byteLength);if(typeof i=="string")return new TextEncoder().encode(i);throw i instanceof Blob?new Error("Pass the Blob/File to send, not blob chunks"):new Error("Unsupported stream chunk")}var S=class{#t;#e=0;#s=[];#i=!1;constructor(e){this.#t=e}async acquire(){if(this.#i)throw new Error("Transfer limiter destroyed");if(this.#e<this.#t)this.#e++;else{let t=Promise.withResolvers();if(this.#s.push(t),await t.promise,this.#i)throw this.#e--,new Error("Transfer limiter destroyed")}let e=!1;return()=>{e||(e=!0,this.#n())}}destroy(){this.#i=!0;let e=new Error("Transfer limiter destroyed");for(let t of this.#s.splice(0))t.reject(e)}#n(){if(this.#i){this.#e--;return}let e=this.#s.shift();if(e){e.resolve();return}this.#e--}};function j(i){let e=i?.maxMessageSize;return!e||!Number.isFinite(e)?65536:e}var b=class extends p{channel;#t=0;#e=null;#s=null;#i;#n=[];#r=null;#a=null;#o=!1;#l=!1;#u=Promise.resolve();#d=0;constructor(e,t,n=new S(1/0)){super(),this.channel=e,this.#i=n,t instanceof RTCPeerConnection?this.#e=t:this.#s=t,this.#f(),this.channel.binaryType="arraybuffer",this.channel.onopen=()=>this.#m(),this.channel.onclose=()=>this.destroy(),this.channel.onmessage=r=>this.#C(r.data),this.channel.onerror=r=>this.emit("error",r),this.channel.onbufferedamountlow=()=>{this.#c(),this.#p()},this.channel.readyState==="open"&&queueMicrotask(()=>this.#m())}async send(e,t={}){if(this.#l)throw new Error("Data channel closed");if(typeof e=="string"){this.#n.push(e),this.#c();return}await this.#v(async()=>{let n=await this.#i.acquire();try{await this.#b(e,t)}finally{n()}})}destroy(){if(this.#l)return;this.#l=!0,this.#n.length=0,this.#o=!1;let e=new Error("Data channel closed");this.#r?.reject(e),this.#r=null,this.#a?.error(e),this.#a=null,this.emit("close"),this.channel.onopen=null,this.channel.onclose=null,this.channel.onmessage=null,this.channel.onerror=null,this.channel.onbufferedamountlow=null,super.destroy();try{this.channel.close()}catch{}}#m(){this.#l||(this.#f(),this.#c(),this.emit("open"))}#f(){let e=j(this.#s??this.#e?.sctp??null);this.#t=Math.max(1,e-2),this.channel.bufferedAmountLowThreshold=e*2}async#v(e){let t=this.#u,{promise:n,resolve:r}=Promise.withResolvers();this.#u=n,await t;try{await e()}finally{r()}}#S(e,t){if(e instanceof Blob)return e.size-t;if(e instanceof ArrayBuffer||ArrayBuffer.isView(e))return e.byteLength-t}#w(e,t,n){n?.(e,t),this.emit("progress",{direction:"send",bytes:e,total:t})}async#b(e,t){this.#f();let n=t.offset??0;if(n!==0&&!(e instanceof Blob))throw new Error("offset requires a Blob/File");let r=e;n!==0&&e instanceof Blob&&(r=e.slice(n));let s=this.#S(e,n),a=0,h=!1;if(t.meta!==void 0){let l=new TextEncoder().encode(JSON.stringify(t.meta));this.#h(5,l),h=!0}let m=_(r),u=m.getReader(),c=!h,d=null,w=()=>{if(t.signal?.aborted)throw t.signal.reason??new Error("Aborted");if(this.#l)throw new Error("Data channel closed")},y=l=>{if(!d)return;let g=d.payload;this.#h(d.flags|(l?2:0),g),d=null,a+=g.byteLength,this.#w(a,s,t.onProgress)},o=()=>{u.cancel(t.signal?.reason??new Error("Aborted"))};t.signal?.addEventListener("abort",o,{once:!0});try{for(;;){w(),await this.#g(t.signal);let{done:l,value:g}=await u.read();if(l){d?y(!0):c?this.#h(3,new Uint8Array(0)):h&&this.#h(2,new Uint8Array(0));break}let k=T(g),C=0;for(;C<k.byteLength;){w(),await this.#g(t.signal);let x=Math.min(C+this.#t,k.byteLength);y(!1);let O=c?1:0;c=!1,d={flags:O,payload:k.subarray(C,x)},C=x}}}catch(l){try{d?y(!0):c||this.#h(2,new Uint8Array(0))}catch{}try{await u.cancel(l)}catch{}throw l}finally{t.signal?.removeEventListener("abort",o),m.locked&&u.releaseLock()}}#C(e){let t=e instanceof ArrayBuffer?B(e):null;if(!t){this.emit("message",e);return}if(this.#o){t.flags&2&&(this.#o=!1);return}if(!this.#a){if(!(t.flags&1))return;if(!this.has("stream")){this.#o=!(t.flags&2);return}let r;if(t.flags&4)try{r=JSON.parse(new TextDecoder().decode(t.payload))}catch(h){this.emit("error",h),this.#o=!(t.flags&2);return}let s,a=new ReadableStream({start:h=>{s=h,this.#a=h},cancel:()=>{this.#a=null}});if(this.#d=0,this.emit("stream",a,r),t.flags&4){t.flags&2&&(s?.close(),this.#a=null);return}}let n=this.#a;if(n){if(n.enqueue(t.payload),this.#d+=t.payload.byteLength,this.emit("progress",{direction:"receive",bytes:this.#d}),(n.desiredSize??0)<-32){n.error(new Error("Receive stream backlog exceeded")),this.#a=null,this.#o=!(t.flags&2);return}t.flags&2&&(n.close(),this.#a=null)}}#h(e,t){this.#l||(this.#n.push(M(e,t)),this.#c())}#y(){return!this.#l&&this.channel.readyState==="open"&&this.#n.length===0&&this.channel.bufferedAmount<=this.channel.bufferedAmountLowThreshold}#T(){let e=this.#n.findIndex(t=>typeof t=="string");return e>=0?this.#n.splice(e,1)[0]:this.#n.shift()}#c(){if(!(this.#l||this.channel.readyState!=="open")){for(;this.#n.length>0&&!(this.channel.bufferedAmount>this.channel.bufferedAmountLowThreshold);){let e=this.#T();if(e===void 0)break;try{typeof e=="string"?this.channel.send(e):this.channel.send(e)}catch(t){this.emit("error",t),this.#n.unshift(e);break}}this.#p()}}#g(e){if(this.#l)return Promise.reject(new Error("Data channel closed"));if(e?.aborted)return Promise.reject(e.reason??new Error("Aborted"));if(this.#y())return Promise.resolve();this.#r||(this.#r=Promise.withResolvers());let{promise:t}=this.#r,n=()=>{this.#r?.reject(e?.reason??new Error("Aborted")),this.#r=null};return e?.addEventListener("abort",n,{once:!0}),t.finally(()=>{e?.removeEventListener("abort",n)})}#p(){this.#y()&&(this.#r?.resolve(),this.#r=null)}};var E=class{remoteId;channels=new Map;#t;#e;#s;#i=[];#n=!1;#r=!1;constructor(e){this.remoteId=e.remoteId,this.#t=e,this.#s=e.localId>e.remoteId,this.#e=new RTCPeerConnection(e.rtcConfig),this.#e.onicecandidate=t=>{t.candidate&&this.#t.onSignal({type:"candidate",candidate:t.candidate})},this.#e.onnegotiationneeded=async()=>{try{this.#n=!0,await this.#e.setLocalDescription(),this.#t.onSignal({type:"description",description:this.#e.localDescription})}catch{}finally{this.#n=!1}},this.#e.onconnectionstatechange=()=>{["disconnected","failed","closed"].includes(this.#e.connectionState)&&this.#t.onDisconnect()},this.#e.ontrack=t=>{let n=t.streams[0];t.track.onended=()=>this.#t.onTrackEnded(t.track,n),this.#t.onTrack(t.track,n)},this.#e.ondatachannel=t=>this.#a(t.channel)}addTrack(e,t){this.#e.addTrack(e,t)}removeTrack(e){let t=this.#e.getSenders().find(n=>n.track===e);if(t)try{this.#e.removeTrack(t)}catch{}}async receiveSignal(e){try{if(e.type==="description"){let t=e.description.type==="offer"&&(this.#n||this.#e.signalingState!=="stable");if(this.#r=!this.#s&&t,this.#r)return;await this.#e.setRemoteDescription(e.description);for(let n of this.#i)await this.#e.addIceCandidate(n);this.#i=[],e.description.type==="offer"&&(await this.#e.setLocalDescription(),this.#t.onSignal({type:"description",description:this.#e.localDescription}));return}this.#e.remoteDescription?await this.#e.addIceCandidate(e.candidate):this.#i.push(e.candidate)}catch{}}createDataChannel(e,t){this.#s||this.#a(this.#e.createDataChannel(e,t))}#a(e){this.channels.get(e.label)?.destroy();let n=new b(e,this.#e,this.#t.limiter);this.channels.set(e.label,n),this.#t.onChannel(n)}destroy(){this.#e.onicecandidate=null,this.#e.onnegotiationneeded=null,this.#e.onconnectionstatechange=null,this.#e.ontrack=null,this.#e.ondatachannel=null;for(let e of this.#e.getReceivers())e.track.onended=null;for(let e of this.channels.values())e.destroy();this.channels.clear();try{this.#e.close()}catch{}}};var R=class extends p{id;rtcConfig;#t=new Map;#e;#s=new Set;#i=new Map;#n;#r=new Set;constructor({id:e,signal:t,rtcConfig:n=P,maxTransfers:r=F}){super(),this.id=e,this.#e=t,this.rtcConfig=n,this.#n=new S(r)}add(e){this.#s.add(e);for(let t of e.getTracks())for(let n of this.#t.values())n.addTrack(t,e)}remove(e){this.#s.delete(e);for(let t of e.getTracks())for(let n of this.#t.values())n.removeTrack(t)}channel(e,t={}){this.#i.set(e,t);for(let n of this.#t.values())n.createDataChannel(e,t)}getChannel(e,t){return this.#t.get(e)?.channels.get(t)}whenOpen(e,t,n={}){let r=this.getChannel(e,t);if(r?.channel.readyState==="open")return Promise.resolve(r);let{promise:s,resolve:a,reject:h}=Promise.withResolvers(),m=[],u=!1,c=(o,l)=>{if(!u){u=!0;for(let g of m)g();n.signal?.removeEventListener("abort",d),l?a(l):h(o??new Error("Channel open failed"))}},d=()=>{c(n.signal?.reason??new Error("Aborted"))};if(n.signal?.aborted)return Promise.reject(n.signal.reason??new Error("Aborted"));n.signal?.addEventListener("abort",d,{once:!0});let w=o=>{if(o.channel.readyState==="open"){c(void 0,o);return}m.push(o.on("open",()=>c(void 0,o)),o.on("close",()=>c(new Error("Data channel closed"))))},y=this.getChannel(e,t);return y&&w(y),m.push(this.on("channel",(o,l)=>{o!==e||l.channel.label!==t||w(l)}),this.on("leave",o=>{o===e&&c(new Error(`Peer left: ${e}`))})),s}connect(e){if(this.#t.has(e))return;let t=new E({localId:this.id,remoteId:e,rtcConfig:this.rtcConfig,limiter:this.#n,onSignal:n=>this.#e(e,n),onTrack:(n,r)=>this.emit("track",e,n,r),onTrackEnded:(n,r)=>this.emit("track-ended",e,n,r),onChannel:n=>{this.emit("channel",e,n);let r=()=>this.#o(e);n.on("open",r),n.channel.readyState==="open"&&r()},onDisconnect:()=>{this.#t.delete(e)&&(this.#r.delete(e),t.destroy(),this.emit("leave",e))}});this.#i.forEach((n,r)=>t.createDataChannel(r,n));for(let n of this.#s)for(let r of n.getTracks())t.addTrack(r,n);this.#t.set(e,t),this.emit("join",e),this.#o(e)}receive(e,t){this.connect(e),this.#t.get(e).receiveSignal(t)}async sendTo(e,t,n,r={}){let s=this.#t.get(e);if(!s)throw new Error(`Unknown peer: ${e}`);let a=s.channels.get(t);if(!a)throw new Error(`No channel "${t}" for peer ${e}`);if(a.channel.readyState!=="open")throw new Error(`Channel "${t}" with ${e} is not open`);await a.send(n,r)}async send(e,t,n={}){let r=[];for(let s of this.#t.values()){let a=s.channels.get(e);a?.channel.readyState==="open"&&r.push(a)}if(r.length!==0){if(r.length>1&&typeof t!="string"&&!U(t))throw new Error("Pass a Blob/File/BufferSource to send to multiple peers");await Promise.all(r.map(s=>s.send(t,n)))}}close(e){let t=this.#t.get(e);t&&(this.#t.delete(e),this.#r.delete(e),t.destroy(),this.emit("leave",e))}destroy(){this.#n.destroy();for(let e of[...this.#t.keys()])this.close(e);this.#s.clear(),this.#i.clear(),this.#r.clear(),super.destroy()}#a(e){let t=this.#t.get(e);if(!t)return!1;for(let n of this.#i.keys()){let r=t.channels.get(n);if(!r||r.channel.readyState!=="open")return!1}return!0}#o(e){this.#r.has(e)||this.#a(e)&&(this.#r.add(e),this.emit("ready",e))}};export{b as FastDataChannel,R as RTCManager};
1
+ var P={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},F=4;var p=class{#t=new Map;on(e,t){let n=t,r=this.#t.get(e)??[];return r.push(n),this.#t.set(e,r),()=>{let s=this.#t.get(e);if(!s)return;let a=s.indexOf(n);a>=0&&s.splice(a,1),s.length===0&&this.#t.delete(e)}}has(e){return(this.#t.get(e)?.length??0)>0}emit(e,...t){let n=this.#t.get(e);if(n)for(let r of n.slice())r(...t)}destroy(){this.#t.clear()}};function M(i,e){let t=new ArrayBuffer(2+e.byteLength),n=new DataView(t);return n.setUint8(0,252),n.setUint8(1,i),new Uint8Array(t,2).set(e),t}function B(i){if(i.byteLength<2)return null;let e=new DataView(i);return e.getUint8(0)!==252?null:{flags:e.getUint8(1),payload:new Uint8Array(i,2)}}function U(i){return i instanceof Blob||i instanceof ArrayBuffer||ArrayBuffer.isView(i)}function _(i){if(i instanceof Blob)return i.stream();if(i instanceof ArrayBuffer||ArrayBuffer.isView(i)){let t=T(i);return new ReadableStream({start(n){n.enqueue(t),n.close()}})}if(i instanceof ReadableStream)return i;let e=i[Symbol.asyncIterator]();return new ReadableStream({async pull(t){let{done:n,value:r}=await e.next();if(n){t.close();return}t.enqueue(T(r))},cancel:()=>{e.return?.()}})}function T(i){if(i instanceof Uint8Array)return i;if(i instanceof ArrayBuffer)return new Uint8Array(i);if(ArrayBuffer.isView(i))return new Uint8Array(i.buffer,i.byteOffset,i.byteLength);if(typeof i=="string")return new TextEncoder().encode(i);throw i instanceof Blob?new Error("Pass the Blob/File to send, not blob chunks"):new Error("Unsupported stream chunk")}var S=class{#t;#e=0;#s=[];#i=!1;constructor(e){this.#t=e}async acquire(){if(this.#i)throw new Error("Transfer limiter destroyed");if(this.#e<this.#t)this.#e++;else{let t=Promise.withResolvers();if(this.#s.push(t),await t.promise,this.#i)throw this.#e--,new Error("Transfer limiter destroyed")}let e=!1;return()=>{e||(e=!0,this.#n())}}destroy(){this.#i=!0;let e=new Error("Transfer limiter destroyed");for(let t of this.#s.splice(0))t.reject(e)}#n(){if(this.#i){this.#e--;return}let e=this.#s.shift();if(e){e.resolve();return}this.#e--}};function j(i){let e=i?.maxMessageSize;return!e||!Number.isFinite(e)?65536:e}var b=class extends p{channel;#t=0;#e=null;#s=null;#i;#n=[];#r=null;#a=null;#o=!1;#l=!1;#u=Promise.resolve();#d=0;constructor(e,t,n=new S(1/0)){super(),this.channel=e,this.#i=n,t instanceof RTCPeerConnection?this.#e=t:this.#s=t,this.#f(),this.channel.binaryType="arraybuffer",this.channel.onopen=()=>this.#m(),this.channel.onclose=()=>this.destroy(),this.channel.onmessage=r=>this.#C(r.data),this.channel.onerror=r=>this.emit("error",r),this.channel.onbufferedamountlow=()=>{this.#h(),this.#p()},this.channel.readyState==="open"&&queueMicrotask(()=>this.#m())}async send(e,t={}){if(this.#l)throw new Error("Data channel closed");if(typeof e=="string"){this.#n.push(e),this.#h();return}await this.#v(async()=>{let n=await this.#i.acquire();try{await this.#b(e,t)}finally{n()}})}destroy(){if(this.#l)return;this.#l=!0,this.#n.length=0,this.#o=!1;let e=new Error("Data channel closed");this.#r?.reject(e),this.#r=null,this.#a?.error(e),this.#a=null,this.emit("close"),this.channel.onopen=null,this.channel.onclose=null,this.channel.onmessage=null,this.channel.onerror=null,this.channel.onbufferedamountlow=null,super.destroy();try{this.channel.close()}catch{}}#m(){this.#l||(this.#f(),this.#h(),this.emit("open"))}#f(){let e=j(this.#s??this.#e?.sctp??null);this.#t=Math.max(1,e-2),this.channel.bufferedAmountLowThreshold=e*2}async#v(e){let t=this.#u,{promise:n,resolve:r}=Promise.withResolvers();this.#u=n,await t;try{await e()}finally{r()}}#S(e,t){if(e instanceof Blob)return e.size-t;if(e instanceof ArrayBuffer||ArrayBuffer.isView(e))return e.byteLength-t}#w(e,t,n){n?.(e,t),this.emit("progress",{direction:"send",bytes:e,total:t})}async#b(e,t){this.#f();let n=t.offset??0;if(n!==0&&!(e instanceof Blob))throw new Error("offset requires a Blob/File");let r=e;n!==0&&e instanceof Blob&&(r=e.slice(n));let s=this.#S(e,n),a=0,c=!1;if(t.meta!==void 0){let l=new TextEncoder().encode(JSON.stringify(t.meta));this.#c(5,l),c=!0}let m=_(r),u=m.getReader(),h=!c,d=null,w=()=>{if(t.signal?.aborted)throw t.signal.reason??new Error("Aborted");if(this.#l)throw new Error("Data channel closed")},y=l=>{if(!d)return;let g=d.payload;this.#c(d.flags|(l?2:0),g),d=null,a+=g.byteLength,this.#w(a,s,t.onProgress)},o=()=>{u.cancel(t.signal?.reason??new Error("Aborted"))};t.signal?.addEventListener("abort",o,{once:!0});try{for(;;){w(),await this.#g(t.signal);let{done:l,value:g}=await u.read();if(l){d?y(!0):h?this.#c(3,new Uint8Array(0)):c&&this.#c(2,new Uint8Array(0));break}let k=T(g),C=0;for(;C<k.byteLength;){w(),await this.#g(t.signal);let x=Math.min(C+this.#t,k.byteLength);y(!1);let O=h?1:0;h=!1,d={flags:O,payload:k.subarray(C,x)},C=x}}}catch(l){try{d?y(!0):h||this.#c(2,new Uint8Array(0))}catch{}try{await u.cancel(l)}catch{}throw l}finally{t.signal?.removeEventListener("abort",o),m.locked&&u.releaseLock()}}#C(e){let t=e instanceof ArrayBuffer?B(e):null;if(!t){this.emit("message",e);return}if(this.#o){t.flags&2&&(this.#o=!1);return}if(!this.#a){if(!(t.flags&1))return;if(!this.has("stream")){this.#o=!(t.flags&2);return}let r;if(t.flags&4)try{r=JSON.parse(new TextDecoder().decode(t.payload))}catch(c){this.emit("error",c),this.#o=!(t.flags&2);return}let s,a=new ReadableStream({start:c=>{s=c,this.#a=c},cancel:()=>{this.#a=null}});if(this.#d=0,this.emit("stream",a,r),t.flags&4){t.flags&2&&(s?.close(),this.#a=null);return}}let n=this.#a;if(n){if(n.enqueue(t.payload),this.#d+=t.payload.byteLength,this.emit("progress",{direction:"receive",bytes:this.#d}),(n.desiredSize??0)<-32){n.error(new Error("Receive stream backlog exceeded")),this.#a=null,this.#o=!(t.flags&2);return}t.flags&2&&(n.close(),this.#a=null)}}#c(e,t){this.#l||(this.#n.push(M(e,t)),this.#h())}#y(){return!this.#l&&this.channel.readyState==="open"&&this.#n.length===0&&this.channel.bufferedAmount<=this.channel.bufferedAmountLowThreshold}#T(){let e=this.#n.findIndex(t=>typeof t=="string");return e>=0?this.#n.splice(e,1)[0]:this.#n.shift()}#h(){if(!(this.#l||this.channel.readyState!=="open")){for(;this.#n.length>0&&!(this.channel.bufferedAmount>this.channel.bufferedAmountLowThreshold);){let e=this.#T();if(e===void 0)break;try{typeof e=="string"?this.channel.send(e):this.channel.send(e)}catch(t){this.emit("error",t),this.#n.unshift(e);break}}this.#p()}}#g(e){if(this.#l)return Promise.reject(new Error("Data channel closed"));if(e?.aborted)return Promise.reject(e.reason??new Error("Aborted"));if(this.#y())return Promise.resolve();this.#r||(this.#r=Promise.withResolvers());let{promise:t}=this.#r,n=()=>{this.#r?.reject(e?.reason??new Error("Aborted")),this.#r=null};return e?.addEventListener("abort",n,{once:!0}),t.finally(()=>{e?.removeEventListener("abort",n)})}#p(){this.#y()&&(this.#r?.resolve(),this.#r=null)}};var E=class{remoteId;channels=new Map;get isConnected(){return this.#e.connectionState==="connected"}#t;#e;#s;#i=[];#n=!1;#r=!1;constructor(e){this.remoteId=e.remoteId,this.#t=e,this.#s=e.localId>e.remoteId,this.#e=new RTCPeerConnection(e.rtcConfig),this.#e.onicecandidate=t=>{t.candidate&&this.#t.onSignal({type:"candidate",candidate:t.candidate})},this.#e.onnegotiationneeded=async()=>{try{this.#n=!0,await this.#e.setLocalDescription(),this.#t.onSignal({type:"description",description:this.#e.localDescription})}catch{}finally{this.#n=!1}},this.#e.onconnectionstatechange=()=>{this.#e.connectionState==="connected"&&this.#t.onConnected(),["disconnected","failed","closed"].includes(this.#e.connectionState)&&this.#t.onDisconnect()},this.#e.ontrack=t=>{let n=t.streams[0];t.track.onended=()=>this.#t.onTrackEnded(t.track,n),this.#t.onTrack(t.track,n)},this.#e.ondatachannel=t=>this.#a(t.channel)}addTrack(e,t){this.#e.addTrack(e,t)}removeTrack(e){let t=this.#e.getSenders().find(n=>n.track===e);if(t)try{this.#e.removeTrack(t)}catch{}}async receiveSignal(e){try{if(e.type==="description"){let t=e.description.type==="offer"&&(this.#n||this.#e.signalingState!=="stable");if(this.#r=!this.#s&&t,this.#r)return;await this.#e.setRemoteDescription(e.description);for(let n of this.#i)await this.#e.addIceCandidate(n);this.#i=[],e.description.type==="offer"&&(await this.#e.setLocalDescription(),this.#t.onSignal({type:"description",description:this.#e.localDescription}));return}this.#e.remoteDescription?await this.#e.addIceCandidate(e.candidate):this.#i.push(e.candidate)}catch{}}createDataChannel(e,t){this.#s||this.#a(this.#e.createDataChannel(e,t))}#a(e){this.channels.get(e.label)?.destroy();let n=new b(e,this.#e,this.#t.limiter);this.channels.set(e.label,n),this.#t.onChannel(n)}destroy(){this.#e.onicecandidate=null,this.#e.onnegotiationneeded=null,this.#e.onconnectionstatechange=null,this.#e.ontrack=null,this.#e.ondatachannel=null;for(let e of this.#e.getReceivers())e.track.onended=null;for(let e of this.channels.values())e.destroy();this.channels.clear();try{this.#e.close()}catch{}}};var R=class extends p{id;rtcConfig;#t=new Map;#e;#s=new Set;#i=new Map;#n;#r=new Set;constructor({id:e,signal:t,rtcConfig:n=P,maxTransfers:r=F}){super(),this.id=e,this.#e=t,this.rtcConfig=n,this.#n=new S(r)}add(e){this.#s.add(e);for(let t of e.getTracks())for(let n of this.#t.values())n.addTrack(t,e)}remove(e){this.#s.delete(e);for(let t of e.getTracks())for(let n of this.#t.values())n.removeTrack(t)}channel(e,t={}){this.#i.set(e,t);for(let n of this.#t.values())n.createDataChannel(e,t)}getChannel(e,t){return this.#t.get(e)?.channels.get(t)}whenOpen(e,t,n={}){let r=this.getChannel(e,t);if(r?.channel.readyState==="open")return Promise.resolve(r);let{promise:s,resolve:a,reject:c}=Promise.withResolvers(),m=[],u=!1,h=(o,l)=>{if(!u){u=!0;for(let g of m)g();n.signal?.removeEventListener("abort",d),l?a(l):c(o??new Error("Channel open failed"))}},d=()=>{h(n.signal?.reason??new Error("Aborted"))};if(n.signal?.aborted)return Promise.reject(n.signal.reason??new Error("Aborted"));n.signal?.addEventListener("abort",d,{once:!0});let w=o=>{if(o.channel.readyState==="open"){h(void 0,o);return}m.push(o.on("open",()=>h(void 0,o)),o.on("close",()=>h(new Error("Data channel closed"))))},y=this.getChannel(e,t);return y&&w(y),m.push(this.on("channel",(o,l)=>{o!==e||l.channel.label!==t||w(l)}),this.on("leave",o=>{o===e&&h(new Error(`Peer left: ${e}`))})),s}connect(e){if(this.#t.has(e))return;let t=new E({localId:this.id,remoteId:e,rtcConfig:this.rtcConfig,limiter:this.#n,onSignal:n=>this.#e(e,n),onTrack:(n,r)=>this.emit("track",e,n,r),onTrackEnded:(n,r)=>this.emit("track-ended",e,n,r),onChannel:n=>{this.emit("channel",e,n);let r=()=>this.#o(e);n.on("open",r),n.channel.readyState==="open"&&r()},onConnected:()=>this.#o(e),onDisconnect:()=>{this.#t.delete(e)&&(this.#r.delete(e),t.destroy(),this.emit("leave",e))}});this.#i.forEach((n,r)=>t.createDataChannel(r,n));for(let n of this.#s)for(let r of n.getTracks())t.addTrack(r,n);this.#t.set(e,t),this.emit("join",e),this.#o(e)}receive(e,t){this.connect(e),this.#t.get(e).receiveSignal(t)}async sendTo(e,t,n,r={}){let s=this.#t.get(e);if(!s)throw new Error(`Unknown peer: ${e}`);let a=s.channels.get(t);if(!a)throw new Error(`No channel "${t}" for peer ${e}`);if(a.channel.readyState!=="open")throw new Error(`Channel "${t}" with ${e} is not open`);await a.send(n,r)}async send(e,t,n={}){let r=[];for(let s of this.#t.values()){let a=s.channels.get(e);a?.channel.readyState==="open"&&r.push(a)}if(r.length!==0){if(r.length>1&&typeof t!="string"&&!U(t))throw new Error("Pass a Blob/File/BufferSource to send to multiple peers");await Promise.all(r.map(s=>s.send(t,n)))}}close(e){let t=this.#t.get(e);t&&(this.#t.delete(e),this.#r.delete(e),t.destroy(),this.emit("leave",e))}destroy(){this.#n.destroy();for(let e of[...this.#t.keys()])this.close(e);this.#s.clear(),this.#i.clear(),this.#r.clear(),super.destroy()}#a(e){let t=this.#t.get(e);if(!t)return!1;if(this.#i.size===0)return t.isConnected;for(let n of this.#i.keys()){let r=t.channels.get(n);if(!r||r.channel.readyState!=="open")return!1}return!0}#o(e){this.#r.has(e)||this.#a(e)&&(this.#r.add(e),this.emit("ready",e))}};export{b as FastDataChannel,R as RTCManager};
@@ -10,12 +10,14 @@ type PeerNodeOptions = {
10
10
  onTrack: (track: MediaStreamTrack, stream: MediaStream) => void;
11
11
  onTrackEnded: (track: MediaStreamTrack, stream: MediaStream) => void;
12
12
  onChannel: (channel: FastDataChannel) => void;
13
+ onConnected: () => void;
13
14
  onDisconnect: () => void;
14
15
  };
15
16
  export declare class PeerNode {
16
17
  #private;
17
18
  readonly remoteId: string;
18
19
  readonly channels: Map<string, FastDataChannel>;
20
+ get isConnected(): boolean;
19
21
  constructor(opts: PeerNodeOptions);
20
22
  addTrack(track: MediaStreamTrack, stream: MediaStream): void;
21
23
  removeTrack(track: MediaStreamTrack): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fastrtc",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Easy to use, zero-dependency WebRTC mesh library for browsers",
5
5
  "keywords": [
6
6
  "datachannel",