tangled
alpha
login
or
join now
evbogue.com
/
wiredove
1
fork
atom
a demonstration replicated social networking web app built with anproto
wiredove.net/
social
ed25519
protocols
1
fork
atom
overview
issues
pulls
pipelines
grab dep from apds
Everett Bogue
4 months ago
40535d51
9e8efbce
+2
-2
2 changed files
expand all
collapse all
unified
split
index.html
trystero-torrent.min.js
+1
-1
index.html
···
15
15
{
16
16
"imports": {
17
17
"apds": "https://esm.sh/gh/evbogue/apds/apds.js",
18
18
-
"h": "https://esm.sh/gh/evbogue/bog5@38ac1c121f/lib/h.js"
18
18
+
"h": "https://esm.sh/gh/evbogue/apds/lib/h.js"
19
19
}
20
20
}
21
21
</script>
+1
-1
trystero-torrent.min.js
···
1
1
-
const{floor:e,random:r}=Math,t="Trystero",n=(e,r)=>Array(e).fill().map(r),a="0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz",o=t=>n(t,(()=>a[e(62*r())])).join(""),s=o(20),i=Promise.all.bind(Promise),c="undefined"!=typeof window,{entries:l,fromEntries:d,keys:f}=Object,u=()=>{},p=e=>Error(`${t}: ${e}`),y=new TextEncoder,m=new TextDecoder,w=e=>y.encode(e),g=e=>m.decode(e),h=(...e)=>e.join("@"),b=JSON.stringify,k=JSON.parse,v={},P="AES-GCM",T={},A=async e=>T[e]||(T[e]=Array.from(new Uint8Array(await crypto.subtle.digest("SHA-1",w(e)))).map((e=>e.toString(36))).join("")),S=async(e,r)=>{const t=crypto.getRandomValues(new Uint8Array(16));return t.join(",")+"$"+(n=await crypto.subtle.encrypt({name:P,iv:t},await e,w(r)),btoa(String.fromCharCode.apply(null,new Uint8Array(n))));var n},D=async(e,r)=>{const[t,n]=r.split("$");return g(await crypto.subtle.decrypt({name:P,iv:new Uint8Array(t.split(","))},await e,(e=>{const r=atob(e);return new Uint8Array(r.length).map(((e,t)=>r.charCodeAt(t))).buffer})(n)))},L="icegatheringstatechange",I="offer";var $=(e,{rtcConfig:r,rtcPolyfill:t,turnConfig:n})=>{const a=new(t||RTCPeerConnection)({iceServers:E.concat(n||[]),...r}),o={};let s=!1,c=!1,l=null;const d=e=>{e.binaryType="arraybuffer",e.bufferedAmountLowThreshold=65535,e.onmessage=e=>o.data?.(e.data),e.onopen=()=>o.connect?.(),e.onclose=()=>o.close?.(),e.onerror=e=>o.error?.(e)},f=e=>Promise.race([new Promise((r=>{const t=()=>{"complete"===e.iceGatheringState&&(e.removeEventListener(L,t),r())};e.addEventListener(L,t),t()})),new Promise((e=>setTimeout(e,5e3)))]).then((()=>({type:e.localDescription.type,sdp:e.localDescription.sdp.replace(/a=ice-options:trickle\s\n/g,"")})));return e?(l=a.createDataChannel("data"),d(l)):a.ondatachannel=({channel:e})=>{l=e,d(e)},a.onnegotiationneeded=async()=>{try{s=!0,await a.setLocalDescription();const e=await f(a);o.signal?.(e)}catch(e){o.error?.(e)}finally{s=!1}},a.onconnectionstatechange=()=>{["disconnected","failed","closed"].includes(a.connectionState)&&o.close?.()},a.ontrack=e=>{o.track?.(e.track,e.streams[0]),o.stream?.(e.streams[0])},a.onremovestream=e=>o.stream?.(e.stream),e&&(a.canTrickleIceCandidates||a.onnegotiationneeded()),{created:Date.now(),connection:a,get channel(){return l},get isDead(){return"closed"===a.connectionState},async signal(r){if("open"!==l?.readyState||r.sdp?.includes("a=rtpmap"))try{if(r.type===I){if(s||"stable"!==a.signalingState&&!c){if(e)return;await i([a.setLocalDescription({type:"rollback"}),a.setRemoteDescription(r)])}else await a.setRemoteDescription(r);await a.setLocalDescription();const t=await f(a);return o.signal?.(t),t}if("answer"===r.type){c=!0;try{await a.setRemoteDescription(r)}finally{c=!1}}}catch(e){o.error?.(e)}},sendData(e){return l.send(e)},destroy(){l?.close(),a.close(),s=!1,c=!1},setHandlers(e){return Object.assign(o,e)},offerPromise:e?new Promise((e=>o.signal=r=>{r.type===I&&e(r)})):Promise.resolve(),addStream(e){return e.getTracks().forEach((r=>a.addTrack(r,e)))},removeStream(e){return a.getSenders().filter((r=>e.getTracks().includes(r.track))).forEach((e=>a.removeTrack(e)))},addTrack(e,r){return a.addTrack(e,r)},removeTrack(e){const r=a.getSenders().find((r=>r.track===e));r&&a.removeTrack(r)},replaceTrack(e,r){const t=a.getSenders().find((r=>r.track===e));if(t)return t.replaceTrack(r)}}};const E=[...n(3,((e,r)=>`stun:stun${r||""}.l.google.com:19302`)),"stun:stun.cloudflare.com:3478"].map((e=>({urls:e}))),C=Object.getPrototypeOf(Uint8Array),U=16369,_=255,O="bufferedamountlow",j=e=>"@_"+e;const H={},J={},M={},R={},x={},q={},B={},G={},N=async e=>{if(J[e])return J[e];const r=(await A(e)).slice(0,20);return J[e]=r,M[r]=e,r},z=async(e,r,t)=>e.send(b({action:"announce",info_hash:await N(r),peer_id:s,...t})),K=(e,r,n)=>console.warn(`${t}: torrent tracker ${n?"failure":"warning"} from ${e} - ${r}`),V=(({init:e,subscribe:r,announce:a})=>{const o={};let y,m,v,T=!1;return(L,I,E)=>{const{appId:H}=L;if(o[H]?.[I])return o[H][I];const J={},M={},R=h(t,H,I),x=A(R),q=A(h(R,s)),B=(async(e,r,t)=>crypto.subtle.importKey("raw",await crypto.subtle.digest({name:"SHA-256"},w(`${e}:${r}:${t}`)),{name:P},!1,["encrypt","decrypt"]))(L.password||"",H,I),G=e=>async r=>({type:r.type,sdp:await e(B,r.sdp)}),N=G(D),z=G(S),K=()=>$(!0,L),V=(e,r,t)=>{M[r]?M[r]!==e&&e.destroy():(M[r]=e,re(e,r),J[r]?.forEach(((e,r)=>{r!==t&&e.destroy()})),delete J[r])},W=(e,r)=>{M[r]===e&&delete M[r]},F=e=>(m.push(...n(e,K)),i(m.splice(0,e).map((e=>e.offerPromise.then(z).then((r=>({peer:e,offer:r}))))))),Q=(e,r)=>E?.({error:`incorrect password (${L.password}) when decrypting ${r}`,appId:H,peerId:e,roomId:I}),X=e=>async(r,t,n)=>{const[a,o]=await i([x,q]);if(r!==a&&r!==o)return;const{peerId:c,offer:l,answer:d,peer:f}="string"==typeof t?k(t):t;if(c!==s&&!M[c])if(!c||l||d){if(l){const r=J[c]?.[e];if(r&&s>c)return;const t=$(!1,L);let a;t.setHandlers({connect(){return V(t,c,e)},close(){return W(t,c)}});try{a=await N(l)}catch{return void Q(c,"offer")}if(t.isDead)return;const[o,d]=await i([A(h(R,c)),t.signal(a)]);n(o,b({peerId:s,answer:await z(d)}))}else if(d){let r;try{r=await N(d)}catch(e){return void Q(c,"answer")}if(f)f.setHandlers({connect(){return V(f,c,e)},close(){return W(f,c)}}),f.signal(r);else{const t=J[c]?.[e];t&&!t.isDead&&t.signal(r)}}}else{if(J[c]?.[e])return;const[[{peer:r,offer:t}],a]=await i([F(1),A(h(R,c))]);J[c]||=[],J[c][e]=r,setTimeout((()=>((e,r)=>{if(M[e])return;const t=J[e]?.[r];t&&(delete J[e][r],t.destroy())})(c,e)),.9*Y[e]),r.setHandlers({connect(){return V(r,c,e)},close(){return W(r,c)}}),n(a,b({peerId:s,offer:t}))}};if(!L)throw p("requires a config map as the first argument");if(!H&&!L.firebaseApp)throw p("config map is missing appId field");if(!I)throw p("roomId argument required");if(!T){const r=e(L);m=n(20,K),y=Array.isArray(r)?r:[r],T=!0,v=setInterval((()=>m=m.filter((e=>{const r=Date.now()-e.created<57333;return r||e.destroy(),r}))),59052.99)}const Y=y.map((()=>5333)),Z=[],ee=y.map((async(e,t)=>r(await e,await x,await q,X(t),F)));i([x,q]).then((([e,r])=>{const t=async(n,o)=>{const s=await a(n,e,r);"number"==typeof s&&(Y[o]=s),Z[o]=setTimeout((()=>t(n,o)),Y[o])};ee.forEach((async(e,r)=>{await e,t(await y[r],r)}))}));let re=u;return o[H]||={},o[H][I]=((e,r,a)=>{const o={},s={},y={},m={},h={},v={},P={},T={onPeerJoin:u,onPeerLeave:u,onPeerStream:u,onPeerTrack:u},A=(e,r)=>(e?Array.isArray(e)?e:[e]:f(o)).flatMap((e=>{const n=o[e];return n?r(e,n):(console.warn(`${t}: no peer with id ${e} found`),[])})),S=e=>{o[e]&&(delete o[e],delete m[e],delete h[e],T.onPeerLeave(e),r(e))},D=e=>{if(s[e])return y[e];if(!e)throw p("action type argument is required");const r=w(e);if(r.byteLength>12)throw p(`action type string "${e}" (${r.byteLength}b) exceeds byte limit (12). Hint: choose a shorter name.`);const t=new Uint8Array(12);t.set(r);let a=0;return s[e]={onComplete:u,onProgress:u,setOnComplete:r=>s[e]={...s[e],onComplete:r},setOnProgress:r=>s[e]={...s[e],onProgress:r},async send(e,r,s,c){if(s&&"object"!=typeof s)throw p("action meta argument must be an object");const l=typeof e;if("undefined"===l)throw p("action data cannot be undefined");const d="string"!==l,f=e instanceof Blob,u=f||e instanceof ArrayBuffer||e instanceof C;if(s&&!u)throw p("action meta argument can only be used with binary data");const y=u?new Uint8Array(f?await e.arrayBuffer():e):w(d?b(e):e),m=s?w(b(s)):null,g=Math.ceil(y.byteLength/U)+(s?1:0)||1,h=n(g,((e,r)=>{const n=r===g-1,o=s&&0===r,i=new Uint8Array(15+(o?m.byteLength:n?y.byteLength-U*(g-(s?2:1)):U));return i.set(t),i.set([a],12),i.set([n|o<<1|u<<2|d<<3],13),i.set([Math.round((r+1)/g*_)],14),i.set(s?o?m:y.subarray((r-1)*U,r*U):y.subarray(r*U,(r+1)*U),15),i}));return a=a+1&_,i(A(r,(async(e,r)=>{const{channel:t}=r;let n=0;for(;n<g;){const a=h[n];if(t.bufferedAmount>t.bufferedAmountLowThreshold&&await new Promise((e=>{const r=()=>{t.removeEventListener(O,r),e()};t.addEventListener(O,r)})),!o[e])break;r.sendData(a),n++,c?.(a[14]/_,e,s)}})))}},y[e]||=[s[e].send,s[e].setOnComplete,s[e].setOnProgress]},L=(e,r)=>{const n=new Uint8Array(r),a=g(n.subarray(0,12)).replaceAll("\0",""),[o]=n.subarray(12,13),[i]=n.subarray(13,14),[c]=n.subarray(14,15),l=n.subarray(15),d=!!(1&i),f=!!(2&i),u=!!(4&i),p=!!(8&i);if(!s[a])return void console.warn(`${t}: received message with unregistered type (${a})`);m[e]||={},m[e][a]||={};const y=m[e][a][o]||={chunks:[]};if(f?y.meta=k(g(l)):y.chunks.push(l),s[a].onProgress(c/_,e,y.meta),!d)return;const w=new Uint8Array(y.chunks.reduce(((e,r)=>e+r.byteLength),0));if(y.chunks.reduce(((e,r)=>(w.set(r,e),e+r.byteLength)),0),delete m[e][a][o],u)s[a].onComplete(w,e,y.meta);else{const r=g(w);s[a].onComplete(p?k(r):r,e)}},I=async()=>{await N(""),await new Promise((e=>setTimeout(e,99))),l(o).forEach((([e,r])=>{r.destroy(),delete o[e]})),a()},[$,E]=D(j("ping")),[H,J]=D(j("pong")),[M,R]=D(j("signal")),[x,q]=D(j("stream")),[B,G]=D(j("track")),[N,z]=D(j("leave"));return e(((e,r)=>{o[r]||(o[r]=e,e.setHandlers({data:e=>L(r,e),stream(e){T.onPeerStream(e,r,v[r]),delete v[r]},track(e,t){T.onPeerTrack(e,t,r,P[r]),delete P[r]},signal:e=>M(e,r),close:()=>S(r),error(e){console.error(e),S(r)}}),T.onPeerJoin(r),e.drainEarlyData?.((e=>L(r,e))))})),E(((e,r)=>H("",r))),J(((e,r)=>{h[r]?.(),delete h[r]})),R(((e,r)=>o[r]?.signal(e))),q(((e,r)=>v[r]=e)),G(((e,r)=>P[r]=e)),z(((e,r)=>S(r))),c&&addEventListener("beforeunload",I),{makeAction:D,leave:I,async ping(e){if(!e)throw p("ping() must be called with target peer ID");const r=Date.now();return $("",e),await new Promise((r=>h[e]=r)),Date.now()-r},getPeers:()=>d(l(o).map((([e,r])=>[e,r.connection]))),addStream:(e,r,t)=>A(r,(async(r,n)=>{t&&await x(t,r),n.addStream(e)})),removeStream:(e,r)=>A(r,((r,t)=>t.removeStream(e))),addTrack:(e,r,t,n)=>A(t,(async(t,a)=>{n&&await B(n,t),a.addTrack(e,r)})),removeTrack:(e,r)=>A(r,((r,t)=>t.removeTrack(e))),replaceTrack:(e,r,t,n)=>A(t,(async(t,a)=>{n&&await B(n,t),a.replaceTrack(e,r)})),onPeerJoin:e=>T.onPeerJoin=e,onPeerLeave:e=>T.onPeerLeave=e,onPeerStream:e=>T.onPeerStream=e,onPeerTrack:e=>T.onPeerTrack=e}})((e=>re=e),(e=>delete M[e]),(()=>{delete o[H][I],Z.forEach(clearTimeout),ee.forEach((async e=>(await e)())),clearInterval(v)}))}})({init(e){return((e,r,t)=>(e.relayUrls||r).slice(0,e.relayUrls?e.relayUrls.length:e.relayRedundancy||t))(e,Q,3).map((e=>{const r=((e,r)=>{const t={},n=()=>{const a=new WebSocket(e);a.onclose=()=>{v[e]??=3333,setTimeout(n,v[e]),v[e]*=2},a.onmessage=e=>r(e.data),t.socket=a,t.url=a.url,t.ready=new Promise((r=>a.onopen=()=>{r(t),v[e]=3333})),t.send=e=>{1===a.readyState&&a.send(e)}};return n(),t})(e,(e=>{const r=k(e),n=r["failure reason"],a=r["warning message"],{interval:o}=r,s=M[r.info_hash];if(n)K(t,n,!0);else{if(a&&K(t,a),o&&1e3*o>q[t]&&x[t][s]){const e=Math.min(1e3*o,120333);clearInterval(R[t][s]),q[t]=e,R[t][s]=setInterval(x[t][s],e)}B[r.offer_id]||(r.offer||r.answer)&&(B[r.offer_id]=!0,G[t][s]?.(r))}})),{url:t}=r;return H[t]=r,G[t]={},r.ready}))},subscribe(e,r,t,n,a){const{url:s}=e,i=async()=>{const t=d((await a(10)).map((e=>[o(20),e])));G[e.url][r]=a=>{if(a.offer)n(r,{offer:a.offer,peerId:a.peer_id},((t,n)=>z(e,r,{answer:k(n).answer,offer_id:a.offer_id,to_peer_id:a.peer_id})));else if(a.answer){const e=t[a.offer_id];e&&n(r,{answer:a.answer,peerId:a.peer_id,peer:e.peer})}},z(e,r,{numwant:10,offers:l(t).map((([e,{offer:r}])=>({offer_id:e,offer:r})))})};return q[s]=33333,x[s]||={},x[s][r]=i,R[s]||={},R[s][r]=setInterval(i,q[s]),i(),()=>{clearInterval(R[s][r]),delete G[s][r],delete x[s][r]}},announce(e){return q[e.url]}}),W=(F=H,()=>d(l(F).map((([e,r])=>[e,r.socket]))));var F;const Q=["tracker.webtorrent.dev","tracker.openwebtorrent.com","tracker.btorrent.xyz","tracker.files.fm:7073/announce"].map((e=>"wss://"+e));export{Q as defaultRelayUrls,W as getRelaySockets,V as joinRoom,s as selfId};
1
1
+
const{floor:e,random:r}=Math,t="Trystero",n=(e,r)=>Array(e).fill().map(r),a="0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz",o=t=>n(t,(()=>a[e(62*r())])).join(""),s=o(20),i=Promise.all.bind(Promise),c="undefined"!=typeof window,{entries:l,fromEntries:d,keys:u}=Object,f=()=>{},y=e=>Error(`${t}: ${e}`),p=new TextEncoder,m=new TextDecoder,w=e=>p.encode(e),g=e=>m.decode(e),h=(...e)=>e.join("@"),b=JSON.stringify,k=JSON.parse,v={};let P=null,T=null;const A=()=>{P||(P=new Promise((e=>{T=e})).finally((()=>{T=null,P=null})))},S=()=>T?.(),L="AES-GCM",D={},I=async e=>D[e]||=Array.from(await(async(e,r)=>new Uint8Array(await crypto.subtle.digest(e,w(r))))("SHA-1",e)).map((e=>e.toString(36))).join(""),$=async(e,r)=>{const t=crypto.getRandomValues(new Uint8Array(16));return t.join(",")+"$"+(n=await crypto.subtle.encrypt({name:L,iv:t},await e,w(r)),btoa(String.fromCharCode.apply(null,new Uint8Array(n))));var n},E=async(e,r)=>{const[t,n]=r.split("$");return g(await crypto.subtle.decrypt({name:L,iv:new Uint8Array(t.split(","))},await e,(e=>{const r=atob(e);return new Uint8Array(r.length).map(((e,t)=>r.charCodeAt(t))).buffer})(n)))},C="icegatheringstatechange",U="offer";var _=(e,{rtcConfig:r,rtcPolyfill:t,turnConfig:n})=>{const a=new(t||RTCPeerConnection)({iceServers:O.concat(n||[]),...r}),o={};let s=!1,c=!1,l=null;const d=e=>{e.binaryType="arraybuffer",e.bufferedAmountLowThreshold=65535,e.onmessage=e=>o.data?.(e.data),e.onopen=()=>o.connect?.(),e.onclose=()=>o.close?.(),e.onerror=e=>o.error?.(e)},u=e=>Promise.race([new Promise((r=>{const t=()=>{"complete"===e.iceGatheringState&&(e.removeEventListener(C,t),r())};e.addEventListener(C,t),t()})),new Promise((e=>setTimeout(e,5e3)))]).then((()=>({type:e.localDescription.type,sdp:e.localDescription.sdp.replace(/a=ice-options:trickle\s\n/g,"")})));return e?(l=a.createDataChannel("data"),d(l)):a.ondatachannel=({channel:e})=>{l=e,d(e)},a.onnegotiationneeded=async()=>{try{s=!0,await a.setLocalDescription();const e=await u(a);o.signal?.(e)}catch(e){o.error?.(e)}finally{s=!1}},a.onconnectionstatechange=()=>{["disconnected","failed","closed"].includes(a.connectionState)&&o.close?.()},a.ontrack=e=>{o.track?.(e.track,e.streams[0]),o.stream?.(e.streams[0])},a.onremovestream=e=>o.stream?.(e.stream),e&&(a.canTrickleIceCandidates||a.onnegotiationneeded()),{created:Date.now(),connection:a,get channel(){return l},get isDead(){return"closed"===a.connectionState},async signal(r){if("open"!==l?.readyState||r.sdp?.includes("a=rtpmap"))try{if(r.type===U){if(s||"stable"!==a.signalingState&&!c){if(e)return;await i([a.setLocalDescription({type:"rollback"}),a.setRemoteDescription(r)])}else await a.setRemoteDescription(r);await a.setLocalDescription();const t=await u(a);return o.signal?.(t),t}if("answer"===r.type){c=!0;try{await a.setRemoteDescription(r)}finally{c=!1}}}catch(e){o.error?.(e)}},sendData(e){return l.send(e)},destroy(){l?.close(),a.close(),s=!1,c=!1},setHandlers(e){return Object.assign(o,e)},offerPromise:e?new Promise((e=>o.signal=r=>{r.type===U&&e(r)})):Promise.resolve(),addStream(e){return e.getTracks().forEach((r=>a.addTrack(r,e)))},removeStream(e){return a.getSenders().filter((r=>e.getTracks().includes(r.track))).forEach((e=>a.removeTrack(e)))},addTrack(e,r){return a.addTrack(e,r)},removeTrack(e){const r=a.getSenders().find((r=>r.track===e));r&&a.removeTrack(r)},replaceTrack(e,r){const t=a.getSenders().find((r=>r.track===e));if(t)return t.replaceTrack(r)}}};const O=[...n(3,((e,r)=>`stun:stun${r||""}.l.google.com:19302`)),"stun:stun.cloudflare.com:3478"].map((e=>({urls:e}))),j=Object.getPrototypeOf(Uint8Array),H=16369,R=255,J="bufferedamountlow",M=e=>"@_"+e;var x=(e,r,a)=>{const o={},s={},p={},m={},h={},v={},P={},T={onPeerJoin:f,onPeerLeave:f,onPeerStream:f,onPeerTrack:f},A=(e,r)=>(e?Array.isArray(e)?e:[e]:u(o)).flatMap((e=>{const n=o[e];return n?r(e,n):(console.warn(`${t}: no peer with id ${e} found`),[])})),S=e=>{o[e]&&(o[e].destroy(),delete o[e],delete m[e],delete h[e],T.onPeerLeave(e),r(e))},L=e=>{if(s[e])return p[e];if(!e)throw y("action type argument is required");const r=w(e);if(r.byteLength>12)throw y(`action type string "${e}" (${r.byteLength}b) exceeds byte limit (12). Hint: choose a shorter name.`);const t=new Uint8Array(12);t.set(r);let a=0;return s[e]={onComplete:f,onProgress:f,setOnComplete(r){return s[e]={...s[e],onComplete:r}},setOnProgress(r){return s[e]={...s[e],onProgress:r}},async send(e,r,s,c){if(s&&"object"!=typeof s)throw y("action meta argument must be an object");const l=typeof e;if("undefined"===l)throw y("action data cannot be undefined");const d="string"!==l,u=e instanceof Blob,f=u||e instanceof ArrayBuffer||e instanceof j;if(s&&!f)throw y("action meta argument can only be used with binary data");const p=f?new Uint8Array(u?await e.arrayBuffer():e):w(d?b(e):e),m=s?w(b(s)):null,g=Math.ceil(p.byteLength/H)+(s?1:0)||1,h=n(g,((e,r)=>{const n=r===g-1,o=s&&0===r,i=new Uint8Array(15+(o?m.byteLength:n?p.byteLength-H*(g-(s?2:1)):H));return i.set(t),i.set([a],12),i.set([n|o<<1|f<<2|d<<3],13),i.set([Math.round((r+1)/g*R)],14),i.set(s?o?m:p.subarray((r-1)*H,r*H):p.subarray(r*H,(r+1)*H),15),i}));return a=a+1&R,i(A(r,(async(e,r)=>{const{channel:t}=r;let n=0;for(;n<g;){const a=h[n];if(t.bufferedAmount>t.bufferedAmountLowThreshold&&await new Promise((e=>{const r=()=>{t.removeEventListener(J,r),e()};t.addEventListener(J,r)})),!o[e])break;r.sendData(a),n++,c?.(a[14]/R,e,s)}})))}},p[e]||=[s[e].send,s[e].setOnComplete,s[e].setOnProgress]},D=async()=>{await G(""),await new Promise((e=>setTimeout(e,99))),l(o).forEach((([e,r])=>{r.destroy(),delete o[e]})),a()},[I,$]=L(M("ping")),[E,C]=L(M("pong")),[U,_]=L(M("signal")),[O,x]=L(M("stream")),[q,B]=L(M("track")),[G,N]=L(M("leave"));return e(((e,r)=>{o[r]||(o[r]=e,e.setHandlers({data(e){return((e,r)=>{const n=new Uint8Array(r),a=g(n.subarray(0,12)).replaceAll("\0",""),[o]=n.subarray(12,13),[i]=n.subarray(13,14),[c]=n.subarray(14,15),l=n.subarray(15),d=!!(1&i),u=!!(2&i),f=!!(4&i),y=!!(8&i);if(!s[a])return void console.warn(`${t}: received message with unregistered type (${a})`);m[e]||={},m[e][a]||={};const p=m[e][a][o]||={chunks:[]};if(u?p.meta=k(g(l)):p.chunks.push(l),s[a].onProgress(c/R,e,p.meta),!d)return;const w=new Uint8Array(p.chunks.reduce(((e,r)=>e+r.byteLength),0));if(p.chunks.reduce(((e,r)=>(w.set(r,e),e+r.byteLength)),0),delete m[e][a][o],f)s[a].onComplete(w,e,p.meta);else{const r=g(w);s[a].onComplete(y?k(r):r,e)}})(r,e)},stream(e){T.onPeerStream(e,r,v[r]),delete v[r]},track(e,t){T.onPeerTrack(e,t,r,P[r]),delete P[r]},signal(e){return U(e,r)},close(){return S(r)},error(e){console.error(e),S(r)}}),T.onPeerJoin(r))})),$(((e,r)=>E("",r))),C(((e,r)=>{h[r]?.(),delete h[r]})),_(((e,r)=>o[r]?.signal(e))),x(((e,r)=>v[r]=e)),B(((e,r)=>P[r]=e)),N(((e,r)=>S(r))),c&&addEventListener("beforeunload",D),{makeAction:L,leave:D,async ping(e){if(!e)throw y("ping() must be called with target peer ID");const r=Date.now();return I("",e),await new Promise((r=>h[e]=r)),Date.now()-r},getPeers(){return d(l(o).map((([e,r])=>[e,r.connection])))},addStream(e,r,t){return A(r,(async(r,n)=>{t&&await O(t,r),n.addStream(e)}))},removeStream(e,r){return A(r,((r,t)=>t.removeStream(e)))},addTrack(e,r,t,n){return A(t,(async(t,a)=>{n&&await q(n,t),a.addTrack(e,r)}))},removeTrack(e,r){return A(r,((r,t)=>t.removeTrack(e)))},replaceTrack(e,r,t,n){return A(t,(async(t,a)=>{n&&await q(n,t),a.replaceTrack(e,r)}))},onPeerJoin(e){return T.onPeerJoin=e},onPeerLeave(e){return T.onPeerLeave=e},onPeerStream(e){return T.onPeerStream=e},onPeerTrack(e){return T.onPeerTrack=e}}};const q={},B={},G={},N={},z={},K={},V={},W={},F=async e=>{if(B[e])return B[e];const r=(await I(e)).slice(0,20);return B[e]=r,G[r]=e,r},Q=async(e,r,t)=>e.send(b({action:"announce",info_hash:await F(r),peer_id:s,...t})),X=(e,r,n)=>console.warn(`${t}: torrent tracker ${n?"failure":"warning"} from ${e} - ${r}`),Y=(({init:e,subscribe:r,announce:a})=>{const o={};let l,d,u,p,m=!1;return(g,v,P)=>{const{appId:T}=g;if(o[T]?.[v])return o[T][v];const D={},C={},U=h(t,T,v),O=I(U),j=I(h(U,s)),H=(async(e,r,t)=>crypto.subtle.importKey("raw",await crypto.subtle.digest({name:"SHA-256"},w(`${e}:${r}:${t}`)),{name:L},!1,["encrypt","decrypt"]))(g.password||"",T,v),R=e=>async r=>({type:r.type,sdp:await e(H,r.sdp)}),J=R(E),M=R($),q=()=>_(!0,g),B=(e,r,t)=>{C[r]?C[r]!==e&&e.destroy():(C[r]=e,Q(e,r),D[r]?.forEach(((e,r)=>{r!==t&&e.destroy()})),delete D[r])},G=(e,r)=>{C[r]===e&&delete C[r]},N=e=>(d.push(...n(e,q)),i(d.splice(0,e).map((e=>e.offerPromise.then(M).then((r=>({peer:e,offer:r}))))))),z=(e,r)=>P?.({error:`incorrect password (${g.password}) when decrypting ${r}`,appId:T,peerId:e,roomId:v}),K=e=>async(r,t,n)=>{const[a,o]=await i([O,j]);if(r!==a&&r!==o)return;const{peerId:c,offer:l,answer:d,peer:u}="string"==typeof t?k(t):t;if(c!==s&&!C[c])if(!c||l||d){if(l){const r=D[c]?.[e];if(r&&s>c)return;const t=_(!1,g);let a;t.setHandlers({connect(){return B(t,c,e)},close(){return G(t,c)}});try{a=await J(l)}catch{return void z(c,"offer")}if(t.isDead)return;const[o,d]=await i([I(h(U,c)),t.signal(a)]);n(o,b({peerId:s,answer:await M(d)}))}else if(d){let r;try{r=await J(d)}catch(e){return void z(c,"answer")}if(u)u.setHandlers({connect(){return B(u,c,e)},close(){return G(u,c)}}),u.signal(r);else{const t=D[c]?.[e];t&&!t.isDead&&t.signal(r)}}}else{if(D[c]?.[e])return;const[[{peer:r,offer:t}],a]=await i([N(1),I(h(U,c))]);D[c]||=[],D[c][e]=r,setTimeout((()=>((e,r)=>{if(C[e])return;const t=D[e]?.[r];t&&(delete D[e][r],t.destroy())})(c,e)),.9*V[e]),r.setHandlers({connect(){return B(r,c,e)},close(){return G(r,c)}}),n(a,b({peerId:s,offer:t}))}};if(!g)throw y("requires a config map as the first argument");if(!T&&!g.firebaseApp)throw y("config map is missing appId field");if(!v)throw y("roomId argument required");if(!m){const r=e(g);d=n(20,q),l=Array.isArray(r)?r:[r],m=!0,u=setInterval((()=>d=d.filter((e=>{const r=Date.now()-e.created<57333;return r||e.destroy(),r}))),59052.99),p=g.manualRelayReconnection?f:(()=>{if(c){const e=new AbortController;return addEventListener("online",S,{signal:e.signal}),addEventListener("offline",A,{signal:e.signal}),()=>e.abort()}return f})()}const V=l.map((()=>5333)),W=[],F=l.map((async(e,t)=>r(await e,await O,await j,K(t),N)));i([O,j]).then((([e,r])=>{const t=async(n,o)=>{const s=await a(n,e,r);"number"==typeof s&&(V[o]=s),W[o]=setTimeout((()=>t(n,o)),V[o])};F.forEach((async(e,r)=>{await e,t(await l[r],r)}))}));let Q=f;return o[T]||={},o[T][v]=x((e=>Q=e),(e=>delete C[e]),(()=>{delete o[T][v],W.forEach(clearTimeout),F.forEach((async e=>(await e)())),clearInterval(u),p(),m=!1}))}})({init(e){return((e,r,t)=>(e.relayUrls||r).slice(0,e.relayUrls?e.relayUrls.length:e.relayRedundancy||t))(e,re,3).map((e=>{const r=((e,r)=>{const t={},n=()=>{const a=new WebSocket(e);a.onclose=()=>{P?P.then(n):(v[e]??=3333,setTimeout(n,v[e]),v[e]*=2)},a.onmessage=e=>r(e.data),t.socket=a,t.url=a.url,t.ready=new Promise((r=>a.onopen=()=>{r(t),v[e]=3333})),t.send=e=>{1===a.readyState&&a.send(e)}};return n(),t})(e,(e=>{const r=k(e),n=r["failure reason"],a=r["warning message"],{interval:o}=r,s=G[r.info_hash];if(n)X(t,n,!0);else{if(a&&X(t,a),o&&1e3*o>K[t]&&z[t][s]){const e=Math.min(1e3*o,120333);clearInterval(N[t][s]),K[t]=e,N[t][s]=setInterval(z[t][s],e)}V[r.offer_id]||(r.offer||r.answer)&&(V[r.offer_id]=!0,W[t][s]?.(r))}})),{url:t}=r;return q[t]=r,W[t]={},r.ready}))},subscribe(e,r,t,n,a){const{url:s}=e,i=async()=>{const t=d((await a(10)).map((e=>[o(20),e])));W[e.url][r]=a=>{if(a.offer)n(r,{offer:a.offer,peerId:a.peer_id},((t,n)=>Q(e,r,{answer:k(n).answer,offer_id:a.offer_id,to_peer_id:a.peer_id})));else if(a.answer){const e=t[a.offer_id];e&&n(r,{answer:a.answer,peerId:a.peer_id,peer:e.peer})}},Q(e,r,{numwant:10,offers:l(t).map((([e,{offer:r}])=>({offer_id:e,offer:r})))})};return K[s]=33333,z[s]||={},z[s][r]=i,N[s]||={},N[s][r]=setInterval(i,K[s]),i(),()=>{clearInterval(N[s][r]),delete W[s][r],delete z[s][r]}},announce(e){return K[e.url]}}),Z=(ee=q,()=>d(l(ee).map((([e,r])=>[e,r.socket]))));var ee;const re=["tracker.webtorrent.dev","tracker.openwebtorrent.com","tracker.btorrent.xyz","tracker.files.fm:7073/announce"].map((e=>"wss://"+e));export{re as defaultRelayUrls,Z as getRelaySockets,Y as joinRoom,A as pauseRelayReconnection,S as resumeRelayReconnection,s as selfId};