import { EventEmitter } from 'events' ; import WebSocket from "ws" ; import { localize } from '../localize' ; export interface MockBreakpoint { id : number ; line : number ; verified : boolean ; } export class DebuggerConnection extends EventEmitter { private connection : WebSocket | null ; private responseAwaiters : { [ key ] : ( ( value : any ) => void ) [ ] ; } = { } ; constructor ( ) { super ( ) ; process . env . NODE_TLS_REJECT_UNAUTHORIZED = "0" ; } public isConnected ( ) { return this . connection != null ; } public async attach ( address , key , stopOnEntry ) { let connection ; return new Promise ( ( resolve , reject ) => { connection = new WebSocket ( `${ address }?key=${ key }` ) . on ( 'error' , e => { if ( this . connection == null || this . connection === connection ) { this . connection = null ; this . sendEvent ( 'end' , localize ( "" , `Can't connect to gateway: ${ e . message }.` ) ) ; } reject ( ) ; } ) . on ( 'open' , ( ) => { this . connection = connection ; this . sendCommand ( 'attach' , { break : stopOnEntry } ) ; resolve ( ) ; } ) . on ( 'message' , e => { this . parseResponse ( e . toString ( ) ) ; } ) ; } ) ; } public async getRequests ( ) { return this . waitForResponse < RequestContract [ ] > ( ( ) => this . sendCommand ( 'getRequests' ) , 'requests' ) ; } public async getStackTrace ( requestId , threadId ) { return this . waitForResponse < StackFrameContract [ ] > ( ( ) => this . sendCommand ( 'getStackTrace' , { requestId : requestId , threadId : threadId } ) , 'stackTrace' ) ; } public async getVariables ( requestId , threadId , path ? ) { return this . waitForResponse < VariableContract [ ] > ( ( ) => this . sendCommand ( 'getVariables' , { requestId : requestId , threadId : threadId , path : path } ) , 'variables' ) ; } public async setBreakpoints ( breakpoints , scopeId ) { this . sendCommand ( 'setBreakpoints' , { scopeId : scopeId , breakpoints : breakpoints } ) ; } public async stepOver ( requestId , threadId ) { this . sendCommand ( 'stepOver' , { requestId : requestId , threadId : threadId } ) ; } public async stepIn ( requestId , threadId ) { this . sendCommand ( 'stepIn' , { requestId : requestId , threadId : threadId } ) ; } public async stepOut ( requestId , threadId ) { this . sendCommand ( 'stepOut' , { requestId : requestId , threadId : threadId } ) ; } public async continue ( requestId , threadId ) { this . sendCommand ( 'continue' , { requestId : requestId , threadId : threadId } ) ; } public async pause ( requestId , threadId ) { this . sendCommand ( 'pause' , { requestId : requestId , threadId : threadId } ) ; } public async terminateRequests ( requests ) { this . sendCommand ( 'terminateRequests' , { requests : requests } ) ; } private sendCommand ( name , args ? ) { if ( this . connection == null ) { return ; } this . connection . send ( JSON . stringify ( { name : name , arguments : args } ) ) ; } private parseResponse ( data ) { try { const event = JSON . parse ( data . trim ( ) ) ; switch ( event . name ) { case 'stopOnEntry' : this . sendEvent ( event . name , event . arguments . requestId , event . arguments . threadId , event . arguments . operationId , event . arguments . apiId , event . arguments . productId ) ; break ; case 'stopOnStep' : this . sendEvent ( event . name , event . arguments . requestId , event . arguments . threadId , event . arguments . operationId , event . arguments . apiId , event . arguments . productId ) ; break ; case 'stopOnException' : this . sendEvent ( event . name , event . arguments . requestId , event . arguments . threadId , event . arguments . operationId , event . arguments . apiId , event . arguments . productId , event . arguments . message ) ; break ; case 'stopOnBreakpoint' : this . sendEvent ( event . name , event . arguments . requestId , event . arguments . threadId , event . arguments . operationId , event . arguments . apiId , event . arguments . productId ) ; break ; case 'threadExited' : this . sendEvent ( event . name , event . arguments . requestId , event . arguments . threadId , event . arguments . operationId , event . arguments . apiId , event . arguments . productId ) ; break ; } const awaiters = this . responseAwaiters [ event . name ] ; if ( awaiters ) { for ( const awaiter of awaiters ) { awaiter ( event . arguments ) ; } delete this . responseAwaiters [ event . name ] ; } } catch { return ; } } private async waitForResponse < T > ( sendCommand , name ) { return new Promise < T > ( resolve => { ( this . responseAwaiters [ name ] || ( this . responseAwaiters [ name ] = [ ] ) ) . push ( resolve ) ; sendCommand ( ) ; } ) ; } private sendEvent ( event , ... args ) { setImmediate ( _ => { this . emit ( event , ... args ) ; } ) ; } } export interface RequestContract { id : string ; threads : number [ ] ; operationId : string ; apiId : string ; productId : string ; } export interface StackFrameContract { scopeId : string ; index : number ; name : string ; section : string ; } export enum StackFrameScopeContract { operation = '/operations' , api = '/apis' , product = '/products' , tenant = '/tenant' } export interface VariableContract { name : string ; type : string ; value : any ; nestedCount : any ; }