fastrtc 1.0.0 → 1.1.0
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/README.md +9 -4
- package/dist/consts.d.ts +1 -0
- package/dist/fast-data-channel.d.ts +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/peer-node.d.ts +4 -1
- package/dist/queue.d.ts +1 -0
- package/dist/rtc-manager.d.ts +1 -0
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -105,10 +105,13 @@ rtc.on("ready", (peerId) => {
|
|
|
105
105
|
| ------- | ---- |
|
|
106
106
|
| `join` | Peer connection created |
|
|
107
107
|
| `ready` | All declared channels open for that peer (fires once) |
|
|
108
|
-
| `leave` | Peer disconnected |
|
|
108
|
+
| `leave` | Peer disconnected (including ICE `disconnected`) |
|
|
109
|
+
| `error` | Signaling or negotiation failed for a peer |
|
|
109
110
|
|
|
110
111
|
Get a channel directly: `rtc.getChannel(peerId, "files")` or `await rtc.whenOpen(peerId, "files")`.
|
|
111
112
|
|
|
113
|
+
**Late channels:** `ready` fires once per peer when all channels declared *at that moment* are open. If you call `channel()` after `ready`, use `whenOpen(peerId, label)` for the new label. On the polite side (lexicographically larger ID), only the impolite peer creates channels — register labels on both sides before `connect()`, or the polite peer will never open them alone.
|
|
114
|
+
|
|
112
115
|
### Sending
|
|
113
116
|
|
|
114
117
|
| Method | Target |
|
|
@@ -141,13 +144,13 @@ Strings are drained before queued file frames, so control messages are not stuck
|
|
|
141
144
|
|
|
142
145
|
### Receiving
|
|
143
146
|
|
|
144
|
-
Attach a `stream` listener before data arrives — streams without a listener are dropped. A slow consumer that lets the receive queue grow past the backlog limit gets an error instead of unbounded RAM.
|
|
147
|
+
Attach a `stream` listener before data arrives — streams without a listener are dropped. A slow consumer that lets the receive queue grow past the backlog limit gets an error instead of unbounded RAM. If the sender aborts or the channel closes mid-transfer, the receive stream errors (not a clean close).
|
|
145
148
|
|
|
146
149
|
## Media
|
|
147
150
|
|
|
148
151
|
```ts
|
|
149
152
|
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
|
|
150
|
-
rtc.add(stream); // tracks
|
|
153
|
+
rtc.add(stream); // tracks on the stream at add-time attach to current and future peers
|
|
151
154
|
|
|
152
155
|
rtc.on("track", (peerId, track, stream) => {
|
|
153
156
|
videoElement.srcObject = stream;
|
|
@@ -158,6 +161,8 @@ rtc.on("track-ended", (peerId, track) => { /* remote track stopped */ });
|
|
|
158
161
|
rtc.remove(stream);
|
|
159
162
|
```
|
|
160
163
|
|
|
164
|
+
Tracks added to a `MediaStream` *after* `add()` are not auto-forwarded. Call `remove(stream)` then `add(stream)`, or add tracks before `add()`.
|
|
165
|
+
|
|
161
166
|
## Transfer limiter
|
|
162
167
|
|
|
163
168
|
Large file sends use memory. `maxTransfers` caps how many run at once (default `4`); extras wait in FIFO.
|
|
@@ -200,7 +205,7 @@ new RTCManager({
|
|
|
200
205
|
| `id` | Local peer ID |
|
|
201
206
|
| `rtcConfig` | Active RTC configuration |
|
|
202
207
|
|
|
203
|
-
**Events:** `join`, `ready`, `leave`, `channel`, `track`, `track-ended`. `.on(event, handler)` returns unsubscribe.
|
|
208
|
+
**Events:** `join`, `ready`, `leave`, `channel`, `track`, `track-ended`, `error`. `.on(event, handler)` returns unsubscribe.
|
|
204
209
|
|
|
205
210
|
### `FastDataChannel`
|
|
206
211
|
|
package/dist/consts.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export declare const FRAME_MAGIC = 252;
|
|
|
2
2
|
export declare const FLAG_FIRST = 1;
|
|
3
3
|
export declare const FLAG_LAST = 2;
|
|
4
4
|
export declare const FLAG_META = 4;
|
|
5
|
+
export declare const FLAG_ABORT = 8;
|
|
5
6
|
export declare const FRAME_HEADER_SIZE = 2;
|
|
6
7
|
export declare const RECEIVE_BACKLOG = 32;
|
|
7
8
|
export declare const SCTP_DEFAULT_MESSAGE_SIZE = 65536;
|
|
@@ -17,7 +17,7 @@ interface FastDataChannelEvents {
|
|
|
17
17
|
export declare class FastDataChannel extends EventEmitter<FastDataChannelEvents> {
|
|
18
18
|
#private;
|
|
19
19
|
readonly channel: RTCDataChannel;
|
|
20
|
-
constructor(channel: RTCDataChannel,
|
|
20
|
+
constructor(channel: RTCDataChannel, pc: RTCPeerConnection | null, limiter?: TransferLimiter);
|
|
21
21
|
send(data: SendData, options?: SendOptions): Promise<void>;
|
|
22
22
|
destroy(): void;
|
|
23
23
|
}
|
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 P=Object.defineProperty;var I=Object.getOwnPropertyDescriptor;var K=Object.getOwnPropertyNames;var z=Object.prototype.hasOwnProperty;var V=(r,e)=>{for(var t in e)P(r,t,{get:e[t],enumerable:!0})},$=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of K(e))!z.call(r,i)&&i!==t&&P(r,i,{get:()=>e[i],enumerable:!(n=I(e,i))||n.enumerable});return r};var Q=r=>$(P({},"__esModule",{value:!0}),r);var W={};V(W,{FastDataChannel:()=>v,RTCManager:()=>R});module.exports=Q(W);var B={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},_=4;var p=class{#t=new Map;on(e,t){let n=t,i=this.#t.get(e)??[];return i.push(n),this.#t.set(e,i),()=>{let a=this.#t.get(e);if(!a)return;let o=a.indexOf(n);o>=0&&a.splice(o,1),a.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 i of n.slice())i(...t)}destroy(){this.#t.clear()}};function A(r,e){let t=new ArrayBuffer(2+e.byteLength),n=new DataView(t);return n.setUint8(0,252),n.setUint8(1,r),new Uint8Array(t,2).set(e),t}function O(r){if(r.byteLength<2)return null;let e=new DataView(r);return e.getUint8(0)!==252?null:{flags:e.getUint8(1),payload:new Uint8Array(r,2)}}function T(){let r=Promise.resolve();return e=>{let t=r.then(e,e);return r=t.then(()=>{},()=>{}),t}}function G(r){return r instanceof Blob||r instanceof ArrayBuffer||ArrayBuffer.isView(r)}function j(r){if(r instanceof Blob)return r.stream();if(r instanceof ArrayBuffer||ArrayBuffer.isView(r)){let t=k(r);return new ReadableStream({start(n){n.enqueue(t),n.close()}})}if(r instanceof ReadableStream)return r;let e=r[Symbol.asyncIterator]();return new ReadableStream({async pull(t){let{done:n,value:i}=await e.next();if(n){t.close();return}t.enqueue(k(i))},cancel:()=>{e.return?.()}})}function k(r){if(r instanceof Uint8Array)return r;if(r instanceof ArrayBuffer)return new Uint8Array(r);if(ArrayBuffer.isView(r))return new Uint8Array(r.buffer,r.byteOffset,r.byteLength);if(typeof r=="string")return new TextEncoder().encode(r);throw r instanceof Blob?new Error("Pass the Blob/File to send, not blob chunks"):new Error("Unsupported stream chunk")}var w=class{#t;#e=0;#o=[];#n=!1;constructor(e){if(e<=0&&e!==1/0)throw new Error("max must be positive or Infinity");this.#t=e}async acquire(){if(this.#n)throw new Error("Transfer limiter destroyed");if(this.#e<this.#t)this.#e++;else{let t=Promise.withResolvers();if(this.#o.push(t),await t.promise,this.#n)throw this.#e--,new Error("Transfer limiter destroyed")}let e=!1;return()=>{e||(e=!0,this.#r())}}destroy(){this.#n=!0;let e=new Error("Transfer limiter destroyed");for(let t of this.#o.splice(0))t.reject(e)}#r(){if(this.#n){this.#e--;return}let e=this.#o.shift();if(e){e.resolve();return}this.#e--}};function J(r){let e=r?.maxMessageSize;return!e||!Number.isFinite(e)?65536:e}var v=class extends p{channel;#t=0;#e=null;#o;#n=[];#r=null;#a=null;#i=!1;#s=!1;#h=!1;#u=T();#l=0;constructor(e,t,n=new w(1/0)){super(),this.channel=e,this.#e=t,this.#o=n,this.#m(),this.channel.binaryType="arraybuffer",this.channel.onopen=()=>this.#g(),this.channel.onclose=()=>this.destroy(),this.channel.onmessage=i=>this.#T(i.data),this.channel.onerror=i=>this.emit("error",i),this.channel.onbufferedamountlow=()=>{this.#c(),this.#S()},this.channel.readyState==="open"&&queueMicrotask(()=>this.#g())}async send(e,t={}){if(this.#s)throw new Error("Data channel closed");if(typeof e=="string"){this.#p(new TextEncoder().encode(e).byteLength),this.#n.push(e),this.#c();return}await this.#u(async()=>{let n=await this.#o.acquire();try{this.#h=!0,await this.#A(e,t)}finally{this.#h=!1,n()}})}destroy(){if(this.#s)return;this.#h&&(this.#n.push(A(10,new Uint8Array(0))),this.#c()),this.#s=!0,this.#n.length=0,this.#d(new Error("Data channel closed"));let e=new Error("Data channel closed");this.#r?.reject(e),this.#r=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{}}#g(){this.#s||(this.#m(),this.#c(),this.emit("open"))}#y(){return J(this.#e?.sctp??null)}#m(){let e=this.#y();this.#t=Math.max(1,e-2),this.channel.bufferedAmountLowThreshold=e*2}#p(e){let t=this.#y();if(e>t)throw new Error(`Message exceeds max SCTP size (${t})`)}#d(e){let t=this.#a;if(t){try{t.error(e)}catch{}this.#a=null}this.#i=!0}#b(){this.#s||this.channel.readyState!=="open"||(this.#n.push(A(10,new Uint8Array(0))),this.#c())}#C(e,t){if(e instanceof Blob)return e.size-t;if(e instanceof ArrayBuffer||ArrayBuffer.isView(e))return e.byteLength-t}#E(e,t,n){n?.(e,t),this.emit("progress",{direction:"send",bytes:e,total:t})}async#A(e,t){this.#m();let n=t.offset??0;if(n!==0&&!(e instanceof Blob))throw new Error("offset requires a Blob/File");let i=e;n!==0&&e instanceof Blob&&(i=e.slice(n));let a=this.#C(e,n),o=0,c=!1;if(t.meta!==void 0){let s=new TextEncoder().encode(JSON.stringify(t.meta));this.#f(5,s),c=!0}let g=j(i),u=g.getReader(),d=!c,f=null,S=()=>{if(t.signal?.aborted)throw t.signal.reason??new Error("Aborted");if(this.#s)throw new Error("Data channel closed")},y=s=>{if(!f)return;let h=f.payload;this.#f(f.flags|(s?2:0),h),f=null,o+=h.byteLength,this.#E(o,a,t.onProgress)},b=()=>{u.cancel(t.signal?.reason??new Error("Aborted"))};t.signal?.addEventListener("abort",b,{once:!0});try{for(;;){S(),await this.#v(t.signal);let{done:s,value:h}=await u.read();if(s){f?y(!0):d?this.#f(3,new Uint8Array(0)):c&&this.#f(2,new Uint8Array(0));break}let C=k(h),E=0;for(;E<C.byteLength;){S(),await this.#v(t.signal);let L=Math.min(E+this.#t,C.byteLength);y(!1);let N=d?1:0;d=!1,f={flags:N,payload:C.slice(E,L)},E=L}}}catch(s){try{this.#b()}catch{}try{await u.cancel(s)}catch{}throw s}finally{t.signal?.removeEventListener("abort",b),g.locked&&u.releaseLock()}}#T(e){let t=e instanceof ArrayBuffer?O(e):null;if(!t){this.emit("message",e);return}if(t.flags&8){this.#d(new Error("Stream aborted")),t.flags&2&&(this.#i=!1);return}if(this.#i){t.flags&2&&(this.#i=!1);return}if(!this.#a){if(!(t.flags&1))return;if(!this.has("stream")){this.#i=!(t.flags&2);return}let i;if(t.flags&4)try{i=JSON.parse(new TextDecoder().decode(t.payload))}catch(c){this.emit("error",c),this.#i=!(t.flags&2);return}let a,o=new ReadableStream({start:c=>{a=c,this.#a=c},cancel:()=>{this.#d(new Error("Stream cancelled"))}});if(this.#l=0,this.emit("stream",o,i),t.flags&4){t.flags&2&&(a?.close(),this.#a=null);return}}let n=this.#a;if(n){if(n.enqueue(t.payload),this.#l+=t.payload.byteLength,this.emit("progress",{direction:"receive",bytes:this.#l}),(n.desiredSize??0)<-32){if(this.#d(new Error("Receive stream backlog exceeded")),!(t.flags&2))return;this.#i=!1;return}t.flags&2&&(n.close(),this.#a=null)}}#f(e,t){this.#s||(this.#p(2+t.byteLength),this.#n.push(A(e,t)),this.#c())}#w(){return!this.#s&&this.channel.readyState==="open"&&this.#n.length===0&&this.channel.bufferedAmount<=this.channel.bufferedAmountLowThreshold}#k(){let e=this.#n.findIndex(t=>typeof t=="string");return e>=0?this.#n.splice(e,1)[0]:this.#n.shift()}#c(){if(!(this.#s||this.channel.readyState!=="open")){for(;this.#n.length>0&&!(this.channel.bufferedAmount>this.channel.bufferedAmountLowThreshold);){let e=this.#k();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.#S()}}#v(e){if(this.#s)return Promise.reject(new Error("Data channel closed"));if(e?.aborted)return Promise.reject(e.reason??new Error("Aborted"));if(this.#w())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)})}#S(){this.#w()&&(this.#r?.resolve(),this.#r=null)}};function q(r){return r instanceof DOMException&&r.name==="InvalidStateError"}var x=class{remoteId;channels=new Map;get isConnected(){return this.#e.connectionState==="connected"}#t;#e;#o;#n=[];#r=!1;#a=!1;#i=!1;#s=T();constructor(e){this.remoteId=e.remoteId,this.#t=e,this.#o=e.localId>e.remoteId,this.#e=new RTCPeerConnection(e.rtcConfig),this.#e.onicecandidate=t=>{this.#i||!t.candidate||this.#t.onSignal({type:"candidate",candidate:t.candidate})},this.#e.onnegotiationneeded=()=>{this.#s(async()=>{if(!this.#i)try{this.#r=!0,await this.#e.setLocalDescription(),!this.#i&&this.#e.localDescription&&this.#t.onSignal({type:"description",description:this.#e.localDescription})}catch(t){q(t)||this.#t.onError(t)}finally{this.#r=!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]??new MediaStream([t.track]);t.track.onended=()=>this.#t.onTrackEnded(t.track,n),this.#t.onTrack(t.track,n)},this.#e.ondatachannel=t=>this.#h(t.channel)}addTrack(e,t){this.#e.getSenders().some(n=>n.track===e)||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{}}receiveSignal(e){this.#s(async()=>{if(!this.#i)try{if(e.type==="description"){let t=e.description.type==="offer"&&(this.#r||this.#e.signalingState!=="stable");if(this.#a=!this.#o&&t,this.#a)return;await this.#e.setRemoteDescription(e.description);for(let n of this.#n)await this.#e.addIceCandidate(n);this.#n=[],e.description.type==="offer"&&(await this.#e.setLocalDescription(),!this.#i&&this.#e.localDescription&&this.#t.onSignal({type:"description",description:this.#e.localDescription}));return}if(this.#a)return;this.#e.remoteDescription?await this.#e.addIceCandidate(e.candidate):this.#n.push(e.candidate)}catch(t){!this.#a&&!q(t)&&this.#t.onError(t)}})}createDataChannel(e,t){this.#o||this.#h(this.#e.createDataChannel(e,t))}#h(e){let t=this.channels.get(e.label),n=new v(e,this.#e,this.#t.limiter);this.channels.set(e.label,n),t?.destroy(),this.#t.onChannel(n)}destroy(){if(!this.#i){this.#i=!0,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;#o=new Set;#n=new Map;#r;#a=new Set;#i=!1;#s=new Set;constructor({id:e,signal:t,rtcConfig:n=B,maxTransfers:i=_}){super(),this.id=e,this.#e=t,this.rtcConfig=n,this.#r=new w(i)}add(e){if(this.#h(),!this.#o.has(e)){this.#o.add(e);for(let t of e.getTracks())for(let n of this.#t.values())n.addTrack(t,e)}}remove(e){this.#h(),this.#o.delete(e);for(let t of e.getTracks())for(let n of this.#t.values())n.removeTrack(t)}channel(e,t={}){this.#h(),this.#n.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={}){this.#h();let i=this.getChannel(e,t);if(i?.channel.readyState==="open")return Promise.resolve(i);let{promise:a,resolve:o,reject:c}=Promise.withResolvers(),g=[],u=!1,d=(s,h)=>{if(!u){u=!0,this.#s.delete(f);for(let C of g)C();n.signal?.removeEventListener("abort",S),h?o(h):c(s??new Error("Channel open failed"))}},f=s=>{d(s)};this.#s.add(f);let S=()=>{d(n.signal?.reason??new Error("Aborted"))};if(n.signal?.aborted)return this.#s.delete(f),Promise.reject(n.signal.reason??new Error("Aborted"));n.signal?.addEventListener("abort",S,{once:!0});let y=s=>{if(s.channel.readyState==="open"){d(void 0,s);return}g.push(s.on("open",()=>d(void 0,s)),s.on("close",()=>{let h=this.getChannel(e,t);if(h&&h!==s){y(h);return}d(new Error("Data channel closed"))}))},b=this.getChannel(e,t);return b&&y(b),g.push(this.on("channel",(s,h)=>{s!==e||h.channel.label!==t||y(h)}),this.on("leave",s=>{s===e&&d(new Error(`Peer left: ${e}`))})),a}connect(e){if(this.#h(),e===this.id)throw new Error("Cannot connect to self");if(this.#t.has(e))return;let t=new x({localId:this.id,remoteId:e,rtcConfig:this.rtcConfig,limiter:this.#r,onSignal:n=>this.#e(e,n),onError:n=>this.emit("error",e,n),onTrack:(n,i)=>this.emit("track",e,n,i),onTrackEnded:(n,i)=>this.emit("track-ended",e,n,i),onChannel:n=>{this.emit("channel",e,n);let i=()=>this.#l(e);n.on("open",i),n.channel.readyState==="open"&&i()},onConnected:()=>this.#l(e),onDisconnect:()=>{this.#t.delete(e)&&(this.#a.delete(e),t.destroy(),this.emit("leave",e))}});this.#n.forEach((n,i)=>t.createDataChannel(i,n));for(let n of this.#o)for(let i of n.getTracks())t.addTrack(i,n);this.#t.set(e,t),this.emit("join",e),this.#l(e)}receive(e,t){this.#h(),this.connect(e),this.#t.get(e).receiveSignal(t)}async sendTo(e,t,n,i={}){this.#h();let a=this.#t.get(e);if(!a)throw new Error(`Unknown peer: ${e}`);let o=a.channels.get(t);if(!o)throw new Error(`No channel "${t}" for peer ${e}`);if(o.channel.readyState!=="open")throw new Error(`Channel "${t}" with ${e} is not open`);await o.send(n,i)}async send(e,t,n={}){this.#h();let i=[];for(let a of this.#t.values()){let o=a.channels.get(e);o?.channel.readyState==="open"&&i.push(o)}if(i.length!==0){if(i.length>1&&typeof t!="string"&&!G(t))throw new Error("Pass a Blob/File/BufferSource to send to multiple peers");await Promise.all(i.map(a=>a.send(t,n)))}}close(e){let t=this.#t.get(e);t&&(this.#t.delete(e),this.#a.delete(e),t.destroy(),this.emit("leave",e))}destroy(){if(this.#i)return;this.#i=!0;let e=new Error("RTCManager destroyed");for(let t of this.#s)t(e);this.#s.clear(),this.#r.destroy();for(let t of[...this.#t.keys()])this.close(t);this.#o.clear(),this.#n.clear(),this.#a.clear(),super.destroy()}#h(){if(this.#i)throw new Error("RTCManager destroyed")}#u(e){let t=this.#t.get(e);if(!t)return!1;if(this.#n.size===0)return t.isConnected;for(let n of this.#n.keys()){let i=t.channels.get(n);if(!i||i.channel.readyState!=="open")return!1}return!0}#l(e){this.#a.has(e)||this.#u(e)&&(this.#a.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 L={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},B=4;var p=class{#t=new Map;on(e,t){let n=t,i=this.#t.get(e)??[];return i.push(n),this.#t.set(e,i),()=>{let a=this.#t.get(e);if(!a)return;let o=a.indexOf(n);o>=0&&a.splice(o,1),a.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 i of n.slice())i(...t)}destroy(){this.#t.clear()}};function A(r,e){let t=new ArrayBuffer(2+e.byteLength),n=new DataView(t);return n.setUint8(0,252),n.setUint8(1,r),new Uint8Array(t,2).set(e),t}function U(r){if(r.byteLength<2)return null;let e=new DataView(r);return e.getUint8(0)!==252?null:{flags:e.getUint8(1),payload:new Uint8Array(r,2)}}function T(){let r=Promise.resolve();return e=>{let t=r.then(e,e);return r=t.then(()=>{},()=>{}),t}}function O(r){return r instanceof Blob||r instanceof ArrayBuffer||ArrayBuffer.isView(r)}function G(r){if(r instanceof Blob)return r.stream();if(r instanceof ArrayBuffer||ArrayBuffer.isView(r)){let t=k(r);return new ReadableStream({start(n){n.enqueue(t),n.close()}})}if(r instanceof ReadableStream)return r;let e=r[Symbol.asyncIterator]();return new ReadableStream({async pull(t){let{done:n,value:i}=await e.next();if(n){t.close();return}t.enqueue(k(i))},cancel:()=>{e.return?.()}})}function k(r){if(r instanceof Uint8Array)return r;if(r instanceof ArrayBuffer)return new Uint8Array(r);if(ArrayBuffer.isView(r))return new Uint8Array(r.buffer,r.byteOffset,r.byteLength);if(typeof r=="string")return new TextEncoder().encode(r);throw r instanceof Blob?new Error("Pass the Blob/File to send, not blob chunks"):new Error("Unsupported stream chunk")}var w=class{#t;#e=0;#o=[];#n=!1;constructor(e){if(e<=0&&e!==1/0)throw new Error("max must be positive or Infinity");this.#t=e}async acquire(){if(this.#n)throw new Error("Transfer limiter destroyed");if(this.#e<this.#t)this.#e++;else{let t=Promise.withResolvers();if(this.#o.push(t),await t.promise,this.#n)throw this.#e--,new Error("Transfer limiter destroyed")}let e=!1;return()=>{e||(e=!0,this.#r())}}destroy(){this.#n=!0;let e=new Error("Transfer limiter destroyed");for(let t of this.#o.splice(0))t.reject(e)}#r(){if(this.#n){this.#e--;return}let e=this.#o.shift();if(e){e.resolve();return}this.#e--}};function K(r){let e=r?.maxMessageSize;return!e||!Number.isFinite(e)?65536:e}var C=class extends p{channel;#t=0;#e=null;#o;#n=[];#r=null;#a=null;#i=!1;#s=!1;#h=!1;#u=T();#l=0;constructor(e,t,n=new w(1/0)){super(),this.channel=e,this.#e=t,this.#o=n,this.#m(),this.channel.binaryType="arraybuffer",this.channel.onopen=()=>this.#g(),this.channel.onclose=()=>this.destroy(),this.channel.onmessage=i=>this.#T(i.data),this.channel.onerror=i=>this.emit("error",i),this.channel.onbufferedamountlow=()=>{this.#c(),this.#S()},this.channel.readyState==="open"&&queueMicrotask(()=>this.#g())}async send(e,t={}){if(this.#s)throw new Error("Data channel closed");if(typeof e=="string"){this.#p(new TextEncoder().encode(e).byteLength),this.#n.push(e),this.#c();return}await this.#u(async()=>{let n=await this.#o.acquire();try{this.#h=!0,await this.#A(e,t)}finally{this.#h=!1,n()}})}destroy(){if(this.#s)return;this.#h&&(this.#n.push(A(10,new Uint8Array(0))),this.#c()),this.#s=!0,this.#n.length=0,this.#d(new Error("Data channel closed"));let e=new Error("Data channel closed");this.#r?.reject(e),this.#r=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{}}#g(){this.#s||(this.#m(),this.#c(),this.emit("open"))}#y(){return K(this.#e?.sctp??null)}#m(){let e=this.#y();this.#t=Math.max(1,e-2),this.channel.bufferedAmountLowThreshold=e*2}#p(e){let t=this.#y();if(e>t)throw new Error(`Message exceeds max SCTP size (${t})`)}#d(e){let t=this.#a;if(t){try{t.error(e)}catch{}this.#a=null}this.#i=!0}#b(){this.#s||this.channel.readyState!=="open"||(this.#n.push(A(10,new Uint8Array(0))),this.#c())}#C(e,t){if(e instanceof Blob)return e.size-t;if(e instanceof ArrayBuffer||ArrayBuffer.isView(e))return e.byteLength-t}#E(e,t,n){n?.(e,t),this.emit("progress",{direction:"send",bytes:e,total:t})}async#A(e,t){this.#m();let n=t.offset??0;if(n!==0&&!(e instanceof Blob))throw new Error("offset requires a Blob/File");let i=e;n!==0&&e instanceof Blob&&(i=e.slice(n));let a=this.#C(e,n),o=0,c=!1;if(t.meta!==void 0){let s=new TextEncoder().encode(JSON.stringify(t.meta));this.#f(5,s),c=!0}let g=G(i),u=g.getReader(),d=!c,f=null,v=()=>{if(t.signal?.aborted)throw t.signal.reason??new Error("Aborted");if(this.#s)throw new Error("Data channel closed")},y=s=>{if(!f)return;let h=f.payload;this.#f(f.flags|(s?2:0),h),f=null,o+=h.byteLength,this.#E(o,a,t.onProgress)},S=()=>{u.cancel(t.signal?.reason??new Error("Aborted"))};t.signal?.addEventListener("abort",S,{once:!0});try{for(;;){v(),await this.#v(t.signal);let{done:s,value:h}=await u.read();if(s){f?y(!0):d?this.#f(3,new Uint8Array(0)):c&&this.#f(2,new Uint8Array(0));break}let b=k(h),E=0;for(;E<b.byteLength;){v(),await this.#v(t.signal);let M=Math.min(E+this.#t,b.byteLength);y(!1);let q=d?1:0;d=!1,f={flags:q,payload:b.slice(E,M)},E=M}}}catch(s){try{this.#b()}catch{}try{await u.cancel(s)}catch{}throw s}finally{t.signal?.removeEventListener("abort",S),g.locked&&u.releaseLock()}}#T(e){let t=e instanceof ArrayBuffer?U(e):null;if(!t){this.emit("message",e);return}if(t.flags&8){this.#d(new Error("Stream aborted")),t.flags&2&&(this.#i=!1);return}if(this.#i){t.flags&2&&(this.#i=!1);return}if(!this.#a){if(!(t.flags&1))return;if(!this.has("stream")){this.#i=!(t.flags&2);return}let i;if(t.flags&4)try{i=JSON.parse(new TextDecoder().decode(t.payload))}catch(c){this.emit("error",c),this.#i=!(t.flags&2);return}let a,o=new ReadableStream({start:c=>{a=c,this.#a=c},cancel:()=>{this.#d(new Error("Stream cancelled"))}});if(this.#l=0,this.emit("stream",o,i),t.flags&4){t.flags&2&&(a?.close(),this.#a=null);return}}let n=this.#a;if(n){if(n.enqueue(t.payload),this.#l+=t.payload.byteLength,this.emit("progress",{direction:"receive",bytes:this.#l}),(n.desiredSize??0)<-32){if(this.#d(new Error("Receive stream backlog exceeded")),!(t.flags&2))return;this.#i=!1;return}t.flags&2&&(n.close(),this.#a=null)}}#f(e,t){this.#s||(this.#p(2+t.byteLength),this.#n.push(A(e,t)),this.#c())}#w(){return!this.#s&&this.channel.readyState==="open"&&this.#n.length===0&&this.channel.bufferedAmount<=this.channel.bufferedAmountLowThreshold}#k(){let e=this.#n.findIndex(t=>typeof t=="string");return e>=0?this.#n.splice(e,1)[0]:this.#n.shift()}#c(){if(!(this.#s||this.channel.readyState!=="open")){for(;this.#n.length>0&&!(this.channel.bufferedAmount>this.channel.bufferedAmountLowThreshold);){let e=this.#k();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.#S()}}#v(e){if(this.#s)return Promise.reject(new Error("Data channel closed"));if(e?.aborted)return Promise.reject(e.reason??new Error("Aborted"));if(this.#w())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)})}#S(){this.#w()&&(this.#r?.resolve(),this.#r=null)}};function j(r){return r instanceof DOMException&&r.name==="InvalidStateError"}var x=class{remoteId;channels=new Map;get isConnected(){return this.#e.connectionState==="connected"}#t;#e;#o;#n=[];#r=!1;#a=!1;#i=!1;#s=T();constructor(e){this.remoteId=e.remoteId,this.#t=e,this.#o=e.localId>e.remoteId,this.#e=new RTCPeerConnection(e.rtcConfig),this.#e.onicecandidate=t=>{this.#i||!t.candidate||this.#t.onSignal({type:"candidate",candidate:t.candidate})},this.#e.onnegotiationneeded=()=>{this.#s(async()=>{if(!this.#i)try{this.#r=!0,await this.#e.setLocalDescription(),!this.#i&&this.#e.localDescription&&this.#t.onSignal({type:"description",description:this.#e.localDescription})}catch(t){j(t)||this.#t.onError(t)}finally{this.#r=!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]??new MediaStream([t.track]);t.track.onended=()=>this.#t.onTrackEnded(t.track,n),this.#t.onTrack(t.track,n)},this.#e.ondatachannel=t=>this.#h(t.channel)}addTrack(e,t){this.#e.getSenders().some(n=>n.track===e)||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{}}receiveSignal(e){this.#s(async()=>{if(!this.#i)try{if(e.type==="description"){let t=e.description.type==="offer"&&(this.#r||this.#e.signalingState!=="stable");if(this.#a=!this.#o&&t,this.#a)return;await this.#e.setRemoteDescription(e.description);for(let n of this.#n)await this.#e.addIceCandidate(n);this.#n=[],e.description.type==="offer"&&(await this.#e.setLocalDescription(),!this.#i&&this.#e.localDescription&&this.#t.onSignal({type:"description",description:this.#e.localDescription}));return}if(this.#a)return;this.#e.remoteDescription?await this.#e.addIceCandidate(e.candidate):this.#n.push(e.candidate)}catch(t){!this.#a&&!j(t)&&this.#t.onError(t)}})}createDataChannel(e,t){this.#o||this.#h(this.#e.createDataChannel(e,t))}#h(e){let t=this.channels.get(e.label),n=new C(e,this.#e,this.#t.limiter);this.channels.set(e.label,n),t?.destroy(),this.#t.onChannel(n)}destroy(){if(!this.#i){this.#i=!0,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 F=class extends p{id;rtcConfig;#t=new Map;#e;#o=new Set;#n=new Map;#r;#a=new Set;#i=!1;#s=new Set;constructor({id:e,signal:t,rtcConfig:n=L,maxTransfers:i=B}){super(),this.id=e,this.#e=t,this.rtcConfig=n,this.#r=new w(i)}add(e){if(this.#h(),!this.#o.has(e)){this.#o.add(e);for(let t of e.getTracks())for(let n of this.#t.values())n.addTrack(t,e)}}remove(e){this.#h(),this.#o.delete(e);for(let t of e.getTracks())for(let n of this.#t.values())n.removeTrack(t)}channel(e,t={}){this.#h(),this.#n.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={}){this.#h();let i=this.getChannel(e,t);if(i?.channel.readyState==="open")return Promise.resolve(i);let{promise:a,resolve:o,reject:c}=Promise.withResolvers(),g=[],u=!1,d=(s,h)=>{if(!u){u=!0,this.#s.delete(f);for(let b of g)b();n.signal?.removeEventListener("abort",v),h?o(h):c(s??new Error("Channel open failed"))}},f=s=>{d(s)};this.#s.add(f);let v=()=>{d(n.signal?.reason??new Error("Aborted"))};if(n.signal?.aborted)return this.#s.delete(f),Promise.reject(n.signal.reason??new Error("Aborted"));n.signal?.addEventListener("abort",v,{once:!0});let y=s=>{if(s.channel.readyState==="open"){d(void 0,s);return}g.push(s.on("open",()=>d(void 0,s)),s.on("close",()=>{let h=this.getChannel(e,t);if(h&&h!==s){y(h);return}d(new Error("Data channel closed"))}))},S=this.getChannel(e,t);return S&&y(S),g.push(this.on("channel",(s,h)=>{s!==e||h.channel.label!==t||y(h)}),this.on("leave",s=>{s===e&&d(new Error(`Peer left: ${e}`))})),a}connect(e){if(this.#h(),e===this.id)throw new Error("Cannot connect to self");if(this.#t.has(e))return;let t=new x({localId:this.id,remoteId:e,rtcConfig:this.rtcConfig,limiter:this.#r,onSignal:n=>this.#e(e,n),onError:n=>this.emit("error",e,n),onTrack:(n,i)=>this.emit("track",e,n,i),onTrackEnded:(n,i)=>this.emit("track-ended",e,n,i),onChannel:n=>{this.emit("channel",e,n);let i=()=>this.#l(e);n.on("open",i),n.channel.readyState==="open"&&i()},onConnected:()=>this.#l(e),onDisconnect:()=>{this.#t.delete(e)&&(this.#a.delete(e),t.destroy(),this.emit("leave",e))}});this.#n.forEach((n,i)=>t.createDataChannel(i,n));for(let n of this.#o)for(let i of n.getTracks())t.addTrack(i,n);this.#t.set(e,t),this.emit("join",e),this.#l(e)}receive(e,t){this.#h(),this.connect(e),this.#t.get(e).receiveSignal(t)}async sendTo(e,t,n,i={}){this.#h();let a=this.#t.get(e);if(!a)throw new Error(`Unknown peer: ${e}`);let o=a.channels.get(t);if(!o)throw new Error(`No channel "${t}" for peer ${e}`);if(o.channel.readyState!=="open")throw new Error(`Channel "${t}" with ${e} is not open`);await o.send(n,i)}async send(e,t,n={}){this.#h();let i=[];for(let a of this.#t.values()){let o=a.channels.get(e);o?.channel.readyState==="open"&&i.push(o)}if(i.length!==0){if(i.length>1&&typeof t!="string"&&!O(t))throw new Error("Pass a Blob/File/BufferSource to send to multiple peers");await Promise.all(i.map(a=>a.send(t,n)))}}close(e){let t=this.#t.get(e);t&&(this.#t.delete(e),this.#a.delete(e),t.destroy(),this.emit("leave",e))}destroy(){if(this.#i)return;this.#i=!0;let e=new Error("RTCManager destroyed");for(let t of this.#s)t(e);this.#s.clear(),this.#r.destroy();for(let t of[...this.#t.keys()])this.close(t);this.#o.clear(),this.#n.clear(),this.#a.clear(),super.destroy()}#h(){if(this.#i)throw new Error("RTCManager destroyed")}#u(e){let t=this.#t.get(e);if(!t)return!1;if(this.#n.size===0)return t.isConnected;for(let n of this.#n.keys()){let i=t.channels.get(n);if(!i||i.channel.readyState!=="open")return!1}return!0}#l(e){this.#a.has(e)||this.#u(e)&&(this.#a.add(e),this.emit("ready",e))}};export{C as FastDataChannel,F as RTCManager};
|
package/dist/peer-node.d.ts
CHANGED
|
@@ -7,19 +7,22 @@ type PeerNodeOptions = {
|
|
|
7
7
|
rtcConfig: RTCConfiguration;
|
|
8
8
|
limiter: TransferLimiter;
|
|
9
9
|
onSignal: (payload: SignalPayload) => void;
|
|
10
|
+
onError: (error: unknown) => void;
|
|
10
11
|
onTrack: (track: MediaStreamTrack, stream: MediaStream) => void;
|
|
11
12
|
onTrackEnded: (track: MediaStreamTrack, stream: MediaStream) => void;
|
|
12
13
|
onChannel: (channel: FastDataChannel) => void;
|
|
14
|
+
onConnected: () => void;
|
|
13
15
|
onDisconnect: () => void;
|
|
14
16
|
};
|
|
15
17
|
export declare class PeerNode {
|
|
16
18
|
#private;
|
|
17
19
|
readonly remoteId: string;
|
|
18
20
|
readonly channels: Map<string, FastDataChannel>;
|
|
21
|
+
get isConnected(): boolean;
|
|
19
22
|
constructor(opts: PeerNodeOptions);
|
|
20
23
|
addTrack(track: MediaStreamTrack, stream: MediaStream): void;
|
|
21
24
|
removeTrack(track: MediaStreamTrack): void;
|
|
22
|
-
receiveSignal(signal: SignalPayload):
|
|
25
|
+
receiveSignal(signal: SignalPayload): void;
|
|
23
26
|
createDataChannel(label: string, options?: RTCDataChannelInit): void;
|
|
24
27
|
destroy(): void;
|
|
25
28
|
}
|
package/dist/queue.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function createQueue(): (job: () => Promise<void>) => Promise<void>;
|
package/dist/rtc-manager.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ interface RTCManagerEvents {
|
|
|
21
21
|
channel: (id: string, channel: FastDataChannel) => void;
|
|
22
22
|
track: (id: string, track: MediaStreamTrack, stream: MediaStream) => void;
|
|
23
23
|
"track-ended": (id: string, track: MediaStreamTrack, stream: MediaStream) => void;
|
|
24
|
+
error: (id: string, error: unknown) => void;
|
|
24
25
|
}
|
|
25
26
|
export declare class RTCManager extends EventEmitter<RTCManagerEvents> {
|
|
26
27
|
#private;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastrtc",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Easy to use, zero-dependency WebRTC mesh library for browsers",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"datachannel",
|
|
@@ -21,17 +21,20 @@
|
|
|
21
21
|
"types": "dist/index.d.ts",
|
|
22
22
|
"exports": {
|
|
23
23
|
".": {
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
24
25
|
"import": "./dist/index.js",
|
|
25
26
|
"require": "./dist/index.cjs"
|
|
26
27
|
}
|
|
27
28
|
},
|
|
28
29
|
"devDependencies": {
|
|
29
30
|
"typescript": "^7.0.2",
|
|
30
|
-
"tsup": "^8.5.1"
|
|
31
|
+
"tsup": "^8.5.1",
|
|
32
|
+
"vitest": "^3.2.4"
|
|
31
33
|
},
|
|
32
34
|
"scripts": {
|
|
33
35
|
"clean": "rm -rf dist",
|
|
34
36
|
"build": "pnpm clean && tsup && tsc",
|
|
35
|
-
"test": "
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"test:watch": "vitest"
|
|
36
39
|
}
|
|
37
40
|
}
|