@dobuki/hello-worker 1.0.26 → 1.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/enter-world.d.ts.map +1 -1
- package/dist/enter-world.js +2 -2
- package/dist/enter-world.js.map +6 -6
- package/dist/impl/signal-room.d.ts +0 -2
- package/dist/impl/signal-room.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +6 -6
- package/dist/signal-room.d.ts +0 -1
- package/dist/signal-room.d.ts.map +1 -1
- package/dist/signal-room.js +2 -2
- package/dist/signal-room.js.map +4 -4
- package/dist/signal-room.worker.d.ts +3 -4
- package/dist/signal-room.worker.d.ts.map +1 -1
- package/dist/signal-room.worker.js +2 -2
- package/dist/signal-room.worker.js.map +4 -4
- package/dist/webrtc-peer-collector.d.ts +1 -0
- package/dist/webrtc-peer-collector.d.ts.map +1 -1
- package/dist/webrtc-peer-collector.js +2 -2
- package/dist/webrtc-peer-collector.js.map +5 -5
- package/package.json +1 -1
|
@@ -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,
|
|
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,EACL,OAAO,EACP,UAAU,EAEX,MAAM,yBAAyB,CAAC;AAEjC,KAAK,YAAY,GAAG,CAClB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GAAG,OAAO,EACxB,KAAK,EAAE,MAAM,EAAE,KACZ,IAAI,CAAC;AAEV,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,OAAuB,EACvB,iBAA6B,EAC7B,sBAAsB,EACtB,SAAS,EACT,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;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC,CAAC;KACtD,GAAG,IAAI,CAAC;IACT,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;CACzC;;iBAuFqB,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;;EAkCnD"}
|
package/dist/enter-world.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
function
|
|
1
|
+
function g(Z){let{userId:H,appId:h,room:J,host:N,autoRejoin:b=!0,logLine:q}=Z,_=!1,V=0,K,A,X=!0,$=new Map,F=`wss://${N}/room/${h}/${J}?userId=${encodeURIComponent(H)}`;function Y(z,T,n){if(console.log("SENDING",z,T,n,K.readyState,K),!K)return!1;if(_||K.readyState!==WebSocket.OPEN)return!1;let D={type:z,to:T,payload:n};return K.send(JSON.stringify(D)),q?.("\uD83D\uDC64 ➡️ \uD83D\uDDA5️",D),!0}function M(){if(_)return;K=new WebSocket(F),K.onopen=()=>{if(X)Z.onOpen?.(),X=!1;V=0},K.onmessage=(z)=>{try{let T=JSON.parse(z.data);if(q?.("\uD83D\uDDA5️ ➡️ \uD83D\uDC64",T),T.type==="peer-joined"||T.type==="peer-left")W(T.users);else if(T.userId)Z.onMessage(T.type,T.payload,{userId:T.userId,receive:(n,D)=>Y(n,T.userId,D)})}catch{q?.("⚠️ ERROR",{error:"invalid-json"})}},K.onclose=(z)=>{let n=[1001,1006,1011,1012,1013].includes(z.code);if(b&&!_&&n){let D=Math.min(Math.pow(2,V)*1000,30000),B=Math.random()*1000,Q=D+B;q?.("\uD83D\uDD04 RECONNECTING",{attempt:V+1,delayMs:Math.round(Q)}),V++,A=setTimeout(M,Q)}else Z.onClose?.({code:z.code,reason:z.reason,wasClean:z.wasClean})},K.onerror=(z)=>{console.error("WS Error",z),Z.onError?.()}}function W(z){let T=[],n=[],D=new Set;z.forEach(({userId:B})=>{if(B===H)return;if(!$.has(H)){let Q={userId:B,receive:(R,x)=>Y(R,B,x)};$.set(H,Q),T.push(Q)}D.add(H)});for(let B of $.keys())if(!D.has(B))$.delete(B),n.push({userId:B});if(T.length)Z.onPeerJoined(T);if(n.length)Z.onPeerLeft(n)}return M(),{exitRoom:()=>{_=!0,clearTimeout(A),K.close()}}}function L({userId:Z,appId:H,room:h,host:J,autoRejoin:N=!0,onOpen:b,onClose:q,onError:_,onPeerJoined:V,onPeerLeft:K,onMessage:A,logLine:X,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"),g({userId:Z,appId:H,room:h,host:J,autoRejoin:N,onOpen:b,onClose:q,onError:_,onPeerJoined:V,onPeerLeft:K,onMessage:A});let F=new Worker($,{type:"module"}),Y=!1;function M({userId:z}){return{userId:z,receive:(T,n)=>{if(Y)return!1;return F.postMessage({cmd:"send",toUserId:z,host:J,room:h,type:T,payload:n}),!0}}}let W=(z)=>{let T=z.data;if(T.kind==="open")b?.();else if(T.kind==="close")F.terminate(),q?.(T.ev);else if(T.kind==="error")_?.();else if(T.kind==="peer-joined")V(T.users.map((n)=>M({userId:n.userId})));else if(T.kind==="peer-left")K(T.users);else if(T.kind==="message")A(T.type,T.payload,M({userId:T.fromUserId}));else if(T.kind==="log")X?.(T.direction,T.obj)};return F.addEventListener("message",W),F.postMessage({cmd:"enter",userId:Z,appId:H,room:h,host:J,autoRejoin:N}),{exitRoom:()=>{Y=!0,F.removeEventListener("message",W),F.postMessage({cmd:"exit"})}}}var v=L;function j({appId:Z,receivePeerConnection:H,peerlessUserExpiration:h=5000,rtcConfig:J={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},enterRoomFunction:N=v,logLine:b=console.debug,onLeaveUser:q,workerUrl:_,onRoomReady:V,onRoomClose:K}){let A=`user-${crypto.randomUUID()}`,X=new Map;function $(n){return n.pc=new RTCPeerConnection(J),n.pc.onicecandidate=(D)=>{if(!D.candidate)return;n.peer.receive("ice",D.candidate.toJSON())},n.pc.onconnectionstatechange=()=>{b("\uD83D\uDCAC",{event:"pc-state",userId:n.userId,state:n.pc?.connectionState})},n.pc}function F(n){let D=X.get(n.userId),B=!1;if(!D){let Q={userId:n.userId,pendingRemoteIce:[],peer:n};X.set(n.userId,Q),$(Q),D=Q,X.set(D.userId,D),B=!0}else if(D)clearTimeout(D.expirationTimeout),D.expirationTimeout=0;if(!D.pc)$(D);return D.peer=n,[D,B]}function Y(n){q?.(n);let D=X.get(n);if(!D)return;try{D.pc?.close()}catch{}X.delete(n)}async function M(n){if(!n.pc?.remoteDescription)return;let D=n.pendingRemoteIce;n.pendingRemoteIce=[];for(let B of D)try{await n.pc.addIceCandidate(B)}catch(Q){b("⚠️ ERROR",{error:"add-ice-failed",userId:n.userId,detail:String(Q)})}}let W=new Map;function z({room:n,host:D}){let B=`${D}/room/${n}`,Q=W.get(B);if(Q)Q.exitRoom(),W.delete(B)}function T({room:n,host:D}){return new Promise((B,Q)=>{async function R(c){let[E]=F(c),C=E.pc,G=await C?.createOffer();await C?.setLocalDescription(G),c.receive("offer",C?.localDescription?.toJSON()),console.log("Make offer",C?.localDescription?.toJSON())}let{exitRoom:x}=N({userId:A,appId:Z,room:n,host:D,logLine:b,workerUrl:_,autoRejoin:!0,onOpen(){V?.({room:n,host:D}),B()},onError(){console.error("onError"),Q()},onClose(c){K?.({room:n,host:D,ev:c})},onPeerJoined(c){console.log("PEER JOINED",c),c.forEach((E)=>{let[C,G]=F(E);if(!G)return;let S=C.pc;if(!S)return;async function f(){let O=X.get(E.userId);if(O){O.pc=void 0;let k=$(O);H({pc:k,userId:E.userId,initiator:!0,restart:f}),await new Promise((P)=>setTimeout(P,3000)),R(E)}}H({pc:S,userId:E.userId,initiator:!0,restart:f}),R(E)})},onPeerLeft(c){c.forEach(({userId:E})=>{let C=X.get(E);if(!C)return;C.expirationTimeout=setTimeout(()=>Y(E),h??0)})},async onMessage(c,E,C){let[G]=F(C),S=G.pc;if(!S)return;if(c==="offer"){H({pc:S,userId:C.userId,initiator:!1,restart(){G.pc=void 0}}),await S.setRemoteDescription(E);let f=await S.createAnswer();await S.setLocalDescription(f),C.receive("answer",S.localDescription?.toJSON()),await M(G);return}if(c==="answer"){await S.setRemoteDescription(E),await M(G);return}if(c==="ice"){let f=E;if(!S.remoteDescription){G.pendingRemoteIce.push(f);return}try{await S.addIceCandidate(f)}catch(O){b("⚠️ ERROR",{error:"add-ice-failed",userId:G.userId,detail:String(O)})}return}}});W.set(`${D}/room/${n}`,{exitRoom:x,room:n,host:D})})}return{userId:A,enterRoom:T,exitRoom:z,leaveUser:Y,end(){W.forEach(({exitRoom:n})=>n()),W.clear(),X.forEach(({userId:n})=>Y(n)),X.clear()}}}function t({appId:Z,logLine:H=console.debug,enterRoomFunction:h=L,peerlessUserExpiration:J,workerUrl:N,onRoomReady:b,onRoomClose:q,dataChannelOptions:_}){let V=[],K={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},A=new Set;function X(c,E,C,G){if(C){let S=c.createDataChannel("data",_);$(E,S,G),F.set(E,S)}else{let S=function(f){let O=f.channel;$(E,O,G),F.set(E,O),c.ondatachannel=null};return c.addEventListener("datachannel",S),()=>{c.removeEventListener("datachannel",S)}}}function $(c,E,C){E.onopen=()=>{H("\uD83D\uDCAC",{event:"dc-open",userId:c}),V.push(c),Y.forEach((S)=>S(c,"join",V))};let G=({data:S})=>{A.forEach((f)=>f(S,c)),H("\uD83D\uDCAC",{event:"dc-message",userId:c,data:S})};E.addEventListener("message",G),E.addEventListener("close",()=>{H("\uD83D\uDCAC",{event:"dc-close",userId:c}),V.splice(V.indexOf(c),1),Y.forEach((S)=>S(c,"leave",V)),E.removeEventListener("message",G),C?.()}),E.onerror=()=>H("⚠️ ERROR",{error:"dc-error",userId:c})}let F=new Map,Y=new Set,{userId:M,enterRoom:W,exitRoom:z,leaveUser:T,end:n}=j({appId:Z,rtcConfig:K,enterRoomFunction:h,logLine:H,workerUrl:N,peerlessUserExpiration:J,onRoomReady:b,onRoomClose:q,onLeaveUser(c){let E=F.get(c);try{E?.close()}catch{}F.delete(c)},receivePeerConnection({pc:c,userId:E,initiator:C,restart:G}){X(c,E,C,G)}});function D(c,E){F.forEach((C,G)=>{if(E&&G!==E)return;if(C.readyState==="open")C.send(c)})}function B(c){A.delete(c)}function Q(c){return A.add(c),()=>{B(c)}}function R(c){Y.delete(c)}function x(c){return Y.add(c),()=>{R(c)}}return{userId:M,send:D,enterRoom:W,exitRoom:z,leaveUser:T,getUsers:()=>V,addMessageListener:Q,removeMessageListener:B,addUserListener:x,removeUserListener:R,end(){F.forEach((c)=>{try{c.close()}catch{}}),F.clear(),n(),Y.clear(),V.length=0}}}export{t as enterWorld};
|
|
2
2
|
|
|
3
|
-
//# debugId=
|
|
3
|
+
//# debugId=5294F0290139007864756E2164756E21
|
|
4
4
|
//# sourceMappingURL=enter-world.js.map
|
package/dist/enter-world.js.map
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/browser/impl/signal-room.ts", "../src/browser/signal-room.ts", "../src/browser/webrtc-peer-collector.ts", "../src/browser/enter-world.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"export interface IPeer<T extends string = string, P = any> {\n
|
|
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\"
|
|
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
|
|
8
|
-
"import { EnterRoom, enterRoom } from \"./signal-room\";\nimport {
|
|
5
|
+
"export interface IPeer<T extends string = string, P = any> {\n userId: 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;\n appId: string;\n room: string;\n host: string;\n onOpen?: () => void;\n onClose?: (ev: Pick<CloseEvent, \"code\" | \"reason\" | \"wasClean\">) => void;\n onError?: () => void;\n logLine?: (direction: string, obj?: any) => void;\n onPeerJoined(users: IPeer<T, P>[]): void;\n onPeerLeft(users: { userId: string }[]): void;\n 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(\n userId\n )}`;\n\n // Helper for sending (uses the current ws instance)\n function send(type: T, to: string, payload: P) {\n console.log(\"SENDING\", type, to, payload, ws.readyState, ws);\n if (!ws) return false;\n if (exited || ws.readyState !== WebSocket.OPEN) return false;\n const obj = { type, to, payload };\n ws.send(JSON.stringify(obj));\n logLine?.(\"👤 ➡️ 🖥️\", obj);\n return true;\n }\n\n function connect() {\n if (exited) return;\n\n ws = new WebSocket(wsUrl);\n\n ws.onopen = () => {\n if (initialConnection) {\n params.onOpen?.();\n initialConnection = false;\n }\n retryCount = 0; // Reset backoff on successful connection\n };\n\n ws.onmessage = (e: MessageEvent) => {\n // ... (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.userId) {\n params.onMessage(msg.type, msg.payload, {\n userId: msg.userId,\n receive: (type: T, payload: P) => send(type, msg.userId, payload),\n });\n }\n } catch {\n logLine?.(\"⚠️ ERROR\", { error: \"invalid-json\" });\n }\n };\n\n ws.onclose = (ev: CloseEvent) => {\n // 1. Check if we should even try to reconnect\n const recoverableCodes = [1001, 1006, 1011, 1012, 1013];\n const isRecoverable = recoverableCodes.includes(ev.code);\n\n if (autoRejoin && !exited && isRecoverable) {\n // 2. Exponential Backoff: 1s, 2s, 4s, 8s... capped at 30s\n const backoff = Math.min(Math.pow(2, retryCount) * 1000, 30000);\n // 3. Add Jitter: +/- 1000ms randomness\n const jitter = Math.random() * 1000;\n const delay = backoff + jitter;\n\n logLine?.(\"🔄 RECONNECTING\", {\n attempt: retryCount + 1,\n delayMs: Math.round(delay),\n });\n\n retryCount++;\n timeoutId = setTimeout(connect, delay);\n } else {\n params.onClose?.({\n code: ev.code,\n reason: ev.reason,\n wasClean: ev.wasClean,\n });\n }\n };\n\n ws.onerror = (ev) => {\n console.error(\"WS Error\", ev);\n params.onError?.();\n };\n }\n\n // Helper for peer tracking (logic from your original code)\n function updatePeers(updatedUsers: { userId: string }[]) {\n const joined: IPeer<T, P>[] = [];\n const left: { userId: string }[] = [];\n const updatedPeerSet = new Set<string>();\n\n updatedUsers.forEach(({ userId: pUserId }) => {\n if (pUserId === userId) return;\n if (!peers.has(userId)) {\n const newPeer = {\n userId: pUserId,\n receive: (t: T, p: P) => send(t, pUserId, p),\n };\n peers.set(userId, newPeer);\n joined.push(newPeer);\n }\n updatedPeerSet.add(userId);\n });\n\n for (const userId of peers.keys()) {\n if (!updatedPeerSet.has(userId)) {\n peers.delete(userId);\n left.push({ 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
|
+
"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 }[]) => 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(\n \"Warning: enterRoom called without workerUrl; this may cause issues in some environments. You should pass workerUrl explicitly. Use:\",\n CDN_WORKER_URL\n );\n return baseEnterRoom<T, P>({\n userId,\n 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 }: { userId: string }): IPeer<T, P> {\n return {\n userId,\n receive: (type: T, payload: P) => {\n if (exited) return false;\n worker.postMessage({\n cmd: \"send\",\n toUserId: userId,\n host,\n room,\n type,\n payload,\n } as WorkerCommand);\n return true;\n },\n };\n }\n\n const onWorkerMessage = (e: MessageEvent<RoomEvent<T, P>>) => {\n const ev = e.data;\n\n if (ev.kind === \"open\") onOpen?.();\n else if (ev.kind === \"close\") {\n worker.terminate();\n onClose?.(ev.ev);\n } else if (ev.kind === \"error\") onError?.();\n else if (ev.kind === \"peer-joined\")\n onPeerJoined(ev.users.map((ev) => makeUser({ userId: ev.userId })));\n else if (ev.kind === \"peer-left\") onPeerLeft(ev.users);\n else if (ev.kind === \"message\")\n onMessage(ev.type, ev.payload, makeUser({ userId: ev.fromUserId }));\n else if (ev.kind === \"log\") logLine?.(ev.direction, ev.obj);\n };\n\n worker.addEventListener(\"message\", onWorkerMessage);\n\n worker.postMessage({\n cmd: \"enter\",\n userId,\n appId,\n room,\n host,\n autoRejoin,\n } as WorkerCommand);\n\n return {\n exitRoom: () => {\n exited = true;\n worker.removeEventListener(\"message\", onWorkerMessage);\n worker.postMessage({ cmd: \"exit\" } as WorkerCommand);\n },\n };\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 peer: IPeer<SigType, SigPayload>;\n\n expirationTimeout?: number;\n};\n\nconst DEFAULT_ENTER_ROOM = enterRoom;\n\nexport function collectPeerConnections({\n appId,\n receivePeerConnection,\n peerlessUserExpiration = 5000,\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: {\n pc: RTCPeerConnection;\n userId: string;\n initiator: boolean;\n restart?: () => void;\n }): void;\n onRoomReady?(info: { host: string; room: string }): void;\n onRoomClose?(info: {\n host: string;\n room: string;\n ev: Pick<CloseEvent, \"reason\" | \"code\" | \"wasClean\">;\n }): void;\n}) {\n const userId = `user-${crypto.randomUUID()}`;\n const users: Map<string, UserState> = new Map();\n\n function setupPC(state: UserState) {\n state.pc = new RTCPeerConnection(rtcConfig);\n // Send local ICE candidates to this peer\n state.pc.onicecandidate = (ev) => {\n if (!ev.candidate) return;\n state.peer.receive(\"ice\", ev.candidate.toJSON());\n };\n\n state.pc.onconnectionstatechange = () => {\n logLine(\"💬\", {\n event: \"pc-state\",\n userId: state.userId,\n state: state.pc?.connectionState,\n });\n };\n return state.pc;\n }\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 pendingRemoteIce: [],\n peer,\n };\n users.set(peer.userId, newState);\n\n setupPC(newState);\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 }\n if (!state.pc) {\n setupPC(state);\n }\n state.peer = peer;\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 {\n p.pc?.close();\n } 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\", {\n error: \"add-ice-failed\",\n userId: state.userId,\n detail: String(e),\n });\n }\n }\n }\n\n const roomsEntered = new Map<\n string,\n { room: string; host: string; exitRoom: () => void }\n >();\n\n function exit({ room, host }: { room: string; host: string }) {\n const key = `${host}/room/${room}`;\n const session = roomsEntered.get(key);\n if (session) {\n session.exitRoom();\n roomsEntered.delete(key);\n }\n }\n\n function enter({ room, host }: { room: string; host: string }) {\n return new Promise<void>((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 console.log(\"Make 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 console.log(\"PEER JOINED\", joiningUsers);\n joiningUsers.forEach((user) => {\n const [state, isNewPeer] = getPeer(user);\n if (!isNewPeer) return;\n const pc = state.pc;\n if (!pc) return;\n\n async function restart() {\n const state = users.get(user.userId);\n if (state) {\n state.pc = undefined;\n const pc = setupPC(state);\n receivePeerConnection({\n pc,\n userId: user.userId,\n initiator: true,\n restart,\n });\n await new Promise((resolve) => setTimeout(resolve, 3000));\n makeOffer(user);\n }\n }\n\n receivePeerConnection({\n pc,\n userId: user.userId,\n initiator: true,\n restart,\n });\n makeOffer(user);\n });\n },\n\n onPeerLeft(leavingUsers: { userId: string }[]) {\n leavingUsers.forEach(({ userId }) => {\n const state = users.get(userId);\n if (!state) return;\n state.expirationTimeout = setTimeout(\n () => leaveUser(userId),\n peerlessUserExpiration ?? 0\n );\n });\n },\n\n async onMessage(type: SigType, payload: any, from: IPeer) {\n const [state] = getPeer(from);\n const pc = state.pc;\n if (!pc) return;\n\n if (type === \"offer\") {\n receivePeerConnection({\n pc,\n userId: from.userId,\n initiator: false,\n restart() {\n // reset PC\n state.pc = undefined;\n },\n });\n // Responder: set remote offer\n await pc.setRemoteDescription(payload as RTCSessionDescriptionInit);\n\n // Create and send answer\n const answer = await pc.createAnswer();\n await pc.setLocalDescription(answer);\n\n from.receive(\"answer\", pc.localDescription?.toJSON()!);\n\n // Now safe to apply any queued ICE from this peer\n await flushRemoteIce(state);\n return;\n }\n\n if (type === \"answer\") {\n // Initiator: set remote answer\n await pc.setRemoteDescription(payload as RTCSessionDescriptionInit);\n await flushRemoteIce(state);\n return;\n }\n\n if (type === \"ice\") {\n const ice = payload as RTCIceCandidateInit;\n\n // If we don't have remoteDescription yet, queue it\n if (!pc.remoteDescription) {\n state.pendingRemoteIce.push(ice);\n return;\n }\n\n try {\n await pc.addIceCandidate(ice);\n } catch (e) {\n logLine(\"⚠️ ERROR\", {\n error: \"add-ice-failed\",\n userId: state.userId,\n detail: String(e),\n });\n }\n return;\n }\n },\n });\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",
|
|
8
|
+
"import { EnterRoom, enterRoom } from \"./signal-room\";\nimport {\n SigType,\n SigPayload,\n collectPeerConnections,\n} from \"./webrtc-peer-collector\";\n\ntype UserListener = (\n user: string,\n action: \"join\" | \"leave\",\n users: string[]\n) => void;\n\nexport function enterWorld({\n appId,\n logLine = console.debug,\n enterRoomFunction = enterRoom,\n peerlessUserExpiration,\n 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: {\n host: string;\n room: string;\n ev: Pick<CloseEvent, \"reason\" | \"code\" | \"wasClean\">;\n }): 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 createDataChannel(\n pc: RTCPeerConnection,\n peerUserId: string,\n initiator: boolean,\n restart?: () => void\n ) {\n if (initiator) {\n const dc = pc.createDataChannel(\"data\", dataChannelOptions);\n wireDataChannel(peerUserId, dc, restart);\n dataChannels.set(peerUserId, dc);\n } else {\n function listener(ev: RTCDataChannelEvent) {\n const dc = ev.channel;\n wireDataChannel(peerUserId, dc, restart);\n dataChannels.set(peerUserId, dc);\n pc.ondatachannel = null;\n }\n pc.addEventListener(\"datachannel\", listener);\n return () => {\n pc.removeEventListener(\"datachannel\", listener);\n };\n }\n }\n\n function wireDataChannel(\n userId: string,\n dc: RTCDataChannel,\n restart?: () => void\n ) {\n dc.onopen = () => {\n logLine(\"💬\", { event: \"dc-open\", userId });\n userIds.push(userId);\n userListeners.forEach((listener) => listener(userId, \"join\", userIds));\n };\n const onmessage = ({ data }: MessageEvent) => {\n messagesListeners.forEach((listener) => listener(data as any, userId));\n logLine(\"💬\", { event: \"dc-message\", userId, data });\n };\n dc.addEventListener(\"message\", onmessage);\n dc.addEventListener(\"close\", () => {\n logLine(\"💬\", { event: \"dc-close\", userId });\n userIds.splice(userIds.indexOf(userId), 1);\n userListeners.forEach((listener) => listener(userId, \"leave\", userIds));\n dc.removeEventListener(\"message\", onmessage);\n restart?.();\n });\n dc.onerror = () => logLine(\"⚠️ ERROR\", { error: \"dc-error\", userId });\n }\n\n const dataChannels = new Map<string, RTCDataChannel>();\n const userListeners = new Set<UserListener>();\n\n const {\n userId,\n enterRoom,\n exitRoom,\n leaveUser,\n end: endPeerCollection,\n } = 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 {\n dc?.close();\n } catch {}\n dataChannels.delete(userId);\n },\n receivePeerConnection({ pc, userId, initiator, restart }) {\n createDataChannel(pc, userId, initiator, restart);\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 {\n dataChannel.close();\n } catch {}\n });\n dataChannels.clear();\n endPeerCollection();\n userListeners.clear();\n userIds.length = 0;\n },\n };\n}\n"
|
|
9
9
|
],
|
|
10
|
-
"mappings": "
|
|
11
|
-
"debugId": "
|
|
10
|
+
"mappings": "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",
|
|
11
|
+
"debugId": "5294F0290139007864756E2164756E21",
|
|
12
12
|
"names": []
|
|
13
13
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export interface IPeer<T extends string = string, P = any> {
|
|
2
2
|
userId: string;
|
|
3
|
-
peerId: string;
|
|
4
3
|
receive(type: T, payload: P): boolean;
|
|
5
4
|
}
|
|
6
5
|
/**
|
|
@@ -18,7 +17,6 @@ export declare function enterRoom<T extends string, P = any>(params: {
|
|
|
18
17
|
onPeerJoined(users: IPeer<T, P>[]): void;
|
|
19
18
|
onPeerLeft(users: {
|
|
20
19
|
userId: string;
|
|
21
|
-
peerId: string;
|
|
22
20
|
}[]): void;
|
|
23
21
|
onMessage(type: T, payload: P, from: IPeer<T, P>): void;
|
|
24
22
|
autoRejoin?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signal-room.d.ts","sourceRoot":"","sources":["../../src/browser/impl/signal-room.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,GAAG;
|
|
1
|
+
{"version":3,"file":"signal-room.d.ts","sourceRoot":"","sources":["../../src/browser/impl/signal-room.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,GAAG;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC;CACvC;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,GAAG,GAAG,EAAE,MAAM,EAAE;IAC3D,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,UAAU,CAAC,KAAK,IAAI,CAAC;IACzE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IACjD,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC;IACzC,UAAU,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,GAAG,IAAI,CAAC;IAC9C,SAAS,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;IACxD,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,GAAG;IAAE,QAAQ,EAAE,MAAM,IAAI,CAAA;CAAE,CAkI3B"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
function
|
|
1
|
+
function g(W){let{userId:F,appId:h,room:J,host:R,autoRejoin:$=!0,logLine:A}=W,q=!1,K=0,G,Y,Q=!0,X=new Map,z=`wss://${R}/room/${h}/${J}?userId=${encodeURIComponent(F)}`;function V(S,T,n){if(console.log("SENDING",S,T,n,G.readyState,G),!G)return!1;if(q||G.readyState!==WebSocket.OPEN)return!1;let D={type:S,to:T,payload:n};return G.send(JSON.stringify(D)),A?.("\uD83D\uDC64 ➡️ \uD83D\uDDA5️",D),!0}function M(){if(q)return;G=new WebSocket(z),G.onopen=()=>{if(Q)W.onOpen?.(),Q=!1;K=0},G.onmessage=(S)=>{try{let T=JSON.parse(S.data);if(A?.("\uD83D\uDDA5️ ➡️ \uD83D\uDC64",T),T.type==="peer-joined"||T.type==="peer-left")Z(T.users);else if(T.userId)W.onMessage(T.type,T.payload,{userId:T.userId,receive:(n,D)=>V(n,T.userId,D)})}catch{A?.("⚠️ ERROR",{error:"invalid-json"})}},G.onclose=(S)=>{let n=[1001,1006,1011,1012,1013].includes(S.code);if($&&!q&&n){let D=Math.min(Math.pow(2,K)*1000,30000),b=Math.random()*1000,H=D+b;A?.("\uD83D\uDD04 RECONNECTING",{attempt:K+1,delayMs:Math.round(H)}),K++,Y=setTimeout(M,H)}else W.onClose?.({code:S.code,reason:S.reason,wasClean:S.wasClean})},G.onerror=(S)=>{console.error("WS Error",S),W.onError?.()}}function Z(S){let T=[],n=[],D=new Set;S.forEach(({userId:b})=>{if(b===F)return;if(!X.has(F)){let H={userId:b,receive:(N,O)=>V(N,b,O)};X.set(F,H),T.push(H)}D.add(F)});for(let b of X.keys())if(!D.has(b))X.delete(b),n.push({userId:b});if(T.length)W.onPeerJoined(T);if(n.length)W.onPeerLeft(n)}return M(),{exitRoom:()=>{q=!0,clearTimeout(Y),G.close()}}}function L({userId:W,appId:F,room:h,host:J,autoRejoin:R=!0,onOpen:$,onClose:A,onError:q,onPeerJoined:K,onPeerLeft:G,onMessage:Y,logLine:Q,workerUrl:X}){if(!X)return console.warn("Warning: enterRoom called without workerUrl; this may cause issues in some environments. You should pass workerUrl explicitly. Use:","https://cdn.jsdelivr.net/npm/@dobuki/hello-worker/dist/signal-room.worker.min.js"),g({userId:W,appId:F,room:h,host:J,autoRejoin:R,onOpen:$,onClose:A,onError:q,onPeerJoined:K,onPeerLeft:G,onMessage:Y});let z=new Worker(X,{type:"module"}),V=!1;function M({userId:S}){return{userId:S,receive:(T,n)=>{if(V)return!1;return z.postMessage({cmd:"send",toUserId:S,host:J,room:h,type:T,payload:n}),!0}}}let Z=(S)=>{let T=S.data;if(T.kind==="open")$?.();else if(T.kind==="close")z.terminate(),A?.(T.ev);else if(T.kind==="error")q?.();else if(T.kind==="peer-joined")K(T.users.map((n)=>M({userId:n.userId})));else if(T.kind==="peer-left")G(T.users);else if(T.kind==="message")Y(T.type,T.payload,M({userId:T.fromUserId}));else if(T.kind==="log")Q?.(T.direction,T.obj)};return z.addEventListener("message",Z),z.postMessage({cmd:"enter",userId:W,appId:F,room:h,host:J,autoRejoin:R}),{exitRoom:()=>{V=!0,z.removeEventListener("message",Z),z.postMessage({cmd:"exit"})}}}var v=L;function j({appId:W,receivePeerConnection:F,peerlessUserExpiration:h=5000,rtcConfig:J={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},enterRoomFunction:R=v,logLine:$=console.debug,onLeaveUser:A,workerUrl:q,onRoomReady:K,onRoomClose:G}){let Y=`user-${crypto.randomUUID()}`,Q=new Map;function X(n){return n.pc=new RTCPeerConnection(J),n.pc.onicecandidate=(D)=>{if(!D.candidate)return;n.peer.receive("ice",D.candidate.toJSON())},n.pc.onconnectionstatechange=()=>{$("\uD83D\uDCAC",{event:"pc-state",userId:n.userId,state:n.pc?.connectionState})},n.pc}function z(n){let D=Q.get(n.userId),b=!1;if(!D){let H={userId:n.userId,pendingRemoteIce:[],peer:n};Q.set(n.userId,H),X(H),D=H,Q.set(D.userId,D),b=!0}else if(D)clearTimeout(D.expirationTimeout),D.expirationTimeout=0;if(!D.pc)X(D);return D.peer=n,[D,b]}function V(n){A?.(n);let D=Q.get(n);if(!D)return;try{D.pc?.close()}catch{}Q.delete(n)}async function M(n){if(!n.pc?.remoteDescription)return;let D=n.pendingRemoteIce;n.pendingRemoteIce=[];for(let b of D)try{await n.pc.addIceCandidate(b)}catch(H){$("⚠️ ERROR",{error:"add-ice-failed",userId:n.userId,detail:String(H)})}}let Z=new Map;function S({room:n,host:D}){let b=`${D}/room/${n}`,H=Z.get(b);if(H)H.exitRoom(),Z.delete(b)}function T({room:n,host:D}){return new Promise((b,H)=>{async function N(c){let[f]=z(c),C=f.pc,B=await C?.createOffer();await C?.setLocalDescription(B),c.receive("offer",C?.localDescription?.toJSON()),console.log("Make offer",C?.localDescription?.toJSON())}let{exitRoom:O}=R({userId:Y,appId:W,room:n,host:D,logLine:$,workerUrl:q,autoRejoin:!0,onOpen(){K?.({room:n,host:D}),b()},onError(){console.error("onError"),H()},onClose(c){G?.({room:n,host:D,ev:c})},onPeerJoined(c){console.log("PEER JOINED",c),c.forEach((f)=>{let[C,B]=z(f);if(!B)return;let E=C.pc;if(!E)return;async function _(){let x=Q.get(f.userId);if(x){x.pc=void 0;let k=X(x);F({pc:k,userId:f.userId,initiator:!0,restart:_}),await new Promise((P)=>setTimeout(P,3000)),N(f)}}F({pc:E,userId:f.userId,initiator:!0,restart:_}),N(f)})},onPeerLeft(c){c.forEach(({userId:f})=>{let C=Q.get(f);if(!C)return;C.expirationTimeout=setTimeout(()=>V(f),h??0)})},async onMessage(c,f,C){let[B]=z(C),E=B.pc;if(!E)return;if(c==="offer"){F({pc:E,userId:C.userId,initiator:!1,restart(){B.pc=void 0}}),await E.setRemoteDescription(f);let _=await E.createAnswer();await E.setLocalDescription(_),C.receive("answer",E.localDescription?.toJSON()),await M(B);return}if(c==="answer"){await E.setRemoteDescription(f),await M(B);return}if(c==="ice"){let _=f;if(!E.remoteDescription){B.pendingRemoteIce.push(_);return}try{await E.addIceCandidate(_)}catch(x){$("⚠️ ERROR",{error:"add-ice-failed",userId:B.userId,detail:String(x)})}return}}});Z.set(`${D}/room/${n}`,{exitRoom:O,room:n,host:D})})}return{userId:Y,enterRoom:T,exitRoom:S,leaveUser:V,end(){Z.forEach(({exitRoom:n})=>n()),Z.clear(),Q.forEach(({userId:n})=>V(n)),Q.clear()}}}function U({appId:W,logLine:F=console.debug,enterRoomFunction:h=L,peerlessUserExpiration:J,workerUrl:R,onRoomReady:$,onRoomClose:A,dataChannelOptions:q}){let K=[],G={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},Y=new Set;function Q(c,f,C,B){if(C){let E=c.createDataChannel("data",q);X(f,E,B),z.set(f,E)}else{let E=function(_){let x=_.channel;X(f,x,B),z.set(f,x),c.ondatachannel=null};return c.addEventListener("datachannel",E),()=>{c.removeEventListener("datachannel",E)}}}function X(c,f,C){f.onopen=()=>{F("\uD83D\uDCAC",{event:"dc-open",userId:c}),K.push(c),V.forEach((E)=>E(c,"join",K))};let B=({data:E})=>{Y.forEach((_)=>_(E,c)),F("\uD83D\uDCAC",{event:"dc-message",userId:c,data:E})};f.addEventListener("message",B),f.addEventListener("close",()=>{F("\uD83D\uDCAC",{event:"dc-close",userId:c}),K.splice(K.indexOf(c),1),V.forEach((E)=>E(c,"leave",K)),f.removeEventListener("message",B),C?.()}),f.onerror=()=>F("⚠️ ERROR",{error:"dc-error",userId:c})}let z=new Map,V=new Set,{userId:M,enterRoom:Z,exitRoom:S,leaveUser:T,end:n}=j({appId:W,rtcConfig:G,enterRoomFunction:h,logLine:F,workerUrl:R,peerlessUserExpiration:J,onRoomReady:$,onRoomClose:A,onLeaveUser(c){let f=z.get(c);try{f?.close()}catch{}z.delete(c)},receivePeerConnection({pc:c,userId:f,initiator:C,restart:B}){Q(c,f,C,B)}});function D(c,f){z.forEach((C,B)=>{if(f&&B!==f)return;if(C.readyState==="open")C.send(c)})}function b(c){Y.delete(c)}function H(c){return Y.add(c),()=>{b(c)}}function N(c){V.delete(c)}function O(c){return V.add(c),()=>{N(c)}}return{userId:M,send:D,enterRoom:Z,exitRoom:S,leaveUser:T,getUsers:()=>K,addMessageListener:H,removeMessageListener:b,addUserListener:O,removeUserListener:N,end(){z.forEach((c)=>{try{c.close()}catch{}}),z.clear(),n(),V.clear(),K.length=0}}}export{U as enterWorld,g as enterRoom,j as collectPeerConnections};
|
|
2
2
|
|
|
3
|
-
//# debugId=
|
|
3
|
+
//# debugId=0DB1B9B51F20811964756E2164756E21
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/browser/impl/signal-room.ts", "../src/browser/signal-room.ts", "../src/browser/webrtc-peer-collector.ts", "../src/browser/enter-world.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"export interface IPeer<T extends string = string, P = any> {\n
|
|
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\"
|
|
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
|
|
8
|
-
"import { EnterRoom, enterRoom } from \"./signal-room\";\nimport {
|
|
5
|
+
"export interface IPeer<T extends string = string, P = any> {\n userId: 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;\n appId: string;\n room: string;\n host: string;\n onOpen?: () => void;\n onClose?: (ev: Pick<CloseEvent, \"code\" | \"reason\" | \"wasClean\">) => void;\n onError?: () => void;\n logLine?: (direction: string, obj?: any) => void;\n onPeerJoined(users: IPeer<T, P>[]): void;\n onPeerLeft(users: { userId: string }[]): void;\n 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(\n userId\n )}`;\n\n // Helper for sending (uses the current ws instance)\n function send(type: T, to: string, payload: P) {\n console.log(\"SENDING\", type, to, payload, ws.readyState, ws);\n if (!ws) return false;\n if (exited || ws.readyState !== WebSocket.OPEN) return false;\n const obj = { type, to, payload };\n ws.send(JSON.stringify(obj));\n logLine?.(\"👤 ➡️ 🖥️\", obj);\n return true;\n }\n\n function connect() {\n if (exited) return;\n\n ws = new WebSocket(wsUrl);\n\n ws.onopen = () => {\n if (initialConnection) {\n params.onOpen?.();\n initialConnection = false;\n }\n retryCount = 0; // Reset backoff on successful connection\n };\n\n ws.onmessage = (e: MessageEvent) => {\n // ... (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.userId) {\n params.onMessage(msg.type, msg.payload, {\n userId: msg.userId,\n receive: (type: T, payload: P) => send(type, msg.userId, payload),\n });\n }\n } catch {\n logLine?.(\"⚠️ ERROR\", { error: \"invalid-json\" });\n }\n };\n\n ws.onclose = (ev: CloseEvent) => {\n // 1. Check if we should even try to reconnect\n const recoverableCodes = [1001, 1006, 1011, 1012, 1013];\n const isRecoverable = recoverableCodes.includes(ev.code);\n\n if (autoRejoin && !exited && isRecoverable) {\n // 2. Exponential Backoff: 1s, 2s, 4s, 8s... capped at 30s\n const backoff = Math.min(Math.pow(2, retryCount) * 1000, 30000);\n // 3. Add Jitter: +/- 1000ms randomness\n const jitter = Math.random() * 1000;\n const delay = backoff + jitter;\n\n logLine?.(\"🔄 RECONNECTING\", {\n attempt: retryCount + 1,\n delayMs: Math.round(delay),\n });\n\n retryCount++;\n timeoutId = setTimeout(connect, delay);\n } else {\n params.onClose?.({\n code: ev.code,\n reason: ev.reason,\n wasClean: ev.wasClean,\n });\n }\n };\n\n ws.onerror = (ev) => {\n console.error(\"WS Error\", ev);\n params.onError?.();\n };\n }\n\n // Helper for peer tracking (logic from your original code)\n function updatePeers(updatedUsers: { userId: string }[]) {\n const joined: IPeer<T, P>[] = [];\n const left: { userId: string }[] = [];\n const updatedPeerSet = new Set<string>();\n\n updatedUsers.forEach(({ userId: pUserId }) => {\n if (pUserId === userId) return;\n if (!peers.has(userId)) {\n const newPeer = {\n userId: pUserId,\n receive: (t: T, p: P) => send(t, pUserId, p),\n };\n peers.set(userId, newPeer);\n joined.push(newPeer);\n }\n updatedPeerSet.add(userId);\n });\n\n for (const userId of peers.keys()) {\n if (!updatedPeerSet.has(userId)) {\n peers.delete(userId);\n left.push({ 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
|
+
"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 }[]) => 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(\n \"Warning: enterRoom called without workerUrl; this may cause issues in some environments. You should pass workerUrl explicitly. Use:\",\n CDN_WORKER_URL\n );\n return baseEnterRoom<T, P>({\n userId,\n 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 }: { userId: string }): IPeer<T, P> {\n return {\n userId,\n receive: (type: T, payload: P) => {\n if (exited) return false;\n worker.postMessage({\n cmd: \"send\",\n toUserId: userId,\n host,\n room,\n type,\n payload,\n } as WorkerCommand);\n return true;\n },\n };\n }\n\n const onWorkerMessage = (e: MessageEvent<RoomEvent<T, P>>) => {\n const ev = e.data;\n\n if (ev.kind === \"open\") onOpen?.();\n else if (ev.kind === \"close\") {\n worker.terminate();\n onClose?.(ev.ev);\n } else if (ev.kind === \"error\") onError?.();\n else if (ev.kind === \"peer-joined\")\n onPeerJoined(ev.users.map((ev) => makeUser({ userId: ev.userId })));\n else if (ev.kind === \"peer-left\") onPeerLeft(ev.users);\n else if (ev.kind === \"message\")\n onMessage(ev.type, ev.payload, makeUser({ userId: ev.fromUserId }));\n else if (ev.kind === \"log\") logLine?.(ev.direction, ev.obj);\n };\n\n worker.addEventListener(\"message\", onWorkerMessage);\n\n worker.postMessage({\n cmd: \"enter\",\n userId,\n appId,\n room,\n host,\n autoRejoin,\n } as WorkerCommand);\n\n return {\n exitRoom: () => {\n exited = true;\n worker.removeEventListener(\"message\", onWorkerMessage);\n worker.postMessage({ cmd: \"exit\" } as WorkerCommand);\n },\n };\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 peer: IPeer<SigType, SigPayload>;\n\n expirationTimeout?: number;\n};\n\nconst DEFAULT_ENTER_ROOM = enterRoom;\n\nexport function collectPeerConnections({\n appId,\n receivePeerConnection,\n peerlessUserExpiration = 5000,\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: {\n pc: RTCPeerConnection;\n userId: string;\n initiator: boolean;\n restart?: () => void;\n }): void;\n onRoomReady?(info: { host: string; room: string }): void;\n onRoomClose?(info: {\n host: string;\n room: string;\n ev: Pick<CloseEvent, \"reason\" | \"code\" | \"wasClean\">;\n }): void;\n}) {\n const userId = `user-${crypto.randomUUID()}`;\n const users: Map<string, UserState> = new Map();\n\n function setupPC(state: UserState) {\n state.pc = new RTCPeerConnection(rtcConfig);\n // Send local ICE candidates to this peer\n state.pc.onicecandidate = (ev) => {\n if (!ev.candidate) return;\n state.peer.receive(\"ice\", ev.candidate.toJSON());\n };\n\n state.pc.onconnectionstatechange = () => {\n logLine(\"💬\", {\n event: \"pc-state\",\n userId: state.userId,\n state: state.pc?.connectionState,\n });\n };\n return state.pc;\n }\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 pendingRemoteIce: [],\n peer,\n };\n users.set(peer.userId, newState);\n\n setupPC(newState);\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 }\n if (!state.pc) {\n setupPC(state);\n }\n state.peer = peer;\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 {\n p.pc?.close();\n } 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\", {\n error: \"add-ice-failed\",\n userId: state.userId,\n detail: String(e),\n });\n }\n }\n }\n\n const roomsEntered = new Map<\n string,\n { room: string; host: string; exitRoom: () => void }\n >();\n\n function exit({ room, host }: { room: string; host: string }) {\n const key = `${host}/room/${room}`;\n const session = roomsEntered.get(key);\n if (session) {\n session.exitRoom();\n roomsEntered.delete(key);\n }\n }\n\n function enter({ room, host }: { room: string; host: string }) {\n return new Promise<void>((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 console.log(\"Make 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 console.log(\"PEER JOINED\", joiningUsers);\n joiningUsers.forEach((user) => {\n const [state, isNewPeer] = getPeer(user);\n if (!isNewPeer) return;\n const pc = state.pc;\n if (!pc) return;\n\n async function restart() {\n const state = users.get(user.userId);\n if (state) {\n state.pc = undefined;\n const pc = setupPC(state);\n receivePeerConnection({\n pc,\n userId: user.userId,\n initiator: true,\n restart,\n });\n await new Promise((resolve) => setTimeout(resolve, 3000));\n makeOffer(user);\n }\n }\n\n receivePeerConnection({\n pc,\n userId: user.userId,\n initiator: true,\n restart,\n });\n makeOffer(user);\n });\n },\n\n onPeerLeft(leavingUsers: { userId: string }[]) {\n leavingUsers.forEach(({ userId }) => {\n const state = users.get(userId);\n if (!state) return;\n state.expirationTimeout = setTimeout(\n () => leaveUser(userId),\n peerlessUserExpiration ?? 0\n );\n });\n },\n\n async onMessage(type: SigType, payload: any, from: IPeer) {\n const [state] = getPeer(from);\n const pc = state.pc;\n if (!pc) return;\n\n if (type === \"offer\") {\n receivePeerConnection({\n pc,\n userId: from.userId,\n initiator: false,\n restart() {\n // reset PC\n state.pc = undefined;\n },\n });\n // Responder: set remote offer\n await pc.setRemoteDescription(payload as RTCSessionDescriptionInit);\n\n // Create and send answer\n const answer = await pc.createAnswer();\n await pc.setLocalDescription(answer);\n\n from.receive(\"answer\", pc.localDescription?.toJSON()!);\n\n // Now safe to apply any queued ICE from this peer\n await flushRemoteIce(state);\n return;\n }\n\n if (type === \"answer\") {\n // Initiator: set remote answer\n await pc.setRemoteDescription(payload as RTCSessionDescriptionInit);\n await flushRemoteIce(state);\n return;\n }\n\n if (type === \"ice\") {\n const ice = payload as RTCIceCandidateInit;\n\n // If we don't have remoteDescription yet, queue it\n if (!pc.remoteDescription) {\n state.pendingRemoteIce.push(ice);\n return;\n }\n\n try {\n await pc.addIceCandidate(ice);\n } catch (e) {\n logLine(\"⚠️ ERROR\", {\n error: \"add-ice-failed\",\n userId: state.userId,\n detail: String(e),\n });\n }\n return;\n }\n },\n });\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",
|
|
8
|
+
"import { EnterRoom, enterRoom } from \"./signal-room\";\nimport {\n SigType,\n SigPayload,\n collectPeerConnections,\n} from \"./webrtc-peer-collector\";\n\ntype UserListener = (\n user: string,\n action: \"join\" | \"leave\",\n users: string[]\n) => void;\n\nexport function enterWorld({\n appId,\n logLine = console.debug,\n enterRoomFunction = enterRoom,\n peerlessUserExpiration,\n 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: {\n host: string;\n room: string;\n ev: Pick<CloseEvent, \"reason\" | \"code\" | \"wasClean\">;\n }): 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 createDataChannel(\n pc: RTCPeerConnection,\n peerUserId: string,\n initiator: boolean,\n restart?: () => void\n ) {\n if (initiator) {\n const dc = pc.createDataChannel(\"data\", dataChannelOptions);\n wireDataChannel(peerUserId, dc, restart);\n dataChannels.set(peerUserId, dc);\n } else {\n function listener(ev: RTCDataChannelEvent) {\n const dc = ev.channel;\n wireDataChannel(peerUserId, dc, restart);\n dataChannels.set(peerUserId, dc);\n pc.ondatachannel = null;\n }\n pc.addEventListener(\"datachannel\", listener);\n return () => {\n pc.removeEventListener(\"datachannel\", listener);\n };\n }\n }\n\n function wireDataChannel(\n userId: string,\n dc: RTCDataChannel,\n restart?: () => void\n ) {\n dc.onopen = () => {\n logLine(\"💬\", { event: \"dc-open\", userId });\n userIds.push(userId);\n userListeners.forEach((listener) => listener(userId, \"join\", userIds));\n };\n const onmessage = ({ data }: MessageEvent) => {\n messagesListeners.forEach((listener) => listener(data as any, userId));\n logLine(\"💬\", { event: \"dc-message\", userId, data });\n };\n dc.addEventListener(\"message\", onmessage);\n dc.addEventListener(\"close\", () => {\n logLine(\"💬\", { event: \"dc-close\", userId });\n userIds.splice(userIds.indexOf(userId), 1);\n userListeners.forEach((listener) => listener(userId, \"leave\", userIds));\n dc.removeEventListener(\"message\", onmessage);\n restart?.();\n });\n dc.onerror = () => logLine(\"⚠️ ERROR\", { error: \"dc-error\", userId });\n }\n\n const dataChannels = new Map<string, RTCDataChannel>();\n const userListeners = new Set<UserListener>();\n\n const {\n userId,\n enterRoom,\n exitRoom,\n leaveUser,\n end: endPeerCollection,\n } = 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 {\n dc?.close();\n } catch {}\n dataChannels.delete(userId);\n },\n receivePeerConnection({ pc, userId, initiator, restart }) {\n createDataChannel(pc, userId, initiator, restart);\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 {\n dataChannel.close();\n } catch {}\n });\n dataChannels.clear();\n endPeerCollection();\n userListeners.clear();\n userIds.length = 0;\n },\n };\n}\n"
|
|
9
9
|
],
|
|
10
|
-
"mappings": "
|
|
11
|
-
"debugId": "
|
|
10
|
+
"mappings": "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",
|
|
11
|
+
"debugId": "0DB1B9B51F20811964756E2164756E21",
|
|
12
12
|
"names": []
|
|
13
13
|
}
|
package/dist/signal-room.d.ts
CHANGED
|
@@ -11,7 +11,6 @@ export declare function enterRoom<T extends string, P = any>({ userId, appId, ro
|
|
|
11
11
|
onPeerJoined: (users: IPeer<T, P>[]) => void;
|
|
12
12
|
onPeerLeft: (users: {
|
|
13
13
|
userId: string;
|
|
14
|
-
peerId: string;
|
|
15
14
|
}[]) => void;
|
|
16
15
|
onMessage: (type: T, payload: P, from: IPeer<T, P>) => void;
|
|
17
16
|
logLine?: (direction: string, obj?: any) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signal-room.d.ts","sourceRoot":"","sources":["../src/browser/signal-room.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAInD,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,GAAG,GAAG,EAAE,EACnD,MAAM,EACN,KAAK,EACL,IAAI,EACJ,IAAI,EACJ,UAAiB,EACjB,MAAM,EACN,OAAO,EACP,OAAO,EACP,YAAY,EACZ,UAAU,EACV,SAAS,EACT,OAAO,EACP,SAAS,GACV,EAAE;IACD,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"signal-room.d.ts","sourceRoot":"","sources":["../src/browser/signal-room.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAInD,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,GAAG,GAAG,EAAE,EACnD,MAAM,EACN,KAAK,EACL,IAAI,EACJ,IAAI,EACJ,UAAiB,EACjB,MAAM,EACN,OAAO,EACP,OAAO,EACP,YAAY,EACZ,UAAU,EACV,SAAS,EACT,OAAO,EACP,SAAS,GACV,EAAE;IACD,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,UAAU,CAAC,KAAK,IAAI,CAAC;IACzE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC;IAC7C,UAAU,EAAE,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,KAAK,IAAI,CAAC;IAClD,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC;IAC5D,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IAGjD,SAAS,CAAC,EAAE,GAAG,CAAC;CACjB,GAAG;IAAE,QAAQ,EAAE,MAAM,IAAI,CAAA;CAAE,CA6E3B;AAED,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,IAAI,OAAO,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC"}
|
package/dist/signal-room.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
function
|
|
1
|
+
function b(D){let{userId:H,appId:Y,room:Z,host:S,autoRejoin:J=!0,logLine:Q}=D,T=!1,V=0,A,_,O=!0,X=new Map,K=`wss://${S}/room/${Y}/${Z}?userId=${encodeURIComponent(H)}`;function $(z,q,B){if(console.log("SENDING",z,q,B,A.readyState,A),!A)return!1;if(T||A.readyState!==WebSocket.OPEN)return!1;let F={type:z,to:q,payload:B};return A.send(JSON.stringify(F)),Q?.("\uD83D\uDC64 ➡️ \uD83D\uDDA5️",F),!0}function N(){if(T)return;A=new WebSocket(K),A.onopen=()=>{if(O)D.onOpen?.(),O=!1;V=0},A.onmessage=(z)=>{try{let q=JSON.parse(z.data);if(Q?.("\uD83D\uDDA5️ ➡️ \uD83D\uDC64",q),q.type==="peer-joined"||q.type==="peer-left")E(q.users);else if(q.userId)D.onMessage(q.type,q.payload,{userId:q.userId,receive:(B,F)=>$(B,q.userId,F)})}catch{Q?.("⚠️ ERROR",{error:"invalid-json"})}},A.onclose=(z)=>{let B=[1001,1006,1011,1012,1013].includes(z.code);if(J&&!T&&B){let F=Math.min(Math.pow(2,V)*1000,30000),G=Math.random()*1000,W=F+G;Q?.("\uD83D\uDD04 RECONNECTING",{attempt:V+1,delayMs:Math.round(W)}),V++,_=setTimeout(N,W)}else D.onClose?.({code:z.code,reason:z.reason,wasClean:z.wasClean})},A.onerror=(z)=>{console.error("WS Error",z),D.onError?.()}}function E(z){let q=[],B=[],F=new Set;z.forEach(({userId:G})=>{if(G===H)return;if(!X.has(H)){let W={userId:G,receive:(x,M)=>$(x,G,M)};X.set(H,W),q.push(W)}F.add(H)});for(let G of X.keys())if(!F.has(G))X.delete(G),B.push({userId:G});if(q.length)D.onPeerJoined(q);if(B.length)D.onPeerLeft(B)}return N(),{exitRoom:()=>{T=!0,clearTimeout(_),A.close()}}}function L({userId:D,appId:H,room:Y,host:Z,autoRejoin:S=!0,onOpen:J,onClose:Q,onError:T,onPeerJoined:V,onPeerLeft:A,onMessage:_,logLine:O,workerUrl:X}){if(!X)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"),b({userId:D,appId:H,room:Y,host:Z,autoRejoin:S,onOpen:J,onClose:Q,onError:T,onPeerJoined:V,onPeerLeft:A,onMessage:_});let K=new Worker(X,{type:"module"}),$=!1;function N({userId:z}){return{userId:z,receive:(q,B)=>{if($)return!1;return K.postMessage({cmd:"send",toUserId:z,host:Z,room:Y,type:q,payload:B}),!0}}}let E=(z)=>{let q=z.data;if(q.kind==="open")J?.();else if(q.kind==="close")K.terminate(),Q?.(q.ev);else if(q.kind==="error")T?.();else if(q.kind==="peer-joined")V(q.users.map((B)=>N({userId:B.userId})));else if(q.kind==="peer-left")A(q.users);else if(q.kind==="message")_(q.type,q.payload,N({userId:q.fromUserId}));else if(q.kind==="log")O?.(q.direction,q.obj)};return K.addEventListener("message",E),K.postMessage({cmd:"enter",userId:D,appId:H,room:Y,host:Z,autoRejoin:S}),{exitRoom:()=>{$=!0,K.removeEventListener("message",E),K.postMessage({cmd:"exit"})}}}export{L as enterRoom};
|
|
2
2
|
|
|
3
|
-
//# debugId=
|
|
3
|
+
//# debugId=E7EF9A02F66AB60864756E2164756E21
|
|
4
4
|
//# sourceMappingURL=signal-room.js.map
|
package/dist/signal-room.js.map
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/browser/impl/signal-room.ts", "../src/browser/signal-room.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"export interface IPeer<T extends string = string, P = any> {\n
|
|
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\"
|
|
5
|
+
"export interface IPeer<T extends string = string, P = any> {\n userId: 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;\n appId: string;\n room: string;\n host: string;\n onOpen?: () => void;\n onClose?: (ev: Pick<CloseEvent, \"code\" | \"reason\" | \"wasClean\">) => void;\n onError?: () => void;\n logLine?: (direction: string, obj?: any) => void;\n onPeerJoined(users: IPeer<T, P>[]): void;\n onPeerLeft(users: { userId: string }[]): void;\n 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(\n userId\n )}`;\n\n // Helper for sending (uses the current ws instance)\n function send(type: T, to: string, payload: P) {\n console.log(\"SENDING\", type, to, payload, ws.readyState, ws);\n if (!ws) return false;\n if (exited || ws.readyState !== WebSocket.OPEN) return false;\n const obj = { type, to, payload };\n ws.send(JSON.stringify(obj));\n logLine?.(\"👤 ➡️ 🖥️\", obj);\n return true;\n }\n\n function connect() {\n if (exited) return;\n\n ws = new WebSocket(wsUrl);\n\n ws.onopen = () => {\n if (initialConnection) {\n params.onOpen?.();\n initialConnection = false;\n }\n retryCount = 0; // Reset backoff on successful connection\n };\n\n ws.onmessage = (e: MessageEvent) => {\n // ... (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.userId) {\n params.onMessage(msg.type, msg.payload, {\n userId: msg.userId,\n receive: (type: T, payload: P) => send(type, msg.userId, payload),\n });\n }\n } catch {\n logLine?.(\"⚠️ ERROR\", { error: \"invalid-json\" });\n }\n };\n\n ws.onclose = (ev: CloseEvent) => {\n // 1. Check if we should even try to reconnect\n const recoverableCodes = [1001, 1006, 1011, 1012, 1013];\n const isRecoverable = recoverableCodes.includes(ev.code);\n\n if (autoRejoin && !exited && isRecoverable) {\n // 2. Exponential Backoff: 1s, 2s, 4s, 8s... capped at 30s\n const backoff = Math.min(Math.pow(2, retryCount) * 1000, 30000);\n // 3. Add Jitter: +/- 1000ms randomness\n const jitter = Math.random() * 1000;\n const delay = backoff + jitter;\n\n logLine?.(\"🔄 RECONNECTING\", {\n attempt: retryCount + 1,\n delayMs: Math.round(delay),\n });\n\n retryCount++;\n timeoutId = setTimeout(connect, delay);\n } else {\n params.onClose?.({\n code: ev.code,\n reason: ev.reason,\n wasClean: ev.wasClean,\n });\n }\n };\n\n ws.onerror = (ev) => {\n console.error(\"WS Error\", ev);\n params.onError?.();\n };\n }\n\n // Helper for peer tracking (logic from your original code)\n function updatePeers(updatedUsers: { userId: string }[]) {\n const joined: IPeer<T, P>[] = [];\n const left: { userId: string }[] = [];\n const updatedPeerSet = new Set<string>();\n\n updatedUsers.forEach(({ userId: pUserId }) => {\n if (pUserId === userId) return;\n if (!peers.has(userId)) {\n const newPeer = {\n userId: pUserId,\n receive: (t: T, p: P) => send(t, pUserId, p),\n };\n peers.set(userId, newPeer);\n joined.push(newPeer);\n }\n updatedPeerSet.add(userId);\n });\n\n for (const userId of peers.keys()) {\n if (!updatedPeerSet.has(userId)) {\n peers.delete(userId);\n left.push({ 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
|
+
"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 }[]) => 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(\n \"Warning: enterRoom called without workerUrl; this may cause issues in some environments. You should pass workerUrl explicitly. Use:\",\n CDN_WORKER_URL\n );\n return baseEnterRoom<T, P>({\n userId,\n 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 }: { userId: string }): IPeer<T, P> {\n return {\n userId,\n receive: (type: T, payload: P) => {\n if (exited) return false;\n worker.postMessage({\n cmd: \"send\",\n toUserId: userId,\n host,\n room,\n type,\n payload,\n } as WorkerCommand);\n return true;\n },\n };\n }\n\n const onWorkerMessage = (e: MessageEvent<RoomEvent<T, P>>) => {\n const ev = e.data;\n\n if (ev.kind === \"open\") onOpen?.();\n else if (ev.kind === \"close\") {\n worker.terminate();\n onClose?.(ev.ev);\n } else if (ev.kind === \"error\") onError?.();\n else if (ev.kind === \"peer-joined\")\n onPeerJoined(ev.users.map((ev) => makeUser({ userId: ev.userId })));\n else if (ev.kind === \"peer-left\") onPeerLeft(ev.users);\n else if (ev.kind === \"message\")\n onMessage(ev.type, ev.payload, makeUser({ userId: ev.fromUserId }));\n else if (ev.kind === \"log\") logLine?.(ev.direction, ev.obj);\n };\n\n worker.addEventListener(\"message\", onWorkerMessage);\n\n worker.postMessage({\n cmd: \"enter\",\n userId,\n appId,\n room,\n host,\n autoRejoin,\n } as WorkerCommand);\n\n return {\n exitRoom: () => {\n exited = true;\n worker.removeEventListener(\"message\", onWorkerMessage);\n worker.postMessage({ cmd: \"exit\" } as WorkerCommand);\n },\n };\n}\n\nexport type EnterRoom<T extends string, P> = typeof enterRoom<T, P>;\n"
|
|
7
7
|
],
|
|
8
|
-
"mappings": "
|
|
9
|
-
"debugId": "
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "E7EF9A02F66AB60864756E2164756E21",
|
|
10
10
|
"names": []
|
|
11
11
|
}
|
|
@@ -9,20 +9,17 @@ export type RoomEvent<T extends string = string, P = any> = {
|
|
|
9
9
|
kind: "peer-joined";
|
|
10
10
|
users: {
|
|
11
11
|
userId: string;
|
|
12
|
-
peerId: string;
|
|
13
12
|
}[];
|
|
14
13
|
} | {
|
|
15
14
|
kind: "peer-left";
|
|
16
15
|
users: {
|
|
17
16
|
userId: string;
|
|
18
|
-
peerId: string;
|
|
19
17
|
}[];
|
|
20
18
|
} | {
|
|
21
19
|
kind: "message";
|
|
22
20
|
type: T;
|
|
23
21
|
payload: P;
|
|
24
22
|
fromUserId: string;
|
|
25
|
-
fromPeerId: string;
|
|
26
23
|
} | {
|
|
27
24
|
kind: "log";
|
|
28
25
|
direction: string;
|
|
@@ -39,7 +36,9 @@ export type WorkerCommand<T extends string = string, P = any> = {
|
|
|
39
36
|
cmd: "exit";
|
|
40
37
|
} | {
|
|
41
38
|
cmd: "send";
|
|
42
|
-
|
|
39
|
+
host: string;
|
|
40
|
+
room: string;
|
|
41
|
+
toUserId: string;
|
|
43
42
|
type: T;
|
|
44
43
|
payload: P;
|
|
45
44
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signal-room.worker.d.ts","sourceRoot":"","sources":["../src/browser/signal-room.worker.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,GAAG,IACpD;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,IAAI,CAAC,UAAU,
|
|
1
|
+
{"version":3,"file":"signal-room.worker.d.ts","sourceRoot":"","sources":["../src/browser/signal-room.worker.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,GAAG,IACpD;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,UAAU,CAAC,CAAA;CAAE,GACvE;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAE,GACpD;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAE,GAClD;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,CAAC,CAAC;IACR,OAAO,EAAE,CAAC,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;CACpB,GACD;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,GAAG,CAAA;CAAE,CAAC;AAElD,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,GAAG,IACxD;IACE,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,OAAO,CAAC;CACrB,GACD;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GACf;IACE,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,CAAC,CAAC;IACR,OAAO,EAAE,CAAC,CAAC;CACZ,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
function
|
|
1
|
+
function h(E){let{userId:q,appId:T,room:A,host:B,autoRejoin:J=!0,logLine:Y}=E,Z=!1,_=0,G,M,N=!0,$=new Map,L=`wss://${B}/room/${T}/${A}?userId=${encodeURIComponent(q)}`;function W(D,z,H){if(console.log("SENDING",D,z,H,G.readyState,G),!G)return!1;if(Z||G.readyState!==WebSocket.OPEN)return!1;let K={type:D,to:z,payload:H};return G.send(JSON.stringify(K)),Y?.("\uD83D\uDC64 ➡️ \uD83D\uDDA5️",K),!0}function F(){if(Z)return;G=new WebSocket(L),G.onopen=()=>{if(N)E.onOpen?.(),N=!1;_=0},G.onmessage=(D)=>{try{let z=JSON.parse(D.data);if(Y?.("\uD83D\uDDA5️ ➡️ \uD83D\uDC64",z),z.type==="peer-joined"||z.type==="peer-left")P(z.users);else if(z.userId)E.onMessage(z.type,z.payload,{userId:z.userId,receive:(H,K)=>W(H,z.userId,K)})}catch{Y?.("⚠️ ERROR",{error:"invalid-json"})}},G.onclose=(D)=>{let H=[1001,1006,1011,1012,1013].includes(D.code);if(J&&!Z&&H){let K=Math.min(Math.pow(2,_)*1000,30000),O=Math.random()*1000,V=K+O;Y?.("\uD83D\uDD04 RECONNECTING",{attempt:_+1,delayMs:Math.round(V)}),_++,M=setTimeout(F,V)}else E.onClose?.({code:D.code,reason:D.reason,wasClean:D.wasClean})},G.onerror=(D)=>{console.error("WS Error",D),E.onError?.()}}function P(D){let z=[],H=[],K=new Set;D.forEach(({userId:O})=>{if(O===q)return;if(!$.has(q)){let V={userId:O,receive:(R,b)=>W(R,O,b)};$.set(q,V),z.push(V)}K.add(q)});for(let O of $.keys())if(!K.has(O))$.delete(O),H.push({userId:O});if(z.length)E.onPeerJoined(z);if(H.length)E.onPeerLeft(H)}return F(),{exitRoom:()=>{Z=!0,clearTimeout(M),G.close()}}}var k=null,X=new Map;function Q(E){self.postMessage(E)}self.addEventListener("message",(E)=>{let q=E.data;if(console.debug("[signal-room.worker] received command",q),q.cmd==="enter"){k?.(),k=null,X.clear(),k=h({userId:q.userId,appId:q.appId,room:q.room,host:q.host,autoRejoin:q.autoRejoin,onOpen:()=>Q({kind:"open"}),onClose:({code:A,reason:B,wasClean:J})=>Q({kind:"close",ev:{code:A,reason:B,wasClean:J}}),onError:()=>Q({kind:"error"}),logLine:(A,B)=>{console.debug(`[signal-room.worker] ${A}`,B),Q({kind:"log",direction:A,obj:B})},onPeerJoined:(A)=>{A.forEach(({userId:B,receive:J})=>X.set(`${q.host}/${q.room}/${B}`,J)),Q({kind:"peer-joined",users:A.map(({userId:B})=>({userId:B}))})},onPeerLeft:(A)=>{A.forEach(({userId:B})=>X.delete(`${q.host}/${q.room}/${B}`)),Q({kind:"peer-left",users:A})},onMessage:(A,B,J)=>{X.set(`${q.host}/${q.room}/${J.userId}`,J.receive),Q({kind:"message",type:A,payload:B,fromUserId:J.userId})}}).exitRoom;return}if(q.cmd==="send"){let T=X.get(`${q.host}/${q.room}/${q.toUserId}`);if(T)T(q.type,q.payload);return}if(q.cmd==="exit"){k?.(),self.close();return}});
|
|
2
2
|
|
|
3
|
-
//# debugId=
|
|
3
|
+
//# debugId=2D9C6183522C4D2464756E2164756E21
|
|
4
4
|
//# sourceMappingURL=signal-room.worker.js.map
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/browser/impl/signal-room.ts", "../src/browser/signal-room.worker.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"export interface IPeer<T extends string = string, P = any> {\n
|
|
6
|
-
"/// <reference lib=\"webworker\" />\n\nimport { enterRoom, type IPeer } from \"./impl/signal-room.js\";\n\nexport type RoomEvent<T extends string = string, P = any> =\n | { kind: \"open\" }\n | { kind: \"close\"
|
|
5
|
+
"export interface IPeer<T extends string = string, P = any> {\n userId: 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;\n appId: string;\n room: string;\n host: string;\n onOpen?: () => void;\n onClose?: (ev: Pick<CloseEvent, \"code\" | \"reason\" | \"wasClean\">) => void;\n onError?: () => void;\n logLine?: (direction: string, obj?: any) => void;\n onPeerJoined(users: IPeer<T, P>[]): void;\n onPeerLeft(users: { userId: string }[]): void;\n 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(\n userId\n )}`;\n\n // Helper for sending (uses the current ws instance)\n function send(type: T, to: string, payload: P) {\n console.log(\"SENDING\", type, to, payload, ws.readyState, ws);\n if (!ws) return false;\n if (exited || ws.readyState !== WebSocket.OPEN) return false;\n const obj = { type, to, payload };\n ws.send(JSON.stringify(obj));\n logLine?.(\"👤 ➡️ 🖥️\", obj);\n return true;\n }\n\n function connect() {\n if (exited) return;\n\n ws = new WebSocket(wsUrl);\n\n ws.onopen = () => {\n if (initialConnection) {\n params.onOpen?.();\n initialConnection = false;\n }\n retryCount = 0; // Reset backoff on successful connection\n };\n\n ws.onmessage = (e: MessageEvent) => {\n // ... (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.userId) {\n params.onMessage(msg.type, msg.payload, {\n userId: msg.userId,\n receive: (type: T, payload: P) => send(type, msg.userId, payload),\n });\n }\n } catch {\n logLine?.(\"⚠️ ERROR\", { error: \"invalid-json\" });\n }\n };\n\n ws.onclose = (ev: CloseEvent) => {\n // 1. Check if we should even try to reconnect\n const recoverableCodes = [1001, 1006, 1011, 1012, 1013];\n const isRecoverable = recoverableCodes.includes(ev.code);\n\n if (autoRejoin && !exited && isRecoverable) {\n // 2. Exponential Backoff: 1s, 2s, 4s, 8s... capped at 30s\n const backoff = Math.min(Math.pow(2, retryCount) * 1000, 30000);\n // 3. Add Jitter: +/- 1000ms randomness\n const jitter = Math.random() * 1000;\n const delay = backoff + jitter;\n\n logLine?.(\"🔄 RECONNECTING\", {\n attempt: retryCount + 1,\n delayMs: Math.round(delay),\n });\n\n retryCount++;\n timeoutId = setTimeout(connect, delay);\n } else {\n params.onClose?.({\n code: ev.code,\n reason: ev.reason,\n wasClean: ev.wasClean,\n });\n }\n };\n\n ws.onerror = (ev) => {\n console.error(\"WS Error\", ev);\n params.onError?.();\n };\n }\n\n // Helper for peer tracking (logic from your original code)\n function updatePeers(updatedUsers: { userId: string }[]) {\n const joined: IPeer<T, P>[] = [];\n const left: { userId: string }[] = [];\n const updatedPeerSet = new Set<string>();\n\n updatedUsers.forEach(({ userId: pUserId }) => {\n if (pUserId === userId) return;\n if (!peers.has(userId)) {\n const newPeer = {\n userId: pUserId,\n receive: (t: T, p: P) => send(t, pUserId, p),\n };\n peers.set(userId, newPeer);\n joined.push(newPeer);\n }\n updatedPeerSet.add(userId);\n });\n\n for (const userId of peers.keys()) {\n if (!updatedPeerSet.has(userId)) {\n peers.delete(userId);\n left.push({ 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
|
+
"/// <reference lib=\"webworker\" />\n\nimport { enterRoom, type IPeer } from \"./impl/signal-room.js\";\n\nexport type RoomEvent<T extends string = string, P = any> =\n | { kind: \"open\" }\n | { kind: \"close\"; ev: Pick<CloseEvent, \"code\" | \"reason\" | \"wasClean\"> }\n | { kind: \"error\" }\n | { kind: \"peer-joined\"; users: { userId: string }[] }\n | { kind: \"peer-left\"; users: { userId: string }[] }\n | {\n kind: \"message\";\n type: T;\n payload: P;\n fromUserId: string;\n }\n | { kind: \"log\"; direction: string; obj?: any };\n\nexport type WorkerCommand<T extends string = string, P = any> =\n | {\n cmd: \"enter\";\n userId: string;\n appId: string;\n room: string;\n host: string;\n autoRejoin: boolean;\n }\n | { cmd: \"exit\" }\n | {\n cmd: \"send\";\n host: string;\n room: string;\n toUserId: string;\n type: T;\n payload: P;\n };\n\nlet exitRoom: (() => void) | null = null;\n\n// Map from userId -> a function to send to that peer (comes from IUser.receive)\nconst peerSend = new Map<string, (type: any, payload: any) => boolean>();\n\nfunction emit<T extends string, P>(ev: RoomEvent<T, P>) {\n (self as DedicatedWorkerGlobalScope).postMessage(ev);\n}\n\nself.addEventListener(\"message\", (e: MessageEvent<WorkerCommand>) => {\n const msg = e.data;\n console.debug(\"[signal-room.worker] received command\", msg);\n\n if (msg.cmd === \"enter\") {\n // If re-entering, clean up first\n exitRoom?.();\n exitRoom = null;\n peerSend.clear();\n\n const result = enterRoom({\n userId: msg.userId,\n appId: msg.appId,\n room: msg.room,\n host: msg.host,\n autoRejoin: msg.autoRejoin,\n onOpen: () => emit({ kind: \"open\" }),\n onClose: ({\n code,\n reason,\n wasClean,\n }: Pick<CloseEvent, \"code\" | \"reason\" | \"wasClean\">) =>\n emit({ kind: \"close\", ev: { code, reason, wasClean } }),\n onError: () => emit({ kind: \"error\" }),\n logLine: (direction: string, obj?: any) => {\n console.debug(`[signal-room.worker] ${direction}`, obj);\n emit({ kind: \"log\", direction, obj });\n },\n onPeerJoined: (users: IPeer[]) => {\n // Save the ability to send to this peer\n users.forEach(({ userId, receive }) =>\n peerSend.set(`${msg.host}/${msg.room}/${userId}`, receive)\n );\n emit({\n kind: \"peer-joined\",\n users: users.map(({ userId }) => ({ userId })),\n });\n },\n onPeerLeft: (users: { userId: string }[]) => {\n users.forEach(({ userId }) =>\n peerSend.delete(`${msg.host}/${msg.room}/${userId}`)\n );\n emit({ kind: \"peer-left\", users });\n },\n onMessage: (type: any, payload: any, from: IPeer) => {\n // We can also learn peerSend via onMessage in case join events vary\n peerSend.set(`${msg.host}/${msg.room}/${from.userId}`, from.receive);\n emit({\n kind: \"message\",\n type,\n payload,\n fromUserId: from.userId,\n });\n },\n });\n\n exitRoom = result.exitRoom;\n return;\n }\n\n if (msg.cmd === \"send\") {\n const sendFn = peerSend.get(`${msg.host}/${msg.room}/${msg.toUserId}`);\n if (sendFn) sendFn(msg.type, msg.payload);\n return;\n }\n\n if (msg.cmd === \"exit\") {\n exitRoom?.();\n self.close();\n return;\n }\n});\n"
|
|
7
7
|
],
|
|
8
|
-
"mappings": "
|
|
9
|
-
"debugId": "
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "2D9C6183522C4D2464756E2164756E21",
|
|
10
10
|
"names": []
|
|
11
11
|
}
|
|
@@ -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;
|
|
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;AAiBzE,wBAAgB,sBAAsB,CAAC,EACrC,KAAK,EACL,qBAAqB,EACrB,sBAA6B,EAC7B,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;QAChC,EAAE,EAAE,iBAAiB,CAAC;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,OAAO,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;KACtB,GAAG,IAAI,CAAC;IACT,WAAW,CAAC,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACzD,WAAW,CAAC,CAAC,IAAI,EAAE;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC,CAAC;KACtD,GAAG,IAAI,CAAC;CACV;;gCA6FgC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;+BAT/B;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;wBAlCjC,MAAM;;EAoMlC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
function
|
|
1
|
+
function P(A){let{userId:F,appId:E,room:j,host:k,autoRejoin:J=!0,logLine:N}=A,b=!1,S=0,Q,O,Y=!0,T=new Map,q=`wss://${k}/room/${E}/${j}?userId=${encodeURIComponent(F)}`;function M(H,B,z){if(console.log("SENDING",H,B,z,Q.readyState,Q),!Q)return!1;if(b||Q.readyState!==WebSocket.OPEN)return!1;let G={type:H,to:B,payload:z};return Q.send(JSON.stringify(G)),N?.("\uD83D\uDC64 ➡️ \uD83D\uDDA5️",G),!0}function x(){if(b)return;Q=new WebSocket(q),Q.onopen=()=>{if(Y)A.onOpen?.(),Y=!1;S=0},Q.onmessage=(H)=>{try{let B=JSON.parse(H.data);if(N?.("\uD83D\uDDA5️ ➡️ \uD83D\uDC64",B),B.type==="peer-joined"||B.type==="peer-left")W(B.users);else if(B.userId)A.onMessage(B.type,B.payload,{userId:B.userId,receive:(z,G)=>M(z,B.userId,G)})}catch{N?.("⚠️ ERROR",{error:"invalid-json"})}},Q.onclose=(H)=>{let z=[1001,1006,1011,1012,1013].includes(H.code);if(J&&!b&&z){let G=Math.min(Math.pow(2,S)*1000,30000),K=Math.random()*1000,V=G+K;N?.("\uD83D\uDD04 RECONNECTING",{attempt:S+1,delayMs:Math.round(V)}),S++,O=setTimeout(x,V)}else A.onClose?.({code:H.code,reason:H.reason,wasClean:H.wasClean})},Q.onerror=(H)=>{console.error("WS Error",H),A.onError?.()}}function W(H){let B=[],z=[],G=new Set;H.forEach(({userId:K})=>{if(K===F)return;if(!T.has(F)){let V={userId:K,receive:(R,h)=>M(R,K,h)};T.set(F,V),B.push(V)}G.add(F)});for(let K of T.keys())if(!G.has(K))T.delete(K),z.push({userId:K});if(B.length)A.onPeerJoined(B);if(z.length)A.onPeerLeft(z)}return x(),{exitRoom:()=>{b=!0,clearTimeout(O),Q.close()}}}function f({userId:A,appId:F,room:E,host:j,autoRejoin:k=!0,onOpen:J,onClose:N,onError:b,onPeerJoined:S,onPeerLeft:Q,onMessage:O,logLine:Y,workerUrl:T}){if(!T)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"),P({userId:A,appId:F,room:E,host:j,autoRejoin:k,onOpen:J,onClose:N,onError:b,onPeerJoined:S,onPeerLeft:Q,onMessage:O});let q=new Worker(T,{type:"module"}),M=!1;function x({userId:H}){return{userId:H,receive:(B,z)=>{if(M)return!1;return q.postMessage({cmd:"send",toUserId:H,host:j,room:E,type:B,payload:z}),!0}}}let W=(H)=>{let B=H.data;if(B.kind==="open")J?.();else if(B.kind==="close")q.terminate(),N?.(B.ev);else if(B.kind==="error")b?.();else if(B.kind==="peer-joined")S(B.users.map((z)=>x({userId:z.userId})));else if(B.kind==="peer-left")Q(B.users);else if(B.kind==="message")O(B.type,B.payload,x({userId:B.fromUserId}));else if(B.kind==="log")Y?.(B.direction,B.obj)};return q.addEventListener("message",W),q.postMessage({cmd:"enter",userId:A,appId:F,room:E,host:j,autoRejoin:k}),{exitRoom:()=>{M=!0,q.removeEventListener("message",W),q.postMessage({cmd:"exit"})}}}var y=f;function m({appId:A,receivePeerConnection:F,peerlessUserExpiration:E=5000,rtcConfig:j={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},enterRoomFunction:k=y,logLine:J=console.debug,onLeaveUser:N,workerUrl:b,onRoomReady:S,onRoomClose:Q}){let O=`user-${crypto.randomUUID()}`,Y=new Map;function T(z){return z.pc=new RTCPeerConnection(j),z.pc.onicecandidate=(G)=>{if(!G.candidate)return;z.peer.receive("ice",G.candidate.toJSON())},z.pc.onconnectionstatechange=()=>{J("\uD83D\uDCAC",{event:"pc-state",userId:z.userId,state:z.pc?.connectionState})},z.pc}function q(z){let G=Y.get(z.userId),K=!1;if(!G){let V={userId:z.userId,pendingRemoteIce:[],peer:z};Y.set(z.userId,V),T(V),G=V,Y.set(G.userId,G),K=!0}else if(G)clearTimeout(G.expirationTimeout),G.expirationTimeout=0;if(!G.pc)T(G);return G.peer=z,[G,K]}function M(z){N?.(z);let G=Y.get(z);if(!G)return;try{G.pc?.close()}catch{}Y.delete(z)}async function x(z){if(!z.pc?.remoteDescription)return;let G=z.pendingRemoteIce;z.pendingRemoteIce=[];for(let K of G)try{await z.pc.addIceCandidate(K)}catch(V){J("⚠️ ERROR",{error:"add-ice-failed",userId:z.userId,detail:String(V)})}}let W=new Map;function H({room:z,host:G}){let K=`${G}/room/${z}`,V=W.get(K);if(V)V.exitRoom(),W.delete(K)}function B({room:z,host:G}){return new Promise((K,V)=>{async function R(Z){let[X]=q(Z),$=X.pc,_=await $?.createOffer();await $?.setLocalDescription(_),Z.receive("offer",$?.localDescription?.toJSON()),console.log("Make offer",$?.localDescription?.toJSON())}let{exitRoom:h}=k({userId:O,appId:A,room:z,host:G,logLine:J,workerUrl:b,autoRejoin:!0,onOpen(){S?.({room:z,host:G}),K()},onError(){console.error("onError"),V()},onClose(Z){Q?.({room:z,host:G,ev:Z})},onPeerJoined(Z){console.log("PEER JOINED",Z),Z.forEach((X)=>{let[$,_]=q(X);if(!_)return;let D=$.pc;if(!D)return;async function L(){let C=Y.get(X.userId);if(C){C.pc=void 0;let U=T(C);F({pc:U,userId:X.userId,initiator:!0,restart:L}),await new Promise((w)=>setTimeout(w,3000)),R(X)}}F({pc:D,userId:X.userId,initiator:!0,restart:L}),R(X)})},onPeerLeft(Z){Z.forEach(({userId:X})=>{let $=Y.get(X);if(!$)return;$.expirationTimeout=setTimeout(()=>M(X),E??0)})},async onMessage(Z,X,$){let[_]=q($),D=_.pc;if(!D)return;if(Z==="offer"){F({pc:D,userId:$.userId,initiator:!1,restart(){_.pc=void 0}}),await D.setRemoteDescription(X);let L=await D.createAnswer();await D.setLocalDescription(L),$.receive("answer",D.localDescription?.toJSON()),await x(_);return}if(Z==="answer"){await D.setRemoteDescription(X),await x(_);return}if(Z==="ice"){let L=X;if(!D.remoteDescription){_.pendingRemoteIce.push(L);return}try{await D.addIceCandidate(L)}catch(C){J("⚠️ ERROR",{error:"add-ice-failed",userId:_.userId,detail:String(C)})}return}}});W.set(`${G}/room/${z}`,{exitRoom:h,room:z,host:G})})}return{userId:O,enterRoom:B,exitRoom:H,leaveUser:M,end(){W.forEach(({exitRoom:z})=>z()),W.clear(),Y.forEach(({userId:z})=>M(z)),Y.clear()}}}export{m as collectPeerConnections};
|
|
2
2
|
|
|
3
|
-
//# debugId=
|
|
3
|
+
//# debugId=F92254A49E54E11D64756E2164756E21
|
|
4
4
|
//# sourceMappingURL=webrtc-peer-collector.js.map
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/browser/impl/signal-room.ts", "../src/browser/signal-room.ts", "../src/browser/webrtc-peer-collector.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"export interface IPeer<T extends string = string, P = any> {\n
|
|
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\"
|
|
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
|
|
5
|
+
"export interface IPeer<T extends string = string, P = any> {\n userId: 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;\n appId: string;\n room: string;\n host: string;\n onOpen?: () => void;\n onClose?: (ev: Pick<CloseEvent, \"code\" | \"reason\" | \"wasClean\">) => void;\n onError?: () => void;\n logLine?: (direction: string, obj?: any) => void;\n onPeerJoined(users: IPeer<T, P>[]): void;\n onPeerLeft(users: { userId: string }[]): void;\n 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(\n userId\n )}`;\n\n // Helper for sending (uses the current ws instance)\n function send(type: T, to: string, payload: P) {\n console.log(\"SENDING\", type, to, payload, ws.readyState, ws);\n if (!ws) return false;\n if (exited || ws.readyState !== WebSocket.OPEN) return false;\n const obj = { type, to, payload };\n ws.send(JSON.stringify(obj));\n logLine?.(\"👤 ➡️ 🖥️\", obj);\n return true;\n }\n\n function connect() {\n if (exited) return;\n\n ws = new WebSocket(wsUrl);\n\n ws.onopen = () => {\n if (initialConnection) {\n params.onOpen?.();\n initialConnection = false;\n }\n retryCount = 0; // Reset backoff on successful connection\n };\n\n ws.onmessage = (e: MessageEvent) => {\n // ... (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.userId) {\n params.onMessage(msg.type, msg.payload, {\n userId: msg.userId,\n receive: (type: T, payload: P) => send(type, msg.userId, payload),\n });\n }\n } catch {\n logLine?.(\"⚠️ ERROR\", { error: \"invalid-json\" });\n }\n };\n\n ws.onclose = (ev: CloseEvent) => {\n // 1. Check if we should even try to reconnect\n const recoverableCodes = [1001, 1006, 1011, 1012, 1013];\n const isRecoverable = recoverableCodes.includes(ev.code);\n\n if (autoRejoin && !exited && isRecoverable) {\n // 2. Exponential Backoff: 1s, 2s, 4s, 8s... capped at 30s\n const backoff = Math.min(Math.pow(2, retryCount) * 1000, 30000);\n // 3. Add Jitter: +/- 1000ms randomness\n const jitter = Math.random() * 1000;\n const delay = backoff + jitter;\n\n logLine?.(\"🔄 RECONNECTING\", {\n attempt: retryCount + 1,\n delayMs: Math.round(delay),\n });\n\n retryCount++;\n timeoutId = setTimeout(connect, delay);\n } else {\n params.onClose?.({\n code: ev.code,\n reason: ev.reason,\n wasClean: ev.wasClean,\n });\n }\n };\n\n ws.onerror = (ev) => {\n console.error(\"WS Error\", ev);\n params.onError?.();\n };\n }\n\n // Helper for peer tracking (logic from your original code)\n function updatePeers(updatedUsers: { userId: string }[]) {\n const joined: IPeer<T, P>[] = [];\n const left: { userId: string }[] = [];\n const updatedPeerSet = new Set<string>();\n\n updatedUsers.forEach(({ userId: pUserId }) => {\n if (pUserId === userId) return;\n if (!peers.has(userId)) {\n const newPeer = {\n userId: pUserId,\n receive: (t: T, p: P) => send(t, pUserId, p),\n };\n peers.set(userId, newPeer);\n joined.push(newPeer);\n }\n updatedPeerSet.add(userId);\n });\n\n for (const userId of peers.keys()) {\n if (!updatedPeerSet.has(userId)) {\n peers.delete(userId);\n left.push({ 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
|
+
"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 }[]) => 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(\n \"Warning: enterRoom called without workerUrl; this may cause issues in some environments. You should pass workerUrl explicitly. Use:\",\n CDN_WORKER_URL\n );\n return baseEnterRoom<T, P>({\n userId,\n 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 }: { userId: string }): IPeer<T, P> {\n return {\n userId,\n receive: (type: T, payload: P) => {\n if (exited) return false;\n worker.postMessage({\n cmd: \"send\",\n toUserId: userId,\n host,\n room,\n type,\n payload,\n } as WorkerCommand);\n return true;\n },\n };\n }\n\n const onWorkerMessage = (e: MessageEvent<RoomEvent<T, P>>) => {\n const ev = e.data;\n\n if (ev.kind === \"open\") onOpen?.();\n else if (ev.kind === \"close\") {\n worker.terminate();\n onClose?.(ev.ev);\n } else if (ev.kind === \"error\") onError?.();\n else if (ev.kind === \"peer-joined\")\n onPeerJoined(ev.users.map((ev) => makeUser({ userId: ev.userId })));\n else if (ev.kind === \"peer-left\") onPeerLeft(ev.users);\n else if (ev.kind === \"message\")\n onMessage(ev.type, ev.payload, makeUser({ userId: ev.fromUserId }));\n else if (ev.kind === \"log\") logLine?.(ev.direction, ev.obj);\n };\n\n worker.addEventListener(\"message\", onWorkerMessage);\n\n worker.postMessage({\n cmd: \"enter\",\n userId,\n appId,\n room,\n host,\n autoRejoin,\n } as WorkerCommand);\n\n return {\n exitRoom: () => {\n exited = true;\n worker.removeEventListener(\"message\", onWorkerMessage);\n worker.postMessage({ cmd: \"exit\" } as WorkerCommand);\n },\n };\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 peer: IPeer<SigType, SigPayload>;\n\n expirationTimeout?: number;\n};\n\nconst DEFAULT_ENTER_ROOM = enterRoom;\n\nexport function collectPeerConnections({\n appId,\n receivePeerConnection,\n peerlessUserExpiration = 5000,\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: {\n pc: RTCPeerConnection;\n userId: string;\n initiator: boolean;\n restart?: () => void;\n }): void;\n onRoomReady?(info: { host: string; room: string }): void;\n onRoomClose?(info: {\n host: string;\n room: string;\n ev: Pick<CloseEvent, \"reason\" | \"code\" | \"wasClean\">;\n }): void;\n}) {\n const userId = `user-${crypto.randomUUID()}`;\n const users: Map<string, UserState> = new Map();\n\n function setupPC(state: UserState) {\n state.pc = new RTCPeerConnection(rtcConfig);\n // Send local ICE candidates to this peer\n state.pc.onicecandidate = (ev) => {\n if (!ev.candidate) return;\n state.peer.receive(\"ice\", ev.candidate.toJSON());\n };\n\n state.pc.onconnectionstatechange = () => {\n logLine(\"💬\", {\n event: \"pc-state\",\n userId: state.userId,\n state: state.pc?.connectionState,\n });\n };\n return state.pc;\n }\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 pendingRemoteIce: [],\n peer,\n };\n users.set(peer.userId, newState);\n\n setupPC(newState);\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 }\n if (!state.pc) {\n setupPC(state);\n }\n state.peer = peer;\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 {\n p.pc?.close();\n } 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\", {\n error: \"add-ice-failed\",\n userId: state.userId,\n detail: String(e),\n });\n }\n }\n }\n\n const roomsEntered = new Map<\n string,\n { room: string; host: string; exitRoom: () => void }\n >();\n\n function exit({ room, host }: { room: string; host: string }) {\n const key = `${host}/room/${room}`;\n const session = roomsEntered.get(key);\n if (session) {\n session.exitRoom();\n roomsEntered.delete(key);\n }\n }\n\n function enter({ room, host }: { room: string; host: string }) {\n return new Promise<void>((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 console.log(\"Make 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 console.log(\"PEER JOINED\", joiningUsers);\n joiningUsers.forEach((user) => {\n const [state, isNewPeer] = getPeer(user);\n if (!isNewPeer) return;\n const pc = state.pc;\n if (!pc) return;\n\n async function restart() {\n const state = users.get(user.userId);\n if (state) {\n state.pc = undefined;\n const pc = setupPC(state);\n receivePeerConnection({\n pc,\n userId: user.userId,\n initiator: true,\n restart,\n });\n await new Promise((resolve) => setTimeout(resolve, 3000));\n makeOffer(user);\n }\n }\n\n receivePeerConnection({\n pc,\n userId: user.userId,\n initiator: true,\n restart,\n });\n makeOffer(user);\n });\n },\n\n onPeerLeft(leavingUsers: { userId: string }[]) {\n leavingUsers.forEach(({ userId }) => {\n const state = users.get(userId);\n if (!state) return;\n state.expirationTimeout = setTimeout(\n () => leaveUser(userId),\n peerlessUserExpiration ?? 0\n );\n });\n },\n\n async onMessage(type: SigType, payload: any, from: IPeer) {\n const [state] = getPeer(from);\n const pc = state.pc;\n if (!pc) return;\n\n if (type === \"offer\") {\n receivePeerConnection({\n pc,\n userId: from.userId,\n initiator: false,\n restart() {\n // reset PC\n state.pc = undefined;\n },\n });\n // Responder: set remote offer\n await pc.setRemoteDescription(payload as RTCSessionDescriptionInit);\n\n // Create and send answer\n const answer = await pc.createAnswer();\n await pc.setLocalDescription(answer);\n\n from.receive(\"answer\", pc.localDescription?.toJSON()!);\n\n // Now safe to apply any queued ICE from this peer\n await flushRemoteIce(state);\n return;\n }\n\n if (type === \"answer\") {\n // Initiator: set remote answer\n await pc.setRemoteDescription(payload as RTCSessionDescriptionInit);\n await flushRemoteIce(state);\n return;\n }\n\n if (type === \"ice\") {\n const ice = payload as RTCIceCandidateInit;\n\n // If we don't have remoteDescription yet, queue it\n if (!pc.remoteDescription) {\n state.pendingRemoteIce.push(ice);\n return;\n }\n\n try {\n await pc.addIceCandidate(ice);\n } catch (e) {\n logLine(\"⚠️ ERROR\", {\n error: \"add-ice-failed\",\n userId: state.userId,\n detail: String(e),\n });\n }\n return;\n }\n },\n });\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"
|
|
8
8
|
],
|
|
9
|
-
"mappings": "
|
|
10
|
-
"debugId": "
|
|
9
|
+
"mappings": "AAQO,SAAS,CAAoC,CAAC,EAaxB,CAC3B,IAAQ,SAAQ,QAAO,OAAM,OAAM,aAAa,GAAM,WAAY,EAE9D,EAAS,GACT,EAAa,EACb,EACA,EACA,EAAoB,GAElB,EAAQ,IAAI,IACZ,EAAQ,SAAS,UAAa,KAAS,YAAe,mBAC1D,CACF,IAGA,SAAS,CAAI,CAAC,EAAS,EAAY,EAAY,CAE7C,GADA,QAAQ,IAAI,UAAW,EAAM,EAAI,EAAS,EAAG,WAAY,CAAE,EACvD,CAAC,EAAI,MAAO,GAChB,GAAI,GAAU,EAAG,aAAe,UAAU,KAAM,MAAO,GACvD,IAAM,EAAM,CAAE,OAAM,KAAI,SAAQ,EAGhC,OAFA,EAAG,KAAK,KAAK,UAAU,CAAG,CAAC,EAC3B,IAAU,gCAAY,CAAG,EAClB,GAGT,SAAS,CAAO,EAAG,CACjB,GAAI,EAAQ,OAEZ,EAAK,IAAI,UAAU,CAAK,EAExB,EAAG,OAAS,IAAM,CAChB,GAAI,EACF,EAAO,SAAS,EAChB,EAAoB,GAEtB,EAAa,GAGf,EAAG,UAAY,CAAC,IAAoB,CAElC,GAAI,CACF,IAAM,EAAM,KAAK,MAAM,EAAE,IAAI,EAE7B,GADA,IAAU,gCAAY,CAAG,EACrB,EAAI,OAAS,eAAiB,EAAI,OAAS,YAC7C,EAAY,EAAI,KAAK,EAChB,QAAI,EAAI,OACb,EAAO,UAAU,EAAI,KAAM,EAAI,QAAS,CACtC,OAAQ,EAAI,OACZ,QAAS,CAAC,EAAS,IAAe,EAAK,EAAM,EAAI,OAAQ,CAAO,CAClE,CAAC,EAEH,KAAM,CACN,IAAU,WAAW,CAAE,MAAO,cAAe,CAAC,IAIlD,EAAG,QAAU,CAAC,IAAmB,CAG/B,IAAM,EADmB,CAAC,KAAM,KAAM,KAAM,KAAM,IAAI,EACf,SAAS,EAAG,IAAI,EAEvD,GAAI,GAAc,CAAC,GAAU,EAAe,CAE1C,IAAM,EAAU,KAAK,IAAI,KAAK,IAAI,EAAG,CAAU,EAAI,KAAM,KAAK,EAExD,EAAS,KAAK,OAAO,EAAI,KACzB,EAAQ,EAAU,EAExB,IAAU,4BAAkB,CAC1B,QAAS,EAAa,EACtB,QAAS,KAAK,MAAM,CAAK,CAC3B,CAAC,EAED,IACA,EAAY,WAAW,EAAS,CAAK,EAErC,OAAO,UAAU,CACf,KAAM,EAAG,KACT,OAAQ,EAAG,OACX,SAAU,EAAG,QACf,CAAC,GAIL,EAAG,QAAU,CAAC,IAAO,CACnB,QAAQ,MAAM,WAAY,CAAE,EAC5B,EAAO,UAAU,GAKrB,SAAS,CAAW,CAAC,EAAoC,CACvD,IAAM,EAAwB,CAAC,EACzB,EAA6B,CAAC,EAC9B,EAAiB,IAAI,IAE3B,EAAa,QAAQ,EAAG,OAAQ,KAAc,CAC5C,GAAI,IAAY,EAAQ,OACxB,GAAI,CAAC,EAAM,IAAI,CAAM,EAAG,CACtB,IAAM,EAAU,CACd,OAAQ,EACR,QAAS,CAAC,EAAM,IAAS,EAAK,EAAG,EAAS,CAAC,CAC7C,EACA,EAAM,IAAI,EAAQ,CAAO,EACzB,EAAO,KAAK,CAAO,EAErB,EAAe,IAAI,CAAM,EAC1B,EAED,QAAW,KAAU,EAAM,KAAK,EAC9B,GAAI,CAAC,EAAe,IAAI,CAAM,EAC5B,EAAM,OAAO,CAAM,EACnB,EAAK,KAAK,CAAE,QAAO,CAAC,EAIxB,GAAI,EAAO,OAAQ,EAAO,aAAa,CAAM,EAC7C,GAAI,EAAK,OAAQ,EAAO,WAAW,CAAI,EAMzC,OAFA,EAAQ,EAED,CACL,SAAU,IAAM,CACd,EAAS,GACT,aAAa,CAAS,EACtB,EAAG,MAAM,EAEb,EClJK,SAAS,CAAoC,EAClD,SACA,QACA,OACA,OACA,aAAa,GACb,SACA,UACA,UACA,eACA,aACA,YACA,UACA,aAiB2B,CAC3B,GAAI,CAAC,EAOH,OAJA,QAAQ,KACN,sIAHqB,kFAKvB,EACO,EAAoB,CACzB,SACA,QACA,OACA,OACA,aACA,SACA,UACA,UACA,eACA,aACA,WACF,CAAC,EAEH,IAAM,EAAS,IAAI,OAAO,EAAW,CAAE,KAAM,QAAS,CAAC,EACnD,EAAS,GAEb,SAAS,CAAQ,EAAG,UAA2C,CAC7D,MAAO,CACL,SACA,QAAS,CAAC,EAAS,IAAe,CAChC,GAAI,EAAQ,MAAO,GASnB,OARA,EAAO,YAAY,CACjB,IAAK,OACL,SAAU,EACV,OACA,OACA,OACA,SACF,CAAkB,EACX,GAEX,EAGF,IAAM,EAAkB,CAAC,IAAqC,CAC5D,IAAM,EAAK,EAAE,KAEb,GAAI,EAAG,OAAS,OAAQ,IAAS,EAC5B,QAAI,EAAG,OAAS,QACnB,EAAO,UAAU,EACjB,IAAU,EAAG,EAAE,EACV,QAAI,EAAG,OAAS,QAAS,IAAU,EACrC,QAAI,EAAG,OAAS,cACnB,EAAa,EAAG,MAAM,IAAI,CAAC,IAAO,EAAS,CAAE,OAAQ,EAAG,MAAO,CAAC,CAAC,CAAC,EAC/D,QAAI,EAAG,OAAS,YAAa,EAAW,EAAG,KAAK,EAChD,QAAI,EAAG,OAAS,UACnB,EAAU,EAAG,KAAM,EAAG,QAAS,EAAS,CAAE,OAAQ,EAAG,UAAW,CAAC,CAAC,EAC/D,QAAI,EAAG,OAAS,MAAO,IAAU,EAAG,UAAW,EAAG,GAAG,GAc5D,OAXA,EAAO,iBAAiB,UAAW,CAAe,EAElD,EAAO,YAAY,CACjB,IAAK,QACL,SACA,QACA,OACA,OACA,YACF,CAAkB,EAEX,CACL,SAAU,IAAM,CACd,EAAS,GACT,EAAO,oBAAoB,UAAW,CAAe,EACrD,EAAO,YAAY,CAAE,IAAK,MAAO,CAAkB,EAEvD,EC3FF,IAAM,EAAqB,EAEpB,SAAS,CAAsB,EACpC,QACA,wBACA,yBAAyB,KACzB,YAAY,CAAE,WAAY,CAAC,CAAE,KAAM,8BAA+B,CAAC,CAAE,EACrE,kBAAmB,EAAY,EAC/B,UAAU,QAAQ,MAClB,cACA,YACA,cACA,eAqBC,CACD,IAAM,EAAS,QAAQ,OAAO,WAAW,IACnC,EAAgC,IAAI,IAE1C,SAAS,CAAO,CAAC,EAAkB,CAejC,OAdA,EAAM,GAAK,IAAI,kBAAkB,CAAS,EAE1C,EAAM,GAAG,eAAiB,CAAC,IAAO,CAChC,GAAI,CAAC,EAAG,UAAW,OACnB,EAAM,KAAK,QAAQ,MAAO,EAAG,UAAU,OAAO,CAAC,GAGjD,EAAM,GAAG,wBAA0B,IAAM,CACvC,EAAQ,eAAK,CACX,MAAO,WACP,OAAQ,EAAM,OACd,MAAO,EAAM,IAAI,eACnB,CAAC,GAEI,EAAM,GAGf,SAAS,CAAO,CAAC,EAAwD,CACvE,IAAI,EAAQ,EAAM,IAAI,EAAK,MAAM,EAC7B,EAAY,GAChB,GAAI,CAAC,EAAO,CACV,IAAM,EAAsB,CAC1B,OAAQ,EAAK,OACb,iBAAkB,CAAC,EACnB,MACF,EACA,EAAM,IAAI,EAAK,OAAQ,CAAQ,EAE/B,EAAQ,CAAQ,EAChB,EAAQ,EAGR,EAAM,IAAI,EAAM,OAAQ,CAAK,EAC7B,EAAY,GACP,QAAI,EACT,aAAa,EAAM,iBAAiB,EACpC,EAAM,kBAAoB,EAE5B,GAAI,CAAC,EAAM,GACT,EAAQ,CAAK,EAGf,OADA,EAAM,KAAO,EACN,CAAC,EAAO,CAAS,EAG1B,SAAS,CAAS,CAAC,EAAgB,CACjC,IAAc,CAAM,EACpB,IAAM,EAAI,EAAM,IAAI,CAAM,EAC1B,GAAI,CAAC,EAAG,OACR,GAAI,CACF,EAAE,IAAI,MAAM,EACZ,KAAM,EACR,EAAM,OAAO,CAAM,EAGrB,eAAe,CAAc,CAAC,EAAkB,CAC9C,GAAI,CAAC,EAAM,IAAI,kBAAmB,OAElC,IAAM,EAAS,EAAM,iBACrB,EAAM,iBAAmB,CAAC,EAE1B,QAAW,KAAO,EAChB,GAAI,CACF,MAAM,EAAM,GAAG,gBAAgB,CAAG,EAClC,MAAO,EAAG,CACV,EAAQ,WAAW,CACjB,MAAO,iBACP,OAAQ,EAAM,OACd,OAAQ,OAAO,CAAC,CAClB,CAAC,GAKP,IAAM,EAAe,IAAI,IAKzB,SAAS,CAAI,EAAG,OAAM,QAAwC,CAC5D,IAAM,EAAM,GAAG,UAAa,IACtB,EAAU,EAAa,IAAI,CAAG,EACpC,GAAI,EACF,EAAQ,SAAS,EACjB,EAAa,OAAO,CAAG,EAI3B,SAAS,CAAK,EAAG,OAAM,QAAwC,CAC7D,OAAO,IAAI,QAAc,CAAC,EAAS,IAAW,CAC5C,eAAe,CAAS,CAAC,EAAa,CAEpC,IAAO,GAAS,EAAQ,CAAI,EACtB,EAAK,EAAM,GACX,EAAQ,MAAM,GAAI,YAAY,EACpC,MAAM,GAAI,oBAAoB,CAAK,EACnC,EAAK,QAAQ,QAAS,GAAI,kBAAkB,OAAO,CAAE,EACrD,QAAQ,IAAI,aAAc,GAAI,kBAAkB,OAAO,CAAC,EAG1D,IAAQ,YAAa,EAAU,CAC7B,SACA,QACA,OACA,OACA,UACA,YACA,WAAY,GAEZ,MAAM,EAAG,CACP,IAAc,CAAE,OAAM,MAAK,CAAC,EAC5B,EAAQ,GAEV,OAAO,EAAG,CACR,QAAQ,MAAM,SAAS,EACvB,EAAO,GAET,OAAO,CAAC,EAAI,CACV,IAAc,CAAE,OAAM,OAAM,IAAG,CAAC,GAIlC,YAAY,CAAC,EAA4C,CACvD,QAAQ,IAAI,cAAe,CAAY,EACvC,EAAa,QAAQ,CAAC,IAAS,CAC7B,IAAO,EAAO,GAAa,EAAQ,CAAI,EACvC,GAAI,CAAC,EAAW,OAChB,IAAM,EAAK,EAAM,GACjB,GAAI,CAAC,EAAI,OAET,eAAe,CAAO,EAAG,CACvB,IAAM,EAAQ,EAAM,IAAI,EAAK,MAAM,EACnC,GAAI,EAAO,CACT,EAAM,GAAK,OACX,IAAM,EAAK,EAAQ,CAAK,EACxB,EAAsB,CACpB,KACA,OAAQ,EAAK,OACb,UAAW,GACX,SACF,CAAC,EACD,MAAM,IAAI,QAAQ,CAAC,IAAY,WAAW,EAAS,IAAI,CAAC,EACxD,EAAU,CAAI,GAIlB,EAAsB,CACpB,KACA,OAAQ,EAAK,OACb,UAAW,GACX,SACF,CAAC,EACD,EAAU,CAAI,EACf,GAGH,UAAU,CAAC,EAAoC,CAC7C,EAAa,QAAQ,EAAG,YAAa,CACnC,IAAM,EAAQ,EAAM,IAAI,CAAM,EAC9B,GAAI,CAAC,EAAO,OACZ,EAAM,kBAAoB,WACxB,IAAM,EAAU,CAAM,EACtB,GAA0B,CAC5B,EACD,QAGG,UAAS,CAAC,EAAe,EAAc,EAAa,CACxD,IAAO,GAAS,EAAQ,CAAI,EACtB,EAAK,EAAM,GACjB,GAAI,CAAC,EAAI,OAET,GAAI,IAAS,QAAS,CACpB,EAAsB,CACpB,KACA,OAAQ,EAAK,OACb,UAAW,GACX,OAAO,EAAG,CAER,EAAM,GAAK,OAEf,CAAC,EAED,MAAM,EAAG,qBAAqB,CAAoC,EAGlE,IAAM,EAAS,MAAM,EAAG,aAAa,EACrC,MAAM,EAAG,oBAAoB,CAAM,EAEnC,EAAK,QAAQ,SAAU,EAAG,kBAAkB,OAAO,CAAE,EAGrD,MAAM,EAAe,CAAK,EAC1B,OAGF,GAAI,IAAS,SAAU,CAErB,MAAM,EAAG,qBAAqB,CAAoC,EAClE,MAAM,EAAe,CAAK,EAC1B,OAGF,GAAI,IAAS,MAAO,CAClB,IAAM,EAAM,EAGZ,GAAI,CAAC,EAAG,kBAAmB,CACzB,EAAM,iBAAiB,KAAK,CAAG,EAC/B,OAGF,GAAI,CACF,MAAM,EAAG,gBAAgB,CAAG,EAC5B,MAAO,EAAG,CACV,EAAQ,WAAW,CACjB,MAAO,iBACP,OAAQ,EAAM,OACd,OAAQ,OAAO,CAAC,CAClB,CAAC,EAEH,QAGN,CAAC,EACD,EAAa,IAAI,GAAG,UAAa,IAAQ,CAAE,WAAU,OAAM,MAAK,CAAC,EAClE,EAGH,MAAO,CACL,SACA,UAAW,EACX,SAAU,EACV,YACA,GAAG,EAAG,CACJ,EAAa,QAAQ,EAAG,cAAe,EAAS,CAAC,EACjD,EAAa,MAAM,EACnB,EAAM,QAAQ,EAAG,YAAa,EAAU,CAAM,CAAC,EAC/C,EAAM,MAAM,EAEhB",
|
|
10
|
+
"debugId": "F92254A49E54E11D64756E2164756E21",
|
|
11
11
|
"names": []
|
|
12
12
|
}
|