| export interface WebSocketLike { |
| readonly CONNECTING: number |
| readonly OPEN: number |
| readonly CLOSING: number |
| readonly CLOSED: number |
| readonly readyState: number |
| readonly url: string |
| readonly protocol: string |
|
|
| |
| |
| |
| close(code?: number, reason?: string): void |
| |
| |
| |
| send(data: string | ArrayBufferLike | Blob | ArrayBufferView): void |
|
|
| onopen: ((this: any, ev: Event) => any) | null |
| onmessage: ((this: any, ev: MessageEvent) => any) | null |
| onclose: ((this: any, ev: CloseEvent) => any) | null |
| onerror: ((this: any, ev: Event) => any) | null |
|
|
| |
| |
| |
| addEventListener(type: string, listener: EventListener): void |
| |
| |
| |
| removeEventListener(type: string, listener: EventListener): void |
|
|
| |
| binaryType?: string |
| bufferedAmount?: number |
| extensions?: string |
| dispatchEvent?: (event: Event) => boolean |
| } |
|
|
| export interface WebSocketEnvironment { |
| type: 'native' | 'cloudflare' | 'unsupported' |
| |
| wsConstructor?: typeof WebSocket |
| error?: string |
| workaround?: string |
| } |
|
|
| |
| |
| |
| |
| interface RuntimeGlobals { |
| WebSocket?: { new (url: string, protocols?: string | string[]): WebSocketLike } |
| WebSocketPair?: unknown |
| EdgeRuntime?: unknown |
| } |
|
|
| |
| |
| |
| export class WebSocketFactory { |
| |
| |
| |
| private constructor() {} |
| private static detectEnvironment(): WebSocketEnvironment { |
| if (typeof WebSocket !== 'undefined') { |
| return { type: 'native', wsConstructor: WebSocket } |
| } |
|
|
| const gt = globalThis as typeof globalThis & RuntimeGlobals |
| if (typeof globalThis !== 'undefined' && typeof gt.WebSocket !== 'undefined') { |
| return { type: 'native', wsConstructor: gt.WebSocket as typeof WebSocket } |
| } |
|
|
| const gl = |
| typeof global !== 'undefined' ? (global as typeof global & RuntimeGlobals) : undefined |
| if (gl && typeof gl.WebSocket !== 'undefined') { |
| return { type: 'native', wsConstructor: gl.WebSocket as typeof WebSocket } |
| } |
|
|
| if ( |
| typeof globalThis !== 'undefined' && |
| typeof gt.WebSocketPair !== 'undefined' && |
| typeof globalThis.WebSocket === 'undefined' |
| ) { |
| return { |
| type: 'cloudflare', |
| error: |
| 'Cloudflare Workers detected. WebSocket clients are not supported in Cloudflare Workers.', |
| workaround: |
| 'Use Cloudflare Workers WebSocket API for server-side WebSocket handling, or deploy to a different runtime.', |
| } |
| } |
|
|
| if ( |
| (typeof globalThis !== 'undefined' && gt.EdgeRuntime) || |
| (typeof navigator !== 'undefined' && navigator.userAgent?.includes('Vercel-Edge')) |
| ) { |
| return { |
| type: 'unsupported', |
| error: |
| 'Edge runtime detected (Vercel Edge/Netlify Edge). WebSockets are not supported in edge functions.', |
| workaround: |
| 'Use serverless functions or a different deployment target for WebSocket functionality.', |
| } |
| } |
|
|
| |
| const _process = (globalThis as Record<string, unknown>)['process'] as |
| | { versions?: { node?: string } } |
| | undefined |
| if (_process) { |
| const processVersions = _process['versions'] |
| if (processVersions && processVersions['node']) { |
| |
| |
| return { |
| type: 'unsupported', |
| error: 'Node.js detected but native WebSocket not found.', |
| workaround: |
| 'Ensure you are running Node.js 22+ or provide a WebSocket implementation via the transport option.', |
| } |
| } |
| } |
|
|
| return { |
| type: 'unsupported', |
| error: 'Unknown JavaScript runtime without WebSocket support.', |
| workaround: |
| "Ensure you're running in a supported environment (browser, Node.js, Deno) or provide a custom WebSocket implementation.", |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public static getWebSocketConstructor(): typeof WebSocket { |
| const env = this.detectEnvironment() |
| if (env.wsConstructor) { |
| return env.wsConstructor |
| } |
| let errorMessage = env.error || 'WebSocket not supported in this environment.' |
| if (env.workaround) { |
| errorMessage += `\n\nSuggested solution: ${env.workaround}` |
| } |
| throw new Error(errorMessage) |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public static isWebSocketSupported(): boolean { |
| try { |
| const env = this.detectEnvironment() |
| return env.type === 'native' |
| } catch { |
| return false |
| } |
| } |
| } |
|
|
| export default WebSocketFactory |
|
|