@dobuki/hello-worker 1.0.24 → 1.0.26

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.
@@ -1,12 +1,22 @@
1
1
  import { EnterRoom } from "./signal-room";
2
2
  import { SigType, SigPayload } from "./webrtc-peer-collector";
3
3
  type UserListener = (user: string, action: "join" | "leave", users: string[]) => void;
4
- export declare function enterWorld({ appId, logLine, enterRoomFunction, peerlessUserExpiration, workerUrl, }: {
4
+ export declare function enterWorld({ appId, logLine, enterRoomFunction, peerlessUserExpiration, workerUrl, onRoomReady, onRoomClose, dataChannelOptions, }: {
5
5
  appId: string;
6
6
  logLine?: (direction: string, obj?: any) => void;
7
7
  enterRoomFunction?: EnterRoom<SigType, SigPayload>;
8
8
  peerlessUserExpiration?: number;
9
9
  workerUrl?: URL;
10
+ onRoomReady?(info: {
11
+ host: string;
12
+ room: string;
13
+ }): void;
14
+ onRoomClose?(info: {
15
+ host: string;
16
+ room: string;
17
+ ev: Pick<CloseEvent, "reason" | "code" | "wasClean">;
18
+ }): void;
19
+ dataChannelOptions?: RTCDataChannelInit;
10
20
  }): {
11
21
  userId: string;
12
22
  send: (data: any, userId?: string) => void;
@@ -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,eAAe,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,UAAU,EAA0B,MAAM,yBAAyB,CAAC;AAEtF,KAAK,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;AAEpF,wBAAgB,UAAU,CAAC,EACzB,KAAK,EAAE,OAAuB,EAAE,iBAA6B,EAAE,sBAAsB,EAAE,SAAS,GACjG,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,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;CACjB;;iBAyDqB,GAAG,WAAW,MAAM;;;;;;;;;;;mCAWF,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI;sCAJ9B,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI;gCAevC,YAAY;mCAJT,YAAY;;EAgCnD"}
1
+ {"version":3,"file":"enter-world.d.ts","sourceRoot":"","sources":["../src/browser/enter-world.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAa,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,UAAU,EAA0B,MAAM,yBAAyB,CAAC;AAEtF,KAAK,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;AAEpF,wBAAgB,UAAU,CAAC,EACzB,KAAK,EAAE,OAAuB,EAAE,iBAA6B,EAAE,sBAAsB,EAAE,SAAS,EAChG,WAAW,EACX,WAAW,EACX,kBAAkB,GACnB,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,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;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,GAAC,MAAM,GAAC,UAAU,CAAC,CAAA;KAAE,GAAG,IAAI,CAAC;IAC3G,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;CACzC;;iBA2DqB,GAAG,WAAW,MAAM;;;;;;;;;;;mCAWF,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI;sCAJ9B,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI;gCAevC,YAAY;mCAJT,YAAY;;EAgCnD"}
@@ -1,4 +1,4 @@
1
- function E($){let{userId:A,appId:f,room:h,host:N,autoRejoin:X=!0,logLine:b}=$,W=!1,q=0,G,Z,S=!0,_=new Map,Y=`wss://${N}/room/${f}/${h}?userId=${encodeURIComponent(A)}`;function J(){if(W)return;G=new WebSocket(Y),G.onopen=()=>{if(S)$.onOpen?.(),S=!1;q=0},G.onmessage=(D)=>{try{let c=JSON.parse(D.data);if(b?.("\uD83D\uDDA5️ ➡️ \uD83D\uDC64",c),c.type==="peer-joined"||c.type==="peer-left")B(c.users);else if(c.peerId&&c.userId)$.onMessage(c.type,c.payload,{userId:c.userId,peerId:c.peerId,receive:(F,V)=>M(F,c.peerId,V)})}catch{b?.("⚠️ ERROR",{error:"invalid-json"})}},G.onclose=(D)=>{let F=[1001,1006,1011,1012,1013].includes(D.code);if(X&&!W&&F){let V=Math.min(Math.pow(2,q)*1000,30000),T=Math.random()*1000,z=V+T;b?.("\uD83D\uDD04 RECONNECTING",{attempt:q+1,delayMs:Math.round(z)}),q++,Z=setTimeout(J,z)}else $.onClose?.({code:D.code,reason:D.reason,wasClean:D.wasClean})},G.onerror=()=>$.onError?.()}function M(D,c,F){if(W||G.readyState!==WebSocket.OPEN)return!1;let V={type:D,to:c,payload:F};return G.send(JSON.stringify(V)),b?.("\uD83D\uDC64 ➡️ \uD83D\uDDA5️",V),!0}function B(D){let c=[],F=[],V=new Set;D.forEach(({userId:T,peerId:z})=>{if(T===A)return;if(!_.has(z)){let H={userId:T,peerId:z,receive:(K,Q)=>M(K,z,Q)};_.set(z,H),c.push(H)}V.add(z)});for(let[T,z]of _.entries())if(!V.has(T))_.delete(T),F.push({peerId:T,userId:z.userId});if(c.length)$.onPeerJoined(c);if(F.length)$.onPeerLeft(F)}return J(),{exitRoom:()=>{W=!0,clearTimeout(Z),G.close()}}}function R({userId:$,appId:A,room:f,host:h,autoRejoin:N=!0,onOpen:X,onClose:b,onError:W,onPeerJoined:q,onPeerLeft:G,onMessage:Z,logLine:S,workerUrl:_}){if(!_)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"),E({userId:$,appId:A,room:f,host:h,autoRejoin:N,onOpen:X,onClose:b,onError:W,onPeerJoined:q,onPeerLeft:G,onMessage:Z});let Y=new Worker(_,{type:"module"}),J=!1;function M({userId:D,peerId:c}){return{userId:D,peerId:c,receive:(F,V)=>{if(J)return!1;return Y.postMessage({cmd:"send",toPeerId:c,type:F,payload:V}),!0}}}let B=(D)=>{let c=D.data;if(c.kind==="open")X?.();else if(c.kind==="close")Y.terminate(),b?.(c.ev);else if(c.kind==="error")W?.();else if(c.kind==="peer-joined")q(c.users.map((F)=>M({userId:F.userId,peerId:F.peerId})));else if(c.kind==="peer-left")G(c.users);else if(c.kind==="message")Z(c.type,c.payload,M({userId:c.fromUserId,peerId:c.fromPeerId}));else if(c.kind==="log")S?.(c.direction,c.obj)};return Y.addEventListener("message",B),Y.postMessage({cmd:"enter",userId:$,appId:A,room:f,host:h,autoRejoin:N}),{exitRoom:()=>{J=!0,Y.removeEventListener("message",B),Y.postMessage({cmd:"exit"})}}}var j=R;function x({appId:$,receivePeerConnection:A,peerlessUserExpiration:f,rtcConfig:h={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},enterRoomFunction:N=j,logLine:X=console.debug,onLeaveUser:b,workerUrl:W}){let q=`user-${crypto.randomUUID()}`,G=new Map;function Z(B){let D=G.get(B.userId),c=!1;if(!D){let F={userId:B.userId,pc:new RTCPeerConnection(h),pendingRemoteIce:[],peers:new Map};F.peers.set(B.peerId,B),G.set(B.userId,F),F.pc.onicecandidate=(V)=>{if(!V.candidate)return;for(let T of F.peers.values())if(T.receive("ice",V.candidate.toJSON()))break},F.pc.onconnectionstatechange=()=>{X("\uD83D\uDCAC",{event:"pc-state",userId:F.userId,state:F.pc.connectionState})},D=F,G.set(D.userId,D),c=!0}else if(D)clearTimeout(D.expirationTimeout),D.expirationTimeout=0,D.peers.set(B.peerId,B);return[D,c]}function S(B){b?.(B);let D=G.get(B);if(!D)return;try{D.pc.close()}catch{}G.delete(B)}async function _(B){if(!B.pc.remoteDescription)return;let D=B.pendingRemoteIce;B.pendingRemoteIce=[];for(let c of D)try{await B.pc.addIceCandidate(c)}catch(F){X("⚠️ ERROR",{error:"add-ice-failed",userId:B.userId,detail:String(F)})}}let Y=new Map;function J({room:B,host:D}){let c=`${D}/room/${B}`,F=Y.get(c);if(F)F.exitRoom(),Y.delete(c)}function M({room:B,host:D}){return new Promise((c,F)=>{async function V(z){let[H]=Z(z),K=H.pc,Q=await K.createOffer();await K.setLocalDescription(Q),z.receive("offer",K.localDescription?.toJSON())}let{exitRoom:T}=N({userId:q,appId:$,room:B,host:D,logLine:X,workerUrl:W,autoRejoin:!0,onOpen(){console.log("onOpen"),c()},onError(){console.log("onError"),F()},onClose(z){console.log("onClose",z)},onPeerJoined(z){z.forEach((H)=>{let[K,Q]=Z(H);if(!Q)return;let C=K.pc;A({pc:C,userId:H.userId,initiator:!0}),V(H)})},onPeerLeft(z){z.forEach(({userId:H,peerId:K})=>{let Q=G.get(H);if(!Q)return;if(Q.peers.delete(K),!Q.peers.size)Q.expirationTimeout=setTimeout(()=>S(H),f??0)})},async onMessage(z,H,K){let[Q]=Z(K),C=Q.pc;if(z==="offer"){A({pc:C,userId:K.userId,initiator:!1}),await C.setRemoteDescription(H);let O=await C.createAnswer();await C.setLocalDescription(O),K.receive("answer",C.localDescription?.toJSON()),await _(Q);return}if(z==="answer"){await C.setRemoteDescription(H),await _(Q);return}if(z==="ice"){let O=H;if(!C.remoteDescription){Q.pendingRemoteIce.push(O);return}try{await C.addIceCandidate(O)}catch(L){X("⚠️ ERROR",{error:"add-ice-failed",userId:Q.userId,detail:String(L)})}return}}});Y.set(`${D}/room/${B}`,{exitRoom:T,room:B,host:D})})}return{userId:q,enterRoom:M,exitRoom:J,leaveUser:S,end(){Y.forEach(({exitRoom:B})=>B()),Y.clear(),G.forEach(({userId:B})=>S(B)),G.clear()}}}function u({appId:$,logLine:A=console.debug,enterRoomFunction:f=R,peerlessUserExpiration:h,workerUrl:N}){let X=[],b={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},W=new Set;function q(T,z){z.onopen=()=>{A("\uD83D\uDCAC",{event:"dc-open",userId:T}),X.push(T),Z.forEach((H)=>H(T,"join",X))},z.onmessage=({data:H})=>{W.forEach((K)=>K(H,T)),A("\uD83D\uDCAC",{event:"dc-message",userId:T,data:H})},z.onclose=()=>{A("\uD83D\uDCAC",{event:"dc-close",userId:T}),X.splice(X.indexOf(T),1),Z.forEach((H)=>H(T,"leave",X))},z.onerror=()=>A("⚠️ ERROR",{error:"dc-error",userId:T})}let G=new Map,Z=new Set,{userId:S,enterRoom:_,exitRoom:Y,leaveUser:J,end:M}=x({appId:$,rtcConfig:b,enterRoomFunction:f,logLine:A,workerUrl:N,peerlessUserExpiration:h,onLeaveUser(T){let z=G.get(T);try{z?.close()}catch{}G.delete(T)},receivePeerConnection({pc:T,userId:z,initiator:H}){if(H){let K=T.createDataChannel("data");q(z,K),G.set(z,K)}else T.ondatachannel=(K)=>{let Q=K.channel;q(z,Q),G.set(z,Q),T.ondatachannel=null}}});function B(T,z){G.forEach((H,K)=>{if(z&&K!==z)return;if(H.readyState==="open")H.send(T)})}function D(T){W.delete(T)}function c(T){return W.add(T),()=>{D(T)}}function F(T){Z.delete(T)}function V(T){return Z.add(T),()=>{F(T)}}return{userId:S,send:B,enterRoom:_,exitRoom:Y,leaveUser:J,getUsers:()=>X,addMessageListener:c,removeMessageListener:D,addUserListener:V,removeUserListener:F,end(){G.forEach((T)=>{try{T.close()}catch{}}),G.clear(),M(),Z.clear(),X.length=0}}}export{u as enterWorld};
1
+ function L($){let{userId:A,appId:M,room:O,host:h,autoRejoin:b=!0,logLine:f}=$,S=!1,X=0,Z,_,Y=!0,H=new Map,Q=`wss://${h}/room/${M}/${O}?userId=${encodeURIComponent(A)}`;function J(){if(S)return;Z=new WebSocket(Q),Z.onopen=()=>{if(Y)$.onOpen?.(),Y=!1;X=0},Z.onmessage=(K)=>{try{let c=JSON.parse(K.data);if(f?.("\uD83D\uDDA5️ ➡️ \uD83D\uDC64",c),c.type==="peer-joined"||c.type==="peer-left")E(c.users);else if(c.peerId&&c.userId)$.onMessage(c.type,c.payload,{userId:c.userId,peerId:c.peerId,receive:(D,C)=>W(D,c.peerId,C)})}catch{f?.("⚠️ ERROR",{error:"invalid-json"})}},Z.onclose=(K)=>{let D=[1001,1006,1011,1012,1013].includes(K.code);if(b&&!S&&D){let C=Math.min(Math.pow(2,X)*1000,30000),B=Math.random()*1000,V=C+B;f?.("\uD83D\uDD04 RECONNECTING",{attempt:X+1,delayMs:Math.round(V)}),X++,_=setTimeout(J,V)}else $.onClose?.({code:K.code,reason:K.reason,wasClean:K.wasClean})},Z.onerror=()=>$.onError?.()}function W(K,c,D){if(S||Z.readyState!==WebSocket.OPEN)return!1;let C={type:K,to:c,payload:D};return Z.send(JSON.stringify(C)),f?.("\uD83D\uDC64 ➡️ \uD83D\uDDA5️",C),!0}function E(K){let c=[],D=[],C=new Set;K.forEach(({userId:B,peerId:V})=>{if(B===A)return;if(!H.has(V)){let N={userId:B,peerId:V,receive:(T,z)=>W(T,V,z)};H.set(V,N),c.push(N)}C.add(V)});for(let[B,V]of H.entries())if(!C.has(B))H.delete(B),D.push({peerId:B,userId:V.userId});if(c.length)$.onPeerJoined(c);if(D.length)$.onPeerLeft(D)}return J(),{exitRoom:()=>{S=!0,clearTimeout(_),Z.close()}}}function x({userId:$,appId:A,room:M,host:O,autoRejoin:h=!0,onOpen:b,onClose:f,onError:S,onPeerJoined:X,onPeerLeft:Z,onMessage:_,logLine:Y,workerUrl:H}){if(!H)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"),L({userId:$,appId:A,room:M,host:O,autoRejoin:h,onOpen:b,onClose:f,onError:S,onPeerJoined:X,onPeerLeft:Z,onMessage:_});let Q=new Worker(H,{type:"module"}),J=!1;function W({userId:K,peerId:c}){return{userId:K,peerId:c,receive:(D,C)=>{if(J)return!1;return Q.postMessage({cmd:"send",toPeerId:c,type:D,payload:C}),!0}}}let E=(K)=>{let c=K.data;if(c.kind==="open")b?.();else if(c.kind==="close")Q.terminate(),f?.(c.ev);else if(c.kind==="error")S?.();else if(c.kind==="peer-joined")X(c.users.map((D)=>W({userId:D.userId,peerId:D.peerId})));else if(c.kind==="peer-left")Z(c.users);else if(c.kind==="message")_(c.type,c.payload,W({userId:c.fromUserId,peerId:c.fromPeerId}));else if(c.kind==="log")Y?.(c.direction,c.obj)};return Q.addEventListener("message",E),Q.postMessage({cmd:"enter",userId:$,appId:A,room:M,host:O,autoRejoin:h}),{exitRoom:()=>{J=!0,Q.removeEventListener("message",E),Q.postMessage({cmd:"exit"})}}}var j=x;function n({appId:$,receivePeerConnection:A,peerlessUserExpiration:M,rtcConfig:O={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},enterRoomFunction:h=j,logLine:b=console.debug,onLeaveUser:f,workerUrl:S,onRoomReady:X,onRoomClose:Z}){let _=`user-${crypto.randomUUID()}`,Y=new Map;function H(c){let D=Y.get(c.userId),C=!1;if(!D){let B={userId:c.userId,pc:new RTCPeerConnection(O),pendingRemoteIce:[],peers:new Map};B.peers.set(c.peerId,c),Y.set(c.userId,B),B.pc.onicecandidate=(V)=>{if(!V.candidate)return;for(let N of B.peers.values())if(N.receive("ice",V.candidate.toJSON()))break},B.pc.onconnectionstatechange=()=>{b("\uD83D\uDCAC",{event:"pc-state",userId:B.userId,state:B.pc.connectionState})},D=B,Y.set(D.userId,D),C=!0}else if(D)clearTimeout(D.expirationTimeout),D.expirationTimeout=0,D.peers.set(c.peerId,c);return[D,C]}function Q(c){f?.(c);let D=Y.get(c);if(!D)return;try{D.pc.close()}catch{}Y.delete(c)}async function J(c){if(!c.pc.remoteDescription)return;let D=c.pendingRemoteIce;c.pendingRemoteIce=[];for(let C of D)try{await c.pc.addIceCandidate(C)}catch(B){b("⚠️ ERROR",{error:"add-ice-failed",userId:c.userId,detail:String(B)})}}let W=new Map;function E({room:c,host:D}){let C=`${D}/room/${c}`,B=W.get(C);if(B)B.exitRoom(),W.delete(C)}function K({room:c,host:D}){return new Promise((C,B)=>{async function V(T){let[z]=H(T),F=z.pc,G=await F.createOffer();await F.setLocalDescription(G),T.receive("offer",F.localDescription?.toJSON())}let{exitRoom:N}=h({userId:_,appId:$,room:c,host:D,logLine:b,workerUrl:S,autoRejoin:!0,onOpen(){X?.({room:c,host:D}),C()},onError(){console.error("onError"),B()},onClose(T){Z?.({room:c,host:D,ev:T})},onPeerJoined(T){T.forEach((z)=>{let[F,G]=H(z);if(!G)return;let q=F.pc;A({pc:q,userId:z.userId,initiator:!0}),V(z)})},onPeerLeft(T){T.forEach(({userId:z,peerId:F})=>{let G=Y.get(z);if(!G)return;if(G.peers.delete(F),!G.peers.size)G.expirationTimeout=setTimeout(()=>Q(z),M??0)})},async onMessage(T,z,F){let[G]=H(F),q=G.pc;if(T==="offer"){A({pc:q,userId:F.userId,initiator:!1}),await q.setRemoteDescription(z);let R=await q.createAnswer();await q.setLocalDescription(R),F.receive("answer",q.localDescription?.toJSON()),await J(G);return}if(T==="answer"){await q.setRemoteDescription(z),await J(G);return}if(T==="ice"){let R=z;if(!q.remoteDescription){G.pendingRemoteIce.push(R);return}try{await q.addIceCandidate(R)}catch(g){b("⚠️ ERROR",{error:"add-ice-failed",userId:G.userId,detail:String(g)})}return}}});W.set(`${D}/room/${c}`,{exitRoom:N,room:c,host:D})})}return{userId:_,enterRoom:K,exitRoom:E,leaveUser:Q,end(){W.forEach(({exitRoom:c})=>c()),W.clear(),Y.forEach(({userId:c})=>Q(c)),Y.clear()}}}function a({appId:$,logLine:A=console.debug,enterRoomFunction:M=x,peerlessUserExpiration:O,workerUrl:h,onRoomReady:b,onRoomClose:f,dataChannelOptions:S}){let X=[],Z={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},_=new Set;function Y(T,z){z.onopen=()=>{A("\uD83D\uDCAC",{event:"dc-open",userId:T}),X.push(T),Q.forEach((F)=>F(T,"join",X))},z.onmessage=({data:F})=>{_.forEach((G)=>G(F,T)),A("\uD83D\uDCAC",{event:"dc-message",userId:T,data:F})},z.onclose=()=>{A("\uD83D\uDCAC",{event:"dc-close",userId:T}),X.splice(X.indexOf(T),1),Q.forEach((F)=>F(T,"leave",X))},z.onerror=()=>A("⚠️ ERROR",{error:"dc-error",userId:T})}let H=new Map,Q=new Set,{userId:J,enterRoom:W,exitRoom:E,leaveUser:K,end:c}=n({appId:$,rtcConfig:Z,enterRoomFunction:M,logLine:A,workerUrl:h,peerlessUserExpiration:O,onRoomReady:b,onRoomClose:f,onLeaveUser(T){let z=H.get(T);try{z?.close()}catch{}H.delete(T)},receivePeerConnection({pc:T,userId:z,initiator:F}){if(F){let G=T.createDataChannel("data",S);Y(z,G),H.set(z,G)}else T.ondatachannel=(G)=>{let q=G.channel;Y(z,q),H.set(z,q),T.ondatachannel=null}}});function D(T,z){H.forEach((F,G)=>{if(z&&G!==z)return;if(F.readyState==="open")F.send(T)})}function C(T){_.delete(T)}function B(T){return _.add(T),()=>{C(T)}}function V(T){Q.delete(T)}function N(T){return Q.add(T),()=>{V(T)}}return{userId:J,send:D,enterRoom:W,exitRoom:E,leaveUser:K,getUsers:()=>X,addMessageListener:B,removeMessageListener:C,addUserListener:N,removeUserListener:V,end(){H.forEach((T)=>{try{T.close()}catch{}}),H.clear(),c(),Q.clear(),X.length=0}}}export{a as enterWorld};
2
2
 
3
- //# debugId=D9F15EA59F21AF8564756E2164756E21
3
+ //# debugId=8A40AA56DDA61A8564756E2164756E21
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 peerId: string;\n receive(type: T, payload: P): boolean;\n}\n\n/**\n * enterRoom connects to the signaling room via WebSocket.\n */\nexport function enterRoom<T extends string, P = any>(params: {\n userId: string; appId: string; room: string; 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, peerId: string }[]): void;\n onMessage(type: T, payload: P, from: IPeer<T, P>): void;\n autoRejoin?: boolean;\n}): { exitRoom: () => void } {\n const { userId, appId, 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/${appId}/${room}?userId=${encodeURIComponent(userId)}`;\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 // ... (keep your existing JSON parsing and updatePeers logic here)\n try {\n const msg = JSON.parse(e.data);\n logLine?.(\"🖥️ ➡️ 👤\", msg);\n if (msg.type === \"peer-joined\" || msg.type === \"peer-left\") {\n updatePeers(msg.users);\n } else if (msg.peerId && msg.userId) {\n params.onMessage(msg.type, msg.payload, {\n userId: msg.userId,\n peerId: msg.peerId,\n receive: (type: T, payload: P) => send(type, msg.peerId, payload),\n });\n }\n } catch { logLine?.(\"⚠️ ERROR\", { error: \"invalid-json\" }); }\n };\n\n ws.onclose = (ev: CloseEvent) => {\n\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\", { attempt: retryCount + 1, delayMs: Math.round(delay) });\n \n retryCount++;\n timeoutId = setTimeout(connect, delay);\n } else {\n params.onClose?.({ code: ev.code, reason: ev.reason, wasClean: ev.wasClean });\n }\n };\n\n ws.onerror = () => params.onError?.();\n }\n\n // Helper for sending (uses the current ws instance)\n function send(type: T, toPeerId: string, payload: P) {\n if (exited || ws.readyState !== WebSocket.OPEN) return false;\n const obj = { type, to: toPeerId, payload };\n ws.send(JSON.stringify(obj));\n logLine?.(\"👤 ➡️ 🖥️\", obj);\n return true;\n }\n\n // Helper for peer tracking (logic from your original code)\n function updatePeers(updatedUsers: { peerId: string; userId: string }[]) {\n const joined: IPeer<T, P>[] = [];\n const left: { userId: string; peerId: string }[] = [];\n const updatedPeerSet = new Set<string>();\n\n updatedUsers.forEach(({ userId: pUserId, peerId }) => {\n if (pUserId === userId) return;\n if (!peers.has(peerId)) {\n const newPeer = { userId: pUserId, peerId, receive: (t: T, p: P) => send(t, peerId, p) };\n peers.set(peerId, newPeer);\n joined.push(newPeer);\n }\n updatedPeerSet.add(peerId);\n });\n\n for (const [peerId, peer] of peers.entries()) {\n if (!updatedPeerSet.has(peerId)) {\n peers.delete(peerId);\n left.push({ peerId, userId: peer.userId });\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 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 appId,\n room,\n host,\n autoRejoin = true,\n onOpen,\n onClose,\n onError,\n onPeerJoined,\n onPeerLeft,\n onMessage,\n logLine,\n workerUrl,\n}: {\n userId: string;\n appId: 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, peerId: string}[]) => 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}): { exitRoom: () => void } {\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(\"Warning: enterRoom called without workerUrl; this may cause issues in some environments. You should pass workerUrl explicitly. Use:\", CDN_WORKER_URL);\n return baseEnterRoom<T, P>({\n userId,\n appId,\n room,\n host,\n autoRejoin,\n onOpen,\n onClose,\n onError,\n onPeerJoined,\n onPeerLeft,\n onMessage,\n });\n }\n const worker = new Worker(workerUrl, { type: \"module\" });\n let exited = false;\n\n function makeUser({ userId, peerId }: { userId: string; peerId: string }): IPeer<T, P> {\n return {\n userId,\n peerId,\n receive: (type: T, payload: P) => {\n if (exited) return false;\n worker.postMessage({ cmd: \"send\", toPeerId: peerId, type, payload } 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 }\n else if (ev.kind === \"error\") onError?.();\n else if (ev.kind === \"peer-joined\") onPeerJoined(ev.users.map(ev => makeUser({ userId: ev.userId, peerId: ev.peerId })));\n else if (ev.kind === \"peer-left\") onPeerLeft(ev.users);\n else if (ev.kind === \"message\") onMessage(ev.type, ev.payload, makeUser({ userId: ev.fromUserId, peerId: ev.fromPeerId }));\n else if (ev.kind === \"log\") logLine?.(ev.direction, ev.obj);\n };\n\n worker.addEventListener(\"message\", onWorkerMessage);\n\n worker.postMessage({ cmd: \"enter\", userId, appId, room, host, autoRejoin } as WorkerCommand);\n\n return {\n exitRoom: () => {\n exited = true;\n worker.removeEventListener(\"message\", onWorkerMessage);\n worker.postMessage({ cmd: \"exit\" } as WorkerCommand);\n },\n };\n}\n\nexport type EnterRoom<T extends string, P> = typeof enterRoom<T, P>;\n",
7
- "import { IPeer } from \"./impl/signal-room\";\nimport { EnterRoom, enterRoom } from \"./signal-room\";\n\nexport type SigType = \"offer\" | \"answer\" | \"ice\";\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 peers: Map<string, IPeer<SigType, SigPayload>>;\n\n expirationTimeout?: number;\n};\n\nconst DEFAULT_ENTER_ROOM = enterRoom;\n\n\nexport function collectPeerConnections({\n appId,\n receivePeerConnection,\n peerlessUserExpiration,\n rtcConfig = { iceServers: [{ urls: \"stun:stun.l.google.com:19302\" }] },\n enterRoomFunction: enterRoom = DEFAULT_ENTER_ROOM,\n logLine = console.debug,\n onLeaveUser,\n workerUrl,\n}: {\n appId: string;\n rtcConfig?: 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: { pc: RTCPeerConnection, userId: string, initiator: boolean }): void;\n}) {\n const userId = `user-${crypto.randomUUID()}`;\n const users: Map<string, UserState> = new Map();\n\n function getPeer(peer: IPeer<SigType, SigPayload>): [UserState, boolean] {\n let state = users.get(peer.userId);\n let isNewPeer = false;\n if (!state) {\n const newState: UserState = {\n userId: peer.userId,\n pc: new RTCPeerConnection(rtcConfig),\n pendingRemoteIce: [],\n peers: new Map(),\n };\n newState.peers.set(peer.peerId, peer);\n users.set(peer.userId, newState);\n\n // Send local ICE candidates to this peer\n newState.pc.onicecandidate = (ev) => {\n if (!ev.candidate) return;\n for(let user of newState.peers.values()) {\n const success = user.receive(\"ice\", ev.candidate.toJSON());\n if (success) break;\n }\n };\n \n newState.pc.onconnectionstatechange = () => {\n logLine(\"💬\", { event: \"pc-state\", userId: newState.userId, state: newState.pc.connectionState });\n };\n state = newState;\n\n // New user\n users.set(state.userId, state);\n isNewPeer = true;\n } else if (state) {\n clearTimeout(state.expirationTimeout);\n state.expirationTimeout = 0;\n state.peers.set(peer.peerId, peer);\n }\n return [state, isNewPeer];\n }\n\n function leaveUser(userId: string) {\n onLeaveUser?.(userId);\n const p = users.get(userId);\n if (!p) return;\n try { p.pc.close(); } catch {}\n users.delete(userId);\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\", { error: \"add-ice-failed\", userId: state.userId, detail: String(e) });\n }\n }\n }\n\n const roomsEntered = new Map<string, { room: string; host: string; exitRoom: () => void }>();\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>((resolve, reject) => {\n async function makeOffer(user: IPeer) {\n // Offer flow: createOffer -> setLocalDescription -> send localDescription\n const [state] = 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 const { exitRoom, } = enterRoom({\n userId,\n appId,\n room,\n host,\n logLine,\n workerUrl,\n autoRejoin: true,\n\n onOpen() {\n console.log(\"onOpen\");\n resolve();\n },\n onError() {\n console.log(\"onError\");\n reject();\n },\n onClose(ev) {\n console.log(\"onClose\", ev);\n },\n\n // Existing peers initiate to the newcomer (Option 1)\n onPeerJoined(joiningUsers: IPeer<SigType, SigPayload>[]) {\n joiningUsers.forEach(user => {\n const [state, isNewPeer] = getPeer(user);\n if (!isNewPeer) return;\n const pc = state.pc;\n receivePeerConnection({ pc, userId: user.userId, initiator: true });\n makeOffer(user);\n });\n },\n\n onPeerLeft(leavingUsers: { userId: string; peerId: string }[]) {\n leavingUsers.forEach(({ userId, peerId }) => {\n const state = users.get(userId);\n if (!state) return;\n state.peers.delete(peerId);\n if (!state.peers.size) {\n state.expirationTimeout = setTimeout(() => leaveUser(userId), peerlessUserExpiration ?? 0);\n }\n });\n },\n\n async onMessage(type: SigType, payload: any, from: IPeer) {\n const [state] = getPeer(from);\n const pc = state.pc;\n\n if (type === \"offer\") {\n receivePeerConnection({ pc, userId: from.userId, initiator: false });\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\", { error: \"add-ice-failed\", userId: state.userId, detail: String(e) });\n }\n return;\n }\n },\n });\n roomsEntered.set(`${host}/room/${room}`, { exitRoom, room, host });\n });\n }\n\n return {\n userId,\n enterRoom: enter,\n exitRoom: exit,\n leaveUser,\n end() {\n roomsEntered.forEach(({ exitRoom }) => exitRoom());\n roomsEntered.clear();\n users.forEach(({ userId }) => leaveUser(userId));\n users.clear();\n },\n };\n}\n\n\n\n",
8
- "import { EnterRoom, enterRoom } from \"./signal-room\";\nimport { SigType, SigPayload, collectPeerConnections } from \"./webrtc-peer-collector\";\n\ntype UserListener = (user: string, action: \"join\"|\"leave\", users: string[]) => void;\n\nexport function enterWorld({\n appId, logLine = console.debug, enterRoomFunction = enterRoom, peerlessUserExpiration, workerUrl,\n}: {\n appId: string;\n logLine?: (direction: string, obj?: any) => void;\n enterRoomFunction?: EnterRoom<SigType, SigPayload>;\n peerlessUserExpiration?: number;\n workerUrl?: URL;\n}) {\n const userIds: string[] = [];\n const rtcConfig: RTCConfiguration = {\n iceServers: [{ urls: \"stun:stun.l.google.com:19302\" }],\n };\n\n const messagesListeners = new Set<(data: any, from: string) => void>();\n\n function wireDataChannel(userId: string, dc: RTCDataChannel) {\n dc.onopen = () => {\n logLine(\"💬\", { event: \"dc-open\", userId });\n userIds.push(userId);\n userListeners.forEach(listener => listener(userId, \"join\", userIds));\n };\n dc.onmessage = ({ data }) => {\n messagesListeners.forEach(listener => listener(data as any, userId));\n logLine(\"💬\", { event: \"dc-message\", userId, data });\n };\n dc.onclose = () => {\n logLine(\"💬\", { event: \"dc-close\", userId });\n userIds.splice(userIds.indexOf(userId), 1);\n userListeners.forEach(listener => listener(userId, \"leave\", userIds));\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 { userId, enterRoom, exitRoom, leaveUser, end: endPeerCollection } = collectPeerConnections({\n appId,\n rtcConfig,\n enterRoomFunction,\n logLine,\n workerUrl,\n peerlessUserExpiration,\n onLeaveUser(userId: string) {\n const dc = dataChannels.get(userId);\n try { dc?.close(); } catch { }\n dataChannels.delete(userId);\n },\n receivePeerConnection({ pc, userId, initiator }) {\n if (initiator) {\n const dc = pc.createDataChannel(\"data\");\n wireDataChannel(userId, dc);\n dataChannels.set(userId, dc);\n } else {\n pc.ondatachannel = (ev) => {\n const dc = ev.channel;\n wireDataChannel(userId, dc);\n dataChannels.set(userId, dc);\n pc.ondatachannel = null;\n };\n }\n },\n });\n\n function send(data: any, userId?: string) {\n dataChannels.forEach((dataChannel, pUserId) => {\n if (userId && pUserId !== userId) return;\n if (dataChannel.readyState === \"open\") dataChannel.send(data);\n });\n }\n\n function removeMessageListener(listener: (data: any, from: string) => void) {\n messagesListeners.delete(listener);\n }\n\n function addMessageListener(listener: (data: any, 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 enterRoom,\n exitRoom,\n leaveUser,\n getUsers: () => userIds,\n addMessageListener,\n removeMessageListener,\n addUserListener,\n removeUserListener,\n end() {\n dataChannels.forEach((dataChannel) => {\n try { dataChannel.close(); } catch { }\n });\n dataChannels.clear();\n endPeerCollection();\n userListeners.clear();\n userIds.length = 0;\n },\n };\n}\n"
7
+ "import { IPeer } from \"./impl/signal-room\";\nimport { EnterRoom, enterRoom } from \"./signal-room\";\n\nexport type SigType = \"offer\" | \"answer\" | \"ice\";\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 peers: Map<string, IPeer<SigType, SigPayload>>;\n\n expirationTimeout?: number;\n};\n\nconst DEFAULT_ENTER_ROOM = enterRoom;\n\n\nexport function collectPeerConnections({\n appId,\n receivePeerConnection,\n peerlessUserExpiration,\n rtcConfig = { iceServers: [{ urls: \"stun:stun.l.google.com:19302\" }] },\n enterRoomFunction: enterRoom = DEFAULT_ENTER_ROOM,\n logLine = console.debug,\n onLeaveUser,\n workerUrl,\n onRoomReady,\n onRoomClose,\n}: {\n appId: string;\n rtcConfig?: 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: { pc: RTCPeerConnection, userId: string, initiator: boolean }): void;\n onRoomReady?(info: { host: string; room: string }): void;\n onRoomClose?(info: { host: string; room: string; ev: Pick<CloseEvent, \"reason\"|\"code\"|\"wasClean\"> }): void;\n}) {\n const userId = `user-${crypto.randomUUID()}`;\n const users: Map<string, UserState> = new Map();\n\n function getPeer(peer: IPeer<SigType, SigPayload>): [UserState, boolean] {\n let state = users.get(peer.userId);\n let isNewPeer = false;\n if (!state) {\n const newState: UserState = {\n userId: peer.userId,\n pc: new RTCPeerConnection(rtcConfig),\n pendingRemoteIce: [],\n peers: new Map(),\n };\n newState.peers.set(peer.peerId, peer);\n users.set(peer.userId, newState);\n\n // Send local ICE candidates to this peer\n newState.pc.onicecandidate = (ev) => {\n if (!ev.candidate) return;\n for(let user of newState.peers.values()) {\n const success = user.receive(\"ice\", ev.candidate.toJSON());\n if (success) break;\n }\n };\n \n newState.pc.onconnectionstatechange = () => {\n logLine(\"💬\", { event: \"pc-state\", userId: newState.userId, state: newState.pc.connectionState });\n };\n state = newState;\n\n // New user\n users.set(state.userId, state);\n isNewPeer = true;\n } else if (state) {\n clearTimeout(state.expirationTimeout);\n state.expirationTimeout = 0;\n state.peers.set(peer.peerId, peer);\n }\n return [state, isNewPeer];\n }\n\n function leaveUser(userId: string) {\n onLeaveUser?.(userId);\n const p = users.get(userId);\n if (!p) return;\n try { p.pc.close(); } catch {}\n users.delete(userId);\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\", { error: \"add-ice-failed\", userId: state.userId, detail: String(e) });\n }\n }\n }\n\n const roomsEntered = new Map<string, { room: string; host: string; exitRoom: () => void }>();\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>((resolve, reject) => {\n async function makeOffer(user: IPeer) {\n // Offer flow: createOffer -> setLocalDescription -> send localDescription\n const [state] = 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 const { exitRoom, } = enterRoom({\n userId,\n appId,\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) {\n onRoomClose?.({room, host, ev});\n },\n\n // Existing peers initiate to the newcomer (Option 1)\n onPeerJoined(joiningUsers: IPeer<SigType, SigPayload>[]) {\n joiningUsers.forEach(user => {\n const [state, isNewPeer] = getPeer(user);\n if (!isNewPeer) return;\n const pc = state.pc;\n receivePeerConnection({ pc, userId: user.userId, initiator: true });\n makeOffer(user);\n });\n },\n\n onPeerLeft(leavingUsers: { userId: string; peerId: string }[]) {\n leavingUsers.forEach(({ userId, peerId }) => {\n const state = users.get(userId);\n if (!state) return;\n state.peers.delete(peerId);\n if (!state.peers.size) {\n state.expirationTimeout = setTimeout(() => leaveUser(userId), peerlessUserExpiration ?? 0);\n }\n });\n },\n\n async onMessage(type: SigType, payload: any, from: IPeer) {\n const [state] = getPeer(from);\n const pc = state.pc;\n\n if (type === \"offer\") {\n receivePeerConnection({ pc, userId: from.userId, initiator: false });\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\", { error: \"add-ice-failed\", userId: state.userId, detail: String(e) });\n }\n return;\n }\n },\n });\n roomsEntered.set(`${host}/room/${room}`, { exitRoom, room, host });\n });\n }\n\n return {\n userId,\n enterRoom: enter,\n exitRoom: exit,\n leaveUser,\n end() {\n roomsEntered.forEach(({ exitRoom }) => exitRoom());\n roomsEntered.clear();\n users.forEach(({ userId }) => leaveUser(userId));\n users.clear();\n },\n };\n}\n\n\n\n",
8
+ "import { EnterRoom, enterRoom } from \"./signal-room\";\nimport { SigType, SigPayload, collectPeerConnections } from \"./webrtc-peer-collector\";\n\ntype UserListener = (user: string, action: \"join\"|\"leave\", users: string[]) => void;\n\nexport function enterWorld({\n appId, logLine = console.debug, enterRoomFunction = enterRoom, peerlessUserExpiration, workerUrl,\n onRoomReady,\n onRoomClose,\n dataChannelOptions,\n}: {\n appId: 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: { host: string; room: string; ev: Pick<CloseEvent, \"reason\"|\"code\"|\"wasClean\"> }): void;\n dataChannelOptions?: RTCDataChannelInit;\n}) {\n const userIds: string[] = [];\n const rtcConfig: RTCConfiguration = {\n iceServers: [{ urls: \"stun:stun.l.google.com:19302\" }],\n };\n\n const messagesListeners = new Set<(data: any, from: string) => void>();\n\n function wireDataChannel(userId: string, dc: RTCDataChannel) {\n dc.onopen = () => {\n logLine(\"💬\", { event: \"dc-open\", userId });\n userIds.push(userId);\n userListeners.forEach(listener => listener(userId, \"join\", userIds));\n };\n dc.onmessage = ({ data }) => {\n messagesListeners.forEach(listener => listener(data as any, userId));\n logLine(\"💬\", { event: \"dc-message\", userId, data });\n };\n dc.onclose = () => {\n logLine(\"💬\", { event: \"dc-close\", userId });\n userIds.splice(userIds.indexOf(userId), 1);\n userListeners.forEach(listener => listener(userId, \"leave\", userIds));\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 { userId, enterRoom, exitRoom, leaveUser, end: endPeerCollection } = collectPeerConnections({\n appId,\n rtcConfig,\n enterRoomFunction,\n logLine,\n workerUrl,\n peerlessUserExpiration,\n onRoomReady,\n onRoomClose,\n onLeaveUser(userId: string) {\n const dc = dataChannels.get(userId);\n try { dc?.close(); } catch { }\n dataChannels.delete(userId);\n },\n receivePeerConnection({ pc, userId, initiator }) {\n if (initiator) {\n const dc = pc.createDataChannel(\"data\", dataChannelOptions);\n wireDataChannel(userId, dc);\n dataChannels.set(userId, dc);\n } else {\n pc.ondatachannel = (ev) => {\n const dc = ev.channel;\n wireDataChannel(userId, dc);\n dataChannels.set(userId, dc);\n pc.ondatachannel = null;\n };\n }\n },\n });\n\n function send(data: any, userId?: string) {\n dataChannels.forEach((dataChannel, pUserId) => {\n if (userId && pUserId !== userId) return;\n if (dataChannel.readyState === \"open\") dataChannel.send(data);\n });\n }\n\n function removeMessageListener(listener: (data: any, from: string) => void) {\n messagesListeners.delete(listener);\n }\n\n function addMessageListener(listener: (data: any, 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 enterRoom,\n exitRoom,\n leaveUser,\n getUsers: () => userIds,\n addMessageListener,\n removeMessageListener,\n addUserListener,\n removeUserListener,\n end() {\n dataChannels.forEach((dataChannel) => {\n try { dataChannel.close(); } catch { }\n });\n dataChannels.clear();\n endPeerCollection();\n userListeners.clear();\n userIds.length = 0;\n },\n };\n}\n"
9
9
  ],
10
- "mappings": "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",
11
- "debugId": "D9F15EA59F21AF8564756E2164756E21",
10
+ "mappings": "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",
11
+ "debugId": "8A40AA56DDA61A8564756E2164756E21",
12
12
  "names": []
13
13
  }
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- function O(X){let{userId:Y,appId:M,room:R,host:h,autoRejoin:Q=!0,logLine:q}=X,Z=!1,$=0,B,W,_=!0,A=new Map,V=`wss://${h}/room/${M}/${R}?userId=${encodeURIComponent(Y)}`;function S(){if(Z)return;B=new WebSocket(V),B.onopen=()=>{if(_)X.onOpen?.(),_=!1;$=0},B.onmessage=(D)=>{try{let c=JSON.parse(D.data);if(q?.("\uD83D\uDDA5️ ➡️ \uD83D\uDC64",c),c.type==="peer-joined"||c.type==="peer-left")f(c.users);else if(c.peerId&&c.userId)X.onMessage(c.type,c.payload,{userId:c.userId,peerId:c.peerId,receive:(z,K)=>J(z,c.peerId,K)})}catch{q?.("⚠️ ERROR",{error:"invalid-json"})}},B.onclose=(D)=>{let z=[1001,1006,1011,1012,1013].includes(D.code);if(Q&&!Z&&z){let K=Math.min(Math.pow(2,$)*1000,30000),T=Math.random()*1000,b=K+T;q?.("\uD83D\uDD04 RECONNECTING",{attempt:$+1,delayMs:Math.round(b)}),$++,W=setTimeout(S,b)}else X.onClose?.({code:D.code,reason:D.reason,wasClean:D.wasClean})},B.onerror=()=>X.onError?.()}function J(D,c,z){if(Z||B.readyState!==WebSocket.OPEN)return!1;let K={type:D,to:c,payload:z};return B.send(JSON.stringify(K)),q?.("\uD83D\uDC64 ➡️ \uD83D\uDDA5️",K),!0}function f(D){let c=[],z=[],K=new Set;D.forEach(({userId:T,peerId:b})=>{if(T===Y)return;if(!A.has(b)){let F={userId:T,peerId:b,receive:(G,H)=>J(G,b,H)};A.set(b,F),c.push(F)}K.add(b)});for(let[T,b]of A.entries())if(!K.has(T))A.delete(T),z.push({peerId:T,userId:b.userId});if(c.length)X.onPeerJoined(c);if(z.length)X.onPeerLeft(z)}return S(),{exitRoom:()=>{Z=!0,clearTimeout(W),B.close()}}}function N({userId:X,appId:Y,room:M,host:R,autoRejoin:h=!0,onOpen:Q,onClose:q,onError:Z,onPeerJoined:$,onPeerLeft:B,onMessage:W,logLine:_,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"),O({userId:X,appId:Y,room:M,host:R,autoRejoin:h,onOpen:Q,onClose:q,onError:Z,onPeerJoined:$,onPeerLeft:B,onMessage:W});let V=new Worker(A,{type:"module"}),S=!1;function J({userId:D,peerId:c}){return{userId:D,peerId:c,receive:(z,K)=>{if(S)return!1;return V.postMessage({cmd:"send",toPeerId:c,type:z,payload:K}),!0}}}let f=(D)=>{let c=D.data;if(c.kind==="open")Q?.();else if(c.kind==="close")V.terminate(),q?.(c.ev);else if(c.kind==="error")Z?.();else if(c.kind==="peer-joined")$(c.users.map((z)=>J({userId:z.userId,peerId:z.peerId})));else if(c.kind==="peer-left")B(c.users);else if(c.kind==="message")W(c.type,c.payload,J({userId:c.fromUserId,peerId:c.fromPeerId}));else if(c.kind==="log")_?.(c.direction,c.obj)};return V.addEventListener("message",f),V.postMessage({cmd:"enter",userId:X,appId:Y,room:M,host:R,autoRejoin:h}),{exitRoom:()=>{S=!0,V.removeEventListener("message",f),V.postMessage({cmd:"exit"})}}}var j=N;function E({appId:X,receivePeerConnection:Y,peerlessUserExpiration:M,rtcConfig:R={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},enterRoomFunction:h=j,logLine:Q=console.debug,onLeaveUser:q,workerUrl:Z}){let $=`user-${crypto.randomUUID()}`,B=new Map;function W(f){let D=B.get(f.userId),c=!1;if(!D){let z={userId:f.userId,pc:new RTCPeerConnection(R),pendingRemoteIce:[],peers:new Map};z.peers.set(f.peerId,f),B.set(f.userId,z),z.pc.onicecandidate=(K)=>{if(!K.candidate)return;for(let T of z.peers.values())if(T.receive("ice",K.candidate.toJSON()))break},z.pc.onconnectionstatechange=()=>{Q("\uD83D\uDCAC",{event:"pc-state",userId:z.userId,state:z.pc.connectionState})},D=z,B.set(D.userId,D),c=!0}else if(D)clearTimeout(D.expirationTimeout),D.expirationTimeout=0,D.peers.set(f.peerId,f);return[D,c]}function _(f){q?.(f);let D=B.get(f);if(!D)return;try{D.pc.close()}catch{}B.delete(f)}async function A(f){if(!f.pc.remoteDescription)return;let D=f.pendingRemoteIce;f.pendingRemoteIce=[];for(let c of D)try{await f.pc.addIceCandidate(c)}catch(z){Q("⚠️ ERROR",{error:"add-ice-failed",userId:f.userId,detail:String(z)})}}let V=new Map;function S({room:f,host:D}){let c=`${D}/room/${f}`,z=V.get(c);if(z)z.exitRoom(),V.delete(c)}function J({room:f,host:D}){return new Promise((c,z)=>{async function K(b){let[F]=W(b),G=F.pc,H=await G.createOffer();await G.setLocalDescription(H),b.receive("offer",G.localDescription?.toJSON())}let{exitRoom:T}=h({userId:$,appId:X,room:f,host:D,logLine:Q,workerUrl:Z,autoRejoin:!0,onOpen(){console.log("onOpen"),c()},onError(){console.log("onError"),z()},onClose(b){console.log("onClose",b)},onPeerJoined(b){b.forEach((F)=>{let[G,H]=W(F);if(!H)return;let C=G.pc;Y({pc:C,userId:F.userId,initiator:!0}),K(F)})},onPeerLeft(b){b.forEach(({userId:F,peerId:G})=>{let H=B.get(F);if(!H)return;if(H.peers.delete(G),!H.peers.size)H.expirationTimeout=setTimeout(()=>_(F),M??0)})},async onMessage(b,F,G){let[H]=W(G),C=H.pc;if(b==="offer"){Y({pc:C,userId:G.userId,initiator:!1}),await C.setRemoteDescription(F);let x=await C.createAnswer();await C.setLocalDescription(x),G.receive("answer",C.localDescription?.toJSON()),await A(H);return}if(b==="answer"){await C.setRemoteDescription(F),await A(H);return}if(b==="ice"){let x=F;if(!C.remoteDescription){H.pendingRemoteIce.push(x);return}try{await C.addIceCandidate(x)}catch(L){Q("⚠️ ERROR",{error:"add-ice-failed",userId:H.userId,detail:String(L)})}return}}});V.set(`${D}/room/${f}`,{exitRoom:T,room:f,host:D})})}return{userId:$,enterRoom:J,exitRoom:S,leaveUser:_,end(){V.forEach(({exitRoom:f})=>f()),V.clear(),B.forEach(({userId:f})=>_(f)),B.clear()}}}function U({appId:X,logLine:Y=console.debug,enterRoomFunction:M=N,peerlessUserExpiration:R,workerUrl:h}){let Q=[],q={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},Z=new Set;function $(T,b){b.onopen=()=>{Y("\uD83D\uDCAC",{event:"dc-open",userId:T}),Q.push(T),W.forEach((F)=>F(T,"join",Q))},b.onmessage=({data:F})=>{Z.forEach((G)=>G(F,T)),Y("\uD83D\uDCAC",{event:"dc-message",userId:T,data:F})},b.onclose=()=>{Y("\uD83D\uDCAC",{event:"dc-close",userId:T}),Q.splice(Q.indexOf(T),1),W.forEach((F)=>F(T,"leave",Q))},b.onerror=()=>Y("⚠️ ERROR",{error:"dc-error",userId:T})}let B=new Map,W=new Set,{userId:_,enterRoom:A,exitRoom:V,leaveUser:S,end:J}=E({appId:X,rtcConfig:q,enterRoomFunction:M,logLine:Y,workerUrl:h,peerlessUserExpiration:R,onLeaveUser(T){let b=B.get(T);try{b?.close()}catch{}B.delete(T)},receivePeerConnection({pc:T,userId:b,initiator:F}){if(F){let G=T.createDataChannel("data");$(b,G),B.set(b,G)}else T.ondatachannel=(G)=>{let H=G.channel;$(b,H),B.set(b,H),T.ondatachannel=null}}});function f(T,b){B.forEach((F,G)=>{if(b&&G!==b)return;if(F.readyState==="open")F.send(T)})}function D(T){Z.delete(T)}function c(T){return Z.add(T),()=>{D(T)}}function z(T){W.delete(T)}function K(T){return W.add(T),()=>{z(T)}}return{userId:_,send:f,enterRoom:A,exitRoom:V,leaveUser:S,getUsers:()=>Q,addMessageListener:c,removeMessageListener:D,addUserListener:K,removeUserListener:z,end(){B.forEach((T)=>{try{T.close()}catch{}}),B.clear(),J(),W.clear(),Q.length=0}}}export{U as enterWorld,O as enterRoom,E as collectPeerConnections};
1
+ function n(X){let{userId:Y,appId:M,room:O,host:h,autoRejoin:q=!0,logLine:_}=X,S=!1,Q=0,W,A,V=!0,F=new Map,H=`wss://${h}/room/${M}/${O}?userId=${encodeURIComponent(Y)}`;function J(){if(S)return;W=new WebSocket(H),W.onopen=()=>{if(V)X.onOpen?.(),V=!1;Q=0},W.onmessage=(G)=>{try{let c=JSON.parse(G.data);if(_?.("\uD83D\uDDA5️ ➡️ \uD83D\uDC64",c),c.type==="peer-joined"||c.type==="peer-left")x(c.users);else if(c.peerId&&c.userId)X.onMessage(c.type,c.payload,{userId:c.userId,peerId:c.peerId,receive:(D,b)=>Z(D,c.peerId,b)})}catch{_?.("⚠️ ERROR",{error:"invalid-json"})}},W.onclose=(G)=>{let D=[1001,1006,1011,1012,1013].includes(G.code);if(q&&!S&&D){let b=Math.min(Math.pow(2,Q)*1000,30000),C=Math.random()*1000,K=b+C;_?.("\uD83D\uDD04 RECONNECTING",{attempt:Q+1,delayMs:Math.round(K)}),Q++,A=setTimeout(J,K)}else X.onClose?.({code:G.code,reason:G.reason,wasClean:G.wasClean})},W.onerror=()=>X.onError?.()}function Z(G,c,D){if(S||W.readyState!==WebSocket.OPEN)return!1;let b={type:G,to:c,payload:D};return W.send(JSON.stringify(b)),_?.("\uD83D\uDC64 ➡️ \uD83D\uDDA5️",b),!0}function x(G){let c=[],D=[],b=new Set;G.forEach(({userId:C,peerId:K})=>{if(C===Y)return;if(!F.has(K)){let E={userId:C,peerId:K,receive:(T,f)=>Z(T,K,f)};F.set(K,E),c.push(E)}b.add(K)});for(let[C,K]of F.entries())if(!b.has(C))F.delete(C),D.push({peerId:C,userId:K.userId});if(c.length)X.onPeerJoined(c);if(D.length)X.onPeerLeft(D)}return J(),{exitRoom:()=>{S=!0,clearTimeout(A),W.close()}}}function R({userId:X,appId:Y,room:M,host:O,autoRejoin:h=!0,onOpen:q,onClose:_,onError:S,onPeerJoined:Q,onPeerLeft:W,onMessage:A,logLine:V,workerUrl:F}){if(!F)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"),n({userId:X,appId:Y,room:M,host:O,autoRejoin:h,onOpen:q,onClose:_,onError:S,onPeerJoined:Q,onPeerLeft:W,onMessage:A});let H=new Worker(F,{type:"module"}),J=!1;function Z({userId:G,peerId:c}){return{userId:G,peerId:c,receive:(D,b)=>{if(J)return!1;return H.postMessage({cmd:"send",toPeerId:c,type:D,payload:b}),!0}}}let x=(G)=>{let c=G.data;if(c.kind==="open")q?.();else if(c.kind==="close")H.terminate(),_?.(c.ev);else if(c.kind==="error")S?.();else if(c.kind==="peer-joined")Q(c.users.map((D)=>Z({userId:D.userId,peerId:D.peerId})));else if(c.kind==="peer-left")W(c.users);else if(c.kind==="message")A(c.type,c.payload,Z({userId:c.fromUserId,peerId:c.fromPeerId}));else if(c.kind==="log")V?.(c.direction,c.obj)};return H.addEventListener("message",x),H.postMessage({cmd:"enter",userId:X,appId:Y,room:M,host:O,autoRejoin:h}),{exitRoom:()=>{J=!0,H.removeEventListener("message",x),H.postMessage({cmd:"exit"})}}}var j=R;function L({appId:X,receivePeerConnection:Y,peerlessUserExpiration:M,rtcConfig:O={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},enterRoomFunction:h=j,logLine:q=console.debug,onLeaveUser:_,workerUrl:S,onRoomReady:Q,onRoomClose:W}){let A=`user-${crypto.randomUUID()}`,V=new Map;function F(c){let D=V.get(c.userId),b=!1;if(!D){let C={userId:c.userId,pc:new RTCPeerConnection(O),pendingRemoteIce:[],peers:new Map};C.peers.set(c.peerId,c),V.set(c.userId,C),C.pc.onicecandidate=(K)=>{if(!K.candidate)return;for(let E of C.peers.values())if(E.receive("ice",K.candidate.toJSON()))break},C.pc.onconnectionstatechange=()=>{q("\uD83D\uDCAC",{event:"pc-state",userId:C.userId,state:C.pc.connectionState})},D=C,V.set(D.userId,D),b=!0}else if(D)clearTimeout(D.expirationTimeout),D.expirationTimeout=0,D.peers.set(c.peerId,c);return[D,b]}function H(c){_?.(c);let D=V.get(c);if(!D)return;try{D.pc.close()}catch{}V.delete(c)}async function J(c){if(!c.pc.remoteDescription)return;let D=c.pendingRemoteIce;c.pendingRemoteIce=[];for(let b of D)try{await c.pc.addIceCandidate(b)}catch(C){q("⚠️ ERROR",{error:"add-ice-failed",userId:c.userId,detail:String(C)})}}let Z=new Map;function x({room:c,host:D}){let b=`${D}/room/${c}`,C=Z.get(b);if(C)C.exitRoom(),Z.delete(b)}function G({room:c,host:D}){return new Promise((b,C)=>{async function K(T){let[f]=F(T),z=f.pc,B=await z.createOffer();await z.setLocalDescription(B),T.receive("offer",z.localDescription?.toJSON())}let{exitRoom:E}=h({userId:A,appId:X,room:c,host:D,logLine:q,workerUrl:S,autoRejoin:!0,onOpen(){Q?.({room:c,host:D}),b()},onError(){console.error("onError"),C()},onClose(T){W?.({room:c,host:D,ev:T})},onPeerJoined(T){T.forEach((f)=>{let[z,B]=F(f);if(!B)return;let $=z.pc;Y({pc:$,userId:f.userId,initiator:!0}),K(f)})},onPeerLeft(T){T.forEach(({userId:f,peerId:z})=>{let B=V.get(f);if(!B)return;if(B.peers.delete(z),!B.peers.size)B.expirationTimeout=setTimeout(()=>H(f),M??0)})},async onMessage(T,f,z){let[B]=F(z),$=B.pc;if(T==="offer"){Y({pc:$,userId:z.userId,initiator:!1}),await $.setRemoteDescription(f);let N=await $.createAnswer();await $.setLocalDescription(N),z.receive("answer",$.localDescription?.toJSON()),await J(B);return}if(T==="answer"){await $.setRemoteDescription(f),await J(B);return}if(T==="ice"){let N=f;if(!$.remoteDescription){B.pendingRemoteIce.push(N);return}try{await $.addIceCandidate(N)}catch(g){q("⚠️ ERROR",{error:"add-ice-failed",userId:B.userId,detail:String(g)})}return}}});Z.set(`${D}/room/${c}`,{exitRoom:E,room:c,host:D})})}return{userId:A,enterRoom:G,exitRoom:x,leaveUser:H,end(){Z.forEach(({exitRoom:c})=>c()),Z.clear(),V.forEach(({userId:c})=>H(c)),V.clear()}}}function U({appId:X,logLine:Y=console.debug,enterRoomFunction:M=R,peerlessUserExpiration:O,workerUrl:h,onRoomReady:q,onRoomClose:_,dataChannelOptions:S}){let Q=[],W={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},A=new Set;function V(T,f){f.onopen=()=>{Y("\uD83D\uDCAC",{event:"dc-open",userId:T}),Q.push(T),H.forEach((z)=>z(T,"join",Q))},f.onmessage=({data:z})=>{A.forEach((B)=>B(z,T)),Y("\uD83D\uDCAC",{event:"dc-message",userId:T,data:z})},f.onclose=()=>{Y("\uD83D\uDCAC",{event:"dc-close",userId:T}),Q.splice(Q.indexOf(T),1),H.forEach((z)=>z(T,"leave",Q))},f.onerror=()=>Y("⚠️ ERROR",{error:"dc-error",userId:T})}let F=new Map,H=new Set,{userId:J,enterRoom:Z,exitRoom:x,leaveUser:G,end:c}=L({appId:X,rtcConfig:W,enterRoomFunction:M,logLine:Y,workerUrl:h,peerlessUserExpiration:O,onRoomReady:q,onRoomClose:_,onLeaveUser(T){let f=F.get(T);try{f?.close()}catch{}F.delete(T)},receivePeerConnection({pc:T,userId:f,initiator:z}){if(z){let B=T.createDataChannel("data",S);V(f,B),F.set(f,B)}else T.ondatachannel=(B)=>{let $=B.channel;V(f,$),F.set(f,$),T.ondatachannel=null}}});function D(T,f){F.forEach((z,B)=>{if(f&&B!==f)return;if(z.readyState==="open")z.send(T)})}function b(T){A.delete(T)}function C(T){return A.add(T),()=>{b(T)}}function K(T){H.delete(T)}function E(T){return H.add(T),()=>{K(T)}}return{userId:J,send:D,enterRoom:Z,exitRoom:x,leaveUser:G,getUsers:()=>Q,addMessageListener:C,removeMessageListener:b,addUserListener:E,removeUserListener:K,end(){F.forEach((T)=>{try{T.close()}catch{}}),F.clear(),c(),H.clear(),Q.length=0}}}export{U as enterWorld,n as enterRoom,L as collectPeerConnections};
2
2
 
3
- //# debugId=E6FC686739F0F5E364756E2164756E21
3
+ //# debugId=5576A8B4E2AE921B64756E2164756E21
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 peerId: string;\n receive(type: T, payload: P): boolean;\n}\n\n/**\n * enterRoom connects to the signaling room via WebSocket.\n */\nexport function enterRoom<T extends string, P = any>(params: {\n userId: string; appId: string; room: string; 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, peerId: string }[]): void;\n onMessage(type: T, payload: P, from: IPeer<T, P>): void;\n autoRejoin?: boolean;\n}): { exitRoom: () => void } {\n const { userId, appId, 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/${appId}/${room}?userId=${encodeURIComponent(userId)}`;\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 // ... (keep your existing JSON parsing and updatePeers logic here)\n try {\n const msg = JSON.parse(e.data);\n logLine?.(\"🖥️ ➡️ 👤\", msg);\n if (msg.type === \"peer-joined\" || msg.type === \"peer-left\") {\n updatePeers(msg.users);\n } else if (msg.peerId && msg.userId) {\n params.onMessage(msg.type, msg.payload, {\n userId: msg.userId,\n peerId: msg.peerId,\n receive: (type: T, payload: P) => send(type, msg.peerId, payload),\n });\n }\n } catch { logLine?.(\"⚠️ ERROR\", { error: \"invalid-json\" }); }\n };\n\n ws.onclose = (ev: CloseEvent) => {\n\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\", { attempt: retryCount + 1, delayMs: Math.round(delay) });\n \n retryCount++;\n timeoutId = setTimeout(connect, delay);\n } else {\n params.onClose?.({ code: ev.code, reason: ev.reason, wasClean: ev.wasClean });\n }\n };\n\n ws.onerror = () => params.onError?.();\n }\n\n // Helper for sending (uses the current ws instance)\n function send(type: T, toPeerId: string, payload: P) {\n if (exited || ws.readyState !== WebSocket.OPEN) return false;\n const obj = { type, to: toPeerId, payload };\n ws.send(JSON.stringify(obj));\n logLine?.(\"👤 ➡️ 🖥️\", obj);\n return true;\n }\n\n // Helper for peer tracking (logic from your original code)\n function updatePeers(updatedUsers: { peerId: string; userId: string }[]) {\n const joined: IPeer<T, P>[] = [];\n const left: { userId: string; peerId: string }[] = [];\n const updatedPeerSet = new Set<string>();\n\n updatedUsers.forEach(({ userId: pUserId, peerId }) => {\n if (pUserId === userId) return;\n if (!peers.has(peerId)) {\n const newPeer = { userId: pUserId, peerId, receive: (t: T, p: P) => send(t, peerId, p) };\n peers.set(peerId, newPeer);\n joined.push(newPeer);\n }\n updatedPeerSet.add(peerId);\n });\n\n for (const [peerId, peer] of peers.entries()) {\n if (!updatedPeerSet.has(peerId)) {\n peers.delete(peerId);\n left.push({ peerId, userId: peer.userId });\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 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 appId,\n room,\n host,\n autoRejoin = true,\n onOpen,\n onClose,\n onError,\n onPeerJoined,\n onPeerLeft,\n onMessage,\n logLine,\n workerUrl,\n}: {\n userId: string;\n appId: 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, peerId: string}[]) => 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}): { exitRoom: () => void } {\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(\"Warning: enterRoom called without workerUrl; this may cause issues in some environments. You should pass workerUrl explicitly. Use:\", CDN_WORKER_URL);\n return baseEnterRoom<T, P>({\n userId,\n appId,\n room,\n host,\n autoRejoin,\n onOpen,\n onClose,\n onError,\n onPeerJoined,\n onPeerLeft,\n onMessage,\n });\n }\n const worker = new Worker(workerUrl, { type: \"module\" });\n let exited = false;\n\n function makeUser({ userId, peerId }: { userId: string; peerId: string }): IPeer<T, P> {\n return {\n userId,\n peerId,\n receive: (type: T, payload: P) => {\n if (exited) return false;\n worker.postMessage({ cmd: \"send\", toPeerId: peerId, type, payload } 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 }\n else if (ev.kind === \"error\") onError?.();\n else if (ev.kind === \"peer-joined\") onPeerJoined(ev.users.map(ev => makeUser({ userId: ev.userId, peerId: ev.peerId })));\n else if (ev.kind === \"peer-left\") onPeerLeft(ev.users);\n else if (ev.kind === \"message\") onMessage(ev.type, ev.payload, makeUser({ userId: ev.fromUserId, peerId: ev.fromPeerId }));\n else if (ev.kind === \"log\") logLine?.(ev.direction, ev.obj);\n };\n\n worker.addEventListener(\"message\", onWorkerMessage);\n\n worker.postMessage({ cmd: \"enter\", userId, appId, room, host, autoRejoin } as WorkerCommand);\n\n return {\n exitRoom: () => {\n exited = true;\n worker.removeEventListener(\"message\", onWorkerMessage);\n worker.postMessage({ cmd: \"exit\" } as WorkerCommand);\n },\n };\n}\n\nexport type EnterRoom<T extends string, P> = typeof enterRoom<T, P>;\n",
7
- "import { IPeer } from \"./impl/signal-room\";\nimport { EnterRoom, enterRoom } from \"./signal-room\";\n\nexport type SigType = \"offer\" | \"answer\" | \"ice\";\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 peers: Map<string, IPeer<SigType, SigPayload>>;\n\n expirationTimeout?: number;\n};\n\nconst DEFAULT_ENTER_ROOM = enterRoom;\n\n\nexport function collectPeerConnections({\n appId,\n receivePeerConnection,\n peerlessUserExpiration,\n rtcConfig = { iceServers: [{ urls: \"stun:stun.l.google.com:19302\" }] },\n enterRoomFunction: enterRoom = DEFAULT_ENTER_ROOM,\n logLine = console.debug,\n onLeaveUser,\n workerUrl,\n}: {\n appId: string;\n rtcConfig?: 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: { pc: RTCPeerConnection, userId: string, initiator: boolean }): void;\n}) {\n const userId = `user-${crypto.randomUUID()}`;\n const users: Map<string, UserState> = new Map();\n\n function getPeer(peer: IPeer<SigType, SigPayload>): [UserState, boolean] {\n let state = users.get(peer.userId);\n let isNewPeer = false;\n if (!state) {\n const newState: UserState = {\n userId: peer.userId,\n pc: new RTCPeerConnection(rtcConfig),\n pendingRemoteIce: [],\n peers: new Map(),\n };\n newState.peers.set(peer.peerId, peer);\n users.set(peer.userId, newState);\n\n // Send local ICE candidates to this peer\n newState.pc.onicecandidate = (ev) => {\n if (!ev.candidate) return;\n for(let user of newState.peers.values()) {\n const success = user.receive(\"ice\", ev.candidate.toJSON());\n if (success) break;\n }\n };\n \n newState.pc.onconnectionstatechange = () => {\n logLine(\"💬\", { event: \"pc-state\", userId: newState.userId, state: newState.pc.connectionState });\n };\n state = newState;\n\n // New user\n users.set(state.userId, state);\n isNewPeer = true;\n } else if (state) {\n clearTimeout(state.expirationTimeout);\n state.expirationTimeout = 0;\n state.peers.set(peer.peerId, peer);\n }\n return [state, isNewPeer];\n }\n\n function leaveUser(userId: string) {\n onLeaveUser?.(userId);\n const p = users.get(userId);\n if (!p) return;\n try { p.pc.close(); } catch {}\n users.delete(userId);\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\", { error: \"add-ice-failed\", userId: state.userId, detail: String(e) });\n }\n }\n }\n\n const roomsEntered = new Map<string, { room: string; host: string; exitRoom: () => void }>();\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>((resolve, reject) => {\n async function makeOffer(user: IPeer) {\n // Offer flow: createOffer -> setLocalDescription -> send localDescription\n const [state] = 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 const { exitRoom, } = enterRoom({\n userId,\n appId,\n room,\n host,\n logLine,\n workerUrl,\n autoRejoin: true,\n\n onOpen() {\n console.log(\"onOpen\");\n resolve();\n },\n onError() {\n console.log(\"onError\");\n reject();\n },\n onClose(ev) {\n console.log(\"onClose\", ev);\n },\n\n // Existing peers initiate to the newcomer (Option 1)\n onPeerJoined(joiningUsers: IPeer<SigType, SigPayload>[]) {\n joiningUsers.forEach(user => {\n const [state, isNewPeer] = getPeer(user);\n if (!isNewPeer) return;\n const pc = state.pc;\n receivePeerConnection({ pc, userId: user.userId, initiator: true });\n makeOffer(user);\n });\n },\n\n onPeerLeft(leavingUsers: { userId: string; peerId: string }[]) {\n leavingUsers.forEach(({ userId, peerId }) => {\n const state = users.get(userId);\n if (!state) return;\n state.peers.delete(peerId);\n if (!state.peers.size) {\n state.expirationTimeout = setTimeout(() => leaveUser(userId), peerlessUserExpiration ?? 0);\n }\n });\n },\n\n async onMessage(type: SigType, payload: any, from: IPeer) {\n const [state] = getPeer(from);\n const pc = state.pc;\n\n if (type === \"offer\") {\n receivePeerConnection({ pc, userId: from.userId, initiator: false });\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\", { error: \"add-ice-failed\", userId: state.userId, detail: String(e) });\n }\n return;\n }\n },\n });\n roomsEntered.set(`${host}/room/${room}`, { exitRoom, room, host });\n });\n }\n\n return {\n userId,\n enterRoom: enter,\n exitRoom: exit,\n leaveUser,\n end() {\n roomsEntered.forEach(({ exitRoom }) => exitRoom());\n roomsEntered.clear();\n users.forEach(({ userId }) => leaveUser(userId));\n users.clear();\n },\n };\n}\n\n\n\n",
8
- "import { EnterRoom, enterRoom } from \"./signal-room\";\nimport { SigType, SigPayload, collectPeerConnections } from \"./webrtc-peer-collector\";\n\ntype UserListener = (user: string, action: \"join\"|\"leave\", users: string[]) => void;\n\nexport function enterWorld({\n appId, logLine = console.debug, enterRoomFunction = enterRoom, peerlessUserExpiration, workerUrl,\n}: {\n appId: string;\n logLine?: (direction: string, obj?: any) => void;\n enterRoomFunction?: EnterRoom<SigType, SigPayload>;\n peerlessUserExpiration?: number;\n workerUrl?: URL;\n}) {\n const userIds: string[] = [];\n const rtcConfig: RTCConfiguration = {\n iceServers: [{ urls: \"stun:stun.l.google.com:19302\" }],\n };\n\n const messagesListeners = new Set<(data: any, from: string) => void>();\n\n function wireDataChannel(userId: string, dc: RTCDataChannel) {\n dc.onopen = () => {\n logLine(\"💬\", { event: \"dc-open\", userId });\n userIds.push(userId);\n userListeners.forEach(listener => listener(userId, \"join\", userIds));\n };\n dc.onmessage = ({ data }) => {\n messagesListeners.forEach(listener => listener(data as any, userId));\n logLine(\"💬\", { event: \"dc-message\", userId, data });\n };\n dc.onclose = () => {\n logLine(\"💬\", { event: \"dc-close\", userId });\n userIds.splice(userIds.indexOf(userId), 1);\n userListeners.forEach(listener => listener(userId, \"leave\", userIds));\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 { userId, enterRoom, exitRoom, leaveUser, end: endPeerCollection } = collectPeerConnections({\n appId,\n rtcConfig,\n enterRoomFunction,\n logLine,\n workerUrl,\n peerlessUserExpiration,\n onLeaveUser(userId: string) {\n const dc = dataChannels.get(userId);\n try { dc?.close(); } catch { }\n dataChannels.delete(userId);\n },\n receivePeerConnection({ pc, userId, initiator }) {\n if (initiator) {\n const dc = pc.createDataChannel(\"data\");\n wireDataChannel(userId, dc);\n dataChannels.set(userId, dc);\n } else {\n pc.ondatachannel = (ev) => {\n const dc = ev.channel;\n wireDataChannel(userId, dc);\n dataChannels.set(userId, dc);\n pc.ondatachannel = null;\n };\n }\n },\n });\n\n function send(data: any, userId?: string) {\n dataChannels.forEach((dataChannel, pUserId) => {\n if (userId && pUserId !== userId) return;\n if (dataChannel.readyState === \"open\") dataChannel.send(data);\n });\n }\n\n function removeMessageListener(listener: (data: any, from: string) => void) {\n messagesListeners.delete(listener);\n }\n\n function addMessageListener(listener: (data: any, 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 enterRoom,\n exitRoom,\n leaveUser,\n getUsers: () => userIds,\n addMessageListener,\n removeMessageListener,\n addUserListener,\n removeUserListener,\n end() {\n dataChannels.forEach((dataChannel) => {\n try { dataChannel.close(); } catch { }\n });\n dataChannels.clear();\n endPeerCollection();\n userListeners.clear();\n userIds.length = 0;\n },\n };\n}\n"
7
+ "import { IPeer } from \"./impl/signal-room\";\nimport { EnterRoom, enterRoom } from \"./signal-room\";\n\nexport type SigType = \"offer\" | \"answer\" | \"ice\";\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 peers: Map<string, IPeer<SigType, SigPayload>>;\n\n expirationTimeout?: number;\n};\n\nconst DEFAULT_ENTER_ROOM = enterRoom;\n\n\nexport function collectPeerConnections({\n appId,\n receivePeerConnection,\n peerlessUserExpiration,\n rtcConfig = { iceServers: [{ urls: \"stun:stun.l.google.com:19302\" }] },\n enterRoomFunction: enterRoom = DEFAULT_ENTER_ROOM,\n logLine = console.debug,\n onLeaveUser,\n workerUrl,\n onRoomReady,\n onRoomClose,\n}: {\n appId: string;\n rtcConfig?: 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: { pc: RTCPeerConnection, userId: string, initiator: boolean }): void;\n onRoomReady?(info: { host: string; room: string }): void;\n onRoomClose?(info: { host: string; room: string; ev: Pick<CloseEvent, \"reason\"|\"code\"|\"wasClean\"> }): void;\n}) {\n const userId = `user-${crypto.randomUUID()}`;\n const users: Map<string, UserState> = new Map();\n\n function getPeer(peer: IPeer<SigType, SigPayload>): [UserState, boolean] {\n let state = users.get(peer.userId);\n let isNewPeer = false;\n if (!state) {\n const newState: UserState = {\n userId: peer.userId,\n pc: new RTCPeerConnection(rtcConfig),\n pendingRemoteIce: [],\n peers: new Map(),\n };\n newState.peers.set(peer.peerId, peer);\n users.set(peer.userId, newState);\n\n // Send local ICE candidates to this peer\n newState.pc.onicecandidate = (ev) => {\n if (!ev.candidate) return;\n for(let user of newState.peers.values()) {\n const success = user.receive(\"ice\", ev.candidate.toJSON());\n if (success) break;\n }\n };\n \n newState.pc.onconnectionstatechange = () => {\n logLine(\"💬\", { event: \"pc-state\", userId: newState.userId, state: newState.pc.connectionState });\n };\n state = newState;\n\n // New user\n users.set(state.userId, state);\n isNewPeer = true;\n } else if (state) {\n clearTimeout(state.expirationTimeout);\n state.expirationTimeout = 0;\n state.peers.set(peer.peerId, peer);\n }\n return [state, isNewPeer];\n }\n\n function leaveUser(userId: string) {\n onLeaveUser?.(userId);\n const p = users.get(userId);\n if (!p) return;\n try { p.pc.close(); } catch {}\n users.delete(userId);\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\", { error: \"add-ice-failed\", userId: state.userId, detail: String(e) });\n }\n }\n }\n\n const roomsEntered = new Map<string, { room: string; host: string; exitRoom: () => void }>();\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>((resolve, reject) => {\n async function makeOffer(user: IPeer) {\n // Offer flow: createOffer -> setLocalDescription -> send localDescription\n const [state] = 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 const { exitRoom, } = enterRoom({\n userId,\n appId,\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) {\n onRoomClose?.({room, host, ev});\n },\n\n // Existing peers initiate to the newcomer (Option 1)\n onPeerJoined(joiningUsers: IPeer<SigType, SigPayload>[]) {\n joiningUsers.forEach(user => {\n const [state, isNewPeer] = getPeer(user);\n if (!isNewPeer) return;\n const pc = state.pc;\n receivePeerConnection({ pc, userId: user.userId, initiator: true });\n makeOffer(user);\n });\n },\n\n onPeerLeft(leavingUsers: { userId: string; peerId: string }[]) {\n leavingUsers.forEach(({ userId, peerId }) => {\n const state = users.get(userId);\n if (!state) return;\n state.peers.delete(peerId);\n if (!state.peers.size) {\n state.expirationTimeout = setTimeout(() => leaveUser(userId), peerlessUserExpiration ?? 0);\n }\n });\n },\n\n async onMessage(type: SigType, payload: any, from: IPeer) {\n const [state] = getPeer(from);\n const pc = state.pc;\n\n if (type === \"offer\") {\n receivePeerConnection({ pc, userId: from.userId, initiator: false });\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\", { error: \"add-ice-failed\", userId: state.userId, detail: String(e) });\n }\n return;\n }\n },\n });\n roomsEntered.set(`${host}/room/${room}`, { exitRoom, room, host });\n });\n }\n\n return {\n userId,\n enterRoom: enter,\n exitRoom: exit,\n leaveUser,\n end() {\n roomsEntered.forEach(({ exitRoom }) => exitRoom());\n roomsEntered.clear();\n users.forEach(({ userId }) => leaveUser(userId));\n users.clear();\n },\n };\n}\n\n\n\n",
8
+ "import { EnterRoom, enterRoom } from \"./signal-room\";\nimport { SigType, SigPayload, collectPeerConnections } from \"./webrtc-peer-collector\";\n\ntype UserListener = (user: string, action: \"join\"|\"leave\", users: string[]) => void;\n\nexport function enterWorld({\n appId, logLine = console.debug, enterRoomFunction = enterRoom, peerlessUserExpiration, workerUrl,\n onRoomReady,\n onRoomClose,\n dataChannelOptions,\n}: {\n appId: 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: { host: string; room: string; ev: Pick<CloseEvent, \"reason\"|\"code\"|\"wasClean\"> }): void;\n dataChannelOptions?: RTCDataChannelInit;\n}) {\n const userIds: string[] = [];\n const rtcConfig: RTCConfiguration = {\n iceServers: [{ urls: \"stun:stun.l.google.com:19302\" }],\n };\n\n const messagesListeners = new Set<(data: any, from: string) => void>();\n\n function wireDataChannel(userId: string, dc: RTCDataChannel) {\n dc.onopen = () => {\n logLine(\"💬\", { event: \"dc-open\", userId });\n userIds.push(userId);\n userListeners.forEach(listener => listener(userId, \"join\", userIds));\n };\n dc.onmessage = ({ data }) => {\n messagesListeners.forEach(listener => listener(data as any, userId));\n logLine(\"💬\", { event: \"dc-message\", userId, data });\n };\n dc.onclose = () => {\n logLine(\"💬\", { event: \"dc-close\", userId });\n userIds.splice(userIds.indexOf(userId), 1);\n userListeners.forEach(listener => listener(userId, \"leave\", userIds));\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 { userId, enterRoom, exitRoom, leaveUser, end: endPeerCollection } = collectPeerConnections({\n appId,\n rtcConfig,\n enterRoomFunction,\n logLine,\n workerUrl,\n peerlessUserExpiration,\n onRoomReady,\n onRoomClose,\n onLeaveUser(userId: string) {\n const dc = dataChannels.get(userId);\n try { dc?.close(); } catch { }\n dataChannels.delete(userId);\n },\n receivePeerConnection({ pc, userId, initiator }) {\n if (initiator) {\n const dc = pc.createDataChannel(\"data\", dataChannelOptions);\n wireDataChannel(userId, dc);\n dataChannels.set(userId, dc);\n } else {\n pc.ondatachannel = (ev) => {\n const dc = ev.channel;\n wireDataChannel(userId, dc);\n dataChannels.set(userId, dc);\n pc.ondatachannel = null;\n };\n }\n },\n });\n\n function send(data: any, userId?: string) {\n dataChannels.forEach((dataChannel, pUserId) => {\n if (userId && pUserId !== userId) return;\n if (dataChannel.readyState === \"open\") dataChannel.send(data);\n });\n }\n\n function removeMessageListener(listener: (data: any, from: string) => void) {\n messagesListeners.delete(listener);\n }\n\n function addMessageListener(listener: (data: any, 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 enterRoom,\n exitRoom,\n leaveUser,\n getUsers: () => userIds,\n addMessageListener,\n removeMessageListener,\n addUserListener,\n removeUserListener,\n end() {\n dataChannels.forEach((dataChannel) => {\n try { dataChannel.close(); } catch { }\n });\n dataChannels.clear();\n endPeerCollection();\n userListeners.clear();\n userIds.length = 0;\n },\n };\n}\n"
9
9
  ],
10
- "mappings": "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",
11
- "debugId": "E6FC686739F0F5E364756E2164756E21",
10
+ "mappings": "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",
11
+ "debugId": "5576A8B4E2AE921B64756E2164756E21",
12
12
  "names": []
13
13
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/browser/sample/index.ts"],"names":[],"mappings":"AAuBA,wBAAgB,QAAQ,SAEvB;AAED,wBAAgB,WAAW,eAqC1B;AAED,wBAAgB,UAAU,eAoHzB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/browser/sample/index.ts"],"names":[],"mappings":"AAuBA,wBAAgB,QAAQ,SAEvB;AAED,wBAAgB,WAAW,eAqC1B;AAED,wBAAgB,UAAU,eAuHzB"}
@@ -1,7 +1,7 @@
1
1
  import { EnterRoom } from "./signal-room";
2
2
  export type SigType = "offer" | "answer" | "ice";
3
3
  export type SigPayload = RTCSessionDescriptionInit | RTCIceCandidateInit;
4
- export declare function collectPeerConnections({ appId, receivePeerConnection, peerlessUserExpiration, rtcConfig, enterRoomFunction: enterRoom, logLine, onLeaveUser, workerUrl, }: {
4
+ export declare function collectPeerConnections({ appId, receivePeerConnection, peerlessUserExpiration, rtcConfig, enterRoomFunction: enterRoom, logLine, onLeaveUser, workerUrl, onRoomReady, onRoomClose, }: {
5
5
  appId: string;
6
6
  rtcConfig?: RTCConfiguration;
7
7
  enterRoomFunction?: EnterRoom<SigType, SigPayload>;
@@ -14,6 +14,15 @@ export declare function collectPeerConnections({ appId, receivePeerConnection, p
14
14
  userId: string;
15
15
  initiator: boolean;
16
16
  }): void;
17
+ onRoomReady?(info: {
18
+ host: string;
19
+ room: string;
20
+ }): void;
21
+ onRoomClose?(info: {
22
+ host: string;
23
+ room: string;
24
+ ev: Pick<CloseEvent, "reason" | "code" | "wasClean">;
25
+ }): void;
17
26
  }): {
18
27
  userId: string;
19
28
  enterRoom: ({ room, host }: {
@@ -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,eAAe,CAAC;AAErD,MAAM,MAAM,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;AACjD,MAAM,MAAM,UAAU,GAAG,yBAAyB,GAAG,mBAAmB,CAAC;AAkBzE,wBAAgB,sBAAsB,CAAC,EACrC,KAAK,EACL,qBAAqB,EACrB,sBAAsB,EACtB,SAAsE,EACtE,iBAAiB,EAAE,SAA8B,EACjD,OAAuB,EACvB,WAAW,EACX,SAAS,GACV,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,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;QAAE,EAAE,EAAE,iBAAiB,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;CACxG;;gCA4EgC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;KAAE;+BAThC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;KAAE;wBAzBlC,MAAM;;EAqJlC"}
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,eAAe,CAAC;AAErD,MAAM,MAAM,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;AACjD,MAAM,MAAM,UAAU,GAAG,yBAAyB,GAAG,mBAAmB,CAAC;AAkBzE,wBAAgB,sBAAsB,CAAC,EACrC,KAAK,EACL,qBAAqB,EACrB,sBAAsB,EACtB,SAAsE,EACtE,iBAAiB,EAAE,SAA8B,EACjD,OAAuB,EACvB,WAAW,EACX,SAAS,EACT,WAAW,EACX,WAAW,GACZ,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,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;QAAE,EAAE,EAAE,iBAAiB,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACvG,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;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,GAAC,MAAM,GAAC,UAAU,CAAC,CAAA;KAAE,GAAG,IAAI,CAAC;CAC5G;;gCA4EgC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;KAAE;+BAThC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;KAAE;wBAzBlC,MAAM;;EAqJlC"}
@@ -1,4 +1,4 @@
1
- function j(A){let{userId:b,appId:x,room:E,host:C,autoRejoin:W=!0,logLine:_}=A,J=!1,q=0,Q,N,M=!0,D=new Map,Z=`wss://${C}/room/${x}/${E}?userId=${encodeURIComponent(b)}`;function O(){if(J)return;Q=new WebSocket(Z),Q.onopen=()=>{if(M)A.onOpen?.(),M=!1;q=0},Q.onmessage=(B)=>{try{let z=JSON.parse(B.data);if(_?.("\uD83D\uDDA5️ ➡️ \uD83D\uDC64",z),z.type==="peer-joined"||z.type==="peer-left")G(z.users);else if(z.peerId&&z.userId)A.onMessage(z.type,z.payload,{userId:z.userId,peerId:z.peerId,receive:(H,V)=>S(H,z.peerId,V)})}catch{_?.("⚠️ ERROR",{error:"invalid-json"})}},Q.onclose=(B)=>{let H=[1001,1006,1011,1012,1013].includes(B.code);if(W&&!J&&H){let V=Math.min(Math.pow(2,q)*1000,30000),F=Math.random()*1000,K=V+F;_?.("\uD83D\uDD04 RECONNECTING",{attempt:q+1,delayMs:Math.round(K)}),q++,N=setTimeout(O,K)}else A.onClose?.({code:B.code,reason:B.reason,wasClean:B.wasClean})},Q.onerror=()=>A.onError?.()}function S(B,z,H){if(J||Q.readyState!==WebSocket.OPEN)return!1;let V={type:B,to:z,payload:H};return Q.send(JSON.stringify(V)),_?.("\uD83D\uDC64 ➡️ \uD83D\uDDA5️",V),!0}function G(B){let z=[],H=[],V=new Set;B.forEach(({userId:F,peerId:K})=>{if(F===b)return;if(!D.has(K)){let Y={userId:F,peerId:K,receive:($,X)=>S($,K,X)};D.set(K,Y),z.push(Y)}V.add(K)});for(let[F,K]of D.entries())if(!V.has(F))D.delete(F),H.push({peerId:F,userId:K.userId});if(z.length)A.onPeerJoined(z);if(H.length)A.onPeerLeft(H)}return O(),{exitRoom:()=>{J=!0,clearTimeout(N),Q.close()}}}function h({userId:A,appId:b,room:x,host:E,autoRejoin:C=!0,onOpen:W,onClose:_,onError:J,onPeerJoined:q,onPeerLeft:Q,onMessage:N,logLine:M,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"),j({userId:A,appId:b,room:x,host:E,autoRejoin:C,onOpen:W,onClose:_,onError:J,onPeerJoined:q,onPeerLeft:Q,onMessage:N});let Z=new Worker(D,{type:"module"}),O=!1;function S({userId:B,peerId:z}){return{userId:B,peerId:z,receive:(H,V)=>{if(O)return!1;return Z.postMessage({cmd:"send",toPeerId:z,type:H,payload:V}),!0}}}let G=(B)=>{let z=B.data;if(z.kind==="open")W?.();else if(z.kind==="close")Z.terminate(),_?.(z.ev);else if(z.kind==="error")J?.();else if(z.kind==="peer-joined")q(z.users.map((H)=>S({userId:H.userId,peerId:H.peerId})));else if(z.kind==="peer-left")Q(z.users);else if(z.kind==="message")N(z.type,z.payload,S({userId:z.fromUserId,peerId:z.fromPeerId}));else if(z.kind==="log")M?.(z.direction,z.obj)};return Z.addEventListener("message",G),Z.postMessage({cmd:"enter",userId:A,appId:b,room:x,host:E,autoRejoin:C}),{exitRoom:()=>{O=!0,Z.removeEventListener("message",G),Z.postMessage({cmd:"exit"})}}}var L=h;function c({appId:A,receivePeerConnection:b,peerlessUserExpiration:x,rtcConfig:E={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},enterRoomFunction:C=L,logLine:W=console.debug,onLeaveUser:_,workerUrl:J}){let q=`user-${crypto.randomUUID()}`,Q=new Map;function N(G){let B=Q.get(G.userId),z=!1;if(!B){let H={userId:G.userId,pc:new RTCPeerConnection(E),pendingRemoteIce:[],peers:new Map};H.peers.set(G.peerId,G),Q.set(G.userId,H),H.pc.onicecandidate=(V)=>{if(!V.candidate)return;for(let F of H.peers.values())if(F.receive("ice",V.candidate.toJSON()))break},H.pc.onconnectionstatechange=()=>{W("\uD83D\uDCAC",{event:"pc-state",userId:H.userId,state:H.pc.connectionState})},B=H,Q.set(B.userId,B),z=!0}else if(B)clearTimeout(B.expirationTimeout),B.expirationTimeout=0,B.peers.set(G.peerId,G);return[B,z]}function M(G){_?.(G);let B=Q.get(G);if(!B)return;try{B.pc.close()}catch{}Q.delete(G)}async function D(G){if(!G.pc.remoteDescription)return;let B=G.pendingRemoteIce;G.pendingRemoteIce=[];for(let z of B)try{await G.pc.addIceCandidate(z)}catch(H){W("⚠️ ERROR",{error:"add-ice-failed",userId:G.userId,detail:String(H)})}}let Z=new Map;function O({room:G,host:B}){let z=`${B}/room/${G}`,H=Z.get(z);if(H)H.exitRoom(),Z.delete(z)}function S({room:G,host:B}){return new Promise((z,H)=>{async function V(K){let[Y]=N(K),$=Y.pc,X=await $.createOffer();await $.setLocalDescription(X),K.receive("offer",$.localDescription?.toJSON())}let{exitRoom:F}=C({userId:q,appId:A,room:G,host:B,logLine:W,workerUrl:J,autoRejoin:!0,onOpen(){console.log("onOpen"),z()},onError(){console.log("onError"),H()},onClose(K){console.log("onClose",K)},onPeerJoined(K){K.forEach((Y)=>{let[$,X]=N(Y);if(!X)return;let T=$.pc;b({pc:T,userId:Y.userId,initiator:!0}),V(Y)})},onPeerLeft(K){K.forEach(({userId:Y,peerId:$})=>{let X=Q.get(Y);if(!X)return;if(X.peers.delete($),!X.peers.size)X.expirationTimeout=setTimeout(()=>M(Y),x??0)})},async onMessage(K,Y,$){let[X]=N($),T=X.pc;if(K==="offer"){b({pc:T,userId:$.userId,initiator:!1}),await T.setRemoteDescription(Y);let R=await T.createAnswer();await T.setLocalDescription(R),$.receive("answer",T.localDescription?.toJSON()),await D(X);return}if(K==="answer"){await T.setRemoteDescription(Y),await D(X);return}if(K==="ice"){let R=Y;if(!T.remoteDescription){X.pendingRemoteIce.push(R);return}try{await T.addIceCandidate(R)}catch(k){W("⚠️ ERROR",{error:"add-ice-failed",userId:X.userId,detail:String(k)})}return}}});Z.set(`${B}/room/${G}`,{exitRoom:F,room:G,host:B})})}return{userId:q,enterRoom:S,exitRoom:O,leaveUser:M,end(){Z.forEach(({exitRoom:G})=>G()),Z.clear(),Q.forEach(({userId:G})=>M(G)),Q.clear()}}}export{c as collectPeerConnections};
1
+ function R(A){let{userId:M,appId:x,room:E,host:C,autoRejoin:_=!0,logLine:J}=A,N=!1,b=0,Z,O,$=!0,q=new Map,F=`wss://${C}/room/${x}/${E}?userId=${encodeURIComponent(M)}`;function S(){if(N)return;Z=new WebSocket(F),Z.onopen=()=>{if($)A.onOpen?.(),$=!1;b=0},Z.onmessage=(K)=>{try{let z=JSON.parse(K.data);if(J?.("\uD83D\uDDA5️ ➡️ \uD83D\uDC64",z),z.type==="peer-joined"||z.type==="peer-left")j(z.users);else if(z.peerId&&z.userId)A.onMessage(z.type,z.payload,{userId:z.userId,peerId:z.peerId,receive:(B,H)=>D(B,z.peerId,H)})}catch{J?.("⚠️ ERROR",{error:"invalid-json"})}},Z.onclose=(K)=>{let B=[1001,1006,1011,1012,1013].includes(K.code);if(_&&!N&&B){let H=Math.min(Math.pow(2,b)*1000,30000),G=Math.random()*1000,Q=H+G;J?.("\uD83D\uDD04 RECONNECTING",{attempt:b+1,delayMs:Math.round(Q)}),b++,O=setTimeout(S,Q)}else A.onClose?.({code:K.code,reason:K.reason,wasClean:K.wasClean})},Z.onerror=()=>A.onError?.()}function D(K,z,B){if(N||Z.readyState!==WebSocket.OPEN)return!1;let H={type:K,to:z,payload:B};return Z.send(JSON.stringify(H)),J?.("\uD83D\uDC64 ➡️ \uD83D\uDDA5️",H),!0}function j(K){let z=[],B=[],H=new Set;K.forEach(({userId:G,peerId:Q})=>{if(G===M)return;if(!q.has(Q)){let k={userId:G,peerId:Q,receive:(V,X)=>D(V,Q,X)};q.set(Q,k),z.push(k)}H.add(Q)});for(let[G,Q]of q.entries())if(!H.has(G))q.delete(G),B.push({peerId:G,userId:Q.userId});if(z.length)A.onPeerJoined(z);if(B.length)A.onPeerLeft(B)}return S(),{exitRoom:()=>{N=!0,clearTimeout(O),Z.close()}}}function h({userId:A,appId:M,room:x,host:E,autoRejoin:C=!0,onOpen:_,onClose:J,onError:N,onPeerJoined:b,onPeerLeft:Z,onMessage:O,logLine:$,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"),R({userId:A,appId:M,room:x,host:E,autoRejoin:C,onOpen:_,onClose:J,onError:N,onPeerJoined:b,onPeerLeft:Z,onMessage:O});let F=new Worker(q,{type:"module"}),S=!1;function D({userId:K,peerId:z}){return{userId:K,peerId:z,receive:(B,H)=>{if(S)return!1;return F.postMessage({cmd:"send",toPeerId:z,type:B,payload:H}),!0}}}let j=(K)=>{let z=K.data;if(z.kind==="open")_?.();else if(z.kind==="close")F.terminate(),J?.(z.ev);else if(z.kind==="error")N?.();else if(z.kind==="peer-joined")b(z.users.map((B)=>D({userId:B.userId,peerId:B.peerId})));else if(z.kind==="peer-left")Z(z.users);else if(z.kind==="message")O(z.type,z.payload,D({userId:z.fromUserId,peerId:z.fromPeerId}));else if(z.kind==="log")$?.(z.direction,z.obj)};return F.addEventListener("message",j),F.postMessage({cmd:"enter",userId:A,appId:M,room:x,host:E,autoRejoin:C}),{exitRoom:()=>{S=!0,F.removeEventListener("message",j),F.postMessage({cmd:"exit"})}}}var f=h;function v({appId:A,receivePeerConnection:M,peerlessUserExpiration:x,rtcConfig:E={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},enterRoomFunction:C=f,logLine:_=console.debug,onLeaveUser:J,workerUrl:N,onRoomReady:b,onRoomClose:Z}){let O=`user-${crypto.randomUUID()}`,$=new Map;function q(z){let B=$.get(z.userId),H=!1;if(!B){let G={userId:z.userId,pc:new RTCPeerConnection(E),pendingRemoteIce:[],peers:new Map};G.peers.set(z.peerId,z),$.set(z.userId,G),G.pc.onicecandidate=(Q)=>{if(!Q.candidate)return;for(let k of G.peers.values())if(k.receive("ice",Q.candidate.toJSON()))break},G.pc.onconnectionstatechange=()=>{_("\uD83D\uDCAC",{event:"pc-state",userId:G.userId,state:G.pc.connectionState})},B=G,$.set(B.userId,B),H=!0}else if(B)clearTimeout(B.expirationTimeout),B.expirationTimeout=0,B.peers.set(z.peerId,z);return[B,H]}function F(z){J?.(z);let B=$.get(z);if(!B)return;try{B.pc.close()}catch{}$.delete(z)}async function S(z){if(!z.pc.remoteDescription)return;let B=z.pendingRemoteIce;z.pendingRemoteIce=[];for(let H of B)try{await z.pc.addIceCandidate(H)}catch(G){_("⚠️ ERROR",{error:"add-ice-failed",userId:z.userId,detail:String(G)})}}let D=new Map;function j({room:z,host:B}){let H=`${B}/room/${z}`,G=D.get(H);if(G)G.exitRoom(),D.delete(H)}function K({room:z,host:B}){return new Promise((H,G)=>{async function Q(V){let[X]=q(V),T=X.pc,Y=await T.createOffer();await T.setLocalDescription(Y),V.receive("offer",T.localDescription?.toJSON())}let{exitRoom:k}=C({userId:O,appId:A,room:z,host:B,logLine:_,workerUrl:N,autoRejoin:!0,onOpen(){b?.({room:z,host:B}),H()},onError(){console.error("onError"),G()},onClose(V){Z?.({room:z,host:B,ev:V})},onPeerJoined(V){V.forEach((X)=>{let[T,Y]=q(X);if(!Y)return;let W=T.pc;M({pc:W,userId:X.userId,initiator:!0}),Q(X)})},onPeerLeft(V){V.forEach(({userId:X,peerId:T})=>{let Y=$.get(X);if(!Y)return;if(Y.peers.delete(T),!Y.peers.size)Y.expirationTimeout=setTimeout(()=>F(X),x??0)})},async onMessage(V,X,T){let[Y]=q(T),W=Y.pc;if(V==="offer"){M({pc:W,userId:T.userId,initiator:!1}),await W.setRemoteDescription(X);let L=await W.createAnswer();await W.setLocalDescription(L),T.receive("answer",W.localDescription?.toJSON()),await S(Y);return}if(V==="answer"){await W.setRemoteDescription(X),await S(Y);return}if(V==="ice"){let L=X;if(!W.remoteDescription){Y.pendingRemoteIce.push(L);return}try{await W.addIceCandidate(L)}catch(P){_("⚠️ ERROR",{error:"add-ice-failed",userId:Y.userId,detail:String(P)})}return}}});D.set(`${B}/room/${z}`,{exitRoom:k,room:z,host:B})})}return{userId:O,enterRoom:K,exitRoom:j,leaveUser:F,end(){D.forEach(({exitRoom:z})=>z()),D.clear(),$.forEach(({userId:z})=>F(z)),$.clear()}}}export{v as collectPeerConnections};
2
2
 
3
- //# debugId=2C0632BC28B6CFC864756E2164756E21
3
+ //# debugId=F4E524D250F8313264756E2164756E21
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 peerId: string;\n receive(type: T, payload: P): boolean;\n}\n\n/**\n * enterRoom connects to the signaling room via WebSocket.\n */\nexport function enterRoom<T extends string, P = any>(params: {\n userId: string; appId: string; room: string; 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, peerId: string }[]): void;\n onMessage(type: T, payload: P, from: IPeer<T, P>): void;\n autoRejoin?: boolean;\n}): { exitRoom: () => void } {\n const { userId, appId, 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/${appId}/${room}?userId=${encodeURIComponent(userId)}`;\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 // ... (keep your existing JSON parsing and updatePeers logic here)\n try {\n const msg = JSON.parse(e.data);\n logLine?.(\"🖥️ ➡️ 👤\", msg);\n if (msg.type === \"peer-joined\" || msg.type === \"peer-left\") {\n updatePeers(msg.users);\n } else if (msg.peerId && msg.userId) {\n params.onMessage(msg.type, msg.payload, {\n userId: msg.userId,\n peerId: msg.peerId,\n receive: (type: T, payload: P) => send(type, msg.peerId, payload),\n });\n }\n } catch { logLine?.(\"⚠️ ERROR\", { error: \"invalid-json\" }); }\n };\n\n ws.onclose = (ev: CloseEvent) => {\n\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\", { attempt: retryCount + 1, delayMs: Math.round(delay) });\n \n retryCount++;\n timeoutId = setTimeout(connect, delay);\n } else {\n params.onClose?.({ code: ev.code, reason: ev.reason, wasClean: ev.wasClean });\n }\n };\n\n ws.onerror = () => params.onError?.();\n }\n\n // Helper for sending (uses the current ws instance)\n function send(type: T, toPeerId: string, payload: P) {\n if (exited || ws.readyState !== WebSocket.OPEN) return false;\n const obj = { type, to: toPeerId, payload };\n ws.send(JSON.stringify(obj));\n logLine?.(\"👤 ➡️ 🖥️\", obj);\n return true;\n }\n\n // Helper for peer tracking (logic from your original code)\n function updatePeers(updatedUsers: { peerId: string; userId: string }[]) {\n const joined: IPeer<T, P>[] = [];\n const left: { userId: string; peerId: string }[] = [];\n const updatedPeerSet = new Set<string>();\n\n updatedUsers.forEach(({ userId: pUserId, peerId }) => {\n if (pUserId === userId) return;\n if (!peers.has(peerId)) {\n const newPeer = { userId: pUserId, peerId, receive: (t: T, p: P) => send(t, peerId, p) };\n peers.set(peerId, newPeer);\n joined.push(newPeer);\n }\n updatedPeerSet.add(peerId);\n });\n\n for (const [peerId, peer] of peers.entries()) {\n if (!updatedPeerSet.has(peerId)) {\n peers.delete(peerId);\n left.push({ peerId, userId: peer.userId });\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 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 appId,\n room,\n host,\n autoRejoin = true,\n onOpen,\n onClose,\n onError,\n onPeerJoined,\n onPeerLeft,\n onMessage,\n logLine,\n workerUrl,\n}: {\n userId: string;\n appId: 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, peerId: string}[]) => 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}): { exitRoom: () => void } {\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(\"Warning: enterRoom called without workerUrl; this may cause issues in some environments. You should pass workerUrl explicitly. Use:\", CDN_WORKER_URL);\n return baseEnterRoom<T, P>({\n userId,\n appId,\n room,\n host,\n autoRejoin,\n onOpen,\n onClose,\n onError,\n onPeerJoined,\n onPeerLeft,\n onMessage,\n });\n }\n const worker = new Worker(workerUrl, { type: \"module\" });\n let exited = false;\n\n function makeUser({ userId, peerId }: { userId: string; peerId: string }): IPeer<T, P> {\n return {\n userId,\n peerId,\n receive: (type: T, payload: P) => {\n if (exited) return false;\n worker.postMessage({ cmd: \"send\", toPeerId: peerId, type, payload } 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 }\n else if (ev.kind === \"error\") onError?.();\n else if (ev.kind === \"peer-joined\") onPeerJoined(ev.users.map(ev => makeUser({ userId: ev.userId, peerId: ev.peerId })));\n else if (ev.kind === \"peer-left\") onPeerLeft(ev.users);\n else if (ev.kind === \"message\") onMessage(ev.type, ev.payload, makeUser({ userId: ev.fromUserId, peerId: ev.fromPeerId }));\n else if (ev.kind === \"log\") logLine?.(ev.direction, ev.obj);\n };\n\n worker.addEventListener(\"message\", onWorkerMessage);\n\n worker.postMessage({ cmd: \"enter\", userId, appId, room, host, autoRejoin } as WorkerCommand);\n\n return {\n exitRoom: () => {\n exited = true;\n worker.removeEventListener(\"message\", onWorkerMessage);\n worker.postMessage({ cmd: \"exit\" } as WorkerCommand);\n },\n };\n}\n\nexport type EnterRoom<T extends string, P> = typeof enterRoom<T, P>;\n",
7
- "import { IPeer } from \"./impl/signal-room\";\nimport { EnterRoom, enterRoom } from \"./signal-room\";\n\nexport type SigType = \"offer\" | \"answer\" | \"ice\";\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 peers: Map<string, IPeer<SigType, SigPayload>>;\n\n expirationTimeout?: number;\n};\n\nconst DEFAULT_ENTER_ROOM = enterRoom;\n\n\nexport function collectPeerConnections({\n appId,\n receivePeerConnection,\n peerlessUserExpiration,\n rtcConfig = { iceServers: [{ urls: \"stun:stun.l.google.com:19302\" }] },\n enterRoomFunction: enterRoom = DEFAULT_ENTER_ROOM,\n logLine = console.debug,\n onLeaveUser,\n workerUrl,\n}: {\n appId: string;\n rtcConfig?: 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: { pc: RTCPeerConnection, userId: string, initiator: boolean }): void;\n}) {\n const userId = `user-${crypto.randomUUID()}`;\n const users: Map<string, UserState> = new Map();\n\n function getPeer(peer: IPeer<SigType, SigPayload>): [UserState, boolean] {\n let state = users.get(peer.userId);\n let isNewPeer = false;\n if (!state) {\n const newState: UserState = {\n userId: peer.userId,\n pc: new RTCPeerConnection(rtcConfig),\n pendingRemoteIce: [],\n peers: new Map(),\n };\n newState.peers.set(peer.peerId, peer);\n users.set(peer.userId, newState);\n\n // Send local ICE candidates to this peer\n newState.pc.onicecandidate = (ev) => {\n if (!ev.candidate) return;\n for(let user of newState.peers.values()) {\n const success = user.receive(\"ice\", ev.candidate.toJSON());\n if (success) break;\n }\n };\n \n newState.pc.onconnectionstatechange = () => {\n logLine(\"💬\", { event: \"pc-state\", userId: newState.userId, state: newState.pc.connectionState });\n };\n state = newState;\n\n // New user\n users.set(state.userId, state);\n isNewPeer = true;\n } else if (state) {\n clearTimeout(state.expirationTimeout);\n state.expirationTimeout = 0;\n state.peers.set(peer.peerId, peer);\n }\n return [state, isNewPeer];\n }\n\n function leaveUser(userId: string) {\n onLeaveUser?.(userId);\n const p = users.get(userId);\n if (!p) return;\n try { p.pc.close(); } catch {}\n users.delete(userId);\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\", { error: \"add-ice-failed\", userId: state.userId, detail: String(e) });\n }\n }\n }\n\n const roomsEntered = new Map<string, { room: string; host: string; exitRoom: () => void }>();\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>((resolve, reject) => {\n async function makeOffer(user: IPeer) {\n // Offer flow: createOffer -> setLocalDescription -> send localDescription\n const [state] = 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 const { exitRoom, } = enterRoom({\n userId,\n appId,\n room,\n host,\n logLine,\n workerUrl,\n autoRejoin: true,\n\n onOpen() {\n console.log(\"onOpen\");\n resolve();\n },\n onError() {\n console.log(\"onError\");\n reject();\n },\n onClose(ev) {\n console.log(\"onClose\", ev);\n },\n\n // Existing peers initiate to the newcomer (Option 1)\n onPeerJoined(joiningUsers: IPeer<SigType, SigPayload>[]) {\n joiningUsers.forEach(user => {\n const [state, isNewPeer] = getPeer(user);\n if (!isNewPeer) return;\n const pc = state.pc;\n receivePeerConnection({ pc, userId: user.userId, initiator: true });\n makeOffer(user);\n });\n },\n\n onPeerLeft(leavingUsers: { userId: string; peerId: string }[]) {\n leavingUsers.forEach(({ userId, peerId }) => {\n const state = users.get(userId);\n if (!state) return;\n state.peers.delete(peerId);\n if (!state.peers.size) {\n state.expirationTimeout = setTimeout(() => leaveUser(userId), peerlessUserExpiration ?? 0);\n }\n });\n },\n\n async onMessage(type: SigType, payload: any, from: IPeer) {\n const [state] = getPeer(from);\n const pc = state.pc;\n\n if (type === \"offer\") {\n receivePeerConnection({ pc, userId: from.userId, initiator: false });\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\", { error: \"add-ice-failed\", userId: state.userId, detail: String(e) });\n }\n return;\n }\n },\n });\n roomsEntered.set(`${host}/room/${room}`, { exitRoom, room, host });\n });\n }\n\n return {\n userId,\n enterRoom: enter,\n exitRoom: exit,\n leaveUser,\n end() {\n roomsEntered.forEach(({ exitRoom }) => exitRoom());\n roomsEntered.clear();\n users.forEach(({ userId }) => leaveUser(userId));\n users.clear();\n },\n };\n}\n\n\n\n"
7
+ "import { IPeer } from \"./impl/signal-room\";\nimport { EnterRoom, enterRoom } from \"./signal-room\";\n\nexport type SigType = \"offer\" | \"answer\" | \"ice\";\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 peers: Map<string, IPeer<SigType, SigPayload>>;\n\n expirationTimeout?: number;\n};\n\nconst DEFAULT_ENTER_ROOM = enterRoom;\n\n\nexport function collectPeerConnections({\n appId,\n receivePeerConnection,\n peerlessUserExpiration,\n rtcConfig = { iceServers: [{ urls: \"stun:stun.l.google.com:19302\" }] },\n enterRoomFunction: enterRoom = DEFAULT_ENTER_ROOM,\n logLine = console.debug,\n onLeaveUser,\n workerUrl,\n onRoomReady,\n onRoomClose,\n}: {\n appId: string;\n rtcConfig?: 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: { pc: RTCPeerConnection, userId: string, initiator: boolean }): void;\n onRoomReady?(info: { host: string; room: string }): void;\n onRoomClose?(info: { host: string; room: string; ev: Pick<CloseEvent, \"reason\"|\"code\"|\"wasClean\"> }): void;\n}) {\n const userId = `user-${crypto.randomUUID()}`;\n const users: Map<string, UserState> = new Map();\n\n function getPeer(peer: IPeer<SigType, SigPayload>): [UserState, boolean] {\n let state = users.get(peer.userId);\n let isNewPeer = false;\n if (!state) {\n const newState: UserState = {\n userId: peer.userId,\n pc: new RTCPeerConnection(rtcConfig),\n pendingRemoteIce: [],\n peers: new Map(),\n };\n newState.peers.set(peer.peerId, peer);\n users.set(peer.userId, newState);\n\n // Send local ICE candidates to this peer\n newState.pc.onicecandidate = (ev) => {\n if (!ev.candidate) return;\n for(let user of newState.peers.values()) {\n const success = user.receive(\"ice\", ev.candidate.toJSON());\n if (success) break;\n }\n };\n \n newState.pc.onconnectionstatechange = () => {\n logLine(\"💬\", { event: \"pc-state\", userId: newState.userId, state: newState.pc.connectionState });\n };\n state = newState;\n\n // New user\n users.set(state.userId, state);\n isNewPeer = true;\n } else if (state) {\n clearTimeout(state.expirationTimeout);\n state.expirationTimeout = 0;\n state.peers.set(peer.peerId, peer);\n }\n return [state, isNewPeer];\n }\n\n function leaveUser(userId: string) {\n onLeaveUser?.(userId);\n const p = users.get(userId);\n if (!p) return;\n try { p.pc.close(); } catch {}\n users.delete(userId);\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\", { error: \"add-ice-failed\", userId: state.userId, detail: String(e) });\n }\n }\n }\n\n const roomsEntered = new Map<string, { room: string; host: string; exitRoom: () => void }>();\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>((resolve, reject) => {\n async function makeOffer(user: IPeer) {\n // Offer flow: createOffer -> setLocalDescription -> send localDescription\n const [state] = 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 const { exitRoom, } = enterRoom({\n userId,\n appId,\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) {\n onRoomClose?.({room, host, ev});\n },\n\n // Existing peers initiate to the newcomer (Option 1)\n onPeerJoined(joiningUsers: IPeer<SigType, SigPayload>[]) {\n joiningUsers.forEach(user => {\n const [state, isNewPeer] = getPeer(user);\n if (!isNewPeer) return;\n const pc = state.pc;\n receivePeerConnection({ pc, userId: user.userId, initiator: true });\n makeOffer(user);\n });\n },\n\n onPeerLeft(leavingUsers: { userId: string; peerId: string }[]) {\n leavingUsers.forEach(({ userId, peerId }) => {\n const state = users.get(userId);\n if (!state) return;\n state.peers.delete(peerId);\n if (!state.peers.size) {\n state.expirationTimeout = setTimeout(() => leaveUser(userId), peerlessUserExpiration ?? 0);\n }\n });\n },\n\n async onMessage(type: SigType, payload: any, from: IPeer) {\n const [state] = getPeer(from);\n const pc = state.pc;\n\n if (type === \"offer\") {\n receivePeerConnection({ pc, userId: from.userId, initiator: false });\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\", { error: \"add-ice-failed\", userId: state.userId, detail: String(e) });\n }\n return;\n }\n },\n });\n roomsEntered.set(`${host}/room/${room}`, { exitRoom, room, host });\n });\n }\n\n return {\n userId,\n enterRoom: enter,\n exitRoom: exit,\n leaveUser,\n end() {\n roomsEntered.forEach(({ exitRoom }) => exitRoom());\n roomsEntered.clear();\n users.forEach(({ userId }) => leaveUser(userId));\n users.clear();\n },\n };\n}\n\n\n\n"
8
8
  ],
9
- "mappings": "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",
10
- "debugId": "2C0632BC28B6CFC864756E2164756E21",
9
+ "mappings": "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",
10
+ "debugId": "F4E524D250F8313264756E2164756E21",
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.24",
3
+ "version": "1.0.26",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",