declare module 'ws' {
  import { EventEmitter } from 'events';
  import { IncomingMessage } from 'http';
  import { Socket } from 'net';

  export type Data = string | Buffer | ArrayBuffer | Buffer[];

  export interface WebSocketEventMap {
    close: CloseEvent;
    error: ErrorEvent;
    message: MessageEvent;
    open: Event;
  }

  export default class WebSocket extends EventEmitter {
    static readonly CONNECTING: 0;
    static readonly OPEN: 1;
    static readonly CLOSING: 2;
    static readonly CLOSED: 3;

    readonly CONNECTING: 0;
    readonly OPEN: 1;
    readonly CLOSING: 2;
    readonly CLOSED: 3;

    readonly readyState: 0 | 1 | 2 | 3;
    readonly url: string;
    readonly protocol: string;

    constructor(address: string | URL, protocols?: string | string[]);

    close(code?: number, reason?: string): void;
    send(data: Data): void;
    ping(data?: Data): void;
    pong(data?: Data): void;
    terminate(): void;

    on(event: 'close', listener: (code: number, reason: Buffer) => void): this;
    on(event: 'error', listener: (error: Error) => void): this;
    on(event: 'message', listener: (data: Data) => void): this;
    on(event: 'open', listener: () => void): this;
    on(event: string | symbol, listener: (...args: any[]) => void): this;

    addEventListener(type: 'close', listener: (event: CloseEvent) => void): void;
    addEventListener(type: 'error', listener: (event: ErrorEvent) => void): void;
    addEventListener(type: 'message', listener: (event: MessageEvent) => void): void;
    addEventListener(type: 'open', listener: (event: Event) => void): void;
  }

  export interface CloseEvent {
    code: number;
    reason: string;
    wasClean: boolean;
  }

  export interface ErrorEvent {
    error: Error;
    message: string;
    type: string;
  }

  export namespace WebSocket {
    export const CONNECTING: 0;
    export const OPEN: 1;
    export const CLOSING: 2;
    export const CLOSED: 3;
  }
}
