@dobuki/hello-worker 1.0.85 → 1.0.87

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.
@@ -3,12 +3,12 @@
3
3
  "sources": ["../src/browser/utils/ice-url-provider.ts", "../src/browser/signal/impl/signal-room.ts", "../src/browser/signal/signal-room.ts", "../src/browser/utils/rtc-config.ts", "../src/browser/webrtc-peer-collector.ts"],
4
4
  "sourcesContent": [
5
5
  "export class IceUrlProvider {\n private sendToServerFunctions = new Array<(command: \"request-ice\") => void>();\n private icePromiseResolve?: (url: {\n url: string;\n expiration: number;\n }) => void;\n private icePromise?: Promise<{ url: string; expiration: number }>;\n\n receiveIce(url: string, expiration: number) {\n this.icePromiseResolve?.({ url, expiration });\n this.icePromiseResolve = undefined;\n this.icePromise = undefined;\n }\n\n addRequester(requester: (command: \"request-ice\") => void) {\n this.sendToServerFunctions.push(requester);\n return () => {\n this.removeRequester(requester);\n };\n }\n\n removeRequester(requester: (command: \"request-ice\") => void) {\n this.sendToServerFunctions.splice(\n this.sendToServerFunctions.indexOf(requester),\n 1,\n );\n }\n\n sendToServer(command: \"request-ice\") {\n this.sendToServerFunctions[\n Math.floor(this.sendToServerFunctions.length * Math.random())\n ](command);\n }\n\n async requestIce() {\n if (!this.icePromise) {\n this.icePromise = new Promise<{ url: string; expiration: number }>(\n (resolve) => {\n this.icePromiseResolve = resolve;\n this.sendToServer(\"request-ice\");\n },\n );\n }\n return await this.icePromise;\n }\n}\n",
6
- "export interface IPeer {\n userId: string;\n joined: number;\n}\n\ntype OutMessage = { type: string; to: string; payload: any };\n\n/**\n * enterRoom connects to the signaling room via WebSocket.\n */\nexport function enterRoom<T extends string, P = any>(params: {\n userId: string;\n worldId: string;\n room: string;\n host: string;\n onOpen?: () => void;\n onClose?: (ev: Pick<CloseEvent, \"code\" | \"reason\" | \"wasClean\">) => void;\n onError?: () => void;\n logLine?: (direction: string, obj?: any) => void;\n onPeerJoined(users: IPeer[], selfJoined: number): void;\n onPeerLeft(users: { userId: string }[]): void;\n onIceUrl?(url: string, expiration: number): void;\n onMessage(type: T, payload: P, from: string): void;\n autoRejoin?: boolean;\n}): {\n exitRoom: () => void;\n send: <P extends any>(\n type: T,\n userId: \"server\" | string,\n payload?: P,\n ) => void;\n} {\n type Message = {\n type: \"peer-joined\" | \"peer-left\" | \"ice-server\" | T;\n userId: string;\n users: { userId: string; joined: number }[];\n payload: P;\n url: string;\n expiration: number;\n };\n\n const { userId, worldId, room, host, autoRejoin = true, logLine } = params;\n\n let exited = false;\n let retryCount = 0;\n let ws: WebSocket;\n let timeoutId: ReturnType<typeof setTimeout>;\n let initialConnection = true;\n\n const peers = new Map<string, IPeer>();\n const wsUrl = `wss://${host}/room/${worldId}/${room}?userId=${encodeURIComponent(\n userId,\n )}`;\n\n // Helper for sending (uses the current ws instance)\n const accumulatedMessages: OutMessage[] = [];\n let timeout: ReturnType<typeof setTimeout> = 0;\n function send(type: string, to: \"server\" | string, payload?: any) {\n if (!ws) {\n logLine?.(\"👤 âžĄī¸ ❌\", \"no ws available\");\n return false;\n }\n const obj: OutMessage = { type, to, payload };\n accumulatedMessages.push(obj);\n logLine?.(\"👤 âžĄī¸ đŸ–Ĩī¸\", obj);\n clearTimeout(timeout);\n if (exited || ws.readyState !== WebSocket.OPEN) {\n logLine?.(\"👤 âžĄī¸ ❌\", \"Not in opened state: \" + ws.readyState);\n return false;\n }\n timeout = setTimeout(() => {\n ws.send(JSON.stringify(accumulatedMessages));\n accumulatedMessages.length = 0;\n });\n return true;\n }\n\n function connect() {\n if (exited) return;\n\n ws = new WebSocket(wsUrl);\n\n ws.onopen = () => {\n if (initialConnection) {\n params.onOpen?.();\n initialConnection = false;\n }\n retryCount = 0; // Reset backoff on successful connection\n };\n\n ws.onmessage = (e: MessageEvent) => {\n try {\n const result = JSON.parse(e.data);\n const msgs: Message[] = Array.isArray(result) ? result : [result];\n msgs.forEach((msg) => {\n logLine?.(\"đŸ–Ĩī¸ âžĄī¸ 👤\", msg);\n if (msg.type === \"peer-joined\" || msg.type === \"peer-left\") {\n updatePeers(msg.users, msg);\n } else if (msg.type === \"ice-server\") {\n params.onIceUrl?.(msg.url, msg.expiration);\n } else if (msg.userId) {\n params.onMessage(msg.type, msg.payload, msg.userId);\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, 15000);\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(\n updatedUsers: { userId: string; joined: number }[],\n msg: Message,\n ) {\n const joined: IPeer[] = [];\n const left: { userId: string }[] = [];\n const updatedPeerSet = new Set<string>();\n\n const selfPeer = updatedUsers.filter((peer) => peer.userId === userId)[0];\n if (!selfPeer) {\n logLine?.(\"âš ī¸\", \"Cannot find self in updated users\");\n return;\n }\n const selfJoined = selfPeer.joined;\n\n updatedUsers.forEach(({ userId: pUserId, joined: joinedTime }) => {\n if (pUserId === userId) return;\n if (\n !peers.has(pUserId) ||\n (msg.type === \"peer-joined\" && pUserId === msg.userId)\n ) {\n const newPeer = {\n userId: pUserId,\n joined: joinedTime,\n };\n peers.set(pUserId, newPeer);\n joined.push(newPeer);\n }\n updatedPeerSet.add(pUserId);\n });\n\n for (const pUserId of peers.keys()) {\n if (\n !updatedPeerSet.has(pUserId) ||\n (msg.type === \"peer-left\" && pUserId === msg.userId)\n ) {\n peers.delete(pUserId);\n left.push({ userId: pUserId });\n }\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, selfJoined);\n if (left.length) params.onPeerLeft(left);\n }\n\n // Start initial connection\n connect();\n\n return {\n send(type, userId, payload) {\n send(type, userId, payload);\n },\n exitRoom: () => {\n exited = true;\n clearTimeout(timeoutId);\n ws.close();\n },\n };\n}\n",
7
- "import type { IPeer } from \"./impl/signal-room.js\";\nimport { enterRoom as baseEnterRoom } from \"./impl/signal-room.js\";\nimport { RoomEvent, WorkerCommand } from \"./signal-room.worker.js\";\n\nexport function enterRoom<T extends string, P = any>({\n userId,\n worldId,\n room,\n host,\n autoRejoin = true,\n onOpen,\n onClose,\n onError,\n onPeerJoined,\n onPeerLeft,\n onIceUrl,\n onMessage,\n logLine,\n workerUrl,\n}: {\n userId: string;\n worldId: string;\n room: string;\n host: string;\n autoRejoin?: boolean;\n onOpen?: () => void;\n onClose?: (ev: Pick<CloseEvent, \"code\" | \"reason\" | \"wasClean\">) => void;\n onError?: () => void;\n onPeerJoined: (users: IPeer[], selfJoined: number) => void;\n onPeerLeft: (users: { userId: string }[]) => void;\n onIceUrl?(url: string, expiration: number): void;\n onMessage: (type: T, payload: P, from: string) => void;\n logLine?: (direction: string, obj?: any) => void;\n\n // Pass the URL to your worker file (bundler will handle it)\n workerUrl?: URL;\n}): {\n exitRoom: () => void;\n send: <P extends any>(\n type: T,\n userId: \"server\" | string,\n payload?: P,\n ) => void;\n} {\n if (!workerUrl) {\n const CDN_WORKER_URL = `https://cdn.jsdelivr.net/npm/@dobuki/hello-worker/dist/signal-room.worker.min.js`;\n\n console.warn(\n \"Warning: enterRoom called without workerUrl; this may cause issues in some environments. You should pass workerUrl explicitly. Use:\",\n CDN_WORKER_URL,\n );\n return baseEnterRoom<T, P>({\n userId,\n worldId,\n room,\n host,\n autoRejoin,\n onOpen,\n onClose,\n onError,\n onPeerJoined,\n onPeerLeft,\n onIceUrl,\n onMessage,\n });\n }\n const worker = new Worker(workerUrl, { type: \"module\" });\n let exited = false;\n\n 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(\n ev.users.map((ev) => ({ userId: ev.userId, joined: ev.joined })),\n ev.joined,\n );\n else if (ev.kind === \"peer-left\") onPeerLeft(ev.users);\n else if (ev.kind === \"ice-server\") onIceUrl?.(ev.url, ev.expiration);\n else if (ev.kind === \"message\")\n onMessage(ev.type, ev.payload, ev.fromUserId);\n else if (ev.kind === \"log\") logLine?.(ev.direction, ev.obj);\n };\n\n worker.addEventListener(\"message\", onWorkerMessage);\n\n worker.postMessage({\n cmd: \"enter\",\n userId,\n worldId,\n room,\n host,\n autoRejoin,\n } as WorkerCommand);\n\n return {\n exitRoom: () => {\n exited = true;\n worker.removeEventListener(\"message\", onWorkerMessage);\n worker.postMessage({ cmd: \"exit\" } as WorkerCommand);\n },\n send: (type, toUserId, payload) => {\n worker.postMessage({\n cmd: \"send\",\n toUserId,\n host,\n room,\n type,\n payload,\n } as WorkerCommand);\n },\n };\n}\n\nexport type EnterRoom<T extends string, P> = typeof enterRoom<T, P>;\n",
6
+ "export interface IPeer {\n userId: string;\n joined: number;\n}\n\ntype OutMessage = { type: string; to: string; payload: any };\n\n/**\n * enterRoom connects to the signaling room via WebSocket.\n */\nexport function enterRoom<T extends string, P = any>(params: {\n userId: string;\n worldId: string;\n room: string;\n protocol?: 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[], selfJoined: number): void;\n onPeerLeft(users: { userId: string }[]): void;\n onIceUrl?(url: string, expiration: number): void;\n onMessage(type: T, payload: P, from: string): void;\n autoRejoin?: boolean;\n}): {\n exitRoom: () => void;\n send: <P extends any>(\n type: T,\n userId: \"server\" | string,\n payload?: P,\n ) => void;\n} {\n type Message = {\n type: \"peer-joined\" | \"peer-left\" | \"ice-server\" | T;\n userId: string;\n users: { userId: string; joined: number }[];\n payload: P;\n url: string;\n expiration: number;\n };\n\n const {\n userId,\n worldId,\n room,\n host,\n protocol,\n autoRejoin = true,\n logLine,\n } = 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>();\n const wsUrl = `${protocol ?? \"wss\"}://${host}/room/${worldId}/${room}?userId=${encodeURIComponent(\n userId,\n )}`;\n\n // Helper for sending (uses the current ws instance)\n const accumulatedMessages: OutMessage[] = [];\n let timeout: ReturnType<typeof setTimeout> = 0;\n function send(type: string, to: \"server\" | string, payload?: any) {\n if (!ws) {\n logLine?.(\"👤 âžĄī¸ ❌\", \"no ws available\");\n return false;\n }\n const obj: OutMessage = { type, to, payload };\n accumulatedMessages.push(obj);\n logLine?.(\"👤 âžĄī¸ đŸ–Ĩī¸\", obj);\n clearTimeout(timeout);\n if (exited || ws.readyState !== WebSocket.OPEN) {\n logLine?.(\"👤 âžĄī¸ ❌\", \"Not in opened state: \" + ws.readyState);\n return false;\n }\n timeout = setTimeout(() => {\n ws.send(JSON.stringify(accumulatedMessages));\n accumulatedMessages.length = 0;\n });\n return true;\n }\n\n function connect() {\n if (exited) return;\n\n ws = new WebSocket(wsUrl);\n\n ws.onopen = () => {\n if (initialConnection) {\n params.onOpen?.();\n initialConnection = false;\n }\n retryCount = 0; // Reset backoff on successful connection\n };\n\n ws.onmessage = (e: MessageEvent) => {\n try {\n const result = JSON.parse(e.data);\n const msgs: Message[] = Array.isArray(result) ? result : [result];\n msgs.forEach((msg) => {\n logLine?.(\"đŸ–Ĩī¸ âžĄī¸ 👤\", msg);\n if (msg.type === \"peer-joined\" || msg.type === \"peer-left\") {\n updatePeers(msg.users, msg);\n } else if (msg.type === \"ice-server\") {\n params.onIceUrl?.(msg.url, msg.expiration);\n } else if (msg.userId) {\n params.onMessage(msg.type, msg.payload, msg.userId);\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, 15000);\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(\n updatedUsers: { userId: string; joined: number }[],\n msg: Message,\n ) {\n const joined: IPeer[] = [];\n const left: { userId: string }[] = [];\n const updatedPeerSet = new Set<string>();\n\n const selfPeer = updatedUsers.filter((peer) => peer.userId === userId)[0];\n if (!selfPeer) {\n logLine?.(\"âš ī¸\", \"Cannot find self in updated users\");\n return;\n }\n const selfJoined = selfPeer.joined;\n\n updatedUsers.forEach(({ userId: pUserId, joined: joinedTime }) => {\n if (pUserId === userId) return;\n if (\n !peers.has(pUserId) ||\n (msg.type === \"peer-joined\" && pUserId === msg.userId)\n ) {\n const newPeer = {\n userId: pUserId,\n joined: joinedTime,\n };\n peers.set(pUserId, newPeer);\n joined.push(newPeer);\n }\n updatedPeerSet.add(pUserId);\n });\n\n for (const pUserId of peers.keys()) {\n if (\n !updatedPeerSet.has(pUserId) ||\n (msg.type === \"peer-left\" && pUserId === msg.userId)\n ) {\n peers.delete(pUserId);\n left.push({ userId: pUserId });\n }\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, selfJoined);\n if (left.length) params.onPeerLeft(left);\n }\n\n // Start initial connection\n connect();\n\n return {\n send(type, userId, payload) {\n send(type, userId, payload);\n },\n exitRoom: () => {\n exited = true;\n clearTimeout(timeoutId);\n ws.close();\n },\n };\n}\n",
7
+ "import type { IPeer } from \"./impl/signal-room.js\";\nimport { enterRoom as baseEnterRoom } from \"./impl/signal-room.js\";\nimport { RoomEvent, WorkerCommand } from \"./signal-room.worker.js\";\n\nexport function enterRoom<T extends string, P = any>({\n userId,\n worldId,\n room,\n protocol = \"wss\",\n host,\n autoRejoin = true,\n onOpen,\n onClose,\n onError,\n onPeerJoined,\n onPeerLeft,\n onIceUrl,\n onMessage,\n logLine,\n workerUrl,\n}: {\n userId: string;\n worldId: string;\n room: string;\n protocol?: 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[], selfJoined: number) => void;\n onPeerLeft: (users: { userId: string }[]) => void;\n onIceUrl?(url: string, expiration: number): void;\n onMessage: (type: T, payload: P, from: string) => void;\n logLine?: (direction: string, obj?: any) => void;\n\n // Pass the URL to your worker file (bundler will handle it)\n workerUrl?: URL;\n}): {\n exitRoom: () => void;\n send: <P extends any>(\n type: T,\n userId: \"server\" | string,\n payload?: P,\n ) => void;\n} {\n if (!workerUrl) {\n const CDN_WORKER_URL = `https://cdn.jsdelivr.net/npm/@dobuki/hello-worker/dist/signal-room.worker.min.js`;\n\n console.warn(\n \"Warning: enterRoom called without workerUrl; this may cause issues in some environments. You should pass workerUrl explicitly. Use:\",\n CDN_WORKER_URL,\n );\n return baseEnterRoom<T, P>({\n userId,\n worldId,\n room,\n protocol,\n host,\n autoRejoin,\n onOpen,\n onClose,\n onError,\n onPeerJoined,\n onPeerLeft,\n onIceUrl,\n onMessage,\n });\n }\n\n let worker: Worker | undefined;\n const res = fetch(workerUrl).then(async (res) => {\n if (!res.ok) {\n throw new Error(`Failed to load worker script: ${res.status}`);\n }\n const source = await res.text();\n\n const blob = new Blob([source], { type: \"text/javascript\" });\n const blobUrl = URL.createObjectURL(blob);\n\n worker = new Worker(blobUrl, { type: \"module\" });\n\n worker.addEventListener(\"message\", onWorkerMessage);\n\n worker.postMessage({\n cmd: \"enter\",\n userId,\n worldId,\n room,\n protocol,\n host,\n autoRejoin,\n } as WorkerCommand);\n });\n let exited = false;\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(\n ev.users.map((ev) => ({ userId: ev.userId, joined: ev.joined })),\n ev.joined,\n );\n else if (ev.kind === \"peer-left\") onPeerLeft(ev.users);\n else if (ev.kind === \"ice-server\") onIceUrl?.(ev.url, ev.expiration);\n else if (ev.kind === \"message\")\n onMessage(ev.type, ev.payload, ev.fromUserId);\n else if (ev.kind === \"log\") logLine?.(ev.direction, ev.obj);\n };\n\n return {\n exitRoom: () => {\n exited = true;\n worker?.removeEventListener(\"message\", onWorkerMessage);\n worker?.postMessage({ cmd: \"exit\" } as WorkerCommand);\n },\n send: (type, toUserId, payload) => {\n worker?.postMessage({\n cmd: \"send\",\n toUserId,\n host,\n room,\n type,\n payload,\n } as WorkerCommand);\n },\n };\n}\n\nexport type EnterRoom<T extends string, P> = typeof enterRoom<T, P>;\n",
8
8
  "import { IceUrlProvider } from \"./ice-url-provider\";\n\nconst FALLBACK_RTC_CONFIG = {\n iceServers: [{ urls: \"stun:stun.l.google.com:19302\" }],\n};\n\nexport class RTCConfigProvider {\n constructor(private iceUrlProvider: IceUrlProvider) {}\n\n private rtcConfig: RTCConfiguration & { timestamp: number } = {\n ...FALLBACK_RTC_CONFIG,\n timestamp: Date.now(),\n };\n private rtcConfigPromise?: Promise<RTCConfiguration & { timestamp: number }>;\n\n async getRtcConfig(): Promise<RTCConfiguration & { timestamp: number }> {\n const now = Date.now();\n if (now - (this.rtcConfig?.timestamp ?? 0) < 10000) {\n return this.rtcConfig;\n }\n\n if (!this.rtcConfigPromise) {\n this.rtcConfigPromise = new Promise<\n RTCConfiguration & { timestamp: number }\n >(async (resolve) => {\n let retries = 3;\n for (let r = 0; r < retries; r++) {\n try {\n const iceUrl = (await this.iceUrlProvider.requestIce()).url;\n const r = await fetch(iceUrl);\n if (!r.ok) throw new Error(`ICE endpoint failed: ${r.status}`);\n const rtcConfig = (await r.json()) as RTCConfiguration & {\n timestamp: number;\n };\n resolve(rtcConfig);\n return;\n } catch (e) {\n console.warn(\"Failed fetching iceUrl\");\n }\n }\n });\n this.rtcConfig = await this.rtcConfigPromise;\n this.rtcConfigPromise = undefined;\n }\n return this.rtcConfig;\n }\n}\n",
9
- "import { IceUrlProvider } from \"./utils/ice-url-provider\";\nimport { IPeer } from \"./signal/impl/signal-room\";\nimport { EnterRoom, enterRoom } from \"./signal/signal-room\";\nimport { RTCConfigProvider } from \"./utils/rtc-config\";\n\nexport type SigType = \"offer\" | \"answer\" | \"ice\" | \"request-ice\" | \"broadcast\";\nexport type SigPayload = {\n connectionId?: string;\n offer?: RTCSessionDescriptionInit;\n answer?: RTCSessionDescriptionInit;\n ice?: RTCIceCandidateInit;\n} & Record<string, any>;\n\ninterface Connection {\n id: string;\n peerConnectionId?: string;\n pc: RTCPeerConnection;\n // ICE that arrived before we had remoteDescription\n pendingRemoteIce: RTCIceCandidateInit[];\n}\n\ntype UserState = {\n connection?: Connection;\n\n // the signaling \"user\" handle so we can send messages\n peer: string;\n joined?: number;\n\n expirationTimeout?: number;\n close(): void;\n reset(): void;\n connectionPromise?: Promise<Connection>;\n};\n\nconst DEFAULT_ENTER_ROOM = enterRoom;\n\n/**\n * Collect peers\n */\nexport function collectPeerConnections({\n userId: passedUserId,\n worldId,\n receivePeerConnection,\n peerlessUserExpiration = 5000,\n enterRoomFunction: enterRoom = DEFAULT_ENTER_ROOM,\n logLine,\n onLeaveUser,\n workerUrl,\n onRoomReady,\n onRoomClose,\n onBroadcastMessage,\n}: {\n userId?: string;\n worldId: string;\n 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 restart?: () => void;\n }): void;\n onRoomReady?(info: { host: string; room: string }): void;\n onRoomClose?(info: {\n host: string;\n room: string;\n ev: Pick<CloseEvent, \"reason\" | \"code\" | \"wasClean\">;\n }): void;\n onBroadcastMessage?<P extends any>(payload: P, from: string): void;\n}) {\n const userId = passedUserId ?? `user-${crypto.randomUUID()}`;\n const users: Map<string, UserState> = new Map();\n\n const roomsEntered = new Map<\n string,\n {\n room: string;\n host: string;\n exitRoom: () => void;\n broadcast: <P extends any>(payload: P) => void;\n }\n >();\n\n function leaveUser(userId: string) {\n onLeaveUser?.(userId);\n const p = users.get(userId);\n if (!p) return;\n users.delete(userId);\n try {\n p.close();\n } catch {}\n }\n\n async function flushRemoteIce(state: UserState) {\n if (!state.connection?.pc?.remoteDescription) return;\n\n const queued = state.connection.pendingRemoteIce;\n state.connection.pendingRemoteIce = [];\n\n for (const ice of queued) {\n try {\n await state.connection.pc.addIceCandidate(ice);\n } catch (e) {\n logLine?.(\"âš ī¸ ERROR\", {\n error: \"add-ice-failed\",\n userId: state.peer,\n detail: String(e),\n });\n }\n }\n }\n\n const iceUrlProvider = new IceUrlProvider();\n const rtcConfigProvider = new RTCConfigProvider(iceUrlProvider);\n\n function exit({ room, host }: { room: string; host: string }) {\n const key = `${host}/room/${room}`;\n const session = roomsEntered.get(key);\n if (session) {\n session.exitRoom();\n roomsEntered.delete(key);\n }\n }\n\n function enter({ room, host }: { room: string; host: string }) {\n return new Promise<void>(async (resolve, reject) => {\n async function setupConnection(state: UserState) {\n if (state.connectionPromise) {\n return state.connectionPromise;\n }\n const promise = new Promise<Connection>(async (resolve) => {\n state.connection = {\n id: `conn-${crypto.randomUUID()}`,\n pc: new RTCPeerConnection(await rtcConfigProvider.getRtcConfig()),\n pendingRemoteIce: [],\n };\n\n // Send local ICE candidates to this peer\n state.connection.pc.onicecandidate = (ev) => {\n if (!ev.candidate) return;\n send(\"ice\", state.peer, {\n connectionId: state.connection?.id,\n ice: ev.candidate.toJSON(),\n });\n };\n\n state.connection.pc.onconnectionstatechange = async () => {\n logLine?.(\"đŸ’Ŧ\", {\n event: \"pc-state\",\n userId: state.peer,\n state: state.connection?.pc?.connectionState,\n });\n if (state.connection?.pc?.connectionState === \"failed\") {\n // reset the connection\n state.close();\n const userState = await getPeer(state.peer, true);\n if (userState.connection?.pc) {\n receivePeerConnection({\n pc: userState.connection?.pc,\n userId: userState.peer,\n restart: () => userState.close(),\n });\n } else {\n logLine?.(\"đŸ‘¤â„šī¸\", \"no pc: \" + userState.peer);\n }\n return;\n }\n };\n\n resolve(state.connection);\n });\n state.connectionPromise = promise;\n await promise;\n state.connectionPromise = undefined;\n return promise;\n }\n\n async function getPeer(\n peer: string,\n forceReset?: boolean,\n ): Promise<UserState> {\n let state = users.get(peer);\n if (!state || forceReset) {\n const newState: UserState = {\n peer,\n close() {\n if (this.connection) {\n this.connection.pc.close();\n this.connection = undefined;\n }\n users.delete(peer);\n },\n async reset() {\n newState.close();\n\n setTimeout(async () => {\n const userState = await getPeer(peer, true);\n if (!userState.connection?.pc) {\n logLine?.(\"âš ī¸\", \"no pc\");\n return;\n }\n receivePeerConnection({\n pc: userState.connection?.pc,\n userId: userState.peer,\n restart: () => userState.close(),\n });\n await makeOffer(userState.peer);\n }, 500);\n },\n };\n state = newState;\n\n await setupConnection(newState);\n\n // New user\n users.set(state.peer, state);\n } else if (state) {\n clearTimeout(state.expirationTimeout);\n state.expirationTimeout = 0;\n if (\n !state.connection?.pc ||\n state.connection?.pc.signalingState === \"closed\"\n ) {\n await setupConnection(state);\n }\n }\n state.peer = peer;\n return state;\n }\n\n async function makeOffer(userId: string) {\n // Offer flow: createOffer -> setLocalDescription -> send localDescription\n const state = await getPeer(userId);\n const pc = state.connection?.pc;\n const offer = await pc?.createOffer();\n await pc?.setLocalDescription(offer);\n send(\"offer\", userId, {\n connectionId: state.connection?.id,\n offer: pc?.localDescription?.toJSON(),\n });\n }\n\n const { exitRoom, send } = enterRoom({\n userId,\n worldId,\n room,\n host,\n logLine,\n workerUrl,\n autoRejoin: true,\n\n onOpen() {\n onRoomReady?.({ room, host });\n resolve();\n },\n onError() {\n console.error(\"onError\");\n reject();\n },\n onClose(ev: Pick<CloseEvent, \"reason\" | \"code\" | \"wasClean\">) {\n onRoomClose?.({ room, host, ev });\n },\n\n // Existing peers initiate to the newcomer\n onPeerJoined(joiningUsers: IPeer[], selfJoined: number) {\n joiningUsers.forEach(async (user) => {\n const state = await getPeer(user.userId, true);\n state.joined = user.joined;\n const pc = state.connection?.pc;\n if (!pc) {\n logLine?.(\"đŸ‘¤â„šī¸\", \"no pc: \" + user.userId);\n return;\n }\n\n receivePeerConnection({\n pc,\n userId: user.userId,\n restart: () => state.close(),\n });\n if (\n user.joined > selfJoined ||\n (user.joined === selfJoined &&\n user.userId.localeCompare(userId) > 0)\n ) {\n await makeOffer(user.userId);\n }\n });\n },\n\n onPeerLeft(leavingUsers: { userId: string }[]) {\n leavingUsers.forEach(({ userId }) => {\n const state = users.get(userId);\n if (!state) return;\n state.expirationTimeout = setTimeout(\n () => leaveUser(userId),\n peerlessUserExpiration ?? 0,\n );\n });\n },\n\n onIceUrl(url: string, expiration: number) {\n iceUrlProvider.receiveIce(url, expiration);\n },\n\n async onMessage(type, payload, from: string) {\n if (type === \"offer\" && payload.offer) {\n // Grab state and connection\n const state = await getPeer(from, false);\n const connection =\n !state.connection ||\n state.connection.pc.signalingState === \"stable\"\n ? await setupConnection(state)\n : state.connection; // reset\n logLine?.(\"đŸ’Ŧ\", {\n type,\n signalingState: connection.pc.signalingState,\n });\n\n connection.peerConnectionId = payload.connectionId;\n receivePeerConnection({\n pc: connection.pc,\n userId: from,\n restart: () => state.close(),\n });\n // Responder: set remote offer\n await connection.pc.setRemoteDescription(payload.offer);\n\n // Create and send answer\n const answer = await connection.pc.createAnswer();\n await connection.pc.setLocalDescription(answer);\n\n send(\"answer\", from, {\n connectionId: connection.id,\n answer: connection.pc.localDescription?.toJSON(),\n });\n\n // Now safe to apply any queued ICE from this peer\n await flushRemoteIce(state);\n return;\n }\n\n if (type === \"answer\" && payload.answer) {\n const state = await getPeer(from, false);\n const connection =\n state.connection &&\n state.connection.pc.signalingState !== \"closed\"\n ? state.connection\n : await setupConnection(state);\n logLine?.(\"đŸ’Ŧ\", {\n type,\n signalingState: connection.pc.signalingState,\n });\n\n // Initiator: set remote answer\n await connection.pc.setRemoteDescription(payload.answer);\n connection.peerConnectionId = payload.connectionId;\n await flushRemoteIce(state);\n return;\n }\n\n if (type === \"ice\" && payload.ice) {\n // Grab state and connection\n const state = await getPeer(from, false);\n const connection =\n state.connection ?? (await state.connectionPromise);\n if (!connection) {\n logLine?.(\"âš ī¸\", \"No connection\");\n return;\n }\n logLine?.(\"đŸ’Ŧ\", {\n type,\n signalingState: connection.pc.signalingState,\n });\n\n if (\n connection.peerConnectionId &&\n payload.connectionId !== connection.peerConnectionId\n ) {\n logLine?.(\n \"âš ī¸\",\n \"Mismatch peerConnectionID\" +\n payload.connectionId +\n \"vs\" +\n connection.peerConnectionId,\n );\n return;\n }\n\n // If we don't have remoteDescription yet (or if connectionId doesn't match), queue it\n if (\n !connection.pc.remoteDescription ||\n !connection.peerConnectionId\n ) {\n connection.peerConnectionId = payload.connectionId;\n connection.pendingRemoteIce.push(payload.ice);\n return;\n }\n\n try {\n await connection.pc.addIceCandidate(payload.ice);\n } catch (e) {\n logLine?.(\"âš ī¸ ERROR\", {\n error: \"add-ice-failed\",\n userId: state.peer,\n detail: String(e),\n });\n }\n return;\n }\n\n if (type === \"broadcast\") {\n onBroadcastMessage?.(payload, from);\n }\n },\n });\n\n const removeRequester = iceUrlProvider.addRequester((command) =>\n send(command, \"server\"),\n );\n\n roomsEntered.set(`${host}/room/${room}`, {\n exitRoom: () => {\n exitRoom();\n removeRequester();\n },\n room,\n host,\n broadcast: (payload) => send(\"broadcast\", \"server\", payload),\n });\n });\n }\n\n return {\n userId,\n enterRoom: enter,\n exitRoom: exit,\n leaveUser,\n async reset(userId: string) {\n const userState = users.get(userId);\n userState?.reset();\n },\n broadcast<P extends any>(payload: P) {\n roomsEntered.forEach((room) => room.broadcast(payload));\n },\n end() {\n roomsEntered.forEach(({ exitRoom }) => exitRoom());\n roomsEntered.clear();\n users.forEach(({ peer }) => leaveUser(peer));\n users.clear();\n },\n };\n}\n\n/*\nTurn Token ID\n<CF_TURN_TOKEN_ID>\n\nAPI Token\n<CF_RTC_API_TOKEN>\n\nCURL\ncurl \\\n\t-H \"Authorization: Bearer <CF_RTC_API_TOKEN>\" \\\n\t-H \"Content-Type: application/json\" -d '{\"ttl\": 86400}' \\\n\thttps://rtc.live.cloudflare.com/v1/turn/keys/<CF_TURN_TOKEN_ID>/credentials/generate-ice-servers\n\nJSON\n{\n\t\"iceServers\": [\n {\n \"urls\": [\n \"stun:stun.cloudflare.com:3478\",\n \"turn:turn.cloudflare.com:3478?transport=udp\",\n \"turn:turn.cloudflare.com:3478?transport=tcp\",\n \"turns:turn.cloudflare.com:5349?transport=tcp\"\n ],\n \"username\": \"xxxx\",\n \"credential\": \"yyyy\",\n }\n ]\n}\n\n*/\n"
9
+ "import { IceUrlProvider } from \"./utils/ice-url-provider\";\nimport { IPeer } from \"./signal/impl/signal-room\";\nimport { EnterRoom, enterRoom } from \"./signal/signal-room\";\nimport { RTCConfigProvider } from \"./utils/rtc-config\";\n\nexport type SigType = \"offer\" | \"answer\" | \"ice\" | \"request-ice\" | \"broadcast\";\nexport type SigPayload = {\n connectionId?: string;\n offer?: RTCSessionDescriptionInit;\n answer?: RTCSessionDescriptionInit;\n ice?: RTCIceCandidateInit;\n} & Record<string, any>;\n\ninterface Connection {\n id: string;\n peerConnectionId?: string;\n pc: RTCPeerConnection;\n // ICE that arrived before we had remoteDescription\n pendingRemoteIce: RTCIceCandidateInit[];\n}\n\ntype UserState = {\n connection?: Connection;\n\n // the signaling \"user\" handle so we can send messages\n peer: string;\n joined?: number;\n\n expirationTimeout?: number;\n close(): void;\n reset(): void;\n connectionPromise?: Promise<Connection>;\n};\n\nconst DEFAULT_ENTER_ROOM = enterRoom;\n\n/**\n * Collect peers\n */\nexport function collectPeerConnections({\n userId: passedUserId,\n worldId,\n receivePeerConnection,\n peerlessUserExpiration = 5000,\n enterRoomFunction: enterRoom = DEFAULT_ENTER_ROOM,\n logLine,\n onLeaveUser,\n workerUrl,\n onRoomReady,\n onRoomClose,\n onBroadcastMessage,\n}: {\n userId?: string;\n worldId: string;\n 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 restart?: () => void;\n }): void;\n onRoomReady?(info: { host: string; room: string }): void;\n onRoomClose?(info: {\n host: string;\n room: string;\n ev: Pick<CloseEvent, \"reason\" | \"code\" | \"wasClean\">;\n }): void;\n onBroadcastMessage?<P extends any>(payload: P, from: string): void;\n}) {\n const userId = passedUserId ?? `user-${crypto.randomUUID()}`;\n const users: Map<string, UserState> = new Map();\n\n const roomsEntered = new Map<\n string,\n {\n room: string;\n host: string;\n exitRoom: () => void;\n broadcast: <P extends any>(payload: P) => void;\n }\n >();\n\n function leaveUser(userId: string) {\n onLeaveUser?.(userId);\n const p = users.get(userId);\n if (!p) return;\n users.delete(userId);\n try {\n p.close();\n } catch {}\n }\n\n async function flushRemoteIce(state: UserState) {\n if (!state.connection?.pc?.remoteDescription) return;\n\n const queued = state.connection.pendingRemoteIce;\n state.connection.pendingRemoteIce = [];\n\n for (const ice of queued) {\n try {\n await state.connection.pc.addIceCandidate(ice);\n } catch (e) {\n logLine?.(\"âš ī¸ ERROR\", {\n error: \"add-ice-failed\",\n userId: state.peer,\n detail: String(e),\n });\n }\n }\n }\n\n const iceUrlProvider = new IceUrlProvider();\n const rtcConfigProvider = new RTCConfigProvider(iceUrlProvider);\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({\n room,\n host,\n protocol,\n }: {\n room: string;\n host: string;\n protocol?: string;\n }) {\n return new Promise<void>(async (resolve, reject) => {\n async function setupConnection(state: UserState) {\n if (state.connectionPromise) {\n return state.connectionPromise;\n }\n const promise = new Promise<Connection>(async (resolve) => {\n state.connection = {\n id: `conn-${crypto.randomUUID()}`,\n pc: new RTCPeerConnection(await rtcConfigProvider.getRtcConfig()),\n pendingRemoteIce: [],\n };\n\n // Send local ICE candidates to this peer\n state.connection.pc.onicecandidate = (ev) => {\n if (!ev.candidate) return;\n send(\"ice\", state.peer, {\n connectionId: state.connection?.id,\n ice: ev.candidate.toJSON(),\n });\n };\n\n state.connection.pc.onconnectionstatechange = async () => {\n logLine?.(\"đŸ’Ŧ\", {\n event: \"pc-state\",\n userId: state.peer,\n state: state.connection?.pc?.connectionState,\n });\n if (state.connection?.pc?.connectionState === \"failed\") {\n // reset the connection\n state.close();\n const userState = await getPeer(state.peer, true);\n if (userState.connection?.pc) {\n receivePeerConnection({\n pc: userState.connection?.pc,\n userId: userState.peer,\n restart: () => userState.close(),\n });\n } else {\n logLine?.(\"đŸ‘¤â„šī¸\", \"no pc: \" + userState.peer);\n }\n return;\n }\n };\n\n resolve(state.connection);\n });\n state.connectionPromise = promise;\n await promise;\n state.connectionPromise = undefined;\n return promise;\n }\n\n async function getPeer(\n peer: string,\n forceReset?: boolean,\n ): Promise<UserState> {\n let state = users.get(peer);\n if (!state || forceReset) {\n const newState: UserState = {\n peer,\n close() {\n if (this.connection) {\n this.connection.pc.close();\n this.connection = undefined;\n }\n users.delete(peer);\n },\n async reset() {\n newState.close();\n\n setTimeout(async () => {\n const userState = await getPeer(peer, true);\n if (!userState.connection?.pc) {\n logLine?.(\"âš ī¸\", \"no pc\");\n return;\n }\n receivePeerConnection({\n pc: userState.connection?.pc,\n userId: userState.peer,\n restart: () => userState.close(),\n });\n await makeOffer(userState.peer);\n }, 500);\n },\n };\n state = newState;\n\n await setupConnection(newState);\n\n // New user\n users.set(state.peer, state);\n } else if (state) {\n clearTimeout(state.expirationTimeout);\n state.expirationTimeout = 0;\n if (\n !state.connection?.pc ||\n state.connection?.pc.signalingState === \"closed\"\n ) {\n await setupConnection(state);\n }\n }\n state.peer = peer;\n return state;\n }\n\n async function makeOffer(userId: string) {\n // Offer flow: createOffer -> setLocalDescription -> send localDescription\n const state = await getPeer(userId);\n const pc = state.connection?.pc;\n const offer = await pc?.createOffer();\n await pc?.setLocalDescription(offer);\n send(\"offer\", userId, {\n connectionId: state.connection?.id,\n offer: pc?.localDescription?.toJSON(),\n });\n }\n\n const { exitRoom, send } = enterRoom({\n userId,\n worldId,\n room,\n protocol,\n host,\n logLine,\n workerUrl,\n autoRejoin: true,\n\n onOpen() {\n onRoomReady?.({ room, host });\n resolve();\n },\n onError() {\n console.error(\"onError\");\n reject();\n },\n onClose(ev: Pick<CloseEvent, \"reason\" | \"code\" | \"wasClean\">) {\n onRoomClose?.({ room, host, ev });\n },\n\n // Existing peers initiate to the newcomer\n onPeerJoined(joiningUsers: IPeer[], selfJoined: number) {\n joiningUsers.forEach(async (user) => {\n const state = await getPeer(user.userId, true);\n state.joined = user.joined;\n const pc = state.connection?.pc;\n if (!pc) {\n logLine?.(\"đŸ‘¤â„šī¸\", \"no pc: \" + user.userId);\n return;\n }\n\n receivePeerConnection({\n pc,\n userId: user.userId,\n restart: () => state.close(),\n });\n if (\n user.joined > selfJoined ||\n (user.joined === selfJoined &&\n user.userId.localeCompare(userId) > 0)\n ) {\n await makeOffer(user.userId);\n }\n });\n },\n\n onPeerLeft(leavingUsers: { userId: string }[]) {\n leavingUsers.forEach(({ userId }) => {\n const state = users.get(userId);\n if (!state) return;\n state.expirationTimeout = setTimeout(\n () => leaveUser(userId),\n peerlessUserExpiration ?? 0,\n );\n });\n },\n\n onIceUrl(url: string, expiration: number) {\n iceUrlProvider.receiveIce(url, expiration);\n },\n\n async onMessage(type, payload, from: string) {\n if (type === \"offer\" && payload.offer) {\n // Grab state and connection\n const state = await getPeer(from, false);\n const connection =\n !state.connection ||\n state.connection.pc.signalingState === \"stable\"\n ? await setupConnection(state)\n : state.connection; // reset\n logLine?.(\"đŸ’Ŧ\", {\n type,\n signalingState: connection.pc.signalingState,\n });\n\n connection.peerConnectionId = payload.connectionId;\n receivePeerConnection({\n pc: connection.pc,\n userId: from,\n restart: () => state.close(),\n });\n // Responder: set remote offer\n await connection.pc.setRemoteDescription(payload.offer);\n\n // Create and send answer\n const answer = await connection.pc.createAnswer();\n await connection.pc.setLocalDescription(answer);\n\n send(\"answer\", from, {\n connectionId: connection.id,\n answer: connection.pc.localDescription?.toJSON(),\n });\n\n // Now safe to apply any queued ICE from this peer\n await flushRemoteIce(state);\n return;\n }\n\n if (type === \"answer\" && payload.answer) {\n const state = await getPeer(from, false);\n const connection =\n state.connection &&\n state.connection.pc.signalingState !== \"closed\"\n ? state.connection\n : await setupConnection(state);\n logLine?.(\"đŸ’Ŧ\", {\n type,\n signalingState: connection.pc.signalingState,\n });\n\n // Initiator: set remote answer\n await connection.pc.setRemoteDescription(payload.answer);\n connection.peerConnectionId = payload.connectionId;\n await flushRemoteIce(state);\n return;\n }\n\n if (type === \"ice\" && payload.ice) {\n // Grab state and connection\n const state = await getPeer(from, false);\n const connection =\n state.connection ?? (await state.connectionPromise);\n if (!connection) {\n logLine?.(\"âš ī¸\", \"No connection\");\n return;\n }\n logLine?.(\"đŸ’Ŧ\", {\n type,\n signalingState: connection.pc.signalingState,\n });\n\n if (\n connection.peerConnectionId &&\n payload.connectionId !== connection.peerConnectionId\n ) {\n logLine?.(\n \"âš ī¸\",\n \"Mismatch peerConnectionID\" +\n payload.connectionId +\n \"vs\" +\n connection.peerConnectionId,\n );\n return;\n }\n\n // If we don't have remoteDescription yet (or if connectionId doesn't match), queue it\n if (\n !connection.pc.remoteDescription ||\n !connection.peerConnectionId\n ) {\n connection.peerConnectionId = payload.connectionId;\n connection.pendingRemoteIce.push(payload.ice);\n return;\n }\n\n try {\n await connection.pc.addIceCandidate(payload.ice);\n } catch (e) {\n logLine?.(\"âš ī¸ ERROR\", {\n error: \"add-ice-failed\",\n userId: state.peer,\n detail: String(e),\n });\n }\n return;\n }\n\n if (type === \"broadcast\") {\n onBroadcastMessage?.(payload, from);\n }\n },\n });\n\n const removeRequester = iceUrlProvider.addRequester((command) =>\n send(command, \"server\"),\n );\n\n roomsEntered.set(`${host}/room/${room}`, {\n exitRoom: () => {\n exitRoom();\n removeRequester();\n },\n room,\n host,\n broadcast: (payload) => send(\"broadcast\", \"server\", payload),\n });\n });\n }\n\n return {\n userId,\n enterRoom: enter,\n exitRoom: exit,\n leaveUser,\n async reset(userId: string) {\n const userState = users.get(userId);\n userState?.reset();\n },\n broadcast<P extends any>(payload: P) {\n roomsEntered.forEach((room) => room.broadcast(payload));\n },\n end() {\n roomsEntered.forEach(({ exitRoom }) => exitRoom());\n roomsEntered.clear();\n users.forEach(({ peer }) => leaveUser(peer));\n users.clear();\n },\n };\n}\n\n/*\nTurn Token ID\n<CF_TURN_TOKEN_ID>\n\nAPI Token\n<CF_RTC_API_TOKEN>\n\nCURL\ncurl \\\n\t-H \"Authorization: Bearer <CF_RTC_API_TOKEN>\" \\\n\t-H \"Content-Type: application/json\" -d '{\"ttl\": 86400}' \\\n\thttps://rtc.live.cloudflare.com/v1/turn/keys/<CF_TURN_TOKEN_ID>/credentials/generate-ice-servers\n\nJSON\n{\n\t\"iceServers\": [\n {\n \"urls\": [\n \"stun:stun.cloudflare.com:3478\",\n \"turn:turn.cloudflare.com:3478?transport=udp\",\n \"turn:turn.cloudflare.com:3478?transport=tcp\",\n \"turns:turn.cloudflare.com:5349?transport=tcp\"\n ],\n \"username\": \"xxxx\",\n \"credential\": \"yyyy\",\n }\n ]\n}\n\n*/\n"
10
10
  ],
