Download packages/client/src/lib/socket.ts from QSLY/ghp: direct link, hf CLI and curl.
- Browser
- Download file 4.2 kB
-
https://huggingface.co/spaces/QSLY/ghp/resolve/main/packages/client/src/lib/socket.ts
- Command line
-
hf download hf://spaces/QSLY/ghp/packages/client/src/lib/socket.ts
-
curl -L -o socket.ts https://huggingface.co/spaces/QSLY/ghp/resolve/main/packages/client/src/lib/socket.ts
4.2 kB
| import type { ClientToServerEvents, ServerToClientEvents } from '@music-together/shared' | |
| import { SERVER_URL } from './config' | |
| interface ConnectionEvents { | |
| connect: () => void | |
| disconnect: () => void | |
| connect_error: (error: Error) => void | |
| } | |
| type SocketInboundEvents = ServerToClientEvents & ConnectionEvents | |
| type StoredHandler = (...args: unknown[]) => void | |
| export type TypedSocket = { | |
| connected: boolean | |
| connect: () => void | |
| disconnect: () => void | |
| on: <E extends keyof SocketInboundEvents>(event: E, handler: SocketInboundEvents[E]) => void | |
| off: <E extends keyof SocketInboundEvents>(event: E, handler: SocketInboundEvents[E]) => void | |
| emit: <E extends keyof ClientToServerEvents>(event: E, ...args: Parameters<ClientToServerEvents[E]>) => void | |
| } | |
| let socket: TypedSocket | null = null | |
| export function getSocket(): TypedSocket { | |
| if (!socket) { | |
| socket = createWebSocket() | |
| } | |
| return socket | |
| } | |
| export function connectSocket(): TypedSocket { | |
| const s = getSocket() | |
| s.connect() | |
| return s | |
| } | |
| export function disconnectSocket(): void { | |
| if (socket) { | |
| socket.disconnect() | |
| socket = null | |
| } | |
| } | |
| export function waitForConnect(): Promise<TypedSocket> { | |
| const s = getSocket() | |
| if (s.connected) return Promise.resolve(s) | |
| return new Promise((resolve) => { | |
| const handler = () => { | |
| s.off('connect', handler) | |
| resolve(s) | |
| } | |
| s.on('connect', handler) | |
| s.connect() | |
| }) | |
| } | |
| function createWebSocket(): TypedSocket { | |
| let ws: WebSocket | null = null | |
| let connected = false | |
| const handlers = new Map<string, Set<StoredHandler>>() | |
| let reconnectTimer: ReturnType<typeof setTimeout> | null = null | |
| let shouldReconnect = true | |
| const connect = () => { | |
| shouldReconnect = true | |
| if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) { | |
| return | |
| } | |
| const wsUrl = SERVER_URL.replace(/^http/, 'ws') + '/ws' | |
| ws = new WebSocket(wsUrl) | |
| ws.onopen = () => { | |
| connected = true | |
| const connectHandlers = handlers.get('connect') | |
| if (connectHandlers) { | |
| for (const h of connectHandlers) h() | |
| } | |
| } | |
| ws.onmessage = (event) => { | |
| try { | |
| const msg = JSON.parse(event.data) | |
| if (msg && typeof msg.event === 'string') { | |
| const eventHandlers = handlers.get(msg.event) | |
| if (eventHandlers) { | |
| for (const h of eventHandlers) h(msg.data) | |
| } | |
| } | |
| } catch (err) { | |
| console.error('Failed to parse WebSocket message', err) | |
| } | |
| } | |
| ws.onclose = () => { | |
| connected = false | |
| const disconnectHandlers = handlers.get('disconnect') | |
| if (disconnectHandlers) { | |
| for (const h of disconnectHandlers) h() | |
| } | |
| if (shouldReconnect) { | |
| reconnectTimer = setTimeout(() => { | |
| connect() | |
| }, 2000) | |
| } | |
| } | |
| ws.onerror = (err) => { | |
| console.error('WebSocket error', err) | |
| const errorHandlers = handlers.get('connect_error') | |
| if (errorHandlers) { | |
| for (const h of errorHandlers) h(new Error('WebSocket error')) | |
| } | |
| } | |
| } | |
| const disconnect = () => { | |
| shouldReconnect = false | |
| if (reconnectTimer) { | |
| clearTimeout(reconnectTimer) | |
| reconnectTimer = null | |
| } | |
| if (ws) { | |
| ws.close() | |
| ws = null | |
| } | |
| connected = false | |
| } | |
| const on = <E extends keyof SocketInboundEvents>(event: E, handler: SocketInboundEvents[E]) => { | |
| let set = handlers.get(event as string) | |
| if (!set) { | |
| set = new Set() | |
| handlers.set(event as string, set) | |
| } | |
| set.add(handler as unknown as StoredHandler) | |
| } | |
| const off = <E extends keyof SocketInboundEvents>(event: E, handler: SocketInboundEvents[E]) => { | |
| const set = handlers.get(event as string) | |
| if (set) { | |
| set.delete(handler as unknown as StoredHandler) | |
| } | |
| } | |
| const emit = <E extends keyof ClientToServerEvents>(event: E, ...args: Parameters<ClientToServerEvents[E]>) => { | |
| if (ws && ws.readyState === WebSocket.OPEN) { | |
| ws.send(JSON.stringify({ event, data: args[0] })) | |
| } | |
| } | |
| return { | |
| get connected() { | |
| return connected | |
| }, | |
| connect, | |
| disconnect, | |
| on, | |
| off, | |
| emit, | |
| } | |
| } | |