Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/danielroe/roe.dev. This is the code and content for my personal website, built in Nuxt. roe.dev
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134import { parseMessage, type WebSocketMessage, type ParseResultOrError } from '#shared/utils/websocket-messages'
export interface WebSocketConfig { /** WebSocket endpoint path (e.g., '_ws', 'voting/_ws') */ endpoint: string /** Host override (defaults to current host or environment) */ host?: string /** Typed message handler for parsed messages */ onParsedMessage?: (message: WebSocketMessage) => void onOpen?: () => void onClose?: () => void onError?: (error: Event) => void onParseError?: (error: ParseResultOrError<never>) => void /** Auto-reconnect on close (default: false) */ autoReconnect?: boolean /** Reconnect delay in milliseconds (default: 1000) */ reconnectDelay?: number}
export class WebSocketConnection { private websocket: WebSocket | null = null private isConnecting = false private reconnectTimeout: NodeJS.Timeout | null = null private config: Required<WebSocketConfig>
constructor (config: WebSocketConfig) { this.config = { host: this.getDefaultHost(), onParsedMessage: () => {}, onOpen: () => {}, onClose: () => {}, onError: () => {}, onParseError: () => {}, autoReconnect: false, reconnectDelay: 1000, ...config, } }
private getDefaultHost (): string { if (import.meta.server) return 'localhost:3000'
// Client-side logic const envHost = import.meta.env?.VITE_PUBLIC_PARTYKIT_SERVER if (envHost) return envHost
return import.meta.dev ? 'localhost:3000' : window.location.host }
private get wsUrl (): string { const protocol = this.config.host.startsWith('localhost') ? 'ws:' : 'wss:' return `${protocol}//${this.config.host}/${this.config.endpoint}` }
connect (): void { if (this.websocket?.readyState === WebSocket.OPEN || this.isConnecting || import.meta.test) { return }
this.isConnecting = true this.websocket = new WebSocket(this.wsUrl)
this.websocket.onopen = () => { this.isConnecting = false console.log(`[ws] connected to ${this.config.endpoint}`) this.config.onOpen() }
this.websocket.onmessage = event => { // Try to parse and call the typed handler if (this.config.onParsedMessage) { const result = parseMessage(event.data as string) if (result.success) { this.config.onParsedMessage(result.data) } else if (this.config.onParseError) { this.config.onParseError(result) } } }
this.websocket.onclose = () => { this.isConnecting = false console.log(`[ws] disconnected from ${this.config.endpoint}`) this.config.onClose()
// Attempt to reconnect if enabled if (this.config.autoReconnect) { this.reconnectTimeout = setTimeout(() => { this.connect() }, this.config.reconnectDelay) } }
this.websocket.onerror = error => { console.error(`[ws] error on ${this.config.endpoint}:`, error) this.isConnecting = false this.config.onError(error) } }
send (message: string): boolean { if (this.websocket?.readyState === WebSocket.OPEN) { this.websocket.send(message) return true } return false }
disconnect (): void { if (this.reconnectTimeout) { clearTimeout(this.reconnectTimeout) this.reconnectTimeout = null }
if (this.websocket) { this.websocket.close() this.websocket = null } }
get readyState (): number | undefined { return this.websocket?.readyState }
get isOpen (): boolean { return this.websocket?.readyState === WebSocket.OPEN }}
export function createWebSocket (config: WebSocketConfig): WebSocketConnection { return new WebSocketConnection(config)}