@dobuki/hello-worker 1.0.73 → 1.0.75

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.
@@ -4,7 +4,7 @@ type UserListener = (user: string, action: "join" | "leave", users: string[]) =>
4
4
  export declare function enterWorld<S extends string | ArrayBufferView = string | ArrayBufferView, R extends string | ArrayBufferLike = string | ArrayBufferLike>({ userId: passedUserId, worldId, logLine, enterRoomFunction, peerlessUserExpiration, workerUrl, onRoomReady, onRoomClose, dataChannelOptions, }: {
5
5
  userId?: string;
6
6
  worldId: string;
7
- logLine?: (direction: string, obj?: any) => void;
7
+ logLine?: (...obj: any[]) => void;
8
8
  enterRoomFunction?: EnterRoom<SigType, SigPayload>;
9
9
  peerlessUserExpiration?: number;
10
10
  workerUrl?: URL;
@@ -1 +1 @@
1
- {"version":3,"file":"enter-world.d.ts","sourceRoot":"","sources":["../src/browser/enter-world.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAa,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EACL,OAAO,EACP,UAAU,EAEX,MAAM,yBAAyB,CAAC;AAEjC,KAAK,YAAY,GAAG,CAClB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GAAG,OAAO,EACxB,KAAK,EAAE,MAAM,EAAE,KACZ,IAAI,CAAC;AAEV,wBAAgB,UAAU,CACxB,CAAC,SAAS,MAAM,GAAG,eAAe,GAAG,MAAM,GAAG,eAAe,EAC7D,CAAC,SAAS,MAAM,GAAG,eAAe,GAAG,MAAM,GAAG,eAAe,EAC7D,EACA,MAAM,EAAE,YAAY,EACpB,OAAO,EACP,OAAO,EACP,iBAA6B,EAC7B,sBAAsB,EACtB,SAAS,EACT,WAAW,EACX,WAAW,EACX,kBAAkB,GACnB,EAAE;IACD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IACjD,iBAAiB,CAAC,EAAE,SAAS,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IACnD,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,SAAS,CAAC,EAAE,GAAG,CAAC;IAChB,WAAW,CAAC,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACzD,WAAW,CAAC,CAAC,IAAI,EAAE;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC,CAAC;KACtD,GAAG,IAAI,CAAC;IACT,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;CACzC;;iBA+FqB,CAAC,WAAW,MAAM;;;;;;;;;;;;mCAaA,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI;sCAJ5B,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI;gCAerC,YAAY;mCAJT,YAAY;;EAmCnD"}
1
+ {"version":3,"file":"enter-world.d.ts","sourceRoot":"","sources":["../src/browser/enter-world.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAa,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EACL,OAAO,EACP,UAAU,EAEX,MAAM,yBAAyB,CAAC;AAEjC,KAAK,YAAY,GAAG,CAClB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GAAG,OAAO,EACxB,KAAK,EAAE,MAAM,EAAE,KACZ,IAAI,CAAC;AAEV,wBAAgB,UAAU,CACxB,CAAC,SAAS,MAAM,GAAG,eAAe,GAAG,MAAM,GAAG,eAAe,EAC7D,CAAC,SAAS,MAAM,GAAG,eAAe,GAAG,MAAM,GAAG,eAAe,EAC7D,EACA,MAAM,EAAE,YAAY,EACpB,OAAO,EACP,OAAO,EACP,iBAA6B,EAC7B,sBAAsB,EACtB,SAAS,EACT,WAAW,EACX,WAAW,EACX,kBAAkB,GACnB,EAAE;IACD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;IAClC,iBAAiB,CAAC,EAAE,SAAS,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IACnD,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,SAAS,CAAC,EAAE,GAAG,CAAC;IAChB,WAAW,CAAC,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACzD,WAAW,CAAC,CAAC,IAAI,EAAE;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC,CAAC;KACtD,GAAG,IAAI,CAAC;IACT,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;CACzC;;iBA+FqB,CAAC,WAAW,MAAM;;;;;;;;;;;;mCAaA,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI;sCAJ5B,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI;gCAerC,YAAY;mCAJT,YAAY;;EAmCnD"}
@@ -1,4 +1,4 @@
1
- function i(K){let{userId:C,worldId:b,room:j,host:w,autoRejoin:v=!0,logLine:W}=K,f=!1,x=0,$,_,k=!0,h=new Map,q=`wss://${w}/room/${b}/${j}?userId=${encodeURIComponent(C)}`,Y=[],H=0;function E(B,A,L){if(!$)return W?.("\uD83D\uDC64 ➡️ ❌","no ws available"),!1;let D={type:B,to:A,payload:L};if(Y.push(D),W?.("\uD83D\uDC64 ➡️ \uD83D\uDDA5️",D),clearTimeout(H),f||$.readyState!==WebSocket.OPEN)return W?.("\uD83D\uDC64 ➡️ ❌","Not in opened state: "+$.readyState),!1;return H=setTimeout(()=>{$.send(JSON.stringify(Y)),Y.length=0}),!0}function P(){if(f)return;$=new WebSocket(q),$.onopen=()=>{if(k)K.onOpen?.(),k=!1;x=0},$.onmessage=(B)=>{try{let A=JSON.parse(B.data);(Array.isArray(A)?A:[A]).forEach((D)=>{if(W?.("\uD83D\uDDA5️ ➡️ \uD83D\uDC64",D),D.type==="peer-joined"||D.type==="peer-left")J(D.users);else if(D.type==="ice-server")K.onIceUrl?.(D.url,D.expiration);else if(D.userId)K.onMessage(D.type,D.payload,{userId:D.userId,receive:(G,F)=>E(G,D.userId,F)})})}catch{W?.("⚠️ ERROR",{error:"invalid-json"})}},$.onclose=(B)=>{let L=[1001,1006,1011,1012,1013].includes(B.code);if(v&&!f&&L){let D=Math.min(Math.pow(2,x)*1000,30000),G=Math.random()*1000,F=D+G;W?.("\uD83D\uDD04 RECONNECTING",{attempt:x+1,delayMs:Math.round(F)}),x++,_=setTimeout(P,F)}else K.onClose?.({code:B.code,reason:B.reason,wasClean:B.wasClean})},$.onerror=(B)=>{console.error("WS Error",B),K.onError?.()}}function J(B){let A=[],L=[],D=new Set;B.forEach(({userId:G})=>{if(G===C)return;if(!h.has(G)){let F={userId:G,receive:(R,n)=>E(R,G,n)};h.set(G,F),A.push(F)}D.add(G)});for(let G of h.keys())if(!D.has(G))h.delete(G),L.push({userId:G});if(A.length)K.onPeerJoined(A);if(L.length)K.onPeerLeft(L)}return P(),{sendToServer(B,A){E(B,"server",A)},exitRoom:()=>{f=!0,clearTimeout(_),$.close()}}}function c({userId:K,worldId:C,room:b,host:j,autoRejoin:w=!0,onOpen:v,onClose:W,onError:f,onPeerJoined:x,onPeerLeft:$,onIceUrl:_,onMessage:k,logLine:h,workerUrl:q}){if(!q)return console.warn("Warning: enterRoom called without workerUrl; this may cause issues in some environments. You should pass workerUrl explicitly. Use:","https://cdn.jsdelivr.net/npm/@dobuki/hello-worker/dist/signal-room.worker.min.js"),i({userId:K,worldId:C,room:b,host:j,autoRejoin:w,onOpen:v,onClose:W,onError:f,onPeerJoined:x,onPeerLeft:$,onIceUrl:_,onMessage:k});let Y=new Worker(q,{type:"module"}),H=!1;function E({userId:J}){return{userId:J,receive:(B,A)=>{if(H)return!1;return Y.postMessage({cmd:"send",toUserId:J,host:j,room:b,type:B,payload:A}),!0}}}let P=(J)=>{let B=J.data;if(B.kind==="open")v?.();else if(B.kind==="close")Y.terminate(),W?.(B.ev);else if(B.kind==="error")f?.();else if(B.kind==="peer-joined")x(B.users.map((A)=>E({userId:A.userId})));else if(B.kind==="peer-left")$(B.users);else if(B.kind==="ice-server")_?.(B.url,B.expiration);else if(B.kind==="message")k(B.type,B.payload,E({userId:B.fromUserId}));else if(B.kind==="log")h?.(B.direction,B.obj)};return Y.addEventListener("message",P),Y.postMessage({cmd:"enter",userId:K,worldId:C,room:b,host:j,autoRejoin:w}),{exitRoom:()=>{H=!0,Y.removeEventListener("message",P),Y.postMessage({cmd:"exit"})},sendToServer:(J,B)=>{Y.postMessage({cmd:"send",toUserId:"server",host:j,room:b,type:J,payload:B})}}}var m=c;function I({userId:K,worldId:C,receivePeerConnection:b,peerlessUserExpiration:j=5000,fallbackRtcConfig:w={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},enterRoomFunction:v=m,logLine:W,onLeaveUser:f,workerUrl:x,onRoomReady:$,onRoomClose:_,onBroadcastMessage:k}){let h=K??`user-${crypto.randomUUID()}`,q=new Map,Y=void 0,H={...w,timestamp:Date.now()},E=new Map;async function P(D){if(D)try{let G=await fetch(D);if(!G.ok)throw Error(`ICE endpoint failed: ${G.status}`);H=await G.json()}catch(G){console.warn("Using fallback rtcConfig:",G)}return H}function J(D){f?.(D);let G=q.get(D);if(!G)return;q.delete(D);try{G.pc?.close()}catch{}}async function B(D){if(!D.pc?.remoteDescription)return;let G=D.pendingRemoteIce;D.pendingRemoteIce=[];for(let F of G)try{await D.pc.addIceCandidate(F)}catch(R){W?.("⚠️ ERROR",{error:"add-ice-failed",userId:D.userId,detail:String(R)})}}function A({room:D,host:G}){let F=`${G}/room/${D}`,R=E.get(F);if(R)R.exitRoom(),E.delete(F)}function L({room:D,host:G}){return new Promise(async(F,R)=>{async function n(Q){let X=q.get(Q.userId);if(!X)return;X.close();let S=await V(X);b({pc:S,userId:Q.userId,initiator:!0,restart:()=>n(Q)}),await new Promise((z)=>setTimeout(z,3000)),await M(Q)}async function V(Q){let X=Date.now();if(X-(H?.timestamp??0)>1e4){let S=!Y||Y.expiration-X<2000?await T():Y;H=await P(S.url)}return Q.pc=new RTCPeerConnection(H),Q.pc.onicecandidate=(S)=>{if(!S.candidate)return;Q.peer.receive("ice",S.candidate.toJSON())},Q.pc.onconnectionstatechange=()=>{W?.("\uD83D\uDCAC",{event:"pc-state",userId:Q.userId,state:Q.pc?.connectionState})},Q.pc}async function Z(Q,X){let S=q.get(Q.userId);if(!S||X){let z={userId:Q.userId,pendingRemoteIce:[],peer:Q,initiateForThisUser:S?.initiateForThisUser??!1,close(){this.pc?.close(),this.pc=void 0}};await V(z),S=z,q.set(S.userId,S)}else if(S)clearTimeout(S.expirationTimeout),S.expirationTimeout=0;if(!S.pc||S.pc?.signalingState==="closed")await V(S);return S.peer=Q,S}async function M(Q){let S=(await Z(Q)).pc,z=await S?.createOffer();await S?.setLocalDescription(z),Q.receive("offer",S?.localDescription?.toJSON())}let N;async function T(){let Q=await new Promise((X)=>{N=X,y("request-ice")});return N=void 0,Q}let{exitRoom:U,sendToServer:y}=v({userId:h,worldId:C,room:D,host:G,logLine:W,workerUrl:x,autoRejoin:!0,onOpen(){$?.({room:D,host:G}),F()},onError(){console.error("onError"),R()},onClose(Q){_?.({room:D,host:G,ev:Q})},onPeerJoined(Q){Q.forEach(async(X)=>{let S=q.has(X.userId),z=await Z(X,!0);if(!S)z.initiateForThisUser=!0;let O=z.pc;if(!O){W?.("\uD83D\uDC64ℹ️","no pc: "+X.userId);return}if(b({pc:O,userId:X.userId,initiator:z.initiateForThisUser,restart:z.initiateForThisUser?()=>n(X):()=>z.close()}),z.initiateForThisUser)await M(X)})},onPeerLeft(Q){Q.forEach(({userId:X})=>{let S=q.get(X);if(!S)return;S.expirationTimeout=setTimeout(()=>J(X),j??0)})},onIceUrl(Q,X){Y={url:Q,expiration:X},N?.(Y)},async onMessage(Q,X,S){let z=await Z(S),O=z.pc;if(!O)return;if(Q==="offer"){z.initiateForThisUser=!1,b({pc:O,userId:S.userId,initiator:!1,restart(){z.close()}}),await O.setRemoteDescription(X);let g=await O.createAnswer();await O.setLocalDescription(g),S.receive("answer",O.localDescription?.toJSON()),await B(z);return}if(Q==="answer"){await O.setRemoteDescription(X),await B(z);return}if(Q==="ice"){let g=X;if(!O.remoteDescription){z.pendingRemoteIce.push(g);return}try{await O.addIceCandidate(g)}catch(d){W?.("⚠️ ERROR",{error:"add-ice-failed",userId:z.userId,detail:String(d)})}return}if(Q==="broadcast")k?.(X,S.userId)}});E.set(`${G}/room/${D}`,{exitRoom:U,room:D,host:G,broadcast:(Q)=>{y("broadcast",Q)}})})}return{userId:h,enterRoom:L,exitRoom:A,leaveUser:J,broadcast(D){E.forEach((G)=>G.broadcast(D))},end(){E.forEach(({exitRoom:D})=>D()),E.clear(),q.forEach(({userId:D})=>J(D)),q.clear()}}}function VD({userId:K,worldId:C,logLine:b,enterRoomFunction:j=c,peerlessUserExpiration:w,workerUrl:v,onRoomReady:W,onRoomClose:f,dataChannelOptions:x}){let $=new Set,_=new Set;function k(V,Z,M,N){if(M){let T=V.createDataChannel("data",x);q(Z,T,N),Y.set(Z,T)}else{let T=function(U){let y=U.channel;q(Z,y,N),Y.set(Z,y)};return V.addEventListener("datachannel",T),()=>{V.removeEventListener("datachannel",T)}}}function h(V,Z){_.forEach((M)=>M(V,Z))}function q(V,Z,M){Z.onopen=()=>{b?.("\uD83D\uDCAC",{event:"dc-open",userId:V}),$.add(V),H.forEach((T)=>T(V,"join",[...$]))};let N=({data:T})=>{h(T,V)};Z.addEventListener("message",N),Z.addEventListener("close",()=>{b?.("\uD83D\uDCAC",{event:"dc-close",userId:V}),$.delete(V),H.forEach((T)=>T(V,"leave",[...$])),Z.removeEventListener("message",N),M?.()}),Z.onerror=()=>b?.("⚠️ ERROR",{error:"dc-error",userId:V})}let Y=new Map,H=new Set,{userId:E,enterRoom:P,exitRoom:J,leaveUser:B,broadcast:A,end:L}=I({userId:K,worldId:C,enterRoomFunction:j,logLine:b,workerUrl:v,peerlessUserExpiration:w,onRoomReady:W,onRoomClose:f,onLeaveUser(V){let Z=Y.get(V);try{Z?.close()}catch{}Y.delete(V)},receivePeerConnection({pc:V,userId:Z,initiator:M,restart:N}){k(V,Z,M,N)},onBroadcastMessage(V,Z){h(V,Z),b?.("\uD83D\uDCE2",{event:"broadcast",userId:E,data:V})}});function D(V,Z){Y.forEach((M,N)=>{if(Z&&N!==Z)return;if(M.readyState==="open")M.send(V)})}function G(V){_.delete(V)}function F(V){return _.add(V),()=>{G(V)}}function R(V){H.delete(V)}function n(V){return H.add(V),()=>{R(V)}}return{userId:E,send:D,broadcast:A,enterRoom:P,exitRoom:J,leaveUser:B,getUsers:()=>[...$],addMessageListener:F,removeMessageListener:G,addUserListener:n,removeUserListener:R,end(){Y.forEach((V)=>{try{V.close()}catch{}}),Y.clear(),L(),H.clear(),$.clear()}}}export{VD as enterWorld};
1
+ function i(K){let{userId:k,worldId:E,room:C,host:v,autoRejoin:n=!0,logLine:b}=K,f=!1,_=0,$,R,P=!0,L=new Map,q=`wss://${v}/room/${E}/${C}?userId=${encodeURIComponent(k)}`,Y=[],W=0;function F(B,A,O){if(!$)return b?.("\uD83D\uDC64 ➡️ ❌","no ws available"),!1;let D={type:B,to:A,payload:O};if(Y.push(D),b?.("\uD83D\uDC64 ➡️ \uD83D\uDDA5️",D),clearTimeout(W),f||$.readyState!==WebSocket.OPEN)return b?.("\uD83D\uDC64 ➡️ ❌","Not in opened state: "+$.readyState),!1;return W=setTimeout(()=>{$.send(JSON.stringify(Y)),Y.length=0}),!0}function w(){if(f)return;$=new WebSocket(q),$.onopen=()=>{if(P)K.onOpen?.(),P=!1;_=0},$.onmessage=(B)=>{try{let A=JSON.parse(B.data);(Array.isArray(A)?A:[A]).forEach((D)=>{if(b?.("\uD83D\uDDA5️ ➡️ \uD83D\uDC64",D),D.type==="peer-joined"||D.type==="peer-left")J(D.users);else if(D.type==="ice-server")K.onIceUrl?.(D.url,D.expiration);else if(D.userId)K.onMessage(D.type,D.payload,{userId:D.userId,receive:(Q,H)=>F(Q,D.userId,H)})})}catch{b?.("⚠️ ERROR",{error:"invalid-json"})}},$.onclose=(B)=>{let O=[1001,1006,1011,1012,1013].includes(B.code);if(n&&!f&&O){let D=Math.min(Math.pow(2,_)*1000,30000),Q=Math.random()*1000,H=D+Q;b?.("\uD83D\uDD04 RECONNECTING",{attempt:_+1,delayMs:Math.round(H)}),_++,R=setTimeout(w,H)}else K.onClose?.({code:B.code,reason:B.reason,wasClean:B.wasClean})},$.onerror=(B)=>{console.error("WS Error",B),K.onError?.()}}function J(B){let A=[],O=[],D=new Set;B.forEach(({userId:Q})=>{if(Q===k)return;if(!L.has(Q)){let H={userId:Q,receive:(M,j)=>F(M,Q,j)};L.set(Q,H),A.push(H)}D.add(Q)});for(let Q of L.keys())if(!D.has(Q))L.delete(Q),O.push({userId:Q});if(A.length)K.onPeerJoined(A);if(O.length)K.onPeerLeft(O)}return w(),{sendToServer(B,A){F(B,"server",A)},exitRoom:()=>{f=!0,clearTimeout(R),$.close()}}}function U({userId:K,worldId:k,room:E,host:C,autoRejoin:v=!0,onOpen:n,onClose:b,onError:f,onPeerJoined:_,onPeerLeft:$,onIceUrl:R,onMessage:P,logLine:L,workerUrl:q}){if(!q)return console.warn("Warning: enterRoom called without workerUrl; this may cause issues in some environments. You should pass workerUrl explicitly. Use:","https://cdn.jsdelivr.net/npm/@dobuki/hello-worker/dist/signal-room.worker.min.js"),i({userId:K,worldId:k,room:E,host:C,autoRejoin:v,onOpen:n,onClose:b,onError:f,onPeerJoined:_,onPeerLeft:$,onIceUrl:R,onMessage:P});let Y=new Worker(q,{type:"module"}),W=!1;function F({userId:J}){return{userId:J,receive:(B,A)=>{if(W)return!1;return Y.postMessage({cmd:"send",toUserId:J,host:C,room:E,type:B,payload:A}),!0}}}let w=(J)=>{let B=J.data;if(B.kind==="open")n?.();else if(B.kind==="close")Y.terminate(),b?.(B.ev);else if(B.kind==="error")f?.();else if(B.kind==="peer-joined")_(B.users.map((A)=>F({userId:A.userId})));else if(B.kind==="peer-left")$(B.users);else if(B.kind==="ice-server")R?.(B.url,B.expiration);else if(B.kind==="message")P(B.type,B.payload,F({userId:B.fromUserId}));else if(B.kind==="log")L?.(B.direction,B.obj)};return Y.addEventListener("message",w),Y.postMessage({cmd:"enter",userId:K,worldId:k,room:E,host:C,autoRejoin:v}),{exitRoom:()=>{W=!0,Y.removeEventListener("message",w),Y.postMessage({cmd:"exit"})},sendToServer:(J,B)=>{Y.postMessage({cmd:"send",toUserId:"server",host:C,room:E,type:J,payload:B})}}}var m=U;function I({userId:K,worldId:k,receivePeerConnection:E,peerlessUserExpiration:C=5000,fallbackRtcConfig:v={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},enterRoomFunction:n=m,logLine:b,onLeaveUser:f,workerUrl:_,onRoomReady:$,onRoomClose:R,onBroadcastMessage:P}){let L=K??`user-${crypto.randomUUID()}`,q=new Map,Y=void 0,W={...v,timestamp:Date.now()},F=new Map;async function w(D,Q){if(D){let H=3;for(let M=0;M<H;M++){try{let j=await fetch(D);if(!j.ok)throw Error(`ICE endpoint failed: ${j.status}`);return W=await j.json(),W}catch(j){console.warn("Failed fetching iceUrl")}D=(await Q()).url}}return W}function J(D){f?.(D);let Q=q.get(D);if(!Q)return;q.delete(D);try{Q.pc?.close()}catch{}}async function B(D){if(!D.pc?.remoteDescription)return;let Q=D.pendingRemoteIce;D.pendingRemoteIce=[];for(let H of Q)try{await D.pc.addIceCandidate(H)}catch(M){b?.("⚠️ ERROR",{error:"add-ice-failed",userId:D.userId,detail:String(M)})}}function A({room:D,host:Q}){let H=`${Q}/room/${D}`,M=F.get(H);if(M)M.exitRoom(),F.delete(H)}function O({room:D,host:Q}){return new Promise(async(H,M)=>{async function j(G){let X=q.get(G.userId);if(!X)return;X.close();let S=await V(X);E({pc:S,userId:G.userId,initiator:!0,restart:()=>j(G)}),await new Promise((z)=>setTimeout(z,3000)),await T(G)}async function V(G){let X=Date.now();if(X-(W?.timestamp??0)>1e4){let S=!Y||Y.expiration-X<2000?await N():Y;W=await w(S.url,N)}return G.pc=new RTCPeerConnection(W),G.pc.onicecandidate=(S)=>{if(!S.candidate)return;G.peer.receive("ice",S.candidate.toJSON())},G.pc.onconnectionstatechange=()=>{b?.("\uD83D\uDCAC",{event:"pc-state",userId:G.userId,state:G.pc?.connectionState})},G.pc}async function Z(G,X){let S=q.get(G.userId);if(!S||X){let z={userId:G.userId,pendingRemoteIce:[],peer:G,initiateForThisUser:S?.initiateForThisUser??!1,close(){this.pc?.close(),this.pc=void 0}};await V(z),S=z,q.set(S.userId,S)}else if(S)clearTimeout(S.expirationTimeout),S.expirationTimeout=0;if(!S.pc||S.pc?.signalingState==="closed")await V(S);return S.peer=G,S}async function T(G){let S=(await Z(G)).pc,z=await S?.createOffer();await S?.setLocalDescription(z),G.receive("offer",S?.localDescription?.toJSON())}let h;async function N(){let G=await new Promise((X)=>{h=X,y("request-ice")});return h=void 0,G}let{exitRoom:c,sendToServer:y}=n({userId:L,worldId:k,room:D,host:Q,logLine:b,workerUrl:_,autoRejoin:!0,onOpen(){$?.({room:D,host:Q}),H()},onError(){console.error("onError"),M()},onClose(G){R?.({room:D,host:Q,ev:G})},onPeerJoined(G){G.forEach(async(X)=>{let S=q.has(X.userId),z=await Z(X,!0);if(!S)z.initiateForThisUser=!0;let x=z.pc;if(!x){b?.("\uD83D\uDC64ℹ️","no pc: "+X.userId);return}if(E({pc:x,userId:X.userId,initiator:z.initiateForThisUser,restart:z.initiateForThisUser?()=>j(X):()=>z.close()}),z.initiateForThisUser)await T(X)})},onPeerLeft(G){G.forEach(({userId:X})=>{let S=q.get(X);if(!S)return;S.expirationTimeout=setTimeout(()=>J(X),C??0)})},onIceUrl(G,X){Y={url:G,expiration:X},h?.(Y)},async onMessage(G,X,S){let z=await Z(S),x=z.pc;if(!x)return;if(G==="offer"){z.initiateForThisUser=!1,E({pc:x,userId:S.userId,initiator:!1,restart(){z.close()}}),await x.setRemoteDescription(X);let g=await x.createAnswer();await x.setLocalDescription(g),S.receive("answer",x.localDescription?.toJSON()),await B(z);return}if(G==="answer"){await x.setRemoteDescription(X),await B(z);return}if(G==="ice"){let g=X;if(!x.remoteDescription){z.pendingRemoteIce.push(g);return}try{await x.addIceCandidate(g)}catch(d){b?.("⚠️ ERROR",{error:"add-ice-failed",userId:z.userId,detail:String(d)})}return}if(G==="broadcast")P?.(X,S.userId)}});F.set(`${Q}/room/${D}`,{exitRoom:c,room:D,host:Q,broadcast:(G)=>{y("broadcast",G)}})})}return{userId:L,enterRoom:O,exitRoom:A,leaveUser:J,broadcast(D){F.forEach((Q)=>Q.broadcast(D))},end(){F.forEach(({exitRoom:D})=>D()),F.clear(),q.forEach(({userId:D})=>J(D)),q.clear()}}}function VD({userId:K,worldId:k,logLine:E,enterRoomFunction:C=U,peerlessUserExpiration:v,workerUrl:n,onRoomReady:b,onRoomClose:f,dataChannelOptions:_}){let $=new Set,R=new Set;function P(V,Z,T,h){if(T){let N=V.createDataChannel("data",_);q(Z,N,h),Y.set(Z,N)}else{let N=function(c){let y=c.channel;q(Z,y,h),Y.set(Z,y)};return V.addEventListener("datachannel",N),()=>{V.removeEventListener("datachannel",N)}}}function L(V,Z){R.forEach((T)=>T(V,Z))}function q(V,Z,T){Z.onopen=()=>{E?.("\uD83D\uDCAC",{event:"dc-open",userId:V}),$.add(V),W.forEach((N)=>N(V,"join",[...$]))};let h=({data:N})=>{L(N,V)};Z.addEventListener("message",h),Z.addEventListener("close",()=>{E?.("\uD83D\uDCAC",{event:"dc-close",userId:V}),$.delete(V),W.forEach((N)=>N(V,"leave",[...$])),Z.removeEventListener("message",h),T?.()}),Z.onerror=()=>E?.("⚠️ ERROR",{error:"dc-error",userId:V})}let Y=new Map,W=new Set,{userId:F,enterRoom:w,exitRoom:J,leaveUser:B,broadcast:A,end:O}=I({userId:K,worldId:k,enterRoomFunction:C,logLine:E,workerUrl:n,peerlessUserExpiration:v,onRoomReady:b,onRoomClose:f,onLeaveUser(V){let Z=Y.get(V);try{Z?.close()}catch{}Y.delete(V)},receivePeerConnection({pc:V,userId:Z,initiator:T,restart:h}){P(V,Z,T,h)},onBroadcastMessage(V,Z){L(V,Z),E?.("\uD83D\uDCE2",{event:"broadcast",userId:F,data:V})}});function D(V,Z){Y.forEach((T,h)=>{if(Z&&h!==Z)return;if(T.readyState==="open")T.send(V)})}function Q(V){R.delete(V)}function H(V){return R.add(V),()=>{Q(V)}}function M(V){W.delete(V)}function j(V){return W.add(V),()=>{M(V)}}return{userId:F,send:D,broadcast:A,enterRoom:w,exitRoom:J,leaveUser:B,getUsers:()=>[...$],addMessageListener:H,removeMessageListener:Q,addUserListener:j,removeUserListener:M,end(){Y.forEach((V)=>{try{V.close()}catch{}}),Y.clear(),O(),W.clear(),$.clear()}}}export{VD as enterWorld};
2
2
 
3
- //# debugId=A17EC2E586033FAE64756E2164756E21
3
+ //# debugId=35AD87B21302624564756E2164756E21
4
4
  //# sourceMappingURL=enter-world.js.map
@@ -4,10 +4,10 @@
4
4
  "sourcesContent": [
5
5
  "export interface IPeer<T extends string = string, P = any> {\n userId: string;\n receive(type: T, payload: P): boolean;\n}\n\ntype OutMessage = { type: string; to: string; payload: any };\n\n/**\n * enterRoom connects to the signaling room via WebSocket.\n */\nexport function enterRoom<T extends string, P = any>(params: {\n userId: string;\n worldId: string;\n room: string;\n host: string;\n onOpen?: () => void;\n onClose?: (ev: Pick<CloseEvent, \"code\" | \"reason\" | \"wasClean\">) => void;\n onError?: () => void;\n logLine?: (direction: string, obj?: any) => void;\n onPeerJoined(users: IPeer<T, P>[]): void;\n onPeerLeft(users: { userId: string }[]): void;\n onIceUrl?(url: string, expiration: number): void;\n onMessage(type: T, payload: P, from: IPeer<T, P>): void;\n autoRejoin?: boolean;\n}): {\n exitRoom: () => void;\n sendToServer: <P extends any>(type: T, payload?: P) => void;\n} {\n type Message = {\n type: \"peer-joined\" | \"peer-left\" | \"ice-server\" | T;\n userId: string;\n users: { userId: string }[];\n payload: P;\n url: string;\n expiration: number;\n };\n\n const { userId, worldId, room, host, autoRejoin = true, logLine } = params;\n\n let exited = false;\n let retryCount = 0;\n let ws: WebSocket;\n let timeoutId: ReturnType<typeof setTimeout>;\n let initialConnection = true;\n\n const peers = new Map<string, IPeer<T, P>>();\n const wsUrl = `wss://${host}/room/${worldId}/${room}?userId=${encodeURIComponent(\n userId,\n )}`;\n\n // Helper for sending (uses the current ws instance)\n const accumulatedMessages: OutMessage[] = [];\n let timeout: ReturnType<typeof setTimeout> = 0;\n function send(type: string, to: \"server\" | string, payload?: any) {\n if (!ws) {\n logLine?.(\"👤 ➡️ ❌\", \"no ws available\");\n return false;\n }\n const obj: OutMessage = { type, to, payload };\n accumulatedMessages.push(obj);\n logLine?.(\"👤 ➡️ 🖥️\", obj);\n clearTimeout(timeout);\n if (exited || ws.readyState !== WebSocket.OPEN) {\n logLine?.(\"👤 ➡️ ❌\", \"Not in opened state: \" + ws.readyState);\n return false;\n }\n timeout = setTimeout(() => {\n ws.send(JSON.stringify(accumulatedMessages));\n accumulatedMessages.length = 0;\n });\n return true;\n }\n\n function connect() {\n if (exited) return;\n\n ws = new WebSocket(wsUrl);\n\n ws.onopen = () => {\n if (initialConnection) {\n params.onOpen?.();\n initialConnection = false;\n }\n retryCount = 0; // Reset backoff on successful connection\n };\n\n ws.onmessage = (e: MessageEvent) => {\n try {\n const result = JSON.parse(e.data);\n const msgs: Message[] = Array.isArray(result) ? result : [result];\n msgs.forEach((msg) => {\n logLine?.(\"🖥️ ➡️ 👤\", msg);\n if (msg.type === \"peer-joined\" || msg.type === \"peer-left\") {\n updatePeers(msg.users);\n } else if (msg.type === \"ice-server\") {\n params.onIceUrl?.(msg.url, msg.expiration);\n } else if (msg.userId) {\n params.onMessage(msg.type, msg.payload, {\n userId: msg.userId,\n receive: (type: T, payload: P) => send(type, msg.userId, payload),\n });\n }\n });\n } catch {\n logLine?.(\"⚠️ ERROR\", { error: \"invalid-json\" });\n }\n };\n\n ws.onclose = (ev: CloseEvent) => {\n // 1. Check if we should even try to reconnect\n const recoverableCodes = [1001, 1006, 1011, 1012, 1013];\n const isRecoverable = recoverableCodes.includes(ev.code);\n\n if (autoRejoin && !exited && isRecoverable) {\n // 2. Exponential Backoff: 1s, 2s, 4s, 8s... capped at 30s\n const backoff = Math.min(Math.pow(2, retryCount) * 1000, 30000);\n // 3. Add Jitter: +/- 1000ms randomness\n const jitter = Math.random() * 1000;\n const delay = backoff + jitter;\n\n logLine?.(\"🔄 RECONNECTING\", {\n attempt: retryCount + 1,\n delayMs: Math.round(delay),\n });\n\n retryCount++;\n timeoutId = setTimeout(connect, delay);\n } else {\n params.onClose?.({\n code: ev.code,\n reason: ev.reason,\n wasClean: ev.wasClean,\n });\n }\n };\n\n ws.onerror = (ev) => {\n console.error(\"WS Error\", ev);\n params.onError?.();\n };\n }\n\n // Helper for peer tracking (logic from your original code)\n function updatePeers(updatedUsers: { userId: string }[]) {\n const joined: IPeer<T, P>[] = [];\n const left: { userId: string }[] = [];\n const updatedPeerSet = new Set<string>();\n\n updatedUsers.forEach(({ userId: pUserId }) => {\n if (pUserId === userId) return;\n if (!peers.has(pUserId)) {\n const newPeer = {\n userId: pUserId,\n receive: (t: T, p: P) => send(t, pUserId, p),\n };\n peers.set(pUserId, newPeer);\n joined.push(newPeer);\n }\n updatedPeerSet.add(pUserId);\n });\n\n for (const pUserId of peers.keys()) {\n if (!updatedPeerSet.has(pUserId)) {\n peers.delete(pUserId);\n left.push({ userId: pUserId });\n }\n }\n // Notify peer joined first then peer left. (avoid disconnect in case the peer leaving / joining is on the same user).\n if (joined.length) params.onPeerJoined(joined);\n if (left.length) params.onPeerLeft(left);\n }\n\n // Start initial connection\n connect();\n\n return {\n sendToServer(type, payload) {\n send(type, \"server\", payload);\n },\n exitRoom: () => {\n exited = true;\n clearTimeout(timeoutId);\n ws.close();\n },\n };\n}\n",
6
6
  "import type { IPeer } from \"./impl/signal-room.js\";\nimport { enterRoom as baseEnterRoom } from \"./impl/signal-room.js\";\nimport { RoomEvent, WorkerCommand } from \"./signal-room.worker.js\";\n\nexport function enterRoom<T extends string, P = any>({\n userId,\n worldId,\n room,\n host,\n autoRejoin = true,\n onOpen,\n onClose,\n onError,\n onPeerJoined,\n onPeerLeft,\n onIceUrl,\n onMessage,\n logLine,\n workerUrl,\n}: {\n userId: string;\n worldId: string;\n room: string;\n host: string;\n autoRejoin?: boolean;\n onOpen?: () => void;\n onClose?: (ev: Pick<CloseEvent, \"code\" | \"reason\" | \"wasClean\">) => void;\n onError?: () => void;\n onPeerJoined: (users: IPeer<T, P>[]) => void;\n onPeerLeft: (users: { userId: string }[]) => void;\n onIceUrl?(url: string, expiration: number): void;\n onMessage: (type: T, payload: P, from: IPeer<T, P>) => void;\n logLine?: (direction: string, obj?: any) => void;\n\n // Pass the URL to your worker file (bundler will handle it)\n workerUrl?: URL;\n}): {\n exitRoom: () => void;\n sendToServer: <P extends any>(type: T, payload?: P) => void;\n} {\n if (!workerUrl) {\n const CDN_WORKER_URL = `https://cdn.jsdelivr.net/npm/@dobuki/hello-worker/dist/signal-room.worker.min.js`;\n\n console.warn(\n \"Warning: enterRoom called without workerUrl; this may cause issues in some environments. You should pass workerUrl explicitly. Use:\",\n CDN_WORKER_URL,\n );\n return baseEnterRoom<T, P>({\n userId,\n worldId,\n room,\n host,\n autoRejoin,\n onOpen,\n onClose,\n onError,\n onPeerJoined,\n onPeerLeft,\n onIceUrl,\n onMessage,\n });\n }\n const worker = new Worker(workerUrl, { type: \"module\" });\n let exited = false;\n\n function makeUser({ userId }: { userId: string }): IPeer<T, P> {\n return {\n userId,\n receive: (type: T, payload: P) => {\n if (exited) return false;\n worker.postMessage({\n cmd: \"send\",\n toUserId: userId,\n host,\n room,\n type,\n payload,\n } as WorkerCommand);\n return true;\n },\n };\n }\n\n const onWorkerMessage = (e: MessageEvent<RoomEvent<T, P>>) => {\n const ev = e.data;\n\n if (ev.kind === \"open\") onOpen?.();\n else if (ev.kind === \"close\") {\n worker.terminate();\n onClose?.(ev.ev);\n } else if (ev.kind === \"error\") onError?.();\n else if (ev.kind === \"peer-joined\")\n onPeerJoined(ev.users.map((ev) => makeUser({ userId: ev.userId })));\n else if (ev.kind === \"peer-left\") onPeerLeft(ev.users);\n else if (ev.kind === \"ice-server\") onIceUrl?.(ev.url, ev.expiration);\n else if (ev.kind === \"message\")\n onMessage(ev.type, ev.payload, makeUser({ userId: ev.fromUserId }));\n else if (ev.kind === \"log\") logLine?.(ev.direction, ev.obj);\n };\n\n worker.addEventListener(\"message\", onWorkerMessage);\n\n worker.postMessage({\n cmd: \"enter\",\n userId,\n worldId,\n room,\n host,\n autoRejoin,\n } as WorkerCommand);\n\n return {\n exitRoom: () => {\n exited = true;\n worker.removeEventListener(\"message\", onWorkerMessage);\n worker.postMessage({ cmd: \"exit\" } as WorkerCommand);\n },\n sendToServer: <P extends any>(type: T, payload?: P) => {\n worker.postMessage({\n cmd: \"send\",\n toUserId: \"server\",\n host,\n room,\n type,\n payload,\n } as WorkerCommand);\n },\n };\n}\n\nexport type EnterRoom<T extends string, P> = typeof enterRoom<T, P>;\n",
7
- "import { IPeer } from \"./signal/impl/signal-room\";\nimport { EnterRoom, enterRoom } from \"./signal/signal-room\";\n\nexport type SigType = \"offer\" | \"answer\" | \"ice\" | \"request-ice\" | \"broadcast\";\nexport type SigPayload = RTCSessionDescriptionInit | RTCIceCandidateInit;\n\ntype UserState = {\n userId: string;\n pc?: RTCPeerConnection;\n\n // ICE that arrived before we had remoteDescription\n pendingRemoteIce: RTCIceCandidateInit[];\n\n // the signaling \"user\" handle so we can send messages\n peer: IPeer<SigType, SigPayload>;\n\n expirationTimeout?: number;\n initiateForThisUser: boolean;\n close: () => void;\n};\n\nconst DEFAULT_ENTER_ROOM = enterRoom;\n\nexport function collectPeerConnections({\n userId: passedUserId,\n worldId,\n receivePeerConnection,\n peerlessUserExpiration = 5000,\n fallbackRtcConfig = {\n iceServers: [{ urls: \"stun:stun.l.google.com:19302\" }],\n },\n enterRoomFunction: enterRoom = DEFAULT_ENTER_ROOM,\n logLine,\n onLeaveUser,\n workerUrl,\n onRoomReady,\n onRoomClose,\n onBroadcastMessage,\n}: {\n userId?: string;\n worldId: string;\n fallbackRtcConfig?: RTCConfiguration;\n enterRoomFunction?: EnterRoom<SigType, SigPayload>;\n onLeaveUser?: (userId: string) => void;\n logLine?: (direction: string, obj?: any) => void;\n workerUrl?: URL;\n peerlessUserExpiration?: number;\n receivePeerConnection(connection: {\n pc: RTCPeerConnection;\n userId: string;\n initiator: boolean;\n restart?: () => void;\n }): void;\n onRoomReady?(info: { host: string; room: string }): void;\n onRoomClose?(info: {\n host: string;\n room: string;\n ev: Pick<CloseEvent, \"reason\" | \"code\" | \"wasClean\">;\n }): void;\n onBroadcastMessage?<P extends any>(payload: P, from: string): void;\n}) {\n const userId = passedUserId ?? `user-${crypto.randomUUID()}`;\n const users: Map<string, UserState> = new Map();\n let iceUrl: { url: string; expiration: number } | undefined = undefined;\n let rtcConfig: RTCConfiguration & { timestamp: number } = {\n ...fallbackRtcConfig,\n timestamp: Date.now(),\n };\n\n const roomsEntered = new Map<\n string,\n {\n room: string;\n host: string;\n exitRoom: () => void;\n broadcast: <P extends any>(payload: P) => void;\n }\n >();\n\n async function getRtcConfig(\n iceUrl: string,\n ): Promise<RTCConfiguration & { timestamp: number }> {\n if (iceUrl) {\n try {\n const r = await fetch(iceUrl);\n if (!r.ok) throw new Error(`ICE endpoint failed: ${r.status}`);\n rtcConfig = (await r.json()) as RTCConfiguration & {\n timestamp: number;\n };\n } catch (e) {\n console.warn(\"Using fallback rtcConfig:\", e);\n }\n }\n return rtcConfig;\n }\n\n function leaveUser(userId: string) {\n onLeaveUser?.(userId);\n const p = users.get(userId);\n if (!p) return;\n users.delete(userId);\n try {\n p.pc?.close();\n } catch {}\n }\n\n async function flushRemoteIce(state: UserState) {\n if (!state.pc?.remoteDescription) return;\n\n const queued = state.pendingRemoteIce;\n state.pendingRemoteIce = [];\n\n for (const ice of queued) {\n try {\n await state.pc.addIceCandidate(ice);\n } catch (e) {\n logLine?.(\"⚠️ ERROR\", {\n error: \"add-ice-failed\",\n userId: state.userId,\n detail: String(e),\n });\n }\n }\n }\n\n function exit({ room, host }: { room: string; host: string }) {\n const key = `${host}/room/${room}`;\n const session = roomsEntered.get(key);\n if (session) {\n session.exitRoom();\n roomsEntered.delete(key);\n }\n }\n\n function enter({ room, host }: { room: string; host: string }) {\n return new Promise<void>(async (resolve, reject) => {\n async function restartInitiator(user: IPeer) {\n const state = users.get(user.userId);\n if (!state) return; // user left\n state.close();\n const pc = await setupPC(state);\n receivePeerConnection({\n pc,\n userId: user.userId,\n initiator: true,\n restart: () => restartInitiator(user),\n });\n await new Promise((resolve) => setTimeout(resolve, 3000));\n await makeOffer(user);\n }\n\n async function setupPC(state: UserState) {\n const now = Date.now();\n if (now - (rtcConfig?.timestamp ?? 0) > 10000) {\n const ice =\n !iceUrl || iceUrl.expiration - now < 2000\n ? await requestIce()\n : iceUrl;\n rtcConfig = await getRtcConfig(ice.url);\n }\n state.pc = new RTCPeerConnection(rtcConfig);\n // Send local ICE candidates to this peer\n state.pc.onicecandidate = (ev) => {\n if (!ev.candidate) return;\n state.peer.receive(\"ice\", ev.candidate.toJSON());\n };\n\n state.pc.onconnectionstatechange = () => {\n logLine?.(\"💬\", {\n event: \"pc-state\",\n userId: state.userId,\n state: state.pc?.connectionState,\n });\n };\n\n return state.pc;\n }\n\n async function getPeer(\n peer: IPeer<SigType, SigPayload>,\n forceReset?: boolean,\n ): Promise<UserState> {\n let state = users.get(peer.userId);\n if (!state || forceReset) {\n const newState: UserState = {\n userId: peer.userId,\n pendingRemoteIce: [],\n peer,\n initiateForThisUser: state?.initiateForThisUser ?? false,\n close() {\n this.pc?.close();\n this.pc = undefined;\n },\n };\n\n await setupPC(newState);\n state = newState;\n\n // New user\n users.set(state.userId, state);\n } else if (state) {\n clearTimeout(state.expirationTimeout);\n state.expirationTimeout = 0;\n }\n if (!state.pc || state.pc?.signalingState === \"closed\") {\n await setupPC(state);\n }\n state.peer = peer;\n return state;\n }\n\n async function makeOffer(user: IPeer) {\n // Offer flow: createOffer -> setLocalDescription -> send localDescription\n const state = await getPeer(user);\n const pc = state.pc;\n const offer = await pc?.createOffer();\n await pc?.setLocalDescription(offer);\n user.receive(\"offer\", pc?.localDescription?.toJSON()!);\n }\n\n let icePromiseResolve:\n | undefined\n | ((url: { url: string; expiration: number }) => void);\n async function requestIce() {\n const iceUrl = await new Promise<{ url: string; expiration: number }>(\n (resolve) => {\n icePromiseResolve = resolve;\n sendToServer(\"request-ice\");\n },\n );\n icePromiseResolve = undefined;\n return iceUrl;\n }\n\n const { exitRoom, sendToServer } = enterRoom({\n userId,\n worldId,\n room,\n host,\n logLine,\n workerUrl,\n autoRejoin: true,\n\n onOpen() {\n onRoomReady?.({ room, host });\n resolve();\n },\n onError() {\n console.error(\"onError\");\n reject();\n },\n onClose(ev: Pick<CloseEvent, \"reason\" | \"code\" | \"wasClean\">) {\n onRoomClose?.({ room, host, ev });\n },\n\n // Existing peers initiate to the newcomer\n onPeerJoined(joiningUsers: IPeer<SigType, SigPayload>[]) {\n joiningUsers.forEach(async (user) => {\n const hadState = users.has(user.userId);\n const state = await getPeer(user, true);\n if (!hadState) {\n state.initiateForThisUser = true;\n }\n const pc = state.pc;\n if (!pc) {\n logLine?.(\"👤ℹ️\", \"no pc: \" + user.userId);\n return;\n }\n\n receivePeerConnection({\n pc,\n userId: user.userId,\n initiator: state.initiateForThisUser,\n restart: state.initiateForThisUser\n ? () => restartInitiator(user)\n : () => state.close(),\n });\n if (state.initiateForThisUser) {\n await makeOffer(user);\n }\n });\n },\n\n onPeerLeft(leavingUsers: { userId: string }[]) {\n leavingUsers.forEach(({ userId }) => {\n const state = users.get(userId);\n if (!state) return;\n state.expirationTimeout = setTimeout(\n () => leaveUser(userId),\n peerlessUserExpiration ?? 0,\n );\n });\n },\n\n onIceUrl(url: string, expiration: number) {\n iceUrl = { url, expiration };\n icePromiseResolve?.(iceUrl);\n },\n\n async onMessage(type: SigType, payload: any, from: IPeer) {\n const state = await getPeer(from);\n const pc = state.pc;\n if (!pc) return;\n\n if (type === \"offer\") {\n state.initiateForThisUser = false;\n receivePeerConnection({\n pc,\n userId: from.userId,\n initiator: false,\n restart() {\n // reset PC\n state.close();\n },\n });\n // Responder: set remote offer\n await pc.setRemoteDescription(payload as RTCSessionDescriptionInit);\n\n // Create and send answer\n const answer = await pc.createAnswer();\n await pc.setLocalDescription(answer);\n\n from.receive(\"answer\", pc.localDescription?.toJSON()!);\n\n // Now safe to apply any queued ICE from this peer\n await flushRemoteIce(state);\n return;\n }\n\n if (type === \"answer\") {\n // Initiator: set remote answer\n await pc.setRemoteDescription(payload as RTCSessionDescriptionInit);\n await flushRemoteIce(state);\n return;\n }\n\n if (type === \"ice\") {\n const ice = payload as RTCIceCandidateInit;\n\n // If we don't have remoteDescription yet, queue it\n if (!pc.remoteDescription) {\n state.pendingRemoteIce.push(ice);\n return;\n }\n\n try {\n await pc.addIceCandidate(ice);\n } catch (e) {\n logLine?.(\"⚠️ ERROR\", {\n error: \"add-ice-failed\",\n userId: state.userId,\n detail: String(e),\n });\n }\n return;\n }\n\n if (type === \"broadcast\") {\n onBroadcastMessage?.(payload, from.userId);\n }\n },\n });\n roomsEntered.set(`${host}/room/${room}`, {\n exitRoom,\n room,\n host,\n broadcast: (payload) => {\n sendToServer(\"broadcast\", payload);\n },\n });\n });\n }\n\n return {\n userId,\n enterRoom: enter,\n exitRoom: exit,\n leaveUser,\n broadcast<P extends any>(payload: P) {\n roomsEntered.forEach((room) => room.broadcast(payload));\n },\n end() {\n roomsEntered.forEach(({ exitRoom }) => exitRoom());\n roomsEntered.clear();\n users.forEach(({ userId }) => leaveUser(userId));\n users.clear();\n },\n };\n}\n\n/*\nTurn Token ID\n<CF_TURN_TOKEN_ID>\n\nAPI Token\n<CF_RTC_API_TOKEN>\n\nCURL\ncurl \\\n\t-H \"Authorization: Bearer <CF_RTC_API_TOKEN>\" \\\n\t-H \"Content-Type: application/json\" -d '{\"ttl\": 86400}' \\\n\thttps://rtc.live.cloudflare.com/v1/turn/keys/<CF_TURN_TOKEN_ID>/credentials/generate-ice-servers\n\nJSON\n{\n\t\"iceServers\": [\n {\n \"urls\": [\n \"stun:stun.cloudflare.com:3478\",\n \"turn:turn.cloudflare.com:3478?transport=udp\",\n \"turn:turn.cloudflare.com:3478?transport=tcp\",\n \"turns:turn.cloudflare.com:5349?transport=tcp\"\n ],\n \"username\": \"xxxx\",\n \"credential\": \"yyyy\",\n }\n ]\n}\n\n*/\n",
8
- "import { EnterRoom, enterRoom } from \"./signal/signal-room\";\nimport {\n SigType,\n SigPayload,\n collectPeerConnections,\n} from \"./webrtc-peer-collector\";\n\ntype UserListener = (\n user: string,\n action: \"join\" | \"leave\",\n users: string[],\n) => void;\n\nexport function enterWorld<\n S extends string | ArrayBufferView = string | ArrayBufferView,\n R extends string | ArrayBufferLike = string | ArrayBufferLike,\n>({\n userId: passedUserId,\n worldId,\n logLine,\n enterRoomFunction = enterRoom,\n peerlessUserExpiration,\n workerUrl,\n onRoomReady,\n onRoomClose,\n dataChannelOptions,\n}: {\n userId?: string;\n worldId: string;\n logLine?: (direction: string, obj?: any) => void;\n enterRoomFunction?: EnterRoom<SigType, SigPayload>;\n peerlessUserExpiration?: number;\n workerUrl?: URL;\n onRoomReady?(info: { host: string; room: string }): void;\n onRoomClose?(info: {\n host: string;\n room: string;\n ev: Pick<CloseEvent, \"reason\" | \"code\" | \"wasClean\">;\n }): void;\n dataChannelOptions?: RTCDataChannelInit;\n}) {\n const userIds = new Set<string>();\n\n const messagesListeners = new Set<(data: R, from: string) => void>();\n\n function createDataChannel(\n pc: RTCPeerConnection,\n peerUserId: string,\n initiator: boolean,\n restart?: () => void,\n ) {\n if (initiator) {\n const dc = pc.createDataChannel(\"data\", dataChannelOptions);\n wireDataChannel(peerUserId, dc, restart);\n dataChannels.set(peerUserId, dc);\n } else {\n function listener(ev: RTCDataChannelEvent) {\n const dc = ev.channel;\n wireDataChannel(peerUserId, dc, restart);\n dataChannels.set(peerUserId, dc);\n }\n pc.addEventListener(\"datachannel\", listener);\n return () => {\n pc.removeEventListener(\"datachannel\", listener);\n };\n }\n }\n\n function conveyMessage(data: any, userId: string) {\n messagesListeners.forEach((listener) => listener(data, userId));\n }\n\n function wireDataChannel(\n userId: string,\n dc: RTCDataChannel,\n restart?: () => void,\n ) {\n dc.onopen = () => {\n logLine?.(\"💬\", { event: \"dc-open\", userId });\n userIds.add(userId);\n userListeners.forEach((listener) =>\n listener(userId, \"join\", [...userIds]),\n );\n };\n const onmessage = ({ data }: MessageEvent) => {\n conveyMessage(data, userId);\n };\n dc.addEventListener(\"message\", onmessage);\n dc.addEventListener(\"close\", () => {\n logLine?.(\"💬\", { event: \"dc-close\", userId });\n userIds.delete(userId);\n userListeners.forEach((listener) =>\n listener(userId, \"leave\", [...userIds]),\n );\n dc.removeEventListener(\"message\", onmessage);\n restart?.();\n });\n dc.onerror = () => logLine?.(\"⚠️ ERROR\", { error: \"dc-error\", userId });\n }\n\n const dataChannels = new Map<string, RTCDataChannel>();\n const userListeners = new Set<UserListener>();\n\n const {\n userId,\n enterRoom,\n exitRoom,\n leaveUser,\n broadcast,\n end: endPeerCollection,\n } = collectPeerConnections({\n userId: passedUserId,\n worldId,\n enterRoomFunction,\n logLine,\n workerUrl,\n peerlessUserExpiration,\n onRoomReady,\n onRoomClose,\n onLeaveUser(userId: string) {\n const dc = dataChannels.get(userId);\n try {\n dc?.close();\n } catch {}\n dataChannels.delete(userId);\n },\n receivePeerConnection({ pc, userId, initiator, restart }) {\n createDataChannel(pc, userId, initiator, restart);\n },\n onBroadcastMessage(payload, from) {\n conveyMessage(payload, from);\n logLine?.(\"📢\", { event: \"broadcast\", userId, data: payload });\n },\n });\n\n function send(data: S, userId?: string) {\n dataChannels.forEach((dataChannel, pUserId) => {\n if (userId && pUserId !== userId) return;\n if (dataChannel.readyState === \"open\") {\n dataChannel.send(data as any);\n }\n });\n }\n\n function removeMessageListener(listener: (data: R, from: string) => void) {\n messagesListeners.delete(listener);\n }\n\n function addMessageListener(listener: (data: R, from: string) => void) {\n messagesListeners.add(listener);\n return () => {\n removeMessageListener(listener);\n };\n }\n\n function removeUserListener(listener: UserListener) {\n userListeners.delete(listener);\n }\n\n function addUserListener(listener: UserListener) {\n userListeners.add(listener);\n return () => {\n removeUserListener(listener);\n };\n }\n\n return {\n userId,\n send,\n broadcast,\n enterRoom,\n exitRoom,\n leaveUser,\n getUsers: () => [...userIds],\n addMessageListener,\n removeMessageListener,\n addUserListener,\n removeUserListener,\n end() {\n dataChannels.forEach((dataChannel) => {\n try {\n dataChannel.close();\n } catch {}\n });\n dataChannels.clear();\n endPeerCollection();\n userListeners.clear();\n userIds.clear();\n },\n };\n}\n"
7
+ "import { IPeer } from \"./signal/impl/signal-room\";\nimport { EnterRoom, enterRoom } from \"./signal/signal-room\";\n\nexport type SigType = \"offer\" | \"answer\" | \"ice\" | \"request-ice\" | \"broadcast\";\nexport type SigPayload = RTCSessionDescriptionInit | RTCIceCandidateInit;\n\ntype UserState = {\n userId: string;\n pc?: RTCPeerConnection;\n\n // ICE that arrived before we had remoteDescription\n pendingRemoteIce: RTCIceCandidateInit[];\n\n // the signaling \"user\" handle so we can send messages\n peer: IPeer<SigType, SigPayload>;\n\n expirationTimeout?: number;\n initiateForThisUser: boolean;\n close: () => void;\n};\n\nconst DEFAULT_ENTER_ROOM = enterRoom;\n\nexport function collectPeerConnections({\n userId: passedUserId,\n worldId,\n receivePeerConnection,\n peerlessUserExpiration = 5000,\n fallbackRtcConfig = {\n iceServers: [{ urls: \"stun:stun.l.google.com:19302\" }],\n },\n enterRoomFunction: enterRoom = DEFAULT_ENTER_ROOM,\n logLine,\n onLeaveUser,\n workerUrl,\n onRoomReady,\n onRoomClose,\n onBroadcastMessage,\n}: {\n userId?: string;\n worldId: string;\n fallbackRtcConfig?: RTCConfiguration;\n enterRoomFunction?: EnterRoom<SigType, SigPayload>;\n onLeaveUser?: (userId: string) => void;\n logLine?: (direction: string, obj?: any) => void;\n workerUrl?: URL;\n peerlessUserExpiration?: number;\n receivePeerConnection(connection: {\n pc: RTCPeerConnection;\n userId: string;\n initiator: boolean;\n restart?: () => void;\n }): void;\n onRoomReady?(info: { host: string; room: string }): void;\n onRoomClose?(info: {\n host: string;\n room: string;\n ev: Pick<CloseEvent, \"reason\" | \"code\" | \"wasClean\">;\n }): void;\n onBroadcastMessage?<P extends any>(payload: P, from: string): void;\n}) {\n const userId = passedUserId ?? `user-${crypto.randomUUID()}`;\n const users: Map<string, UserState> = new Map();\n let iceUrl: { url: string; expiration: number } | undefined = undefined;\n let rtcConfig: RTCConfiguration & { timestamp: number } = {\n ...fallbackRtcConfig,\n timestamp: Date.now(),\n };\n\n const roomsEntered = new Map<\n string,\n {\n room: string;\n host: string;\n exitRoom: () => void;\n broadcast: <P extends any>(payload: P) => void;\n }\n >();\n\n async function getRtcConfig(\n iceUrl: string,\n retryIce: () => Promise<{ url: string }>,\n ): Promise<RTCConfiguration & { timestamp: number }> {\n if (iceUrl) {\n let retries = 3;\n for (let r = 0; r < retries; r++) {\n try {\n const r = await fetch(iceUrl);\n if (!r.ok) throw new Error(`ICE endpoint failed: ${r.status}`);\n rtcConfig = (await r.json()) as RTCConfiguration & {\n timestamp: number;\n };\n return rtcConfig;\n } catch (e) {\n console.warn(\"Failed fetching iceUrl\");\n }\n iceUrl = (await retryIce()).url;\n }\n }\n return rtcConfig;\n }\n\n function leaveUser(userId: string) {\n onLeaveUser?.(userId);\n const p = users.get(userId);\n if (!p) return;\n users.delete(userId);\n try {\n p.pc?.close();\n } catch {}\n }\n\n async function flushRemoteIce(state: UserState) {\n if (!state.pc?.remoteDescription) return;\n\n const queued = state.pendingRemoteIce;\n state.pendingRemoteIce = [];\n\n for (const ice of queued) {\n try {\n await state.pc.addIceCandidate(ice);\n } catch (e) {\n logLine?.(\"⚠️ ERROR\", {\n error: \"add-ice-failed\",\n userId: state.userId,\n detail: String(e),\n });\n }\n }\n }\n\n function exit({ room, host }: { room: string; host: string }) {\n const key = `${host}/room/${room}`;\n const session = roomsEntered.get(key);\n if (session) {\n session.exitRoom();\n roomsEntered.delete(key);\n }\n }\n\n function enter({ room, host }: { room: string; host: string }) {\n return new Promise<void>(async (resolve, reject) => {\n async function restartInitiator(user: IPeer) {\n const state = users.get(user.userId);\n if (!state) return; // user left\n state.close();\n const pc = await setupPC(state);\n receivePeerConnection({\n pc,\n userId: user.userId,\n initiator: true,\n restart: () => restartInitiator(user),\n });\n await new Promise((resolve) => setTimeout(resolve, 3000));\n await makeOffer(user);\n }\n\n async function setupPC(state: UserState) {\n const now = Date.now();\n if (now - (rtcConfig?.timestamp ?? 0) > 10000) {\n const ice =\n !iceUrl || iceUrl.expiration - now < 2000\n ? await requestIce()\n : iceUrl;\n rtcConfig = await getRtcConfig(ice.url, requestIce);\n }\n state.pc = new RTCPeerConnection(rtcConfig);\n // Send local ICE candidates to this peer\n state.pc.onicecandidate = (ev) => {\n if (!ev.candidate) return;\n state.peer.receive(\"ice\", ev.candidate.toJSON());\n };\n\n state.pc.onconnectionstatechange = () => {\n logLine?.(\"💬\", {\n event: \"pc-state\",\n userId: state.userId,\n state: state.pc?.connectionState,\n });\n };\n\n return state.pc;\n }\n\n async function getPeer(\n peer: IPeer<SigType, SigPayload>,\n forceReset?: boolean,\n ): Promise<UserState> {\n let state = users.get(peer.userId);\n if (!state || forceReset) {\n const newState: UserState = {\n userId: peer.userId,\n pendingRemoteIce: [],\n peer,\n initiateForThisUser: state?.initiateForThisUser ?? false,\n close() {\n this.pc?.close();\n this.pc = undefined;\n },\n };\n\n await setupPC(newState);\n state = newState;\n\n // New user\n users.set(state.userId, state);\n } else if (state) {\n clearTimeout(state.expirationTimeout);\n state.expirationTimeout = 0;\n }\n if (!state.pc || state.pc?.signalingState === \"closed\") {\n await setupPC(state);\n }\n state.peer = peer;\n return state;\n }\n\n async function makeOffer(user: IPeer) {\n // Offer flow: createOffer -> setLocalDescription -> send localDescription\n const state = await getPeer(user);\n const pc = state.pc;\n const offer = await pc?.createOffer();\n await pc?.setLocalDescription(offer);\n user.receive(\"offer\", pc?.localDescription?.toJSON()!);\n }\n\n let icePromiseResolve:\n | undefined\n | ((url: { url: string; expiration: number }) => void);\n async function requestIce() {\n const iceUrl = await new Promise<{ url: string; expiration: number }>(\n (resolve) => {\n icePromiseResolve = resolve;\n sendToServer(\"request-ice\");\n },\n );\n icePromiseResolve = undefined;\n return iceUrl;\n }\n\n const { exitRoom, sendToServer } = enterRoom({\n userId,\n worldId,\n room,\n host,\n logLine,\n workerUrl,\n autoRejoin: true,\n\n onOpen() {\n onRoomReady?.({ room, host });\n resolve();\n },\n onError() {\n console.error(\"onError\");\n reject();\n },\n onClose(ev: Pick<CloseEvent, \"reason\" | \"code\" | \"wasClean\">) {\n onRoomClose?.({ room, host, ev });\n },\n\n // Existing peers initiate to the newcomer\n onPeerJoined(joiningUsers: IPeer<SigType, SigPayload>[]) {\n joiningUsers.forEach(async (user) => {\n const hadState = users.has(user.userId);\n const state = await getPeer(user, true);\n if (!hadState) {\n state.initiateForThisUser = true;\n }\n const pc = state.pc;\n if (!pc) {\n logLine?.(\"👤ℹ️\", \"no pc: \" + user.userId);\n return;\n }\n\n receivePeerConnection({\n pc,\n userId: user.userId,\n initiator: state.initiateForThisUser,\n restart: state.initiateForThisUser\n ? () => restartInitiator(user)\n : () => state.close(),\n });\n if (state.initiateForThisUser) {\n await makeOffer(user);\n }\n });\n },\n\n onPeerLeft(leavingUsers: { userId: string }[]) {\n leavingUsers.forEach(({ userId }) => {\n const state = users.get(userId);\n if (!state) return;\n state.expirationTimeout = setTimeout(\n () => leaveUser(userId),\n peerlessUserExpiration ?? 0,\n );\n });\n },\n\n onIceUrl(url: string, expiration: number) {\n iceUrl = { url, expiration };\n icePromiseResolve?.(iceUrl);\n },\n\n async onMessage(type: SigType, payload: any, from: IPeer) {\n const state = await getPeer(from);\n const pc = state.pc;\n if (!pc) return;\n\n if (type === \"offer\") {\n state.initiateForThisUser = false;\n receivePeerConnection({\n pc,\n userId: from.userId,\n initiator: false,\n restart() {\n // reset PC\n state.close();\n },\n });\n // Responder: set remote offer\n await pc.setRemoteDescription(payload as RTCSessionDescriptionInit);\n\n // Create and send answer\n const answer = await pc.createAnswer();\n await pc.setLocalDescription(answer);\n\n from.receive(\"answer\", pc.localDescription?.toJSON()!);\n\n // Now safe to apply any queued ICE from this peer\n await flushRemoteIce(state);\n return;\n }\n\n if (type === \"answer\") {\n // Initiator: set remote answer\n await pc.setRemoteDescription(payload as RTCSessionDescriptionInit);\n await flushRemoteIce(state);\n return;\n }\n\n if (type === \"ice\") {\n const ice = payload as RTCIceCandidateInit;\n\n // If we don't have remoteDescription yet, queue it\n if (!pc.remoteDescription) {\n state.pendingRemoteIce.push(ice);\n return;\n }\n\n try {\n await pc.addIceCandidate(ice);\n } catch (e) {\n logLine?.(\"⚠️ ERROR\", {\n error: \"add-ice-failed\",\n userId: state.userId,\n detail: String(e),\n });\n }\n return;\n }\n\n if (type === \"broadcast\") {\n onBroadcastMessage?.(payload, from.userId);\n }\n },\n });\n roomsEntered.set(`${host}/room/${room}`, {\n exitRoom,\n room,\n host,\n broadcast: (payload) => {\n sendToServer(\"broadcast\", payload);\n },\n });\n });\n }\n\n return {\n userId,\n enterRoom: enter,\n exitRoom: exit,\n leaveUser,\n broadcast<P extends any>(payload: P) {\n roomsEntered.forEach((room) => room.broadcast(payload));\n },\n end() {\n roomsEntered.forEach(({ exitRoom }) => exitRoom());\n roomsEntered.clear();\n users.forEach(({ userId }) => leaveUser(userId));\n users.clear();\n },\n };\n}\n\n/*\nTurn Token ID\n<CF_TURN_TOKEN_ID>\n\nAPI Token\n<CF_RTC_API_TOKEN>\n\nCURL\ncurl \\\n\t-H \"Authorization: Bearer <CF_RTC_API_TOKEN>\" \\\n\t-H \"Content-Type: application/json\" -d '{\"ttl\": 86400}' \\\n\thttps://rtc.live.cloudflare.com/v1/turn/keys/<CF_TURN_TOKEN_ID>/credentials/generate-ice-servers\n\nJSON\n{\n\t\"iceServers\": [\n {\n \"urls\": [\n \"stun:stun.cloudflare.com:3478\",\n \"turn:turn.cloudflare.com:3478?transport=udp\",\n \"turn:turn.cloudflare.com:3478?transport=tcp\",\n \"turns:turn.cloudflare.com:5349?transport=tcp\"\n ],\n \"username\": \"xxxx\",\n \"credential\": \"yyyy\",\n }\n ]\n}\n\n*/\n",
8
+ "import { EnterRoom, enterRoom } from \"./signal/signal-room\";\nimport {\n SigType,\n SigPayload,\n collectPeerConnections,\n} from \"./webrtc-peer-collector\";\n\ntype UserListener = (\n user: string,\n action: \"join\" | \"leave\",\n users: string[],\n) => void;\n\nexport function enterWorld<\n S extends string | ArrayBufferView = string | ArrayBufferView,\n R extends string | ArrayBufferLike = string | ArrayBufferLike,\n>({\n userId: passedUserId,\n worldId,\n logLine,\n enterRoomFunction = enterRoom,\n peerlessUserExpiration,\n workerUrl,\n onRoomReady,\n onRoomClose,\n dataChannelOptions,\n}: {\n userId?: string;\n worldId: string;\n logLine?: (...obj: any[]) => void;\n enterRoomFunction?: EnterRoom<SigType, SigPayload>;\n peerlessUserExpiration?: number;\n workerUrl?: URL;\n onRoomReady?(info: { host: string; room: string }): void;\n onRoomClose?(info: {\n host: string;\n room: string;\n ev: Pick<CloseEvent, \"reason\" | \"code\" | \"wasClean\">;\n }): void;\n dataChannelOptions?: RTCDataChannelInit;\n}) {\n const userIds = new Set<string>();\n\n const messagesListeners = new Set<(data: R, from: string) => void>();\n\n function createDataChannel(\n pc: RTCPeerConnection,\n peerUserId: string,\n initiator: boolean,\n restart?: () => void,\n ) {\n if (initiator) {\n const dc = pc.createDataChannel(\"data\", dataChannelOptions);\n wireDataChannel(peerUserId, dc, restart);\n dataChannels.set(peerUserId, dc);\n } else {\n function listener(ev: RTCDataChannelEvent) {\n const dc = ev.channel;\n wireDataChannel(peerUserId, dc, restart);\n dataChannels.set(peerUserId, dc);\n }\n pc.addEventListener(\"datachannel\", listener);\n return () => {\n pc.removeEventListener(\"datachannel\", listener);\n };\n }\n }\n\n function conveyMessage(data: any, userId: string) {\n messagesListeners.forEach((listener) => listener(data, userId));\n }\n\n function wireDataChannel(\n userId: string,\n dc: RTCDataChannel,\n restart?: () => void,\n ) {\n dc.onopen = () => {\n logLine?.(\"💬\", { event: \"dc-open\", userId });\n userIds.add(userId);\n userListeners.forEach((listener) =>\n listener(userId, \"join\", [...userIds]),\n );\n };\n const onmessage = ({ data }: MessageEvent) => {\n conveyMessage(data, userId);\n };\n dc.addEventListener(\"message\", onmessage);\n dc.addEventListener(\"close\", () => {\n logLine?.(\"💬\", { event: \"dc-close\", userId });\n userIds.delete(userId);\n userListeners.forEach((listener) =>\n listener(userId, \"leave\", [...userIds]),\n );\n dc.removeEventListener(\"message\", onmessage);\n restart?.();\n });\n dc.onerror = () => logLine?.(\"⚠️ ERROR\", { error: \"dc-error\", userId });\n }\n\n const dataChannels = new Map<string, RTCDataChannel>();\n const userListeners = new Set<UserListener>();\n\n const {\n userId,\n enterRoom,\n exitRoom,\n leaveUser,\n broadcast,\n end: endPeerCollection,\n } = collectPeerConnections({\n userId: passedUserId,\n worldId,\n enterRoomFunction,\n logLine,\n workerUrl,\n peerlessUserExpiration,\n onRoomReady,\n onRoomClose,\n onLeaveUser(userId: string) {\n const dc = dataChannels.get(userId);\n try {\n dc?.close();\n } catch {}\n dataChannels.delete(userId);\n },\n receivePeerConnection({ pc, userId, initiator, restart }) {\n createDataChannel(pc, userId, initiator, restart);\n },\n onBroadcastMessage(payload, from) {\n conveyMessage(payload, from);\n logLine?.(\"📢\", { event: \"broadcast\", userId, data: payload });\n },\n });\n\n function send(data: S, userId?: string) {\n dataChannels.forEach((dataChannel, pUserId) => {\n if (userId && pUserId !== userId) return;\n if (dataChannel.readyState === \"open\") {\n dataChannel.send(data as any);\n }\n });\n }\n\n function removeMessageListener(listener: (data: R, from: string) => void) {\n messagesListeners.delete(listener);\n }\n\n function addMessageListener(listener: (data: R, from: string) => void) {\n messagesListeners.add(listener);\n return () => {\n removeMessageListener(listener);\n };\n }\n\n function removeUserListener(listener: UserListener) {\n userListeners.delete(listener);\n }\n\n function addUserListener(listener: UserListener) {\n userListeners.add(listener);\n return () => {\n removeUserListener(listener);\n };\n }\n\n return {\n userId,\n send,\n broadcast,\n enterRoom,\n exitRoom,\n leaveUser,\n getUsers: () => [...userIds],\n addMessageListener,\n removeMessageListener,\n addUserListener,\n removeUserListener,\n end() {\n dataChannels.forEach((dataChannel) => {\n try {\n dataChannel.close();\n } catch {}\n });\n dataChannels.clear();\n endPeerCollection();\n userListeners.clear();\n userIds.clear();\n },\n };\n}\n"
9
9
  ],
10
- "mappings": "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",
11
- "debugId": "A17EC2E586033FAE64756E2164756E21",
10
+ "mappings": "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",
11
+ "debugId": "35AD87B21302624564756E2164756E21",
12
12
  "names": []
13
13
  }
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- function i(K){let{userId:j,worldId:H,room:C,host:w,autoRejoin:n=!0,logLine:z}=K,L=!1,O=0,Y,R,P=!0,T=new Map,A=`wss://${w}/room/${H}/${C}?userId=${encodeURIComponent(j)}`,W=[],q=0;function E(B,$,x){if(!Y)return z?.("\uD83D\uDC64 ➡️ ❌","no ws available"),!1;let D={type:B,to:$,payload:x};if(W.push(D),z?.("\uD83D\uDC64 ➡️ \uD83D\uDDA5️",D),clearTimeout(q),L||Y.readyState!==WebSocket.OPEN)return z?.("\uD83D\uDC64 ➡️ ❌","Not in opened state: "+Y.readyState),!1;return q=setTimeout(()=>{Y.send(JSON.stringify(W)),W.length=0}),!0}function k(){if(L)return;Y=new WebSocket(A),Y.onopen=()=>{if(P)K.onOpen?.(),P=!1;O=0},Y.onmessage=(B)=>{try{let $=JSON.parse(B.data);(Array.isArray($)?$:[$]).forEach((D)=>{if(z?.("\uD83D\uDDA5️ ➡️ \uD83D\uDC64",D),D.type==="peer-joined"||D.type==="peer-left")J(D.users);else if(D.type==="ice-server")K.onIceUrl?.(D.url,D.expiration);else if(D.userId)K.onMessage(D.type,D.payload,{userId:D.userId,receive:(b,F)=>E(b,D.userId,F)})})}catch{z?.("⚠️ ERROR",{error:"invalid-json"})}},Y.onclose=(B)=>{let x=[1001,1006,1011,1012,1013].includes(B.code);if(n&&!L&&x){let D=Math.min(Math.pow(2,O)*1000,30000),b=Math.random()*1000,F=D+b;z?.("\uD83D\uDD04 RECONNECTING",{attempt:O+1,delayMs:Math.round(F)}),O++,R=setTimeout(k,F)}else K.onClose?.({code:B.code,reason:B.reason,wasClean:B.wasClean})},Y.onerror=(B)=>{console.error("WS Error",B),K.onError?.()}}function J(B){let $=[],x=[],D=new Set;B.forEach(({userId:b})=>{if(b===j)return;if(!T.has(b)){let F={userId:b,receive:(_,v)=>E(_,b,v)};T.set(b,F),$.push(F)}D.add(b)});for(let b of T.keys())if(!D.has(b))T.delete(b),x.push({userId:b});if($.length)K.onPeerJoined($);if(x.length)K.onPeerLeft(x)}return k(),{sendToServer(B,$){E(B,"server",$)},exitRoom:()=>{L=!0,clearTimeout(R),Y.close()}}}function c({userId:K,worldId:j,room:H,host:C,autoRejoin:w=!0,onOpen:n,onClose:z,onError:L,onPeerJoined:O,onPeerLeft:Y,onIceUrl:R,onMessage:P,logLine:T,workerUrl:A}){if(!A)return console.warn("Warning: enterRoom called without workerUrl; this may cause issues in some environments. You should pass workerUrl explicitly. Use:","https://cdn.jsdelivr.net/npm/@dobuki/hello-worker/dist/signal-room.worker.min.js"),i({userId:K,worldId:j,room:H,host:C,autoRejoin:w,onOpen:n,onClose:z,onError:L,onPeerJoined:O,onPeerLeft:Y,onIceUrl:R,onMessage:P});let W=new Worker(A,{type:"module"}),q=!1;function E({userId:J}){return{userId:J,receive:(B,$)=>{if(q)return!1;return W.postMessage({cmd:"send",toUserId:J,host:C,room:H,type:B,payload:$}),!0}}}let k=(J)=>{let B=J.data;if(B.kind==="open")n?.();else if(B.kind==="close")W.terminate(),z?.(B.ev);else if(B.kind==="error")L?.();else if(B.kind==="peer-joined")O(B.users.map(($)=>E({userId:$.userId})));else if(B.kind==="peer-left")Y(B.users);else if(B.kind==="ice-server")R?.(B.url,B.expiration);else if(B.kind==="message")P(B.type,B.payload,E({userId:B.fromUserId}));else if(B.kind==="log")T?.(B.direction,B.obj)};return W.addEventListener("message",k),W.postMessage({cmd:"enter",userId:K,worldId:j,room:H,host:C,autoRejoin:w}),{exitRoom:()=>{q=!0,W.removeEventListener("message",k),W.postMessage({cmd:"exit"})},sendToServer:(J,B)=>{W.postMessage({cmd:"send",toUserId:"server",host:C,room:H,type:J,payload:B})}}}var d=c;function I({userId:K,worldId:j,receivePeerConnection:H,peerlessUserExpiration:C=5000,fallbackRtcConfig:w={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},enterRoomFunction:n=d,logLine:z,onLeaveUser:L,workerUrl:O,onRoomReady:Y,onRoomClose:R,onBroadcastMessage:P}){let T=K??`user-${crypto.randomUUID()}`,A=new Map,W=void 0,q={...w,timestamp:Date.now()},E=new Map;async function k(D){if(D)try{let b=await fetch(D);if(!b.ok)throw Error(`ICE endpoint failed: ${b.status}`);q=await b.json()}catch(b){console.warn("Using fallback rtcConfig:",b)}return q}function J(D){L?.(D);let b=A.get(D);if(!b)return;A.delete(D);try{b.pc?.close()}catch{}}async function B(D){if(!D.pc?.remoteDescription)return;let b=D.pendingRemoteIce;D.pendingRemoteIce=[];for(let F of b)try{await D.pc.addIceCandidate(F)}catch(_){z?.("⚠️ ERROR",{error:"add-ice-failed",userId:D.userId,detail:String(_)})}}function $({room:D,host:b}){let F=`${b}/room/${D}`,_=E.get(F);if(_)_.exitRoom(),E.delete(F)}function x({room:D,host:b}){return new Promise(async(F,_)=>{async function v(G){let S=A.get(G.userId);if(!S)return;S.close();let Q=await V(S);H({pc:Q,userId:G.userId,initiator:!0,restart:()=>v(G)}),await new Promise((Z)=>setTimeout(Z,3000)),await M(G)}async function V(G){let S=Date.now();if(S-(q?.timestamp??0)>1e4){let Q=!W||W.expiration-S<2000?await N():W;q=await k(Q.url)}return G.pc=new RTCPeerConnection(q),G.pc.onicecandidate=(Q)=>{if(!Q.candidate)return;G.peer.receive("ice",Q.candidate.toJSON())},G.pc.onconnectionstatechange=()=>{z?.("\uD83D\uDCAC",{event:"pc-state",userId:G.userId,state:G.pc?.connectionState})},G.pc}async function X(G,S){let Q=A.get(G.userId);if(!Q||S){let Z={userId:G.userId,pendingRemoteIce:[],peer:G,initiateForThisUser:Q?.initiateForThisUser??!1,close(){this.pc?.close(),this.pc=void 0}};await V(Z),Q=Z,A.set(Q.userId,Q)}else if(Q)clearTimeout(Q.expirationTimeout),Q.expirationTimeout=0;if(!Q.pc||Q.pc?.signalingState==="closed")await V(Q);return Q.peer=G,Q}async function M(G){let Q=(await X(G)).pc,Z=await Q?.createOffer();await Q?.setLocalDescription(Z),G.receive("offer",Q?.localDescription?.toJSON())}let f;async function N(){let G=await new Promise((S)=>{f=S,y("request-ice")});return f=void 0,G}let{exitRoom:U,sendToServer:y}=n({userId:T,worldId:j,room:D,host:b,logLine:z,workerUrl:O,autoRejoin:!0,onOpen(){Y?.({room:D,host:b}),F()},onError(){console.error("onError"),_()},onClose(G){R?.({room:D,host:b,ev:G})},onPeerJoined(G){G.forEach(async(S)=>{let Q=A.has(S.userId),Z=await X(S,!0);if(!Q)Z.initiateForThisUser=!0;let h=Z.pc;if(!h){z?.("\uD83D\uDC64ℹ️","no pc: "+S.userId);return}if(H({pc:h,userId:S.userId,initiator:Z.initiateForThisUser,restart:Z.initiateForThisUser?()=>v(S):()=>Z.close()}),Z.initiateForThisUser)await M(S)})},onPeerLeft(G){G.forEach(({userId:S})=>{let Q=A.get(S);if(!Q)return;Q.expirationTimeout=setTimeout(()=>J(S),C??0)})},onIceUrl(G,S){W={url:G,expiration:S},f?.(W)},async onMessage(G,S,Q){let Z=await X(Q),h=Z.pc;if(!h)return;if(G==="offer"){Z.initiateForThisUser=!1,H({pc:h,userId:Q.userId,initiator:!1,restart(){Z.close()}}),await h.setRemoteDescription(S);let g=await h.createAnswer();await h.setLocalDescription(g),Q.receive("answer",h.localDescription?.toJSON()),await B(Z);return}if(G==="answer"){await h.setRemoteDescription(S),await B(Z);return}if(G==="ice"){let g=S;if(!h.remoteDescription){Z.pendingRemoteIce.push(g);return}try{await h.addIceCandidate(g)}catch(m){z?.("⚠️ ERROR",{error:"add-ice-failed",userId:Z.userId,detail:String(m)})}return}if(G==="broadcast")P?.(S,Q.userId)}});E.set(`${b}/room/${D}`,{exitRoom:U,room:D,host:b,broadcast:(G)=>{y("broadcast",G)}})})}return{userId:T,enterRoom:x,exitRoom:$,leaveUser:J,broadcast(D){E.forEach((b)=>b.broadcast(D))},end(){E.forEach(({exitRoom:D})=>D()),E.clear(),A.forEach(({userId:D})=>J(D)),A.clear()}}}function u({userId:K,worldId:j,logLine:H,enterRoomFunction:C=c,peerlessUserExpiration:w,workerUrl:n,onRoomReady:z,onRoomClose:L,dataChannelOptions:O}){let Y=new Set,R=new Set;function P(V,X,M,f){if(M){let N=V.createDataChannel("data",O);A(X,N,f),W.set(X,N)}else{let N=function(U){let y=U.channel;A(X,y,f),W.set(X,y)};return V.addEventListener("datachannel",N),()=>{V.removeEventListener("datachannel",N)}}}function T(V,X){R.forEach((M)=>M(V,X))}function A(V,X,M){X.onopen=()=>{H?.("\uD83D\uDCAC",{event:"dc-open",userId:V}),Y.add(V),q.forEach((N)=>N(V,"join",[...Y]))};let f=({data:N})=>{T(N,V)};X.addEventListener("message",f),X.addEventListener("close",()=>{H?.("\uD83D\uDCAC",{event:"dc-close",userId:V}),Y.delete(V),q.forEach((N)=>N(V,"leave",[...Y])),X.removeEventListener("message",f),M?.()}),X.onerror=()=>H?.("⚠️ ERROR",{error:"dc-error",userId:V})}let W=new Map,q=new Set,{userId:E,enterRoom:k,exitRoom:J,leaveUser:B,broadcast:$,end:x}=I({userId:K,worldId:j,enterRoomFunction:C,logLine:H,workerUrl:n,peerlessUserExpiration:w,onRoomReady:z,onRoomClose:L,onLeaveUser(V){let X=W.get(V);try{X?.close()}catch{}W.delete(V)},receivePeerConnection({pc:V,userId:X,initiator:M,restart:f}){P(V,X,M,f)},onBroadcastMessage(V,X){T(V,X),H?.("\uD83D\uDCE2",{event:"broadcast",userId:E,data:V})}});function D(V,X){W.forEach((M,f)=>{if(X&&f!==X)return;if(M.readyState==="open")M.send(V)})}function b(V){R.delete(V)}function F(V){return R.add(V),()=>{b(V)}}function _(V){q.delete(V)}function v(V){return q.add(V),()=>{_(V)}}return{userId:E,send:D,broadcast:$,enterRoom:k,exitRoom:J,leaveUser:B,getUsers:()=>[...Y],addMessageListener:F,removeMessageListener:b,addUserListener:v,removeUserListener:_,end(){W.forEach((V)=>{try{V.close()}catch{}}),W.clear(),x(),q.clear(),Y.clear()}}}export{u as enterWorld,i as enterRoom,I as collectPeerConnections};
1
+ function i(K){let{userId:P,worldId:E,room:C,host:n,autoRejoin:v=!0,logLine:H}=K,O=!1,R=0,Y,_,k=!0,x=new Map,q=`wss://${n}/room/${E}/${C}?userId=${encodeURIComponent(P)}`,W=[],z=0;function F(B,$,h){if(!Y)return H?.("\uD83D\uDC64 ➡️ ❌","no ws available"),!1;let D={type:B,to:$,payload:h};if(W.push(D),H?.("\uD83D\uDC64 ➡️ \uD83D\uDDA5️",D),clearTimeout(z),O||Y.readyState!==WebSocket.OPEN)return H?.("\uD83D\uDC64 ➡️ ❌","Not in opened state: "+Y.readyState),!1;return z=setTimeout(()=>{Y.send(JSON.stringify(W)),W.length=0}),!0}function w(){if(O)return;Y=new WebSocket(q),Y.onopen=()=>{if(k)K.onOpen?.(),k=!1;R=0},Y.onmessage=(B)=>{try{let $=JSON.parse(B.data);(Array.isArray($)?$:[$]).forEach((D)=>{if(H?.("\uD83D\uDDA5️ ➡️ \uD83D\uDC64",D),D.type==="peer-joined"||D.type==="peer-left")J(D.users);else if(D.type==="ice-server")K.onIceUrl?.(D.url,D.expiration);else if(D.userId)K.onMessage(D.type,D.payload,{userId:D.userId,receive:(G,A)=>F(G,D.userId,A)})})}catch{H?.("⚠️ ERROR",{error:"invalid-json"})}},Y.onclose=(B)=>{let h=[1001,1006,1011,1012,1013].includes(B.code);if(v&&!O&&h){let D=Math.min(Math.pow(2,R)*1000,30000),G=Math.random()*1000,A=D+G;H?.("\uD83D\uDD04 RECONNECTING",{attempt:R+1,delayMs:Math.round(A)}),R++,_=setTimeout(w,A)}else K.onClose?.({code:B.code,reason:B.reason,wasClean:B.wasClean})},Y.onerror=(B)=>{console.error("WS Error",B),K.onError?.()}}function J(B){let $=[],h=[],D=new Set;B.forEach(({userId:G})=>{if(G===P)return;if(!x.has(G)){let A={userId:G,receive:(M,j)=>F(M,G,j)};x.set(G,A),$.push(A)}D.add(G)});for(let G of x.keys())if(!D.has(G))x.delete(G),h.push({userId:G});if($.length)K.onPeerJoined($);if(h.length)K.onPeerLeft(h)}return w(),{sendToServer(B,$){F(B,"server",$)},exitRoom:()=>{O=!0,clearTimeout(_),Y.close()}}}function c({userId:K,worldId:P,room:E,host:C,autoRejoin:n=!0,onOpen:v,onClose:H,onError:O,onPeerJoined:R,onPeerLeft:Y,onIceUrl:_,onMessage:k,logLine:x,workerUrl:q}){if(!q)return console.warn("Warning: enterRoom called without workerUrl; this may cause issues in some environments. You should pass workerUrl explicitly. Use:","https://cdn.jsdelivr.net/npm/@dobuki/hello-worker/dist/signal-room.worker.min.js"),i({userId:K,worldId:P,room:E,host:C,autoRejoin:n,onOpen:v,onClose:H,onError:O,onPeerJoined:R,onPeerLeft:Y,onIceUrl:_,onMessage:k});let W=new Worker(q,{type:"module"}),z=!1;function F({userId:J}){return{userId:J,receive:(B,$)=>{if(z)return!1;return W.postMessage({cmd:"send",toUserId:J,host:C,room:E,type:B,payload:$}),!0}}}let w=(J)=>{let B=J.data;if(B.kind==="open")v?.();else if(B.kind==="close")W.terminate(),H?.(B.ev);else if(B.kind==="error")O?.();else if(B.kind==="peer-joined")R(B.users.map(($)=>F({userId:$.userId})));else if(B.kind==="peer-left")Y(B.users);else if(B.kind==="ice-server")_?.(B.url,B.expiration);else if(B.kind==="message")k(B.type,B.payload,F({userId:B.fromUserId}));else if(B.kind==="log")x?.(B.direction,B.obj)};return W.addEventListener("message",w),W.postMessage({cmd:"enter",userId:K,worldId:P,room:E,host:C,autoRejoin:n}),{exitRoom:()=>{z=!0,W.removeEventListener("message",w),W.postMessage({cmd:"exit"})},sendToServer:(J,B)=>{W.postMessage({cmd:"send",toUserId:"server",host:C,room:E,type:J,payload:B})}}}var d=c;function I({userId:K,worldId:P,receivePeerConnection:E,peerlessUserExpiration:C=5000,fallbackRtcConfig:n={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},enterRoomFunction:v=d,logLine:H,onLeaveUser:O,workerUrl:R,onRoomReady:Y,onRoomClose:_,onBroadcastMessage:k}){let x=K??`user-${crypto.randomUUID()}`,q=new Map,W=void 0,z={...n,timestamp:Date.now()},F=new Map;async function w(D,G){if(D){let A=3;for(let M=0;M<A;M++){try{let j=await fetch(D);if(!j.ok)throw Error(`ICE endpoint failed: ${j.status}`);return z=await j.json(),z}catch(j){console.warn("Failed fetching iceUrl")}D=(await G()).url}}return z}function J(D){O?.(D);let G=q.get(D);if(!G)return;q.delete(D);try{G.pc?.close()}catch{}}async function B(D){if(!D.pc?.remoteDescription)return;let G=D.pendingRemoteIce;D.pendingRemoteIce=[];for(let A of G)try{await D.pc.addIceCandidate(A)}catch(M){H?.("⚠️ ERROR",{error:"add-ice-failed",userId:D.userId,detail:String(M)})}}function $({room:D,host:G}){let A=`${G}/room/${D}`,M=F.get(A);if(M)M.exitRoom(),F.delete(A)}function h({room:D,host:G}){return new Promise(async(A,M)=>{async function j(b){let S=q.get(b.userId);if(!S)return;S.close();let Q=await V(S);E({pc:Q,userId:b.userId,initiator:!0,restart:()=>j(b)}),await new Promise((Z)=>setTimeout(Z,3000)),await N(b)}async function V(b){let S=Date.now();if(S-(z?.timestamp??0)>1e4){let Q=!W||W.expiration-S<2000?await f():W;z=await w(Q.url,f)}return b.pc=new RTCPeerConnection(z),b.pc.onicecandidate=(Q)=>{if(!Q.candidate)return;b.peer.receive("ice",Q.candidate.toJSON())},b.pc.onconnectionstatechange=()=>{H?.("\uD83D\uDCAC",{event:"pc-state",userId:b.userId,state:b.pc?.connectionState})},b.pc}async function X(b,S){let Q=q.get(b.userId);if(!Q||S){let Z={userId:b.userId,pendingRemoteIce:[],peer:b,initiateForThisUser:Q?.initiateForThisUser??!1,close(){this.pc?.close(),this.pc=void 0}};await V(Z),Q=Z,q.set(Q.userId,Q)}else if(Q)clearTimeout(Q.expirationTimeout),Q.expirationTimeout=0;if(!Q.pc||Q.pc?.signalingState==="closed")await V(Q);return Q.peer=b,Q}async function N(b){let Q=(await X(b)).pc,Z=await Q?.createOffer();await Q?.setLocalDescription(Z),b.receive("offer",Q?.localDescription?.toJSON())}let T;async function f(){let b=await new Promise((S)=>{T=S,y("request-ice")});return T=void 0,b}let{exitRoom:U,sendToServer:y}=v({userId:x,worldId:P,room:D,host:G,logLine:H,workerUrl:R,autoRejoin:!0,onOpen(){Y?.({room:D,host:G}),A()},onError(){console.error("onError"),M()},onClose(b){_?.({room:D,host:G,ev:b})},onPeerJoined(b){b.forEach(async(S)=>{let Q=q.has(S.userId),Z=await X(S,!0);if(!Q)Z.initiateForThisUser=!0;let L=Z.pc;if(!L){H?.("\uD83D\uDC64ℹ️","no pc: "+S.userId);return}if(E({pc:L,userId:S.userId,initiator:Z.initiateForThisUser,restart:Z.initiateForThisUser?()=>j(S):()=>Z.close()}),Z.initiateForThisUser)await N(S)})},onPeerLeft(b){b.forEach(({userId:S})=>{let Q=q.get(S);if(!Q)return;Q.expirationTimeout=setTimeout(()=>J(S),C??0)})},onIceUrl(b,S){W={url:b,expiration:S},T?.(W)},async onMessage(b,S,Q){let Z=await X(Q),L=Z.pc;if(!L)return;if(b==="offer"){Z.initiateForThisUser=!1,E({pc:L,userId:Q.userId,initiator:!1,restart(){Z.close()}}),await L.setRemoteDescription(S);let g=await L.createAnswer();await L.setLocalDescription(g),Q.receive("answer",L.localDescription?.toJSON()),await B(Z);return}if(b==="answer"){await L.setRemoteDescription(S),await B(Z);return}if(b==="ice"){let g=S;if(!L.remoteDescription){Z.pendingRemoteIce.push(g);return}try{await L.addIceCandidate(g)}catch(m){H?.("⚠️ ERROR",{error:"add-ice-failed",userId:Z.userId,detail:String(m)})}return}if(b==="broadcast")k?.(S,Q.userId)}});F.set(`${G}/room/${D}`,{exitRoom:U,room:D,host:G,broadcast:(b)=>{y("broadcast",b)}})})}return{userId:x,enterRoom:h,exitRoom:$,leaveUser:J,broadcast(D){F.forEach((G)=>G.broadcast(D))},end(){F.forEach(({exitRoom:D})=>D()),F.clear(),q.forEach(({userId:D})=>J(D)),q.clear()}}}function u({userId:K,worldId:P,logLine:E,enterRoomFunction:C=c,peerlessUserExpiration:n,workerUrl:v,onRoomReady:H,onRoomClose:O,dataChannelOptions:R}){let Y=new Set,_=new Set;function k(V,X,N,T){if(N){let f=V.createDataChannel("data",R);q(X,f,T),W.set(X,f)}else{let f=function(U){let y=U.channel;q(X,y,T),W.set(X,y)};return V.addEventListener("datachannel",f),()=>{V.removeEventListener("datachannel",f)}}}function x(V,X){_.forEach((N)=>N(V,X))}function q(V,X,N){X.onopen=()=>{E?.("\uD83D\uDCAC",{event:"dc-open",userId:V}),Y.add(V),z.forEach((f)=>f(V,"join",[...Y]))};let T=({data:f})=>{x(f,V)};X.addEventListener("message",T),X.addEventListener("close",()=>{E?.("\uD83D\uDCAC",{event:"dc-close",userId:V}),Y.delete(V),z.forEach((f)=>f(V,"leave",[...Y])),X.removeEventListener("message",T),N?.()}),X.onerror=()=>E?.("⚠️ ERROR",{error:"dc-error",userId:V})}let W=new Map,z=new Set,{userId:F,enterRoom:w,exitRoom:J,leaveUser:B,broadcast:$,end:h}=I({userId:K,worldId:P,enterRoomFunction:C,logLine:E,workerUrl:v,peerlessUserExpiration:n,onRoomReady:H,onRoomClose:O,onLeaveUser(V){let X=W.get(V);try{X?.close()}catch{}W.delete(V)},receivePeerConnection({pc:V,userId:X,initiator:N,restart:T}){k(V,X,N,T)},onBroadcastMessage(V,X){x(V,X),E?.("\uD83D\uDCE2",{event:"broadcast",userId:F,data:V})}});function D(V,X){W.forEach((N,T)=>{if(X&&T!==X)return;if(N.readyState==="open")N.send(V)})}function G(V){_.delete(V)}function A(V){return _.add(V),()=>{G(V)}}function M(V){z.delete(V)}function j(V){return z.add(V),()=>{M(V)}}return{userId:F,send:D,broadcast:$,enterRoom:w,exitRoom:J,leaveUser:B,getUsers:()=>[...Y],addMessageListener:A,removeMessageListener:G,addUserListener:j,removeUserListener:M,end(){W.forEach((V)=>{try{V.close()}catch{}}),W.clear(),h(),z.clear(),Y.clear()}}}export{u as enterWorld,i as enterRoom,I as collectPeerConnections};
2
2
 
3
- //# debugId=6417898587532A6564756E2164756E21
3
+ //# debugId=0F567A29A4525EC964756E2164756E21
4
4
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -4,10 +4,10 @@
4
4
  "sourcesContent": [
5
5
  "export interface IPeer<T extends string = string, P = any> {\n userId: string;\n receive(type: T, payload: P): boolean;\n}\n\ntype OutMessage = { type: string; to: string; payload: any };\n\n/**\n * enterRoom connects to the signaling room via WebSocket.\n */\nexport function enterRoom<T extends string, P = any>(params: {\n userId: string;\n worldId: string;\n room: string;\n host: string;\n onOpen?: () => void;\n onClose?: (ev: Pick<CloseEvent, \"code\" | \"reason\" | \"wasClean\">) => void;\n onError?: () => void;\n logLine?: (direction: string, obj?: any) => void;\n onPeerJoined(users: IPeer<T, P>[]): void;\n onPeerLeft(users: { userId: string }[]): void;\n onIceUrl?(url: string, expiration: number): void;\n onMessage(type: T, payload: P, from: IPeer<T, P>): void;\n autoRejoin?: boolean;\n}): {\n exitRoom: () => void;\n sendToServer: <P extends any>(type: T, payload?: P) => void;\n} {\n type Message = {\n type: \"peer-joined\" | \"peer-left\" | \"ice-server\" | T;\n userId: string;\n users: { userId: string }[];\n payload: P;\n url: string;\n expiration: number;\n };\n\n const { userId, worldId, room, host, autoRejoin = true, logLine } = params;\n\n let exited = false;\n let retryCount = 0;\n let ws: WebSocket;\n let timeoutId: ReturnType<typeof setTimeout>;\n let initialConnection = true;\n\n const peers = new Map<string, IPeer<T, P>>();\n const wsUrl = `wss://${host}/room/${worldId}/${room}?userId=${encodeURIComponent(\n userId,\n )}`;\n\n // Helper for sending (uses the current ws instance)\n const accumulatedMessages: OutMessage[] = [];\n let timeout: ReturnType<typeof setTimeout> = 0;\n function send(type: string, to: \"server\" | string, payload?: any) {\n if (!ws) {\n logLine?.(\"👤 ➡️ ❌\", \"no ws available\");\n return false;\n }\n const obj: OutMessage = { type, to, payload };\n accumulatedMessages.push(obj);\n logLine?.(\"👤 ➡️ 🖥️\", obj);\n clearTimeout(timeout);\n if (exited || ws.readyState !== WebSocket.OPEN) {\n logLine?.(\"👤 ➡️ ❌\", \"Not in opened state: \" + ws.readyState);\n return false;\n }\n timeout = setTimeout(() => {\n ws.send(JSON.stringify(accumulatedMessages));\n accumulatedMessages.length = 0;\n });\n return true;\n }\n\n function connect() {\n if (exited) return;\n\n ws = new WebSocket(wsUrl);\n\n ws.onopen = () => {\n if (initialConnection) {\n params.onOpen?.();\n initialConnection = false;\n }\n retryCount = 0; // Reset backoff on successful connection\n };\n\n ws.onmessage = (e: MessageEvent) => {\n try {\n const result = JSON.parse(e.data);\n const msgs: Message[] = Array.isArray(result) ? result : [result];\n msgs.forEach((msg) => {\n logLine?.(\"🖥️ ➡️ 👤\", msg);\n if (msg.type === \"peer-joined\" || msg.type === \"peer-left\") {\n updatePeers(msg.users);\n } else if (msg.type === \"ice-server\") {\n params.onIceUrl?.(msg.url, msg.expiration);\n } else if (msg.userId) {\n params.onMessage(msg.type, msg.payload, {\n userId: msg.userId,\n receive: (type: T, payload: P) => send(type, msg.userId, payload),\n });\n }\n });\n } catch {\n logLine?.(\"⚠️ ERROR\", { error: \"invalid-json\" });\n }\n };\n\n ws.onclose = (ev: CloseEvent) => {\n // 1. Check if we should even try to reconnect\n const recoverableCodes = [1001, 1006, 1011, 1012, 1013];\n const isRecoverable = recoverableCodes.includes(ev.code);\n\n if (autoRejoin && !exited && isRecoverable) {\n // 2. Exponential Backoff: 1s, 2s, 4s, 8s... capped at 30s\n const backoff = Math.min(Math.pow(2, retryCount) * 1000, 30000);\n // 3. Add Jitter: +/- 1000ms randomness\n const jitter = Math.random() * 1000;\n const delay = backoff + jitter;\n\n logLine?.(\"🔄 RECONNECTING\", {\n attempt: retryCount + 1,\n delayMs: Math.round(delay),\n });\n\n retryCount++;\n timeoutId = setTimeout(connect, delay);\n } else {\n params.onClose?.({\n code: ev.code,\n reason: ev.reason,\n wasClean: ev.wasClean,\n });\n }\n };\n\n ws.onerror = (ev) => {\n console.error(\"WS Error\", ev);\n params.onError?.();\n };\n }\n\n // Helper for peer tracking (logic from your original code)\n function updatePeers(updatedUsers: { userId: string }[]) {\n const joined: IPeer<T, P>[] = [];\n const left: { userId: string }[] = [];\n const updatedPeerSet = new Set<string>();\n\n updatedUsers.forEach(({ userId: pUserId }) => {\n if (pUserId === userId) return;\n if (!peers.has(pUserId)) {\n const newPeer = {\n userId: pUserId,\n receive: (t: T, p: P) => send(t, pUserId, p),\n };\n peers.set(pUserId, newPeer);\n joined.push(newPeer);\n }\n updatedPeerSet.add(pUserId);\n });\n\n for (const pUserId of peers.keys()) {\n if (!updatedPeerSet.has(pUserId)) {\n peers.delete(pUserId);\n left.push({ userId: pUserId });\n }\n }\n // Notify peer joined first then peer left. (avoid disconnect in case the peer leaving / joining is on the same user).\n if (joined.length) params.onPeerJoined(joined);\n if (left.length) params.onPeerLeft(left);\n }\n\n // Start initial connection\n connect();\n\n return {\n sendToServer(type, payload) {\n send(type, \"server\", payload);\n },\n exitRoom: () => {\n exited = true;\n clearTimeout(timeoutId);\n ws.close();\n },\n };\n}\n",
6
6
  "import type { IPeer } from \"./impl/signal-room.js\";\nimport { enterRoom as baseEnterRoom } from \"./impl/signal-room.js\";\nimport { RoomEvent, WorkerCommand } from \"./signal-room.worker.js\";\n\nexport function enterRoom<T extends string, P = any>({\n userId,\n worldId,\n room,\n host,\n autoRejoin = true,\n onOpen,\n onClose,\n onError,\n onPeerJoined,\n onPeerLeft,\n onIceUrl,\n onMessage,\n logLine,\n workerUrl,\n}: {\n userId: string;\n worldId: string;\n room: string;\n host: string;\n autoRejoin?: boolean;\n onOpen?: () => void;\n onClose?: (ev: Pick<CloseEvent, \"code\" | \"reason\" | \"wasClean\">) => void;\n onError?: () => void;\n onPeerJoined: (users: IPeer<T, P>[]) => void;\n onPeerLeft: (users: { userId: string }[]) => void;\n onIceUrl?(url: string, expiration: number): void;\n onMessage: (type: T, payload: P, from: IPeer<T, P>) => void;\n logLine?: (direction: string, obj?: any) => void;\n\n // Pass the URL to your worker file (bundler will handle it)\n workerUrl?: URL;\n}): {\n exitRoom: () => void;\n sendToServer: <P extends any>(type: T, payload?: P) => void;\n} {\n if (!workerUrl) {\n const CDN_WORKER_URL = `https://cdn.jsdelivr.net/npm/@dobuki/hello-worker/dist/signal-room.worker.min.js`;\n\n console.warn(\n \"Warning: enterRoom called without workerUrl; this may cause issues in some environments. You should pass workerUrl explicitly. Use:\",\n CDN_WORKER_URL,\n );\n return baseEnterRoom<T, P>({\n userId,\n worldId,\n room,\n host,\n autoRejoin,\n onOpen,\n onClose,\n onError,\n onPeerJoined,\n onPeerLeft,\n onIceUrl,\n onMessage,\n });\n }\n const worker = new Worker(workerUrl, { type: \"module\" });\n let exited = false;\n\n function makeUser({ userId }: { userId: string }): IPeer<T, P> {\n return {\n userId,\n receive: (type: T, payload: P) => {\n if (exited) return false;\n worker.postMessage({\n cmd: \"send\",\n toUserId: userId,\n host,\n room,\n type,\n payload,\n } as WorkerCommand);\n return true;\n },\n };\n }\n\n const onWorkerMessage = (e: MessageEvent<RoomEvent<T, P>>) => {\n const ev = e.data;\n\n if (ev.kind === \"open\") onOpen?.();\n else if (ev.kind === \"close\") {\n worker.terminate();\n onClose?.(ev.ev);\n } else if (ev.kind === \"error\") onError?.();\n else if (ev.kind === \"peer-joined\")\n onPeerJoined(ev.users.map((ev) => makeUser({ userId: ev.userId })));\n else if (ev.kind === \"peer-left\") onPeerLeft(ev.users);\n else if (ev.kind === \"ice-server\") onIceUrl?.(ev.url, ev.expiration);\n else if (ev.kind === \"message\")\n onMessage(ev.type, ev.payload, makeUser({ userId: ev.fromUserId }));\n else if (ev.kind === \"log\") logLine?.(ev.direction, ev.obj);\n };\n\n worker.addEventListener(\"message\", onWorkerMessage);\n\n worker.postMessage({\n cmd: \"enter\",\n userId,\n worldId,\n room,\n host,\n autoRejoin,\n } as WorkerCommand);\n\n return {\n exitRoom: () => {\n exited = true;\n worker.removeEventListener(\"message\", onWorkerMessage);\n worker.postMessage({ cmd: \"exit\" } as WorkerCommand);\n },\n sendToServer: <P extends any>(type: T, payload?: P) => {\n worker.postMessage({\n cmd: \"send\",\n toUserId: \"server\",\n host,\n room,\n type,\n payload,\n } as WorkerCommand);\n },\n };\n}\n\nexport type EnterRoom<T extends string, P> = typeof enterRoom<T, P>;\n",
7
- "import { IPeer } from \"./signal/impl/signal-room\";\nimport { EnterRoom, enterRoom } from \"./signal/signal-room\";\n\nexport type SigType = \"offer\" | \"answer\" | \"ice\" | \"request-ice\" | \"broadcast\";\nexport type SigPayload = RTCSessionDescriptionInit | RTCIceCandidateInit;\n\ntype UserState = {\n userId: string;\n pc?: RTCPeerConnection;\n\n // ICE that arrived before we had remoteDescription\n pendingRemoteIce: RTCIceCandidateInit[];\n\n // the signaling \"user\" handle so we can send messages\n peer: IPeer<SigType, SigPayload>;\n\n expirationTimeout?: number;\n initiateForThisUser: boolean;\n close: () => void;\n};\n\nconst DEFAULT_ENTER_ROOM = enterRoom;\n\nexport function collectPeerConnections({\n userId: passedUserId,\n worldId,\n receivePeerConnection,\n peerlessUserExpiration = 5000,\n fallbackRtcConfig = {\n iceServers: [{ urls: \"stun:stun.l.google.com:19302\" }],\n },\n enterRoomFunction: enterRoom = DEFAULT_ENTER_ROOM,\n logLine,\n onLeaveUser,\n workerUrl,\n onRoomReady,\n onRoomClose,\n onBroadcastMessage,\n}: {\n userId?: string;\n worldId: string;\n fallbackRtcConfig?: RTCConfiguration;\n enterRoomFunction?: EnterRoom<SigType, SigPayload>;\n onLeaveUser?: (userId: string) => void;\n logLine?: (direction: string, obj?: any) => void;\n workerUrl?: URL;\n peerlessUserExpiration?: number;\n receivePeerConnection(connection: {\n pc: RTCPeerConnection;\n userId: string;\n initiator: boolean;\n restart?: () => void;\n }): void;\n onRoomReady?(info: { host: string; room: string }): void;\n onRoomClose?(info: {\n host: string;\n room: string;\n ev: Pick<CloseEvent, \"reason\" | \"code\" | \"wasClean\">;\n }): void;\n onBroadcastMessage?<P extends any>(payload: P, from: string): void;\n}) {\n const userId = passedUserId ?? `user-${crypto.randomUUID()}`;\n const users: Map<string, UserState> = new Map();\n let iceUrl: { url: string; expiration: number } | undefined = undefined;\n let rtcConfig: RTCConfiguration & { timestamp: number } = {\n ...fallbackRtcConfig,\n timestamp: Date.now(),\n };\n\n const roomsEntered = new Map<\n string,\n {\n room: string;\n host: string;\n exitRoom: () => void;\n broadcast: <P extends any>(payload: P) => void;\n }\n >();\n\n async function getRtcConfig(\n iceUrl: string,\n ): Promise<RTCConfiguration & { timestamp: number }> {\n if (iceUrl) {\n try {\n const r = await fetch(iceUrl);\n if (!r.ok) throw new Error(`ICE endpoint failed: ${r.status}`);\n rtcConfig = (await r.json()) as RTCConfiguration & {\n timestamp: number;\n };\n } catch (e) {\n console.warn(\"Using fallback rtcConfig:\", e);\n }\n }\n return rtcConfig;\n }\n\n function leaveUser(userId: string) {\n onLeaveUser?.(userId);\n const p = users.get(userId);\n if (!p) return;\n users.delete(userId);\n try {\n p.pc?.close();\n } catch {}\n }\n\n async function flushRemoteIce(state: UserState) {\n if (!state.pc?.remoteDescription) return;\n\n const queued = state.pendingRemoteIce;\n state.pendingRemoteIce = [];\n\n for (const ice of queued) {\n try {\n await state.pc.addIceCandidate(ice);\n } catch (e) {\n logLine?.(\"⚠️ ERROR\", {\n error: \"add-ice-failed\",\n userId: state.userId,\n detail: String(e),\n });\n }\n }\n }\n\n function exit({ room, host }: { room: string; host: string }) {\n const key = `${host}/room/${room}`;\n const session = roomsEntered.get(key);\n if (session) {\n session.exitRoom();\n roomsEntered.delete(key);\n }\n }\n\n function enter({ room, host }: { room: string; host: string }) {\n return new Promise<void>(async (resolve, reject) => {\n async function restartInitiator(user: IPeer) {\n const state = users.get(user.userId);\n if (!state) return; // user left\n state.close();\n const pc = await setupPC(state);\n receivePeerConnection({\n pc,\n userId: user.userId,\n initiator: true,\n restart: () => restartInitiator(user),\n });\n await new Promise((resolve) => setTimeout(resolve, 3000));\n await makeOffer(user);\n }\n\n async function setupPC(state: UserState) {\n const now = Date.now();\n if (now - (rtcConfig?.timestamp ?? 0) > 10000) {\n const ice =\n !iceUrl || iceUrl.expiration - now < 2000\n ? await requestIce()\n : iceUrl;\n rtcConfig = await getRtcConfig(ice.url);\n }\n state.pc = new RTCPeerConnection(rtcConfig);\n // Send local ICE candidates to this peer\n state.pc.onicecandidate = (ev) => {\n if (!ev.candidate) return;\n state.peer.receive(\"ice\", ev.candidate.toJSON());\n };\n\n state.pc.onconnectionstatechange = () => {\n logLine?.(\"💬\", {\n event: \"pc-state\",\n userId: state.userId,\n state: state.pc?.connectionState,\n });\n };\n\n return state.pc;\n }\n\n async function getPeer(\n peer: IPeer<SigType, SigPayload>,\n forceReset?: boolean,\n ): Promise<UserState> {\n let state = users.get(peer.userId);\n if (!state || forceReset) {\n const newState: UserState = {\n userId: peer.userId,\n pendingRemoteIce: [],\n peer,\n initiateForThisUser: state?.initiateForThisUser ?? false,\n close() {\n this.pc?.close();\n this.pc = undefined;\n },\n };\n\n await setupPC(newState);\n state = newState;\n\n // New user\n users.set(state.userId, state);\n } else if (state) {\n clearTimeout(state.expirationTimeout);\n state.expirationTimeout = 0;\n }\n if (!state.pc || state.pc?.signalingState === \"closed\") {\n await setupPC(state);\n }\n state.peer = peer;\n return state;\n }\n\n async function makeOffer(user: IPeer) {\n // Offer flow: createOffer -> setLocalDescription -> send localDescription\n const state = await getPeer(user);\n const pc = state.pc;\n const offer = await pc?.createOffer();\n await pc?.setLocalDescription(offer);\n user.receive(\"offer\", pc?.localDescription?.toJSON()!);\n }\n\n let icePromiseResolve:\n | undefined\n | ((url: { url: string; expiration: number }) => void);\n async function requestIce() {\n const iceUrl = await new Promise<{ url: string; expiration: number }>(\n (resolve) => {\n icePromiseResolve = resolve;\n sendToServer(\"request-ice\");\n },\n );\n icePromiseResolve = undefined;\n return iceUrl;\n }\n\n const { exitRoom, sendToServer } = enterRoom({\n userId,\n worldId,\n room,\n host,\n logLine,\n workerUrl,\n autoRejoin: true,\n\n onOpen() {\n onRoomReady?.({ room, host });\n resolve();\n },\n onError() {\n console.error(\"onError\");\n reject();\n },\n onClose(ev: Pick<CloseEvent, \"reason\" | \"code\" | \"wasClean\">) {\n onRoomClose?.({ room, host, ev });\n },\n\n // Existing peers initiate to the newcomer\n onPeerJoined(joiningUsers: IPeer<SigType, SigPayload>[]) {\n joiningUsers.forEach(async (user) => {\n const hadState = users.has(user.userId);\n const state = await getPeer(user, true);\n if (!hadState) {\n state.initiateForThisUser = true;\n }\n const pc = state.pc;\n if (!pc) {\n logLine?.(\"👤ℹ️\", \"no pc: \" + user.userId);\n return;\n }\n\n receivePeerConnection({\n pc,\n userId: user.userId,\n initiator: state.initiateForThisUser,\n restart: state.initiateForThisUser\n ? () => restartInitiator(user)\n : () => state.close(),\n });\n if (state.initiateForThisUser) {\n await makeOffer(user);\n }\n });\n },\n\n onPeerLeft(leavingUsers: { userId: string }[]) {\n leavingUsers.forEach(({ userId }) => {\n const state = users.get(userId);\n if (!state) return;\n state.expirationTimeout = setTimeout(\n () => leaveUser(userId),\n peerlessUserExpiration ?? 0,\n );\n });\n },\n\n onIceUrl(url: string, expiration: number) {\n iceUrl = { url, expiration };\n icePromiseResolve?.(iceUrl);\n },\n\n async onMessage(type: SigType, payload: any, from: IPeer) {\n const state = await getPeer(from);\n const pc = state.pc;\n if (!pc) return;\n\n if (type === \"offer\") {\n state.initiateForThisUser = false;\n receivePeerConnection({\n pc,\n userId: from.userId,\n initiator: false,\n restart() {\n // reset PC\n state.close();\n },\n });\n // Responder: set remote offer\n await pc.setRemoteDescription(payload as RTCSessionDescriptionInit);\n\n // Create and send answer\n const answer = await pc.createAnswer();\n await pc.setLocalDescription(answer);\n\n from.receive(\"answer\", pc.localDescription?.toJSON()!);\n\n // Now safe to apply any queued ICE from this peer\n await flushRemoteIce(state);\n return;\n }\n\n if (type === \"answer\") {\n // Initiator: set remote answer\n await pc.setRemoteDescription(payload as RTCSessionDescriptionInit);\n await flushRemoteIce(state);\n return;\n }\n\n if (type === \"ice\") {\n const ice = payload as RTCIceCandidateInit;\n\n // If we don't have remoteDescription yet, queue it\n if (!pc.remoteDescription) {\n state.pendingRemoteIce.push(ice);\n return;\n }\n\n try {\n await pc.addIceCandidate(ice);\n } catch (e) {\n logLine?.(\"⚠️ ERROR\", {\n error: \"add-ice-failed\",\n userId: state.userId,\n detail: String(e),\n });\n }\n return;\n }\n\n if (type === \"broadcast\") {\n onBroadcastMessage?.(payload, from.userId);\n }\n },\n });\n roomsEntered.set(`${host}/room/${room}`, {\n exitRoom,\n room,\n host,\n broadcast: (payload) => {\n sendToServer(\"broadcast\", payload);\n },\n });\n });\n }\n\n return {\n userId,\n enterRoom: enter,\n exitRoom: exit,\n leaveUser,\n broadcast<P extends any>(payload: P) {\n roomsEntered.forEach((room) => room.broadcast(payload));\n },\n end() {\n roomsEntered.forEach(({ exitRoom }) => exitRoom());\n roomsEntered.clear();\n users.forEach(({ userId }) => leaveUser(userId));\n users.clear();\n },\n };\n}\n\n/*\nTurn Token ID\n<CF_TURN_TOKEN_ID>\n\nAPI Token\n<CF_RTC_API_TOKEN>\n\nCURL\ncurl \\\n\t-H \"Authorization: Bearer <CF_RTC_API_TOKEN>\" \\\n\t-H \"Content-Type: application/json\" -d '{\"ttl\": 86400}' \\\n\thttps://rtc.live.cloudflare.com/v1/turn/keys/<CF_TURN_TOKEN_ID>/credentials/generate-ice-servers\n\nJSON\n{\n\t\"iceServers\": [\n {\n \"urls\": [\n \"stun:stun.cloudflare.com:3478\",\n \"turn:turn.cloudflare.com:3478?transport=udp\",\n \"turn:turn.cloudflare.com:3478?transport=tcp\",\n \"turns:turn.cloudflare.com:5349?transport=tcp\"\n ],\n \"username\": \"xxxx\",\n \"credential\": \"yyyy\",\n }\n ]\n}\n\n*/\n",
8
- "import { EnterRoom, enterRoom } from \"./signal/signal-room\";\nimport {\n SigType,\n SigPayload,\n collectPeerConnections,\n} from \"./webrtc-peer-collector\";\n\ntype UserListener = (\n user: string,\n action: \"join\" | \"leave\",\n users: string[],\n) => void;\n\nexport function enterWorld<\n S extends string | ArrayBufferView = string | ArrayBufferView,\n R extends string | ArrayBufferLike = string | ArrayBufferLike,\n>({\n userId: passedUserId,\n worldId,\n logLine,\n enterRoomFunction = enterRoom,\n peerlessUserExpiration,\n workerUrl,\n onRoomReady,\n onRoomClose,\n dataChannelOptions,\n}: {\n userId?: string;\n worldId: string;\n logLine?: (direction: string, obj?: any) => void;\n enterRoomFunction?: EnterRoom<SigType, SigPayload>;\n peerlessUserExpiration?: number;\n workerUrl?: URL;\n onRoomReady?(info: { host: string; room: string }): void;\n onRoomClose?(info: {\n host: string;\n room: string;\n ev: Pick<CloseEvent, \"reason\" | \"code\" | \"wasClean\">;\n }): void;\n dataChannelOptions?: RTCDataChannelInit;\n}) {\n const userIds = new Set<string>();\n\n const messagesListeners = new Set<(data: R, from: string) => void>();\n\n function createDataChannel(\n pc: RTCPeerConnection,\n peerUserId: string,\n initiator: boolean,\n restart?: () => void,\n ) {\n if (initiator) {\n const dc = pc.createDataChannel(\"data\", dataChannelOptions);\n wireDataChannel(peerUserId, dc, restart);\n dataChannels.set(peerUserId, dc);\n } else {\n function listener(ev: RTCDataChannelEvent) {\n const dc = ev.channel;\n wireDataChannel(peerUserId, dc, restart);\n dataChannels.set(peerUserId, dc);\n }\n pc.addEventListener(\"datachannel\", listener);\n return () => {\n pc.removeEventListener(\"datachannel\", listener);\n };\n }\n }\n\n function conveyMessage(data: any, userId: string) {\n messagesListeners.forEach((listener) => listener(data, userId));\n }\n\n function wireDataChannel(\n userId: string,\n dc: RTCDataChannel,\n restart?: () => void,\n ) {\n dc.onopen = () => {\n logLine?.(\"💬\", { event: \"dc-open\", userId });\n userIds.add(userId);\n userListeners.forEach((listener) =>\n listener(userId, \"join\", [...userIds]),\n );\n };\n const onmessage = ({ data }: MessageEvent) => {\n conveyMessage(data, userId);\n };\n dc.addEventListener(\"message\", onmessage);\n dc.addEventListener(\"close\", () => {\n logLine?.(\"💬\", { event: \"dc-close\", userId });\n userIds.delete(userId);\n userListeners.forEach((listener) =>\n listener(userId, \"leave\", [...userIds]),\n );\n dc.removeEventListener(\"message\", onmessage);\n restart?.();\n });\n dc.onerror = () => logLine?.(\"⚠️ ERROR\", { error: \"dc-error\", userId });\n }\n\n const dataChannels = new Map<string, RTCDataChannel>();\n const userListeners = new Set<UserListener>();\n\n const {\n userId,\n enterRoom,\n exitRoom,\n leaveUser,\n broadcast,\n end: endPeerCollection,\n } = collectPeerConnections({\n userId: passedUserId,\n worldId,\n enterRoomFunction,\n logLine,\n workerUrl,\n peerlessUserExpiration,\n onRoomReady,\n onRoomClose,\n onLeaveUser(userId: string) {\n const dc = dataChannels.get(userId);\n try {\n dc?.close();\n } catch {}\n dataChannels.delete(userId);\n },\n receivePeerConnection({ pc, userId, initiator, restart }) {\n createDataChannel(pc, userId, initiator, restart);\n },\n onBroadcastMessage(payload, from) {\n conveyMessage(payload, from);\n logLine?.(\"📢\", { event: \"broadcast\", userId, data: payload });\n },\n });\n\n function send(data: S, userId?: string) {\n dataChannels.forEach((dataChannel, pUserId) => {\n if (userId && pUserId !== userId) return;\n if (dataChannel.readyState === \"open\") {\n dataChannel.send(data as any);\n }\n });\n }\n\n function removeMessageListener(listener: (data: R, from: string) => void) {\n messagesListeners.delete(listener);\n }\n\n function addMessageListener(listener: (data: R, from: string) => void) {\n messagesListeners.add(listener);\n return () => {\n removeMessageListener(listener);\n };\n }\n\n function removeUserListener(listener: UserListener) {\n userListeners.delete(listener);\n }\n\n function addUserListener(listener: UserListener) {\n userListeners.add(listener);\n return () => {\n removeUserListener(listener);\n };\n }\n\n return {\n userId,\n send,\n broadcast,\n enterRoom,\n exitRoom,\n leaveUser,\n getUsers: () => [...userIds],\n addMessageListener,\n removeMessageListener,\n addUserListener,\n removeUserListener,\n end() {\n dataChannels.forEach((dataChannel) => {\n try {\n dataChannel.close();\n } catch {}\n });\n dataChannels.clear();\n endPeerCollection();\n userListeners.clear();\n userIds.clear();\n },\n };\n}\n"
7
+ "import { IPeer } from \"./signal/impl/signal-room\";\nimport { EnterRoom, enterRoom } from \"./signal/signal-room\";\n\nexport type SigType = \"offer\" | \"answer\" | \"ice\" | \"request-ice\" | \"broadcast\";\nexport type SigPayload = RTCSessionDescriptionInit | RTCIceCandidateInit;\n\ntype UserState = {\n userId: string;\n pc?: RTCPeerConnection;\n\n // ICE that arrived before we had remoteDescription\n pendingRemoteIce: RTCIceCandidateInit[];\n\n // the signaling \"user\" handle so we can send messages\n peer: IPeer<SigType, SigPayload>;\n\n expirationTimeout?: number;\n initiateForThisUser: boolean;\n close: () => void;\n};\n\nconst DEFAULT_ENTER_ROOM = enterRoom;\n\nexport function collectPeerConnections({\n userId: passedUserId,\n worldId,\n receivePeerConnection,\n peerlessUserExpiration = 5000,\n fallbackRtcConfig = {\n iceServers: [{ urls: \"stun:stun.l.google.com:19302\" }],\n },\n enterRoomFunction: enterRoom = DEFAULT_ENTER_ROOM,\n logLine,\n onLeaveUser,\n workerUrl,\n onRoomReady,\n onRoomClose,\n onBroadcastMessage,\n}: {\n userId?: string;\n worldId: string;\n fallbackRtcConfig?: RTCConfiguration;\n enterRoomFunction?: EnterRoom<SigType, SigPayload>;\n onLeaveUser?: (userId: string) => void;\n logLine?: (direction: string, obj?: any) => void;\n workerUrl?: URL;\n peerlessUserExpiration?: number;\n receivePeerConnection(connection: {\n pc: RTCPeerConnection;\n userId: string;\n initiator: boolean;\n restart?: () => void;\n }): void;\n onRoomReady?(info: { host: string; room: string }): void;\n onRoomClose?(info: {\n host: string;\n room: string;\n ev: Pick<CloseEvent, \"reason\" | \"code\" | \"wasClean\">;\n }): void;\n onBroadcastMessage?<P extends any>(payload: P, from: string): void;\n}) {\n const userId = passedUserId ?? `user-${crypto.randomUUID()}`;\n const users: Map<string, UserState> = new Map();\n let iceUrl: { url: string; expiration: number } | undefined = undefined;\n let rtcConfig: RTCConfiguration & { timestamp: number } = {\n ...fallbackRtcConfig,\n timestamp: Date.now(),\n };\n\n const roomsEntered = new Map<\n string,\n {\n room: string;\n host: string;\n exitRoom: () => void;\n broadcast: <P extends any>(payload: P) => void;\n }\n >();\n\n async function getRtcConfig(\n iceUrl: string,\n retryIce: () => Promise<{ url: string }>,\n ): Promise<RTCConfiguration & { timestamp: number }> {\n if (iceUrl) {\n let retries = 3;\n for (let r = 0; r < retries; r++) {\n try {\n const r = await fetch(iceUrl);\n if (!r.ok) throw new Error(`ICE endpoint failed: ${r.status}`);\n rtcConfig = (await r.json()) as RTCConfiguration & {\n timestamp: number;\n };\n return rtcConfig;\n } catch (e) {\n console.warn(\"Failed fetching iceUrl\");\n }\n iceUrl = (await retryIce()).url;\n }\n }\n return rtcConfig;\n }\n\n function leaveUser(userId: string) {\n onLeaveUser?.(userId);\n const p = users.get(userId);\n if (!p) return;\n users.delete(userId);\n try {\n p.pc?.close();\n } catch {}\n }\n\n async function flushRemoteIce(state: UserState) {\n if (!state.pc?.remoteDescription) return;\n\n const queued = state.pendingRemoteIce;\n state.pendingRemoteIce = [];\n\n for (const ice of queued) {\n try {\n await state.pc.addIceCandidate(ice);\n } catch (e) {\n logLine?.(\"⚠️ ERROR\", {\n error: \"add-ice-failed\",\n userId: state.userId,\n detail: String(e),\n });\n }\n }\n }\n\n function exit({ room, host }: { room: string; host: string }) {\n const key = `${host}/room/${room}`;\n const session = roomsEntered.get(key);\n if (session) {\n session.exitRoom();\n roomsEntered.delete(key);\n }\n }\n\n function enter({ room, host }: { room: string; host: string }) {\n return new Promise<void>(async (resolve, reject) => {\n async function restartInitiator(user: IPeer) {\n const state = users.get(user.userId);\n if (!state) return; // user left\n state.close();\n const pc = await setupPC(state);\n receivePeerConnection({\n pc,\n userId: user.userId,\n initiator: true,\n restart: () => restartInitiator(user),\n });\n await new Promise((resolve) => setTimeout(resolve, 3000));\n await makeOffer(user);\n }\n\n async function setupPC(state: UserState) {\n const now = Date.now();\n if (now - (rtcConfig?.timestamp ?? 0) > 10000) {\n const ice =\n !iceUrl || iceUrl.expiration - now < 2000\n ? await requestIce()\n : iceUrl;\n rtcConfig = await getRtcConfig(ice.url, requestIce);\n }\n state.pc = new RTCPeerConnection(rtcConfig);\n // Send local ICE candidates to this peer\n state.pc.onicecandidate = (ev) => {\n if (!ev.candidate) return;\n state.peer.receive(\"ice\", ev.candidate.toJSON());\n };\n\n state.pc.onconnectionstatechange = () => {\n logLine?.(\"💬\", {\n event: \"pc-state\",\n userId: state.userId,\n state: state.pc?.connectionState,\n });\n };\n\n return state.pc;\n }\n\n async function getPeer(\n peer: IPeer<SigType, SigPayload>,\n forceReset?: boolean,\n ): Promise<UserState> {\n let state = users.get(peer.userId);\n if (!state || forceReset) {\n const newState: UserState = {\n userId: peer.userId,\n pendingRemoteIce: [],\n peer,\n initiateForThisUser: state?.initiateForThisUser ?? false,\n close() {\n this.pc?.close();\n this.pc = undefined;\n },\n };\n\n await setupPC(newState);\n state = newState;\n\n // New user\n users.set(state.userId, state);\n } else if (state) {\n clearTimeout(state.expirationTimeout);\n state.expirationTimeout = 0;\n }\n if (!state.pc || state.pc?.signalingState === \"closed\") {\n await setupPC(state);\n }\n state.peer = peer;\n return state;\n }\n\n async function makeOffer(user: IPeer) {\n // Offer flow: createOffer -> setLocalDescription -> send localDescription\n const state = await getPeer(user);\n const pc = state.pc;\n const offer = await pc?.createOffer();\n await pc?.setLocalDescription(offer);\n user.receive(\"offer\", pc?.localDescription?.toJSON()!);\n }\n\n let icePromiseResolve:\n | undefined\n | ((url: { url: string; expiration: number }) => void);\n async function requestIce() {\n const iceUrl = await new Promise<{ url: string; expiration: number }>(\n (resolve) => {\n icePromiseResolve = resolve;\n sendToServer(\"request-ice\");\n },\n );\n icePromiseResolve = undefined;\n return iceUrl;\n }\n\n const { exitRoom, sendToServer } = enterRoom({\n userId,\n worldId,\n room,\n host,\n logLine,\n workerUrl,\n autoRejoin: true,\n\n onOpen() {\n onRoomReady?.({ room, host });\n resolve();\n },\n onError() {\n console.error(\"onError\");\n reject();\n },\n onClose(ev: Pick<CloseEvent, \"reason\" | \"code\" | \"wasClean\">) {\n onRoomClose?.({ room, host, ev });\n },\n\n // Existing peers initiate to the newcomer\n onPeerJoined(joiningUsers: IPeer<SigType, SigPayload>[]) {\n joiningUsers.forEach(async (user) => {\n const hadState = users.has(user.userId);\n const state = await getPeer(user, true);\n if (!hadState) {\n state.initiateForThisUser = true;\n }\n const pc = state.pc;\n if (!pc) {\n logLine?.(\"👤ℹ️\", \"no pc: \" + user.userId);\n return;\n }\n\n receivePeerConnection({\n pc,\n userId: user.userId,\n initiator: state.initiateForThisUser,\n restart: state.initiateForThisUser\n ? () => restartInitiator(user)\n : () => state.close(),\n });\n if (state.initiateForThisUser) {\n await makeOffer(user);\n }\n });\n },\n\n onPeerLeft(leavingUsers: { userId: string }[]) {\n leavingUsers.forEach(({ userId }) => {\n const state = users.get(userId);\n if (!state) return;\n state.expirationTimeout = setTimeout(\n () => leaveUser(userId),\n peerlessUserExpiration ?? 0,\n );\n });\n },\n\n onIceUrl(url: string, expiration: number) {\n iceUrl = { url, expiration };\n icePromiseResolve?.(iceUrl);\n },\n\n async onMessage(type: SigType, payload: any, from: IPeer) {\n const state = await getPeer(from);\n const pc = state.pc;\n if (!pc) return;\n\n if (type === \"offer\") {\n state.initiateForThisUser = false;\n receivePeerConnection({\n pc,\n userId: from.userId,\n initiator: false,\n restart() {\n // reset PC\n state.close();\n },\n });\n // Responder: set remote offer\n await pc.setRemoteDescription(payload as RTCSessionDescriptionInit);\n\n // Create and send answer\n const answer = await pc.createAnswer();\n await pc.setLocalDescription(answer);\n\n from.receive(\"answer\", pc.localDescription?.toJSON()!);\n\n // Now safe to apply any queued ICE from this peer\n await flushRemoteIce(state);\n return;\n }\n\n if (type === \"answer\") {\n // Initiator: set remote answer\n await pc.setRemoteDescription(payload as RTCSessionDescriptionInit);\n await flushRemoteIce(state);\n return;\n }\n\n if (type === \"ice\") {\n const ice = payload as RTCIceCandidateInit;\n\n // If we don't have remoteDescription yet, queue it\n if (!pc.remoteDescription) {\n state.pendingRemoteIce.push(ice);\n return;\n }\n\n try {\n await pc.addIceCandidate(ice);\n } catch (e) {\n logLine?.(\"⚠️ ERROR\", {\n error: \"add-ice-failed\",\n userId: state.userId,\n detail: String(e),\n });\n }\n return;\n }\n\n if (type === \"broadcast\") {\n onBroadcastMessage?.(payload, from.userId);\n }\n },\n });\n roomsEntered.set(`${host}/room/${room}`, {\n exitRoom,\n room,\n host,\n broadcast: (payload) => {\n sendToServer(\"broadcast\", payload);\n },\n });\n });\n }\n\n return {\n userId,\n enterRoom: enter,\n exitRoom: exit,\n leaveUser,\n broadcast<P extends any>(payload: P) {\n roomsEntered.forEach((room) => room.broadcast(payload));\n },\n end() {\n roomsEntered.forEach(({ exitRoom }) => exitRoom());\n roomsEntered.clear();\n users.forEach(({ userId }) => leaveUser(userId));\n users.clear();\n },\n };\n}\n\n/*\nTurn Token ID\n<CF_TURN_TOKEN_ID>\n\nAPI Token\n<CF_RTC_API_TOKEN>\n\nCURL\ncurl \\\n\t-H \"Authorization: Bearer <CF_RTC_API_TOKEN>\" \\\n\t-H \"Content-Type: application/json\" -d '{\"ttl\": 86400}' \\\n\thttps://rtc.live.cloudflare.com/v1/turn/keys/<CF_TURN_TOKEN_ID>/credentials/generate-ice-servers\n\nJSON\n{\n\t\"iceServers\": [\n {\n \"urls\": [\n \"stun:stun.cloudflare.com:3478\",\n \"turn:turn.cloudflare.com:3478?transport=udp\",\n \"turn:turn.cloudflare.com:3478?transport=tcp\",\n \"turns:turn.cloudflare.com:5349?transport=tcp\"\n ],\n \"username\": \"xxxx\",\n \"credential\": \"yyyy\",\n }\n ]\n}\n\n*/\n",
8
+ "import { EnterRoom, enterRoom } from \"./signal/signal-room\";\nimport {\n SigType,\n SigPayload,\n collectPeerConnections,\n} from \"./webrtc-peer-collector\";\n\ntype UserListener = (\n user: string,\n action: \"join\" | \"leave\",\n users: string[],\n) => void;\n\nexport function enterWorld<\n S extends string | ArrayBufferView = string | ArrayBufferView,\n R extends string | ArrayBufferLike = string | ArrayBufferLike,\n>({\n userId: passedUserId,\n worldId,\n logLine,\n enterRoomFunction = enterRoom,\n peerlessUserExpiration,\n workerUrl,\n onRoomReady,\n onRoomClose,\n dataChannelOptions,\n}: {\n userId?: string;\n worldId: string;\n logLine?: (...obj: any[]) => void;\n enterRoomFunction?: EnterRoom<SigType, SigPayload>;\n peerlessUserExpiration?: number;\n workerUrl?: URL;\n onRoomReady?(info: { host: string; room: string }): void;\n onRoomClose?(info: {\n host: string;\n room: string;\n ev: Pick<CloseEvent, \"reason\" | \"code\" | \"wasClean\">;\n }): void;\n dataChannelOptions?: RTCDataChannelInit;\n}) {\n const userIds = new Set<string>();\n\n const messagesListeners = new Set<(data: R, from: string) => void>();\n\n function createDataChannel(\n pc: RTCPeerConnection,\n peerUserId: string,\n initiator: boolean,\n restart?: () => void,\n ) {\n if (initiator) {\n const dc = pc.createDataChannel(\"data\", dataChannelOptions);\n wireDataChannel(peerUserId, dc, restart);\n dataChannels.set(peerUserId, dc);\n } else {\n function listener(ev: RTCDataChannelEvent) {\n const dc = ev.channel;\n wireDataChannel(peerUserId, dc, restart);\n dataChannels.set(peerUserId, dc);\n }\n pc.addEventListener(\"datachannel\", listener);\n return () => {\n pc.removeEventListener(\"datachannel\", listener);\n };\n }\n }\n\n function conveyMessage(data: any, userId: string) {\n messagesListeners.forEach((listener) => listener(data, userId));\n }\n\n function wireDataChannel(\n userId: string,\n dc: RTCDataChannel,\n restart?: () => void,\n ) {\n dc.onopen = () => {\n logLine?.(\"💬\", { event: \"dc-open\", userId });\n userIds.add(userId);\n userListeners.forEach((listener) =>\n listener(userId, \"join\", [...userIds]),\n );\n };\n const onmessage = ({ data }: MessageEvent) => {\n conveyMessage(data, userId);\n };\n dc.addEventListener(\"message\", onmessage);\n dc.addEventListener(\"close\", () => {\n logLine?.(\"💬\", { event: \"dc-close\", userId });\n userIds.delete(userId);\n userListeners.forEach((listener) =>\n listener(userId, \"leave\", [...userIds]),\n );\n dc.removeEventListener(\"message\", onmessage);\n restart?.();\n });\n dc.onerror = () => logLine?.(\"⚠️ ERROR\", { error: \"dc-error\", userId });\n }\n\n const dataChannels = new Map<string, RTCDataChannel>();\n const userListeners = new Set<UserListener>();\n\n const {\n userId,\n enterRoom,\n exitRoom,\n leaveUser,\n broadcast,\n end: endPeerCollection,\n } = collectPeerConnections({\n userId: passedUserId,\n worldId,\n enterRoomFunction,\n logLine,\n workerUrl,\n peerlessUserExpiration,\n onRoomReady,\n onRoomClose,\n onLeaveUser(userId: string) {\n const dc = dataChannels.get(userId);\n try {\n dc?.close();\n } catch {}\n dataChannels.delete(userId);\n },\n receivePeerConnection({ pc, userId, initiator, restart }) {\n createDataChannel(pc, userId, initiator, restart);\n },\n onBroadcastMessage(payload, from) {\n conveyMessage(payload, from);\n logLine?.(\"📢\", { event: \"broadcast\", userId, data: payload });\n },\n });\n\n function send(data: S, userId?: string) {\n dataChannels.forEach((dataChannel, pUserId) => {\n if (userId && pUserId !== userId) return;\n if (dataChannel.readyState === \"open\") {\n dataChannel.send(data as any);\n }\n });\n }\n\n function removeMessageListener(listener: (data: R, from: string) => void) {\n messagesListeners.delete(listener);\n }\n\n function addMessageListener(listener: (data: R, from: string) => void) {\n messagesListeners.add(listener);\n return () => {\n removeMessageListener(listener);\n };\n }\n\n function removeUserListener(listener: UserListener) {\n userListeners.delete(listener);\n }\n\n function addUserListener(listener: UserListener) {\n userListeners.add(listener);\n return () => {\n removeUserListener(listener);\n };\n }\n\n return {\n userId,\n send,\n broadcast,\n enterRoom,\n exitRoom,\n leaveUser,\n getUsers: () => [...userIds],\n addMessageListener,\n removeMessageListener,\n addUserListener,\n removeUserListener,\n end() {\n dataChannels.forEach((dataChannel) => {\n try {\n dataChannel.close();\n } catch {}\n });\n dataChannels.clear();\n endPeerCollection();\n userListeners.clear();\n userIds.clear();\n },\n };\n}\n"
9
9
  ],
10
- "mappings": "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",
11
- "debugId": "6417898587532A6564756E2164756E21",
10
+ "mappings": "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",
11
+ "debugId": "0F567A29A4525EC964756E2164756E21",
12
12
  "names": []
13
13
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/browser/sample/index.ts"],"names":[],"mappings":"AA6BA,wBAAgB,QAAQ,SAEvB;AAED,wBAAgB,WAAW,eAuC1B;AAED,wBAAgB,UAAU,CAAC,kBAAkB,EAAE,OAAO,cA4HrD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/browser/sample/index.ts"],"names":[],"mappings":"AAkCA,wBAAgB,QAAQ,SAEvB;AAED,wBAAgB,WAAW,eAuC1B;AAED,wBAAgB,UAAU,CAAC,kBAAkB,EAAE,OAAO,cA4HrD"}
@@ -1 +1 @@
1
- {"version":3,"file":"webrtc-peer-collector.d.ts","sourceRoot":"","sources":["../src/browser/webrtc-peer-collector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAa,MAAM,sBAAsB,CAAC;AAE5D,MAAM,MAAM,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,GAAG,aAAa,GAAG,WAAW,CAAC;AAC/E,MAAM,MAAM,UAAU,GAAG,yBAAyB,GAAG,mBAAmB,CAAC;AAmBzE,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,EAAE,YAAY,EACpB,OAAO,EACP,qBAAqB,EACrB,sBAA6B,EAC7B,iBAEC,EACD,iBAAiB,EAAE,SAA8B,EACjD,OAAO,EACP,WAAW,EACX,SAAS,EACT,WAAW,EACX,WAAW,EACX,kBAAkB,GACnB,EAAE;IACD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,gBAAgB,CAAC;IACrC,iBAAiB,CAAC,EAAE,SAAS,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IACnD,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IACjD,SAAS,CAAC,EAAE,GAAG,CAAC;IAChB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,qBAAqB,CAAC,UAAU,EAAE;QAChC,EAAE,EAAE,iBAAiB,CAAC;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,OAAO,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;KACtB,GAAG,IAAI,CAAC;IACT,WAAW,CAAC,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACzD,WAAW,CAAC,CAAC,IAAI,EAAE;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC,CAAC;KACtD,GAAG,IAAI,CAAC;IACT,kBAAkB,CAAC,CAAC,CAAC,SAAS,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACpE;;gCA0EgC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;+BAT/B;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;wBA7BjC,MAAM;cA0RrB,CAAC,2BAAuB,CAAC;;EAUtC"}
1
+ {"version":3,"file":"webrtc-peer-collector.d.ts","sourceRoot":"","sources":["../src/browser/webrtc-peer-collector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAa,MAAM,sBAAsB,CAAC;AAE5D,MAAM,MAAM,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,GAAG,aAAa,GAAG,WAAW,CAAC;AAC/E,MAAM,MAAM,UAAU,GAAG,yBAAyB,GAAG,mBAAmB,CAAC;AAmBzE,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,EAAE,YAAY,EACpB,OAAO,EACP,qBAAqB,EACrB,sBAA6B,EAC7B,iBAEC,EACD,iBAAiB,EAAE,SAA8B,EACjD,OAAO,EACP,WAAW,EACX,SAAS,EACT,WAAW,EACX,WAAW,EACX,kBAAkB,GACnB,EAAE;IACD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,gBAAgB,CAAC;IACrC,iBAAiB,CAAC,EAAE,SAAS,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IACnD,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IACjD,SAAS,CAAC,EAAE,GAAG,CAAC;IAChB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,qBAAqB,CAAC,UAAU,EAAE;QAChC,EAAE,EAAE,iBAAiB,CAAC;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,OAAO,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;KACtB,GAAG,IAAI,CAAC;IACT,WAAW,CAAC,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACzD,WAAW,CAAC,CAAC,IAAI,EAAE;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC,CAAC;KACtD,GAAG,IAAI,CAAC;IACT,kBAAkB,CAAC,CAAC,CAAC,SAAS,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACpE;;gCAgFgC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;+BAT/B;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;wBA7BjC,MAAM;cA0RrB,CAAC,2BAAuB,CAAC;;EAUtC"}
@@ -1,4 +1,4 @@
1
- function v(K){let{userId:j,worldId:N,room:T,host:h,autoRejoin:P=!0,logLine:H}=K,M=!1,O=0,W,E,L=!0,_=new Map,B=`wss://${h}/room/${N}/${T}?userId=${encodeURIComponent(j)}`,q=[],b=0;function A(Q,z,S){if(!W)return H?.("\uD83D\uDC64 ➡️ ❌","no ws available"),!1;let G={type:Q,to:z,payload:S};if(q.push(G),H?.("\uD83D\uDC64 ➡️ \uD83D\uDDA5️",G),clearTimeout(b),M||W.readyState!==WebSocket.OPEN)return H?.("\uD83D\uDC64 ➡️ ❌","Not in opened state: "+W.readyState),!1;return b=setTimeout(()=>{W.send(JSON.stringify(q)),q.length=0}),!0}function k(){if(M)return;W=new WebSocket(B),W.onopen=()=>{if(L)K.onOpen?.(),L=!1;O=0},W.onmessage=(Q)=>{try{let z=JSON.parse(Q.data);(Array.isArray(z)?z:[z]).forEach((G)=>{if(H?.("\uD83D\uDDA5️ ➡️ \uD83D\uDC64",G),G.type==="peer-joined"||G.type==="peer-left")F(G.users);else if(G.type==="ice-server")K.onIceUrl?.(G.url,G.expiration);else if(G.userId)K.onMessage(G.type,G.payload,{userId:G.userId,receive:(X,D)=>A(X,G.userId,D)})})}catch{H?.("⚠️ ERROR",{error:"invalid-json"})}},W.onclose=(Q)=>{let S=[1001,1006,1011,1012,1013].includes(Q.code);if(P&&!M&&S){let G=Math.min(Math.pow(2,O)*1000,30000),X=Math.random()*1000,D=G+X;H?.("\uD83D\uDD04 RECONNECTING",{attempt:O+1,delayMs:Math.round(D)}),O++,E=setTimeout(k,D)}else K.onClose?.({code:Q.code,reason:Q.reason,wasClean:Q.wasClean})},W.onerror=(Q)=>{console.error("WS Error",Q),K.onError?.()}}function F(Q){let z=[],S=[],G=new Set;Q.forEach(({userId:X})=>{if(X===j)return;if(!_.has(X)){let D={userId:X,receive:(x,C)=>A(x,X,C)};_.set(X,D),z.push(D)}G.add(X)});for(let X of _.keys())if(!G.has(X))_.delete(X),S.push({userId:X});if(z.length)K.onPeerJoined(z);if(S.length)K.onPeerLeft(S)}return k(),{sendToServer(Q,z){A(Q,"server",z)},exitRoom:()=>{M=!0,clearTimeout(E),W.close()}}}function g({userId:K,worldId:j,room:N,host:T,autoRejoin:h=!0,onOpen:P,onClose:H,onError:M,onPeerJoined:O,onPeerLeft:W,onIceUrl:E,onMessage:L,logLine:_,workerUrl:B}){if(!B)return console.warn("Warning: enterRoom called without workerUrl; this may cause issues in some environments. You should pass workerUrl explicitly. Use:","https://cdn.jsdelivr.net/npm/@dobuki/hello-worker/dist/signal-room.worker.min.js"),v({userId:K,worldId:j,room:N,host:T,autoRejoin:h,onOpen:P,onClose:H,onError:M,onPeerJoined:O,onPeerLeft:W,onIceUrl:E,onMessage:L});let q=new Worker(B,{type:"module"}),b=!1;function A({userId:F}){return{userId:F,receive:(Q,z)=>{if(b)return!1;return q.postMessage({cmd:"send",toUserId:F,host:T,room:N,type:Q,payload:z}),!0}}}let k=(F)=>{let Q=F.data;if(Q.kind==="open")P?.();else if(Q.kind==="close")q.terminate(),H?.(Q.ev);else if(Q.kind==="error")M?.();else if(Q.kind==="peer-joined")O(Q.users.map((z)=>A({userId:z.userId})));else if(Q.kind==="peer-left")W(Q.users);else if(Q.kind==="ice-server")E?.(Q.url,Q.expiration);else if(Q.kind==="message")L(Q.type,Q.payload,A({userId:Q.fromUserId}));else if(Q.kind==="log")_?.(Q.direction,Q.obj)};return q.addEventListener("message",k),q.postMessage({cmd:"enter",userId:K,worldId:j,room:N,host:T,autoRejoin:h}),{exitRoom:()=>{b=!0,q.removeEventListener("message",k),q.postMessage({cmd:"exit"})},sendToServer:(F,Q)=>{q.postMessage({cmd:"send",toUserId:"server",host:T,room:N,type:F,payload:Q})}}}var p=g;function t({userId:K,worldId:j,receivePeerConnection:N,peerlessUserExpiration:T=5000,fallbackRtcConfig:h={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},enterRoomFunction:P=p,logLine:H,onLeaveUser:M,workerUrl:O,onRoomReady:W,onRoomClose:E,onBroadcastMessage:L}){let _=K??`user-${crypto.randomUUID()}`,B=new Map,q=void 0,b={...h,timestamp:Date.now()},A=new Map;async function k(G){if(G)try{let X=await fetch(G);if(!X.ok)throw Error(`ICE endpoint failed: ${X.status}`);b=await X.json()}catch(X){console.warn("Using fallback rtcConfig:",X)}return b}function F(G){M?.(G);let X=B.get(G);if(!X)return;B.delete(G);try{X.pc?.close()}catch{}}async function Q(G){if(!G.pc?.remoteDescription)return;let X=G.pendingRemoteIce;G.pendingRemoteIce=[];for(let D of X)try{await G.pc.addIceCandidate(D)}catch(x){H?.("⚠️ ERROR",{error:"add-ice-failed",userId:G.userId,detail:String(x)})}}function z({room:G,host:X}){let D=`${X}/room/${G}`,x=A.get(D);if(x)x.exitRoom(),A.delete(D)}function S({room:G,host:X}){return new Promise(async(D,x)=>{async function C(V){let Z=B.get(V.userId);if(!Z)return;Z.close();let Y=await w(Z);N({pc:Y,userId:V.userId,initiator:!0,restart:()=>C(V)}),await new Promise(($)=>setTimeout($,3000)),await U(V)}async function w(V){let Z=Date.now();if(Z-(b?.timestamp??0)>1e4){let Y=!q||q.expiration-Z<2000?await c():q;b=await k(Y.url)}return V.pc=new RTCPeerConnection(b),V.pc.onicecandidate=(Y)=>{if(!Y.candidate)return;V.peer.receive("ice",Y.candidate.toJSON())},V.pc.onconnectionstatechange=()=>{H?.("\uD83D\uDCAC",{event:"pc-state",userId:V.userId,state:V.pc?.connectionState})},V.pc}async function f(V,Z){let Y=B.get(V.userId);if(!Y||Z){let $={userId:V.userId,pendingRemoteIce:[],peer:V,initiateForThisUser:Y?.initiateForThisUser??!1,close(){this.pc?.close(),this.pc=void 0}};await w($),Y=$,B.set(Y.userId,Y)}else if(Y)clearTimeout(Y.expirationTimeout),Y.expirationTimeout=0;if(!Y.pc||Y.pc?.signalingState==="closed")await w(Y);return Y.peer=V,Y}async function U(V){let Y=(await f(V)).pc,$=await Y?.createOffer();await Y?.setLocalDescription($),V.receive("offer",Y?.localDescription?.toJSON())}let y;async function c(){let V=await new Promise((Z)=>{y=Z,I("request-ice")});return y=void 0,V}let{exitRoom:m,sendToServer:I}=P({userId:_,worldId:j,room:G,host:X,logLine:H,workerUrl:O,autoRejoin:!0,onOpen(){W?.({room:G,host:X}),D()},onError(){console.error("onError"),x()},onClose(V){E?.({room:G,host:X,ev:V})},onPeerJoined(V){V.forEach(async(Z)=>{let Y=B.has(Z.userId),$=await f(Z,!0);if(!Y)$.initiateForThisUser=!0;let J=$.pc;if(!J){H?.("\uD83D\uDC64ℹ️","no pc: "+Z.userId);return}if(N({pc:J,userId:Z.userId,initiator:$.initiateForThisUser,restart:$.initiateForThisUser?()=>C(Z):()=>$.close()}),$.initiateForThisUser)await U(Z)})},onPeerLeft(V){V.forEach(({userId:Z})=>{let Y=B.get(Z);if(!Y)return;Y.expirationTimeout=setTimeout(()=>F(Z),T??0)})},onIceUrl(V,Z){q={url:V,expiration:Z},y?.(q)},async onMessage(V,Z,Y){let $=await f(Y),J=$.pc;if(!J)return;if(V==="offer"){$.initiateForThisUser=!1,N({pc:J,userId:Y.userId,initiator:!1,restart(){$.close()}}),await J.setRemoteDescription(Z);let R=await J.createAnswer();await J.setLocalDescription(R),Y.receive("answer",J.localDescription?.toJSON()),await Q($);return}if(V==="answer"){await J.setRemoteDescription(Z),await Q($);return}if(V==="ice"){let R=Z;if(!J.remoteDescription){$.pendingRemoteIce.push(R);return}try{await J.addIceCandidate(R)}catch(u){H?.("⚠️ ERROR",{error:"add-ice-failed",userId:$.userId,detail:String(u)})}return}if(V==="broadcast")L?.(Z,Y.userId)}});A.set(`${X}/room/${G}`,{exitRoom:m,room:G,host:X,broadcast:(V)=>{I("broadcast",V)}})})}return{userId:_,enterRoom:S,exitRoom:z,leaveUser:F,broadcast(G){A.forEach((X)=>X.broadcast(G))},end(){A.forEach(({exitRoom:G})=>G()),A.clear(),B.forEach(({userId:G})=>F(G)),B.clear()}}}export{t as collectPeerConnections};
1
+ function g(K){let{userId:E,worldId:S,room:j,host:P,autoRejoin:R=!0,logLine:q}=K,O=!1,_=0,B,L,k=!0,T=new Map,D=`wss://${P}/room/${S}/${j}?userId=${encodeURIComponent(E)}`,H=[],J=0;function A(Q,z,M){if(!B)return q?.("\uD83D\uDC64 ➡️ ❌","no ws available"),!1;let G={type:Q,to:z,payload:M};if(H.push(G),q?.("\uD83D\uDC64 ➡️ \uD83D\uDDA5️",G),clearTimeout(J),O||B.readyState!==WebSocket.OPEN)return q?.("\uD83D\uDC64 ➡️ ❌","Not in opened state: "+B.readyState),!1;return J=setTimeout(()=>{B.send(JSON.stringify(H)),H.length=0}),!0}function h(){if(O)return;B=new WebSocket(D),B.onopen=()=>{if(k)K.onOpen?.(),k=!1;_=0},B.onmessage=(Q)=>{try{let z=JSON.parse(Q.data);(Array.isArray(z)?z:[z]).forEach((G)=>{if(q?.("\uD83D\uDDA5️ ➡️ \uD83D\uDC64",G),G.type==="peer-joined"||G.type==="peer-left")F(G.users);else if(G.type==="ice-server")K.onIceUrl?.(G.url,G.expiration);else if(G.userId)K.onMessage(G.type,G.payload,{userId:G.userId,receive:(Y,W)=>A(Y,G.userId,W)})})}catch{q?.("⚠️ ERROR",{error:"invalid-json"})}},B.onclose=(Q)=>{let M=[1001,1006,1011,1012,1013].includes(Q.code);if(R&&!O&&M){let G=Math.min(Math.pow(2,_)*1000,30000),Y=Math.random()*1000,W=G+Y;q?.("\uD83D\uDD04 RECONNECTING",{attempt:_+1,delayMs:Math.round(W)}),_++,L=setTimeout(h,W)}else K.onClose?.({code:Q.code,reason:Q.reason,wasClean:Q.wasClean})},B.onerror=(Q)=>{console.error("WS Error",Q),K.onError?.()}}function F(Q){let z=[],M=[],G=new Set;Q.forEach(({userId:Y})=>{if(Y===E)return;if(!T.has(Y)){let W={userId:Y,receive:(N,x)=>A(N,Y,x)};T.set(Y,W),z.push(W)}G.add(Y)});for(let Y of T.keys())if(!G.has(Y))T.delete(Y),M.push({userId:Y});if(z.length)K.onPeerJoined(z);if(M.length)K.onPeerLeft(M)}return h(),{sendToServer(Q,z){A(Q,"server",z)},exitRoom:()=>{O=!0,clearTimeout(L),B.close()}}}function c({userId:K,worldId:E,room:S,host:j,autoRejoin:P=!0,onOpen:R,onClose:q,onError:O,onPeerJoined:_,onPeerLeft:B,onIceUrl:L,onMessage:k,logLine:T,workerUrl:D}){if(!D)return console.warn("Warning: enterRoom called without workerUrl; this may cause issues in some environments. You should pass workerUrl explicitly. Use:","https://cdn.jsdelivr.net/npm/@dobuki/hello-worker/dist/signal-room.worker.min.js"),g({userId:K,worldId:E,room:S,host:j,autoRejoin:P,onOpen:R,onClose:q,onError:O,onPeerJoined:_,onPeerLeft:B,onIceUrl:L,onMessage:k});let H=new Worker(D,{type:"module"}),J=!1;function A({userId:F}){return{userId:F,receive:(Q,z)=>{if(J)return!1;return H.postMessage({cmd:"send",toUserId:F,host:j,room:S,type:Q,payload:z}),!0}}}let h=(F)=>{let Q=F.data;if(Q.kind==="open")R?.();else if(Q.kind==="close")H.terminate(),q?.(Q.ev);else if(Q.kind==="error")O?.();else if(Q.kind==="peer-joined")_(Q.users.map((z)=>A({userId:z.userId})));else if(Q.kind==="peer-left")B(Q.users);else if(Q.kind==="ice-server")L?.(Q.url,Q.expiration);else if(Q.kind==="message")k(Q.type,Q.payload,A({userId:Q.fromUserId}));else if(Q.kind==="log")T?.(Q.direction,Q.obj)};return H.addEventListener("message",h),H.postMessage({cmd:"enter",userId:K,worldId:E,room:S,host:j,autoRejoin:P}),{exitRoom:()=>{J=!0,H.removeEventListener("message",h),H.postMessage({cmd:"exit"})},sendToServer:(F,Q)=>{H.postMessage({cmd:"send",toUserId:"server",host:j,room:S,type:F,payload:Q})}}}var p=c;function t({userId:K,worldId:E,receivePeerConnection:S,peerlessUserExpiration:j=5000,fallbackRtcConfig:P={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},enterRoomFunction:R=p,logLine:q,onLeaveUser:O,workerUrl:_,onRoomReady:B,onRoomClose:L,onBroadcastMessage:k}){let T=K??`user-${crypto.randomUUID()}`,D=new Map,H=void 0,J={...P,timestamp:Date.now()},A=new Map;async function h(G,Y){if(G){let W=3;for(let N=0;N<W;N++){try{let x=await fetch(G);if(!x.ok)throw Error(`ICE endpoint failed: ${x.status}`);return J=await x.json(),J}catch(x){console.warn("Failed fetching iceUrl")}G=(await Y()).url}}return J}function F(G){O?.(G);let Y=D.get(G);if(!Y)return;D.delete(G);try{Y.pc?.close()}catch{}}async function Q(G){if(!G.pc?.remoteDescription)return;let Y=G.pendingRemoteIce;G.pendingRemoteIce=[];for(let W of Y)try{await G.pc.addIceCandidate(W)}catch(N){q?.("⚠️ ERROR",{error:"add-ice-failed",userId:G.userId,detail:String(N)})}}function z({room:G,host:Y}){let W=`${Y}/room/${G}`,N=A.get(W);if(N)N.exitRoom(),A.delete(W)}function M({room:G,host:Y}){return new Promise(async(W,N)=>{async function x(V){let Z=D.get(V.userId);if(!Z)return;Z.close();let X=await w(Z);S({pc:X,userId:V.userId,initiator:!0,restart:()=>x(V)}),await new Promise(($)=>setTimeout($,3000)),await U(V)}async function w(V){let Z=Date.now();if(Z-(J?.timestamp??0)>1e4){let X=!H||H.expiration-Z<2000?await v():H;J=await h(X.url,v)}return V.pc=new RTCPeerConnection(J),V.pc.onicecandidate=(X)=>{if(!X.candidate)return;V.peer.receive("ice",X.candidate.toJSON())},V.pc.onconnectionstatechange=()=>{q?.("\uD83D\uDCAC",{event:"pc-state",userId:V.userId,state:V.pc?.connectionState})},V.pc}async function f(V,Z){let X=D.get(V.userId);if(!X||Z){let $={userId:V.userId,pendingRemoteIce:[],peer:V,initiateForThisUser:X?.initiateForThisUser??!1,close(){this.pc?.close(),this.pc=void 0}};await w($),X=$,D.set(X.userId,X)}else if(X)clearTimeout(X.expirationTimeout),X.expirationTimeout=0;if(!X.pc||X.pc?.signalingState==="closed")await w(X);return X.peer=V,X}async function U(V){let X=(await f(V)).pc,$=await X?.createOffer();await X?.setLocalDescription($),V.receive("offer",X?.localDescription?.toJSON())}let y;async function v(){let V=await new Promise((Z)=>{y=Z,I("request-ice")});return y=void 0,V}let{exitRoom:m,sendToServer:I}=R({userId:T,worldId:E,room:G,host:Y,logLine:q,workerUrl:_,autoRejoin:!0,onOpen(){B?.({room:G,host:Y}),W()},onError(){console.error("onError"),N()},onClose(V){L?.({room:G,host:Y,ev:V})},onPeerJoined(V){V.forEach(async(Z)=>{let X=D.has(Z.userId),$=await f(Z,!0);if(!X)$.initiateForThisUser=!0;let b=$.pc;if(!b){q?.("\uD83D\uDC64ℹ️","no pc: "+Z.userId);return}if(S({pc:b,userId:Z.userId,initiator:$.initiateForThisUser,restart:$.initiateForThisUser?()=>x(Z):()=>$.close()}),$.initiateForThisUser)await U(Z)})},onPeerLeft(V){V.forEach(({userId:Z})=>{let X=D.get(Z);if(!X)return;X.expirationTimeout=setTimeout(()=>F(Z),j??0)})},onIceUrl(V,Z){H={url:V,expiration:Z},y?.(H)},async onMessage(V,Z,X){let $=await f(X),b=$.pc;if(!b)return;if(V==="offer"){$.initiateForThisUser=!1,S({pc:b,userId:X.userId,initiator:!1,restart(){$.close()}}),await b.setRemoteDescription(Z);let C=await b.createAnswer();await b.setLocalDescription(C),X.receive("answer",b.localDescription?.toJSON()),await Q($);return}if(V==="answer"){await b.setRemoteDescription(Z),await Q($);return}if(V==="ice"){let C=Z;if(!b.remoteDescription){$.pendingRemoteIce.push(C);return}try{await b.addIceCandidate(C)}catch(u){q?.("⚠️ ERROR",{error:"add-ice-failed",userId:$.userId,detail:String(u)})}return}if(V==="broadcast")k?.(Z,X.userId)}});A.set(`${Y}/room/${G}`,{exitRoom:m,room:G,host:Y,broadcast:(V)=>{I("broadcast",V)}})})}return{userId:T,enterRoom:M,exitRoom:z,leaveUser:F,broadcast(G){A.forEach((Y)=>Y.broadcast(G))},end(){A.forEach(({exitRoom:G})=>G()),A.clear(),D.forEach(({userId:G})=>F(G)),D.clear()}}}export{t as collectPeerConnections};
2
2
 
3
- //# debugId=B8C169F711088F9E64756E2164756E21
3
+ //# debugId=F6B57658029B03E264756E2164756E21
4
4
  //# sourceMappingURL=webrtc-peer-collector.js.map
@@ -4,9 +4,9 @@
4
4
  "sourcesContent": [
5
5
  "export interface IPeer<T extends string = string, P = any> {\n userId: string;\n receive(type: T, payload: P): boolean;\n}\n\ntype OutMessage = { type: string; to: string; payload: any };\n\n/**\n * enterRoom connects to the signaling room via WebSocket.\n */\nexport function enterRoom<T extends string, P = any>(params: {\n userId: string;\n worldId: string;\n room: string;\n host: string;\n onOpen?: () => void;\n onClose?: (ev: Pick<CloseEvent, \"code\" | \"reason\" | \"wasClean\">) => void;\n onError?: () => void;\n logLine?: (direction: string, obj?: any) => void;\n onPeerJoined(users: IPeer<T, P>[]): void;\n onPeerLeft(users: { userId: string }[]): void;\n onIceUrl?(url: string, expiration: number): void;\n onMessage(type: T, payload: P, from: IPeer<T, P>): void;\n autoRejoin?: boolean;\n}): {\n exitRoom: () => void;\n sendToServer: <P extends any>(type: T, payload?: P) => void;\n} {\n type Message = {\n type: \"peer-joined\" | \"peer-left\" | \"ice-server\" | T;\n userId: string;\n users: { userId: string }[];\n payload: P;\n url: string;\n expiration: number;\n };\n\n const { userId, worldId, room, host, autoRejoin = true, logLine } = params;\n\n let exited = false;\n let retryCount = 0;\n let ws: WebSocket;\n let timeoutId: ReturnType<typeof setTimeout>;\n let initialConnection = true;\n\n const peers = new Map<string, IPeer<T, P>>();\n const wsUrl = `wss://${host}/room/${worldId}/${room}?userId=${encodeURIComponent(\n userId,\n )}`;\n\n // Helper for sending (uses the current ws instance)\n const accumulatedMessages: OutMessage[] = [];\n let timeout: ReturnType<typeof setTimeout> = 0;\n function send(type: string, to: \"server\" | string, payload?: any) {\n if (!ws) {\n logLine?.(\"👤 ➡️ ❌\", \"no ws available\");\n return false;\n }\n const obj: OutMessage = { type, to, payload };\n accumulatedMessages.push(obj);\n logLine?.(\"👤 ➡️ 🖥️\", obj);\n clearTimeout(timeout);\n if (exited || ws.readyState !== WebSocket.OPEN) {\n logLine?.(\"👤 ➡️ ❌\", \"Not in opened state: \" + ws.readyState);\n return false;\n }\n timeout = setTimeout(() => {\n ws.send(JSON.stringify(accumulatedMessages));\n accumulatedMessages.length = 0;\n });\n return true;\n }\n\n function connect() {\n if (exited) return;\n\n ws = new WebSocket(wsUrl);\n\n ws.onopen = () => {\n if (initialConnection) {\n params.onOpen?.();\n initialConnection = false;\n }\n retryCount = 0; // Reset backoff on successful connection\n };\n\n ws.onmessage = (e: MessageEvent) => {\n try {\n const result = JSON.parse(e.data);\n const msgs: Message[] = Array.isArray(result) ? result : [result];\n msgs.forEach((msg) => {\n logLine?.(\"🖥️ ➡️ 👤\", msg);\n if (msg.type === \"peer-joined\" || msg.type === \"peer-left\") {\n updatePeers(msg.users);\n } else if (msg.type === \"ice-server\") {\n params.onIceUrl?.(msg.url, msg.expiration);\n } else if (msg.userId) {\n params.onMessage(msg.type, msg.payload, {\n userId: msg.userId,\n receive: (type: T, payload: P) => send(type, msg.userId, payload),\n });\n }\n });\n } catch {\n logLine?.(\"⚠️ ERROR\", { error: \"invalid-json\" });\n }\n };\n\n ws.onclose = (ev: CloseEvent) => {\n // 1. Check if we should even try to reconnect\n const recoverableCodes = [1001, 1006, 1011, 1012, 1013];\n const isRecoverable = recoverableCodes.includes(ev.code);\n\n if (autoRejoin && !exited && isRecoverable) {\n // 2. Exponential Backoff: 1s, 2s, 4s, 8s... capped at 30s\n const backoff = Math.min(Math.pow(2, retryCount) * 1000, 30000);\n // 3. Add Jitter: +/- 1000ms randomness\n const jitter = Math.random() * 1000;\n const delay = backoff + jitter;\n\n logLine?.(\"🔄 RECONNECTING\", {\n attempt: retryCount + 1,\n delayMs: Math.round(delay),\n });\n\n retryCount++;\n timeoutId = setTimeout(connect, delay);\n } else {\n params.onClose?.({\n code: ev.code,\n reason: ev.reason,\n wasClean: ev.wasClean,\n });\n }\n };\n\n ws.onerror = (ev) => {\n console.error(\"WS Error\", ev);\n params.onError?.();\n };\n }\n\n // Helper for peer tracking (logic from your original code)\n function updatePeers(updatedUsers: { userId: string }[]) {\n const joined: IPeer<T, P>[] = [];\n const left: { userId: string }[] = [];\n const updatedPeerSet = new Set<string>();\n\n updatedUsers.forEach(({ userId: pUserId }) => {\n if (pUserId === userId) return;\n if (!peers.has(pUserId)) {\n const newPeer = {\n userId: pUserId,\n receive: (t: T, p: P) => send(t, pUserId, p),\n };\n peers.set(pUserId, newPeer);\n joined.push(newPeer);\n }\n updatedPeerSet.add(pUserId);\n });\n\n for (const pUserId of peers.keys()) {\n if (!updatedPeerSet.has(pUserId)) {\n peers.delete(pUserId);\n left.push({ userId: pUserId });\n }\n }\n // Notify peer joined first then peer left. (avoid disconnect in case the peer leaving / joining is on the same user).\n if (joined.length) params.onPeerJoined(joined);\n if (left.length) params.onPeerLeft(left);\n }\n\n // Start initial connection\n connect();\n\n return {\n sendToServer(type, payload) {\n send(type, \"server\", payload);\n },\n exitRoom: () => {\n exited = true;\n clearTimeout(timeoutId);\n ws.close();\n },\n };\n}\n",
6
6
  "import type { IPeer } from \"./impl/signal-room.js\";\nimport { enterRoom as baseEnterRoom } from \"./impl/signal-room.js\";\nimport { RoomEvent, WorkerCommand } from \"./signal-room.worker.js\";\n\nexport function enterRoom<T extends string, P = any>({\n userId,\n worldId,\n room,\n host,\n autoRejoin = true,\n onOpen,\n onClose,\n onError,\n onPeerJoined,\n onPeerLeft,\n onIceUrl,\n onMessage,\n logLine,\n workerUrl,\n}: {\n userId: string;\n worldId: string;\n room: string;\n host: string;\n autoRejoin?: boolean;\n onOpen?: () => void;\n onClose?: (ev: Pick<CloseEvent, \"code\" | \"reason\" | \"wasClean\">) => void;\n onError?: () => void;\n onPeerJoined: (users: IPeer<T, P>[]) => void;\n onPeerLeft: (users: { userId: string }[]) => void;\n onIceUrl?(url: string, expiration: number): void;\n onMessage: (type: T, payload: P, from: IPeer<T, P>) => void;\n logLine?: (direction: string, obj?: any) => void;\n\n // Pass the URL to your worker file (bundler will handle it)\n workerUrl?: URL;\n}): {\n exitRoom: () => void;\n sendToServer: <P extends any>(type: T, payload?: P) => void;\n} {\n if (!workerUrl) {\n const CDN_WORKER_URL = `https://cdn.jsdelivr.net/npm/@dobuki/hello-worker/dist/signal-room.worker.min.js`;\n\n console.warn(\n \"Warning: enterRoom called without workerUrl; this may cause issues in some environments. You should pass workerUrl explicitly. Use:\",\n CDN_WORKER_URL,\n );\n return baseEnterRoom<T, P>({\n userId,\n worldId,\n room,\n host,\n autoRejoin,\n onOpen,\n onClose,\n onError,\n onPeerJoined,\n onPeerLeft,\n onIceUrl,\n onMessage,\n });\n }\n const worker = new Worker(workerUrl, { type: \"module\" });\n let exited = false;\n\n function makeUser({ userId }: { userId: string }): IPeer<T, P> {\n return {\n userId,\n receive: (type: T, payload: P) => {\n if (exited) return false;\n worker.postMessage({\n cmd: \"send\",\n toUserId: userId,\n host,\n room,\n type,\n payload,\n } as WorkerCommand);\n return true;\n },\n };\n }\n\n const onWorkerMessage = (e: MessageEvent<RoomEvent<T, P>>) => {\n const ev = e.data;\n\n if (ev.kind === \"open\") onOpen?.();\n else if (ev.kind === \"close\") {\n worker.terminate();\n onClose?.(ev.ev);\n } else if (ev.kind === \"error\") onError?.();\n else if (ev.kind === \"peer-joined\")\n onPeerJoined(ev.users.map((ev) => makeUser({ userId: ev.userId })));\n else if (ev.kind === \"peer-left\") onPeerLeft(ev.users);\n else if (ev.kind === \"ice-server\") onIceUrl?.(ev.url, ev.expiration);\n else if (ev.kind === \"message\")\n onMessage(ev.type, ev.payload, makeUser({ userId: ev.fromUserId }));\n else if (ev.kind === \"log\") logLine?.(ev.direction, ev.obj);\n };\n\n worker.addEventListener(\"message\", onWorkerMessage);\n\n worker.postMessage({\n cmd: \"enter\",\n userId,\n worldId,\n room,\n host,\n autoRejoin,\n } as WorkerCommand);\n\n return {\n exitRoom: () => {\n exited = true;\n worker.removeEventListener(\"message\", onWorkerMessage);\n worker.postMessage({ cmd: \"exit\" } as WorkerCommand);\n },\n sendToServer: <P extends any>(type: T, payload?: P) => {\n worker.postMessage({\n cmd: \"send\",\n toUserId: \"server\",\n host,\n room,\n type,\n payload,\n } as WorkerCommand);\n },\n };\n}\n\nexport type EnterRoom<T extends string, P> = typeof enterRoom<T, P>;\n",
7
- "import { IPeer } from \"./signal/impl/signal-room\";\nimport { EnterRoom, enterRoom } from \"./signal/signal-room\";\n\nexport type SigType = \"offer\" | \"answer\" | \"ice\" | \"request-ice\" | \"broadcast\";\nexport type SigPayload = RTCSessionDescriptionInit | RTCIceCandidateInit;\n\ntype UserState = {\n userId: string;\n pc?: RTCPeerConnection;\n\n // ICE that arrived before we had remoteDescription\n pendingRemoteIce: RTCIceCandidateInit[];\n\n // the signaling \"user\" handle so we can send messages\n peer: IPeer<SigType, SigPayload>;\n\n expirationTimeout?: number;\n initiateForThisUser: boolean;\n close: () => void;\n};\n\nconst DEFAULT_ENTER_ROOM = enterRoom;\n\nexport function collectPeerConnections({\n userId: passedUserId,\n worldId,\n receivePeerConnection,\n peerlessUserExpiration = 5000,\n fallbackRtcConfig = {\n iceServers: [{ urls: \"stun:stun.l.google.com:19302\" }],\n },\n enterRoomFunction: enterRoom = DEFAULT_ENTER_ROOM,\n logLine,\n onLeaveUser,\n workerUrl,\n onRoomReady,\n onRoomClose,\n onBroadcastMessage,\n}: {\n userId?: string;\n worldId: string;\n fallbackRtcConfig?: RTCConfiguration;\n enterRoomFunction?: EnterRoom<SigType, SigPayload>;\n onLeaveUser?: (userId: string) => void;\n logLine?: (direction: string, obj?: any) => void;\n workerUrl?: URL;\n peerlessUserExpiration?: number;\n receivePeerConnection(connection: {\n pc: RTCPeerConnection;\n userId: string;\n initiator: boolean;\n restart?: () => void;\n }): void;\n onRoomReady?(info: { host: string; room: string }): void;\n onRoomClose?(info: {\n host: string;\n room: string;\n ev: Pick<CloseEvent, \"reason\" | \"code\" | \"wasClean\">;\n }): void;\n onBroadcastMessage?<P extends any>(payload: P, from: string): void;\n}) {\n const userId = passedUserId ?? `user-${crypto.randomUUID()}`;\n const users: Map<string, UserState> = new Map();\n let iceUrl: { url: string; expiration: number } | undefined = undefined;\n let rtcConfig: RTCConfiguration & { timestamp: number } = {\n ...fallbackRtcConfig,\n timestamp: Date.now(),\n };\n\n const roomsEntered = new Map<\n string,\n {\n room: string;\n host: string;\n exitRoom: () => void;\n broadcast: <P extends any>(payload: P) => void;\n }\n >();\n\n async function getRtcConfig(\n iceUrl: string,\n ): Promise<RTCConfiguration & { timestamp: number }> {\n if (iceUrl) {\n try {\n const r = await fetch(iceUrl);\n if (!r.ok) throw new Error(`ICE endpoint failed: ${r.status}`);\n rtcConfig = (await r.json()) as RTCConfiguration & {\n timestamp: number;\n };\n } catch (e) {\n console.warn(\"Using fallback rtcConfig:\", e);\n }\n }\n return rtcConfig;\n }\n\n function leaveUser(userId: string) {\n onLeaveUser?.(userId);\n const p = users.get(userId);\n if (!p) return;\n users.delete(userId);\n try {\n p.pc?.close();\n } catch {}\n }\n\n async function flushRemoteIce(state: UserState) {\n if (!state.pc?.remoteDescription) return;\n\n const queued = state.pendingRemoteIce;\n state.pendingRemoteIce = [];\n\n for (const ice of queued) {\n try {\n await state.pc.addIceCandidate(ice);\n } catch (e) {\n logLine?.(\"⚠️ ERROR\", {\n error: \"add-ice-failed\",\n userId: state.userId,\n detail: String(e),\n });\n }\n }\n }\n\n function exit({ room, host }: { room: string; host: string }) {\n const key = `${host}/room/${room}`;\n const session = roomsEntered.get(key);\n if (session) {\n session.exitRoom();\n roomsEntered.delete(key);\n }\n }\n\n function enter({ room, host }: { room: string; host: string }) {\n return new Promise<void>(async (resolve, reject) => {\n async function restartInitiator(user: IPeer) {\n const state = users.get(user.userId);\n if (!state) return; // user left\n state.close();\n const pc = await setupPC(state);\n receivePeerConnection({\n pc,\n userId: user.userId,\n initiator: true,\n restart: () => restartInitiator(user),\n });\n await new Promise((resolve) => setTimeout(resolve, 3000));\n await makeOffer(user);\n }\n\n async function setupPC(state: UserState) {\n const now = Date.now();\n if (now - (rtcConfig?.timestamp ?? 0) > 10000) {\n const ice =\n !iceUrl || iceUrl.expiration - now < 2000\n ? await requestIce()\n : iceUrl;\n rtcConfig = await getRtcConfig(ice.url);\n }\n state.pc = new RTCPeerConnection(rtcConfig);\n // Send local ICE candidates to this peer\n state.pc.onicecandidate = (ev) => {\n if (!ev.candidate) return;\n state.peer.receive(\"ice\", ev.candidate.toJSON());\n };\n\n state.pc.onconnectionstatechange = () => {\n logLine?.(\"💬\", {\n event: \"pc-state\",\n userId: state.userId,\n state: state.pc?.connectionState,\n });\n };\n\n return state.pc;\n }\n\n async function getPeer(\n peer: IPeer<SigType, SigPayload>,\n forceReset?: boolean,\n ): Promise<UserState> {\n let state = users.get(peer.userId);\n if (!state || forceReset) {\n const newState: UserState = {\n userId: peer.userId,\n pendingRemoteIce: [],\n peer,\n initiateForThisUser: state?.initiateForThisUser ?? false,\n close() {\n this.pc?.close();\n this.pc = undefined;\n },\n };\n\n await setupPC(newState);\n state = newState;\n\n // New user\n users.set(state.userId, state);\n } else if (state) {\n clearTimeout(state.expirationTimeout);\n state.expirationTimeout = 0;\n }\n if (!state.pc || state.pc?.signalingState === \"closed\") {\n await setupPC(state);\n }\n state.peer = peer;\n return state;\n }\n\n async function makeOffer(user: IPeer) {\n // Offer flow: createOffer -> setLocalDescription -> send localDescription\n const state = await getPeer(user);\n const pc = state.pc;\n const offer = await pc?.createOffer();\n await pc?.setLocalDescription(offer);\n user.receive(\"offer\", pc?.localDescription?.toJSON()!);\n }\n\n let icePromiseResolve:\n | undefined\n | ((url: { url: string; expiration: number }) => void);\n async function requestIce() {\n const iceUrl = await new Promise<{ url: string; expiration: number }>(\n (resolve) => {\n icePromiseResolve = resolve;\n sendToServer(\"request-ice\");\n },\n );\n icePromiseResolve = undefined;\n return iceUrl;\n }\n\n const { exitRoom, sendToServer } = enterRoom({\n userId,\n worldId,\n room,\n host,\n logLine,\n workerUrl,\n autoRejoin: true,\n\n onOpen() {\n onRoomReady?.({ room, host });\n resolve();\n },\n onError() {\n console.error(\"onError\");\n reject();\n },\n onClose(ev: Pick<CloseEvent, \"reason\" | \"code\" | \"wasClean\">) {\n onRoomClose?.({ room, host, ev });\n },\n\n // Existing peers initiate to the newcomer\n onPeerJoined(joiningUsers: IPeer<SigType, SigPayload>[]) {\n joiningUsers.forEach(async (user) => {\n const hadState = users.has(user.userId);\n const state = await getPeer(user, true);\n if (!hadState) {\n state.initiateForThisUser = true;\n }\n const pc = state.pc;\n if (!pc) {\n logLine?.(\"👤ℹ️\", \"no pc: \" + user.userId);\n return;\n }\n\n receivePeerConnection({\n pc,\n userId: user.userId,\n initiator: state.initiateForThisUser,\n restart: state.initiateForThisUser\n ? () => restartInitiator(user)\n : () => state.close(),\n });\n if (state.initiateForThisUser) {\n await makeOffer(user);\n }\n });\n },\n\n onPeerLeft(leavingUsers: { userId: string }[]) {\n leavingUsers.forEach(({ userId }) => {\n const state = users.get(userId);\n if (!state) return;\n state.expirationTimeout = setTimeout(\n () => leaveUser(userId),\n peerlessUserExpiration ?? 0,\n );\n });\n },\n\n onIceUrl(url: string, expiration: number) {\n iceUrl = { url, expiration };\n icePromiseResolve?.(iceUrl);\n },\n\n async onMessage(type: SigType, payload: any, from: IPeer) {\n const state = await getPeer(from);\n const pc = state.pc;\n if (!pc) return;\n\n if (type === \"offer\") {\n state.initiateForThisUser = false;\n receivePeerConnection({\n pc,\n userId: from.userId,\n initiator: false,\n restart() {\n // reset PC\n state.close();\n },\n });\n // Responder: set remote offer\n await pc.setRemoteDescription(payload as RTCSessionDescriptionInit);\n\n // Create and send answer\n const answer = await pc.createAnswer();\n await pc.setLocalDescription(answer);\n\n from.receive(\"answer\", pc.localDescription?.toJSON()!);\n\n // Now safe to apply any queued ICE from this peer\n await flushRemoteIce(state);\n return;\n }\n\n if (type === \"answer\") {\n // Initiator: set remote answer\n await pc.setRemoteDescription(payload as RTCSessionDescriptionInit);\n await flushRemoteIce(state);\n return;\n }\n\n if (type === \"ice\") {\n const ice = payload as RTCIceCandidateInit;\n\n // If we don't have remoteDescription yet, queue it\n if (!pc.remoteDescription) {\n state.pendingRemoteIce.push(ice);\n return;\n }\n\n try {\n await pc.addIceCandidate(ice);\n } catch (e) {\n logLine?.(\"⚠️ ERROR\", {\n error: \"add-ice-failed\",\n userId: state.userId,\n detail: String(e),\n });\n }\n return;\n }\n\n if (type === \"broadcast\") {\n onBroadcastMessage?.(payload, from.userId);\n }\n },\n });\n roomsEntered.set(`${host}/room/${room}`, {\n exitRoom,\n room,\n host,\n broadcast: (payload) => {\n sendToServer(\"broadcast\", payload);\n },\n });\n });\n }\n\n return {\n userId,\n enterRoom: enter,\n exitRoom: exit,\n leaveUser,\n broadcast<P extends any>(payload: P) {\n roomsEntered.forEach((room) => room.broadcast(payload));\n },\n end() {\n roomsEntered.forEach(({ exitRoom }) => exitRoom());\n roomsEntered.clear();\n users.forEach(({ userId }) => leaveUser(userId));\n users.clear();\n },\n };\n}\n\n/*\nTurn Token ID\n<CF_TURN_TOKEN_ID>\n\nAPI Token\n<CF_RTC_API_TOKEN>\n\nCURL\ncurl \\\n\t-H \"Authorization: Bearer <CF_RTC_API_TOKEN>\" \\\n\t-H \"Content-Type: application/json\" -d '{\"ttl\": 86400}' \\\n\thttps://rtc.live.cloudflare.com/v1/turn/keys/<CF_TURN_TOKEN_ID>/credentials/generate-ice-servers\n\nJSON\n{\n\t\"iceServers\": [\n {\n \"urls\": [\n \"stun:stun.cloudflare.com:3478\",\n \"turn:turn.cloudflare.com:3478?transport=udp\",\n \"turn:turn.cloudflare.com:3478?transport=tcp\",\n \"turns:turn.cloudflare.com:5349?transport=tcp\"\n ],\n \"username\": \"xxxx\",\n \"credential\": \"yyyy\",\n }\n ]\n}\n\n*/\n"
7
+ "import { IPeer } from \"./signal/impl/signal-room\";\nimport { EnterRoom, enterRoom } from \"./signal/signal-room\";\n\nexport type SigType = \"offer\" | \"answer\" | \"ice\" | \"request-ice\" | \"broadcast\";\nexport type SigPayload = RTCSessionDescriptionInit | RTCIceCandidateInit;\n\ntype UserState = {\n userId: string;\n pc?: RTCPeerConnection;\n\n // ICE that arrived before we had remoteDescription\n pendingRemoteIce: RTCIceCandidateInit[];\n\n // the signaling \"user\" handle so we can send messages\n peer: IPeer<SigType, SigPayload>;\n\n expirationTimeout?: number;\n initiateForThisUser: boolean;\n close: () => void;\n};\n\nconst DEFAULT_ENTER_ROOM = enterRoom;\n\nexport function collectPeerConnections({\n userId: passedUserId,\n worldId,\n receivePeerConnection,\n peerlessUserExpiration = 5000,\n fallbackRtcConfig = {\n iceServers: [{ urls: \"stun:stun.l.google.com:19302\" }],\n },\n enterRoomFunction: enterRoom = DEFAULT_ENTER_ROOM,\n logLine,\n onLeaveUser,\n workerUrl,\n onRoomReady,\n onRoomClose,\n onBroadcastMessage,\n}: {\n userId?: string;\n worldId: string;\n fallbackRtcConfig?: RTCConfiguration;\n enterRoomFunction?: EnterRoom<SigType, SigPayload>;\n onLeaveUser?: (userId: string) => void;\n logLine?: (direction: string, obj?: any) => void;\n workerUrl?: URL;\n peerlessUserExpiration?: number;\n receivePeerConnection(connection: {\n pc: RTCPeerConnection;\n userId: string;\n initiator: boolean;\n restart?: () => void;\n }): void;\n onRoomReady?(info: { host: string; room: string }): void;\n onRoomClose?(info: {\n host: string;\n room: string;\n ev: Pick<CloseEvent, \"reason\" | \"code\" | \"wasClean\">;\n }): void;\n onBroadcastMessage?<P extends any>(payload: P, from: string): void;\n}) {\n const userId = passedUserId ?? `user-${crypto.randomUUID()}`;\n const users: Map<string, UserState> = new Map();\n let iceUrl: { url: string; expiration: number } | undefined = undefined;\n let rtcConfig: RTCConfiguration & { timestamp: number } = {\n ...fallbackRtcConfig,\n timestamp: Date.now(),\n };\n\n const roomsEntered = new Map<\n string,\n {\n room: string;\n host: string;\n exitRoom: () => void;\n broadcast: <P extends any>(payload: P) => void;\n }\n >();\n\n async function getRtcConfig(\n iceUrl: string,\n retryIce: () => Promise<{ url: string }>,\n ): Promise<RTCConfiguration & { timestamp: number }> {\n if (iceUrl) {\n let retries = 3;\n for (let r = 0; r < retries; r++) {\n try {\n const r = await fetch(iceUrl);\n if (!r.ok) throw new Error(`ICE endpoint failed: ${r.status}`);\n rtcConfig = (await r.json()) as RTCConfiguration & {\n timestamp: number;\n };\n return rtcConfig;\n } catch (e) {\n console.warn(\"Failed fetching iceUrl\");\n }\n iceUrl = (await retryIce()).url;\n }\n }\n return rtcConfig;\n }\n\n function leaveUser(userId: string) {\n onLeaveUser?.(userId);\n const p = users.get(userId);\n if (!p) return;\n users.delete(userId);\n try {\n p.pc?.close();\n } catch {}\n }\n\n async function flushRemoteIce(state: UserState) {\n if (!state.pc?.remoteDescription) return;\n\n const queued = state.pendingRemoteIce;\n state.pendingRemoteIce = [];\n\n for (const ice of queued) {\n try {\n await state.pc.addIceCandidate(ice);\n } catch (e) {\n logLine?.(\"⚠️ ERROR\", {\n error: \"add-ice-failed\",\n userId: state.userId,\n detail: String(e),\n });\n }\n }\n }\n\n function exit({ room, host }: { room: string; host: string }) {\n const key = `${host}/room/${room}`;\n const session = roomsEntered.get(key);\n if (session) {\n session.exitRoom();\n roomsEntered.delete(key);\n }\n }\n\n function enter({ room, host }: { room: string; host: string }) {\n return new Promise<void>(async (resolve, reject) => {\n async function restartInitiator(user: IPeer) {\n const state = users.get(user.userId);\n if (!state) return; // user left\n state.close();\n const pc = await setupPC(state);\n receivePeerConnection({\n pc,\n userId: user.userId,\n initiator: true,\n restart: () => restartInitiator(user),\n });\n await new Promise((resolve) => setTimeout(resolve, 3000));\n await makeOffer(user);\n }\n\n async function setupPC(state: UserState) {\n const now = Date.now();\n if (now - (rtcConfig?.timestamp ?? 0) > 10000) {\n const ice =\n !iceUrl || iceUrl.expiration - now < 2000\n ? await requestIce()\n : iceUrl;\n rtcConfig = await getRtcConfig(ice.url, requestIce);\n }\n state.pc = new RTCPeerConnection(rtcConfig);\n // Send local ICE candidates to this peer\n state.pc.onicecandidate = (ev) => {\n if (!ev.candidate) return;\n state.peer.receive(\"ice\", ev.candidate.toJSON());\n };\n\n state.pc.onconnectionstatechange = () => {\n logLine?.(\"💬\", {\n event: \"pc-state\",\n userId: state.userId,\n state: state.pc?.connectionState,\n });\n };\n\n return state.pc;\n }\n\n async function getPeer(\n peer: IPeer<SigType, SigPayload>,\n forceReset?: boolean,\n ): Promise<UserState> {\n let state = users.get(peer.userId);\n if (!state || forceReset) {\n const newState: UserState = {\n userId: peer.userId,\n pendingRemoteIce: [],\n peer,\n initiateForThisUser: state?.initiateForThisUser ?? false,\n close() {\n this.pc?.close();\n this.pc = undefined;\n },\n };\n\n await setupPC(newState);\n state = newState;\n\n // New user\n users.set(state.userId, state);\n } else if (state) {\n clearTimeout(state.expirationTimeout);\n state.expirationTimeout = 0;\n }\n if (!state.pc || state.pc?.signalingState === \"closed\") {\n await setupPC(state);\n }\n state.peer = peer;\n return state;\n }\n\n async function makeOffer(user: IPeer) {\n // Offer flow: createOffer -> setLocalDescription -> send localDescription\n const state = await getPeer(user);\n const pc = state.pc;\n const offer = await pc?.createOffer();\n await pc?.setLocalDescription(offer);\n user.receive(\"offer\", pc?.localDescription?.toJSON()!);\n }\n\n let icePromiseResolve:\n | undefined\n | ((url: { url: string; expiration: number }) => void);\n async function requestIce() {\n const iceUrl = await new Promise<{ url: string; expiration: number }>(\n (resolve) => {\n icePromiseResolve = resolve;\n sendToServer(\"request-ice\");\n },\n );\n icePromiseResolve = undefined;\n return iceUrl;\n }\n\n const { exitRoom, sendToServer } = enterRoom({\n userId,\n worldId,\n room,\n host,\n logLine,\n workerUrl,\n autoRejoin: true,\n\n onOpen() {\n onRoomReady?.({ room, host });\n resolve();\n },\n onError() {\n console.error(\"onError\");\n reject();\n },\n onClose(ev: Pick<CloseEvent, \"reason\" | \"code\" | \"wasClean\">) {\n onRoomClose?.({ room, host, ev });\n },\n\n // Existing peers initiate to the newcomer\n onPeerJoined(joiningUsers: IPeer<SigType, SigPayload>[]) {\n joiningUsers.forEach(async (user) => {\n const hadState = users.has(user.userId);\n const state = await getPeer(user, true);\n if (!hadState) {\n state.initiateForThisUser = true;\n }\n const pc = state.pc;\n if (!pc) {\n logLine?.(\"👤ℹ️\", \"no pc: \" + user.userId);\n return;\n }\n\n receivePeerConnection({\n pc,\n userId: user.userId,\n initiator: state.initiateForThisUser,\n restart: state.initiateForThisUser\n ? () => restartInitiator(user)\n : () => state.close(),\n });\n if (state.initiateForThisUser) {\n await makeOffer(user);\n }\n });\n },\n\n onPeerLeft(leavingUsers: { userId: string }[]) {\n leavingUsers.forEach(({ userId }) => {\n const state = users.get(userId);\n if (!state) return;\n state.expirationTimeout = setTimeout(\n () => leaveUser(userId),\n peerlessUserExpiration ?? 0,\n );\n });\n },\n\n onIceUrl(url: string, expiration: number) {\n iceUrl = { url, expiration };\n icePromiseResolve?.(iceUrl);\n },\n\n async onMessage(type: SigType, payload: any, from: IPeer) {\n const state = await getPeer(from);\n const pc = state.pc;\n if (!pc) return;\n\n if (type === \"offer\") {\n state.initiateForThisUser = false;\n receivePeerConnection({\n pc,\n userId: from.userId,\n initiator: false,\n restart() {\n // reset PC\n state.close();\n },\n });\n // Responder: set remote offer\n await pc.setRemoteDescription(payload as RTCSessionDescriptionInit);\n\n // Create and send answer\n const answer = await pc.createAnswer();\n await pc.setLocalDescription(answer);\n\n from.receive(\"answer\", pc.localDescription?.toJSON()!);\n\n // Now safe to apply any queued ICE from this peer\n await flushRemoteIce(state);\n return;\n }\n\n if (type === \"answer\") {\n // Initiator: set remote answer\n await pc.setRemoteDescription(payload as RTCSessionDescriptionInit);\n await flushRemoteIce(state);\n return;\n }\n\n if (type === \"ice\") {\n const ice = payload as RTCIceCandidateInit;\n\n // If we don't have remoteDescription yet, queue it\n if (!pc.remoteDescription) {\n state.pendingRemoteIce.push(ice);\n return;\n }\n\n try {\n await pc.addIceCandidate(ice);\n } catch (e) {\n logLine?.(\"⚠️ ERROR\", {\n error: \"add-ice-failed\",\n userId: state.userId,\n detail: String(e),\n });\n }\n return;\n }\n\n if (type === \"broadcast\") {\n onBroadcastMessage?.(payload, from.userId);\n }\n },\n });\n roomsEntered.set(`${host}/room/${room}`, {\n exitRoom,\n room,\n host,\n broadcast: (payload) => {\n sendToServer(\"broadcast\", payload);\n },\n });\n });\n }\n\n return {\n userId,\n enterRoom: enter,\n exitRoom: exit,\n leaveUser,\n broadcast<P extends any>(payload: P) {\n roomsEntered.forEach((room) => room.broadcast(payload));\n },\n end() {\n roomsEntered.forEach(({ exitRoom }) => exitRoom());\n roomsEntered.clear();\n users.forEach(({ userId }) => leaveUser(userId));\n users.clear();\n },\n };\n}\n\n/*\nTurn Token ID\n<CF_TURN_TOKEN_ID>\n\nAPI Token\n<CF_RTC_API_TOKEN>\n\nCURL\ncurl \\\n\t-H \"Authorization: Bearer <CF_RTC_API_TOKEN>\" \\\n\t-H \"Content-Type: application/json\" -d '{\"ttl\": 86400}' \\\n\thttps://rtc.live.cloudflare.com/v1/turn/keys/<CF_TURN_TOKEN_ID>/credentials/generate-ice-servers\n\nJSON\n{\n\t\"iceServers\": [\n {\n \"urls\": [\n \"stun:stun.cloudflare.com:3478\",\n \"turn:turn.cloudflare.com:3478?transport=udp\",\n \"turn:turn.cloudflare.com:3478?transport=tcp\",\n \"turns:turn.cloudflare.com:5349?transport=tcp\"\n ],\n \"username\": \"xxxx\",\n \"credential\": \"yyyy\",\n }\n ]\n}\n\n*/\n"
8
8
  ],
9
- "mappings": "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",
10
- "debugId": "B8C169F711088F9E64756E2164756E21",
9
+ "mappings": "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",
10
+ "debugId": "F6B57658029B03E264756E2164756E21",
11
11
  "names": []
12
12
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dobuki/hello-worker",
3
- "version": "1.0.73",
3
+ "version": "1.0.75",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",