11
- "mappings": "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",
12
- "debugId": "676E5AB7574F379664756E2164756E21",
11
+ "mappings": "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",
12
+ "debugId": "FD438A7CC3C2722364756E2164756E21",
13
13
  "names": []
14
14
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dobuki/hello-worker",
3
- "version": "1.0.85",
3
+ "version": "1.0.87",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -16,9 +16,9 @@
16
16
  "scripts": {
17
17
  "dev": "wrangler dev src/index.ts --config wrangler.dev.toml",
18
18
  "deploy": "wrangler deploy",
19
- "build:sample": "bun build src/browser/sample/index.ts src/browser/signal/signal-room.worker.ts --target browser --format esm --outdir public --minify --sourcemap",
19
+ "build:sample": "bun build src/browser/sample/index.ts src/browser/signal/signal-room.worker.ts --target browser --format esm --outdir public --sourcemap",
20
20
  "build:types": "tsc -p tsconfig.json",
21
- "build:main": "bun build src/browser/*.* --target browser --format esm --outdir dist --minify --sourcemap",
21
+ "build:main": "bun build src/browser/*.* --target browser --format esm --outdir dist --sourcemap",
22
22
  "build": "bun i && bun run build:main && bun run build:sample && bun run build:types",
23
23
  "prepublishOnly": "bun run build",
24
24
  "npm-patch": "npm version patch && npm publish"
@@ -27,5 +27,8 @@
27
27
  "@cloudflare/workers-types": "latest",
28
28
  "typescript": "latest",
29
29
  "wrangler": "latest"
30
+ },
31
+ "dependencies": {
32
+ "ts-md5": "^2.0.1"
30
33
  }
31
34
  }