import assert from "assert" ; import { InputGatedEventTarget , RequestContext , getRequestContext , kWrapListener , waitForOpenOutputGate , } from "@miniflare/shared" ; export class MessageEvent extends Event { readonly data : ArrayBuffer | string ; constructor ( type , init ) { super ( type ) ; this . data = init . data ; } } export class CloseEvent extends Event { readonly code : number ; readonly reason : string ; readonly wasClean : boolean ; constructor ( type , init ? ) { super ( type ) ; this . code = init ?. code ?? 1005 ; this . reason = init ?. reason ?? "" ; this . wasClean = init ?. wasClean ?? false ; } } export class ErrorEvent extends Event { readonly error : Error | null ; constructor ( type , init ? ) { super ( type ) ; this . error = init ?. error ?? null ; } } const kPair = Symbol ( "kPair" ) ; export const kAccepted = Symbol ( "kAccepted" ) ; export const kCoupled = Symbol ( "kCoupled" ) ; export const kClosed = Symbol ( "kClosed" ) ; export const kClose = Symbol ( "kClose" ) ; export type WebSocketEventMap = { message : MessageEvent ; close : CloseEvent ; error : ErrorEvent ; } ; export class WebSocket extends InputGatedEventTarget < WebSocketEventMap > { #sendQueue ? : MessageEvent [ ] = [ ] ; [ kPair ] : WebSocket ; [ kAccepted ] = false ; [ kCoupled ] = false ; [ kClosed ] = false ; protected [ kWrapListener ] < Type extends keyof WebSocketEventMap > ( listener ) { const wrappedListener = super [ kWrapListener ] ( listener ) ; const addListenerCtx = getRequestContext ( ) ; return ( event ) => { if ( addListenerCtx ?. durableObject || addListenerCtx === undefined ) { const ctx = new RequestContext ( { requestDepth : addListenerCtx ?. requestDepth , pipelineDepth : addListenerCtx ?. pipelineDepth , } ) ; ctx . runWith ( ( ) => wrappedListener ( event ) ) ; } else { addListenerCtx . runWith ( ( ) => wrappedListener ( event ) ) ; } } ; } accept ( ) { if ( this [ kCoupled ] ) { throw new TypeError ( "Can't accept() WebSocket that was already used in a response." ) ; } if ( this [ kAccepted ] ) return ; this [ kAccepted ] = true ; const sendQueue = this . #sendQueue ; if ( sendQueue ) { for ( const event of sendQueue ) this . dispatchEvent ( event ) ; this . #sendQueue = undefined ; } } send ( message ) { if ( ! this [ kAccepted ] ) { throw new TypeError ( "You must call accept() on this WebSocket before sending messages." ) ; } if ( this [ kClosed ] ) { throw new TypeError ( "Can't call WebSocket send() after close()." ) ; } const event = new MessageEvent ( "message" , { data : message } ) ; void this . #dispatchMessageEvent ( event ) ; } async #dispatchMessageEvent ( event ) { await waitForOpenOutputGate ( ) ; const pair = this [ kPair ] ; if ( pair [ kAccepted ] ) { pair . dispatchEvent ( event ) ; } else { const sendQueue = pair . #sendQueue ; assert ( sendQueue !== undefined ) ; sendQueue . push ( event ) ; } } close ( code ? , reason ? ) { if ( code ) { const validCode = code >= 1000 && code < 5000 && code !== 1004 && code !== 1005 && code !== 1006 && code !== 1015 ; if ( ! validCode ) throw new TypeError ( "Invalid WebSocket close code." ) ; } if ( reason !== undefined && code === undefined ) { throw new TypeError ( "If you specify a WebSocket close reason, you must also specify a code." ) ; } this [ kClose ] ( code , reason ) ; } [ kClose ] ( code ? , reason ? ) { if ( ! this [ kAccepted ] ) { throw new TypeError ( "You must call accept() on this WebSocket before sending messages." ) ; } if ( this [ kClosed ] ) throw new TypeError ( "WebSocket already closed" ) ; this [ kClosed ] = true ; this [ kPair ] [ kClosed ] = true ; void this . #dispatchCloseEvent ( code , reason ) ; } async #dispatchCloseEvent ( code ? , reason ? ) { await waitForOpenOutputGate ( ) ; this . dispatchEvent ( new CloseEvent ( "close" , { code , reason } ) ) ; this [ kPair ] . dispatchEvent ( new CloseEvent ( "close" , { code , reason } ) ) ; } } export type WebSocketPair = { 0 : WebSocket ; 1 : WebSocket ; } ; export const WebSocketPair = function ( this ) { if ( ! ( this instanceof WebSocketPair ) ) { throw new TypeError ( "Failed to construct 'WebSocketPair': Please use the 'new' operator, this object constructor cannot be called as a function." ) ; } this [ 0 ] = new WebSocket ( ) ; this [ 1 ] = new WebSocket ( ) ; this [ 0 ] [ kPair ] = this [ 1 ] ; this [ 1 ] [ kPair ] = this [ 0 ] ; } as any ;