/// /// import { PROTOCOL_ERROR_CODES, Command } from '../Constants'; import { ExecuteV3Response } from '../events/responses/ExecuteV3Response'; import { ListBreakpointsResponse } from '../events/responses/ListBreakpointsResponse'; import { AddBreakpointsResponse } from '../events/responses/AddBreakpointsResponse'; import { ProtocolCapabilities } from './ProtocolCapabilities'; import { ThreadsRequest } from '../events/requests/ThreadsRequest'; import type { ExceptionBreakpoint } from '../events/requests/SetExceptionBreakpointsRequest'; import type { ProtocolRequest, ProtocolResponse, ProtocolUpdate } from '../events/ProtocolEvent'; import { HandshakeResponse } from '../events/responses/HandshakeResponse'; import { HandshakeV3Response } from '../events/responses/HandshakeV3Response'; import { GenericV3Response } from '../events/responses/GenericV3Response'; import { AllThreadsStoppedUpdate } from '../events/updates/AllThreadsStoppedUpdate'; import { CompileErrorUpdate } from '../events/updates/CompileErrorUpdate'; import { GenericResponse } from '../events/responses/GenericResponse'; import type { StackTraceResponse } from '../events/responses/StackTraceResponse'; import { ThreadsResponse } from '../events/responses/ThreadsResponse'; import { SetExceptionBreakpointsResponse } from '../events/responses/SetExceptionBreakpointsResponse'; import { VariablesResponse } from '../events/responses/VariablesResponse'; import { ThreadAttachedUpdate } from '../events/updates/ThreadAttachedUpdate'; import { StackTraceV3Response } from '../events/responses/StackTraceV3Response'; import type { DebugProtocolClientPlugin } from './DebugProtocolClientPlugin'; import PluginInterface from '../PluginInterface'; import type { VerifiedBreakpoint } from '../events/updates/BreakpointVerifiedUpdate'; export declare class DebugProtocolClient { logger: import("@rokucommunity/logger").Logger; supportedVersionRange: string; constructor(options?: ConstructorOptions); private addCorePlugin; static DEBUGGER_MAGIC: string; scriptTitle: string; isHandshakeComplete: boolean; connectedToIoPort: boolean; /** * Debug protocol version 3.0.0 introduced a packet_length to all responses. Prior to that, most responses had no packet length at all. * This field indicates whether we should be looking for packet_length or not in the responses we get from the device */ watchPacketLength: boolean; /** * Capability flags derived from the negotiated protocol version. Undefined until the * handshake completes, then assigned a fresh `ProtocolCapabilities` keyed off the version * the device reported. */ capabilities: ProtocolCapabilities | undefined; /** * The protocol version negotiated with the device during the handshake. Undefined until * the handshake has completed. */ get protocolVersion(): string | undefined; primaryThread: number; stackFrameIndex: number; /** * A collection of plugins that can interact with the client at lifecycle points */ plugins: PluginInterface; private emitter; /** * The primary socket for this session. It's used to communicate with the debugger by sending commands and receives responses or updates */ private controlSocket; /** * Promise that is resolved when the control socket is closed */ private controlSocketClosed; /** * A socket where the debug server will send stdio */ private ioSocket; /** * Resolves when the ioSocket has closed */ private ioSocketClosed; /** * The buffer where all unhandled data will be stored until successfully consumed */ private buffer; /** * Is the debugger currently stopped at a line of code in the program */ isStopped: boolean; private requestIdSequence; private activeRequests; private options; /** * Get a promise that resolves after an event occurs exactly once */ once(eventName: 'app-exit' | 'cannot-continue' | 'close' | 'start'): Promise; once(eventName: 'breakpoints-verified'): Promise; once(eventName: 'runtime-error' | 'suspend'): Promise; once(eventName: 'io-output'): Promise; once(eventName: 'data'): Promise; once(eventName: 'response'): Promise; once(eventName: 'update'): Promise; once(eventName: 'protocol-version'): Promise; once(eventName: 'handshake-verified'): Promise; on(eventName: 'compile-error', handler: (event: CompileErrorUpdate) => void): any; on(eventName: 'app-exit' | 'cannot-continue' | 'close' | 'start', handler: () => void): any; on(eventName: 'breakpoints-verified', handler: (event: BreakpointsVerifiedEvent) => void): any; on(eventName: 'response', handler: (response: ProtocolResponse) => void): any; on(eventName: 'update', handler: (update: ProtocolUpdate) => void): any; /** * The raw data from the server socket. You probably don't need this... */ on(eventName: 'data', handler: (data: Buffer) => void): any; on(eventName: 'runtime-error' | 'suspend', handler: (data: T) => void): any; on(eventName: 'io-output', handler: (output: string) => void): any; on(eventName: 'protocol-version', handler: (data: ProtocolVersionDetails) => void): any; on(eventName: 'handshake-verified', handler: (data: HandshakeResponse) => void): any; private emit; /** * A collection of sockets created when trying to connect to the debug protocol's control socket. We keep these around for quicker tear-down * whenever there is an early-terminated debug session */ private establishControlConnection; /** * A queue for processing the incoming buffer, every transmission at a time */ private bufferQueue; /** * Connect to the debug server. * @param sendHandshake should the handshake be sent as part of this connect process. If false, `.sendHandshake()` will need to be called before a session can begin */ connect(sendHandshake?: boolean): Promise; /** * Send the initial handshake request, and wait for the handshake response */ sendHandshake(): Promise; private processHandshakeRequest; /** * Write a specific buffer log entry to the logger, which, when file logging is enabled * can be extracted and processed through the DebugProtocolClientReplaySession */ private writeToBufferLog; continue(): Promise; private processContinueRequest; pause(force?: boolean): Promise; private processStopRequest; /** * Send the "exit channel" command, which will tell the debug session to immediately quit */ exitChannel(): Promise; stepIn(threadIndex?: number): Promise; stepOver(threadIndex?: number): Promise; stepOut(threadIndex?: number): Promise; private step; private processStepRequest; threads(): Promise; processThreadsRequest(request: ThreadsRequest): Promise; setExceptionBreakpoints(filters: ExceptionBreakpoint[]): Promise; /** * Get the stackTrace from the device IF currently stopped */ getStackTrace(threadIndex?: number): Promise; private processStackTraceRequest; /** * @param variablePathEntries One or more path entries to the variable to be inspected. E.g., m.top.myObj["someKey"] can be accessed with ["m","top","myobj","\"someKey\""]. * * If no path is specified, the variables accessible from the specified stack frame are returned. * * Starting in protocol v3.1.0, The keys for indexed gets (i.e. obj["key"]) should be wrapped in quotes so they can be handled in a case-sensitive fashion (if applicable on device). * All non-quoted keys (i.e. strings without leading and trailing quotes inside them) will be treated as case-insensitive). * @param getChildKeys If set, VARIABLES response include the child keys for container types like lists and associative arrays * @param stackFrameIndex 0 = first function called, nframes-1 = last function. This indexing does not match the order of the frames returned from the STACKTRACE command * @param threadIndex the index (or perhaps ID?) of the thread to get variables for */ getVariables(variablePathEntries?: Array, stackFrameIndex?: number, threadIndex?: number): Promise; private processVariablesRequest; executeCommand(sourceCode: string, stackFrameIndex?: number, threadIndex?: number): Promise; private processExecuteRequest; addBreakpoints(breakpoints: Array): Promise; listBreakpoints(): Promise; /** * Remove breakpoints having the specified IDs */ removeBreakpoints(breakpointIds: number[]): Promise; private processRemoveBreakpointsRequest; /** * Given a request, process it in the proper fashion. This is mostly used for external mocking/testing of * this client, but it should force the client to flow in the same fashion as a live debug session */ processRequest(request: ProtocolRequest): Promise; /** * Send a request to the roku device, and get a promise that resolves once we have received the response */ private sendRequest; /** * Sometimes a request arrives that we don't understand. If that's the case, this function can be used * to discard that entire response by discarding `packet_length` number of bytes */ private discardNextResponseOrUpdate; /** * A counter to help give a unique id to each update (mostly just for logging purposes) */ private updateSequence; private logEvent; private process; /** * Given a buffer, try to parse into a specific ProtocolResponse or ProtocolUpdate */ getResponseOrUpdate(buffer: Buffer): Promise; static getResponse(buffer: Buffer, command: Command): ExecuteV3Response | ListBreakpointsResponse | GenericV3Response | StackTraceV3Response | ThreadsResponse | SetExceptionBreakpointsResponse | VariablesResponse; getUpdate(buffer: Buffer): ProtocolUpdate; private handleUpdateQueue; /** * Handle/process any received updates from the debug protocol */ private handleUpdate; /** * Verify all the handshake data */ private verifyHandshake; /** * When the debugger emits the IOPortOpenedUpdate, we need to immediately connect to the IO port to start receiving that data */ private connectToIoPort; /** * Destroy this instance, shutting down any sockets or other long-running items and cleaning up. * @param immediate if true, all sockets are immediately closed and do not gracefully shut down */ destroy(immediate?: boolean): Promise; private shutdownPromise; private shutdown; private _shutdown; private isDestroyingControlSocket; private destroyControlSocket; private isDestroyingIOSocket; /** * @param immediate if true, force close immediately instead of waiting for it to settle */ private destroyIOSocket; } export interface ProtocolVersionDetails { message: string; errorCode: PROTOCOL_ERROR_CODES; } export interface BreakpointSpec { /** * The path of the source file where the breakpoint is to be inserted. */ filePath: string; /** * The (1-based) line number in the channel application code where the breakpoint is to be executed. */ lineNumber: number; /** * The number of times to ignore the breakpoint condition before executing the breakpoint. This number is decremented each time the channel application reaches the breakpoint. */ hitCount?: number; /** * BrightScript code that evaluates to a boolean value. The expression is compiled and executed in * the context where the breakpoint is located. If specified, the hitCount is only be * updated if this evaluates to true. * @avaiable since protocol version 3.1.0 */ conditionalExpression?: string; } export interface ConstructorOptions { /** * The host/ip address of the Roku */ host: string; /** * The port number used to send all debugger commands. This is static/unchanging for Roku devices, * but is configurable here to support unit testing or alternate runtimes (i.e. https://www.npmjs.com/package/brs) */ controlPort?: number; /** * The interval (in milliseconds) for how frequently the `connect` * call should retry connecting to the control port. At the start of a debug session, * the protocol debugger will start trying to connect the moment the channel is sideloaded, * and keep trying until a successful connection is established or the debug session is terminated * @default 250 */ controlConnectInterval?: number; /** * The maximum time (in milliseconds) the debugger will keep retrying connections. * This is here to prevent infinitely pinging the Roku device. */ controlConnectMaxTime?: number; /** * The number of milliseconds that the client should wait during a shutdown request before forcefully terminating the sockets */ shutdownTimeout?: number; /** * The max time the client will wait for the `exit channel` response before forcefully terminating the sockets */ exitChannelTimeout?: number; } /** * Is the event a ProtocolRequest */ export declare function isProtocolRequest(event: ProtocolRequest | ProtocolResponse | ProtocolUpdate): event is ProtocolRequest; /** * Is the event a ProtocolResponse */ export declare function isProtocolResponse(event: ProtocolRequest | ProtocolResponse | ProtocolUpdate): event is ProtocolResponse; /** * Is the event a ProtocolUpdate update */ export declare function isProtocolUpdate(event: ProtocolRequest | ProtocolResponse | ProtocolUpdate): event is ProtocolUpdate; export interface BreakpointsVerifiedEvent { breakpoints: VerifiedBreakpoint[]; }