import WebSocket from 'ws' ; import { IDisposable } from '../util/types' ; export const ICDPClientAddress = Symbol ( 'ICDPClientAddress' ) ; export interface ICDPClientAddress { host : string ; port : number ; /**
     * TODO Make required once https://github.com/microsoft/vscode-js-debug/issues/987 is released
     */ path ? : string ; } interface IFulfillRequest { resolve : ( result : unknown ) => void ; reject : ( e : Error ) => void ; } type Method = string ; type SubscriptionCallback = ( data : Record < string , unknown > ) => void ; type MessageId = number ; interface IProtocolCommand { id ? : number ; method : string ; params : Record < string , unknown > ; sessionId ? : string ; } interface IProtocolError { id : number ; method ? : string ; error : { code : number ; message : string ; } ; sessionId ? : string ; } interface IProtocolSuccess { id : number ; result : Record < string , unknown > ; sessionId ? : string ; } type ProtocolMessage = IProtocolCommand | ProtocolResponse ; type ProtocolResponse = IProtocolError | IProtocolSuccess ; export interface ICDPClient extends IDisposable { connect ( ) : Promise < void > ; request ( domain , method , params ? ) : Promise < unknown > ; subscribe ( domain , event , callback ) : Promise < unknown > ; } see https export default class CDPClient implements ICDPClient { private webSocket ? : WebSocket ; private lastMessageId = 0 ; private pendingRequests : Map < MessageId , IFulfillRequest > = new Map ( ) ; private subscriptions : Map < Method , SubscriptionCallback [ ] > = new Map ( ) ; constructor ( private readonly host , private readonly port , private readonly path ) { } connect ( ) { if ( ! this . webSocket ) { return new Promise ( ( resolve ) => { const webSocket = new WebSocket ( `ws://${ this . host }:${ this . port }${ this . path }` ) ; webSocket . on ( 'message' , ( d ) => { const message = d . toString ( ) ; onMessage ( message , { Response : ( response ) => { const pendingRequest = this . pendingRequests . get ( response . id ) ; if ( ! pendingRequest ) { return ; } this . pendingRequests . delete ( response . id ) ; if ( 'error' in response ) { pendingRequest . reject ( new Error ( JSON . stringify ( response . error ) ) ) ; } else if ( 'result' in response ) { pendingRequest . resolve ( response . result ) ; } } , Event : ( event ) => { const callbacks = this . subscriptions . get ( event . method ) || [ ] ; for ( const callback of callbacks ) { callback ( event . params ) ; } } , } ) ; } ) ; webSocket . on ( 'open' , async ( ) => { this . webSocket = webSocket ; resolve ( ) ; } ) ; } ) ; } return Promise . resolve ( ) ; } request ( domain , method , params ? ) { return this . send ( `${ domain }.${ method }` , params ) ; } async subscribe ( domain , event , callback ) { const domainAndEvent = `${ domain }.${ event }` ; if ( this . subscriptions . has ( domainAndEvent ) ) { this . subscriptions . get ( domainAndEvent ) ?. push ( callback ) ; } else { this . subscriptions . set ( domainAndEvent , [ callback ] ) ; } return this . request ( 'JsDebug' , 'subscribe' , { events : [ domainAndEvent ] } ) ; } dispose ( ) { if ( this . webSocket ) { this . webSocket . close ( ) ; this . webSocket = undefined ; } } private async send ( method , params ? ) { if ( ! this . webSocket ) { throw new Error ( 'WebSocket not initialized.' ) ; } const messageId = ++ this . lastMessageId ; return new Promise ( ( resolve , reject ) => { this . pendingRequests . set ( messageId , { resolve , reject , } ) ; const message = { id : messageId , method , params , } ; const json = JSON . stringify ( message ) ; this . webSocket ?. send ( json ) ; } ) ; } } function onMessage < T > ( message , handlers ) { const json = JSON . parse ( message ) ; const response = json as ProtocolMessage ; if ( response . id === undefined ) { return handlers . Event ( response as IProtocolCommand ) ; } return handlers . Response ( response as ProtocolResponse ) ; }