1*5234fe38SCedric van Puttendeclare module 'metro-inspector-proxy/src/Device' { 2*5234fe38SCedric van Putten import { Device } from 'metro-inspector-proxy'; 3*5234fe38SCedric van Putten export = Device; 4*5234fe38SCedric van Putten} 5*5234fe38SCedric van Putten 633643b60SEvan Bacondeclare module 'metro-inspector-proxy' { 7*5234fe38SCedric van Putten import WS from 'ws'; 8*5234fe38SCedric van Putten import type { Server as HttpsServer } from 'https'; 9*5234fe38SCedric van Putten import type { 10*5234fe38SCedric van Putten IncomingMessage as HttpRequest, 11*5234fe38SCedric van Putten ServerResponse as HttpResponse, 12*5234fe38SCedric van Putten Server as HttpServer, 13*5234fe38SCedric van Putten } from 'http'; 14*5234fe38SCedric van Putten 15*5234fe38SCedric van Putten type Middleware = (error?: Error) => any; 16*5234fe38SCedric van Putten 17*5234fe38SCedric van Putten /** 18*5234fe38SCedric van Putten * Page information received from the device. New page is created for 19*5234fe38SCedric van Putten * each new instance of VM and can appear when user reloads React Native 20*5234fe38SCedric van Putten * application. 21*5234fe38SCedric van Putten */ 22*5234fe38SCedric van Putten type Page = { 23*5234fe38SCedric van Putten id: string; 24*5234fe38SCedric van Putten title: string; 25*5234fe38SCedric van Putten vm: string; 26*5234fe38SCedric van Putten app: string; 27*5234fe38SCedric van Putten 28*5234fe38SCedric van Putten // Allow objects too 29*5234fe38SCedric van Putten [key: string]: string; 30*5234fe38SCedric van Putten }; 31*5234fe38SCedric van Putten 32*5234fe38SCedric van Putten type DebuggerInfo = { 33*5234fe38SCedric van Putten // Debugger web socket connection 34*5234fe38SCedric van Putten socket: WS; 35*5234fe38SCedric van Putten // If we replaced address (like '10.0.2.2') to localhost we need to store original 36*5234fe38SCedric van Putten // address because Chrome uses URL or urlRegex params (instead of scriptId) to set breakpoints. 37*5234fe38SCedric van Putten originalSourceURLAddress?: string; 38*5234fe38SCedric van Putten prependedFilePrefix: boolean; 39*5234fe38SCedric van Putten pageId: string; 40*5234fe38SCedric van Putten 41*5234fe38SCedric van Putten // Allow objects too 42*5234fe38SCedric van Putten [key: string]: string; 43*5234fe38SCedric van Putten }; 44*5234fe38SCedric van Putten 45*5234fe38SCedric van Putten type PageDescription = any; 46*5234fe38SCedric van Putten 47*5234fe38SCedric van Putten function runInspectorProxy(port: number, projectRoot: string): void; 48*5234fe38SCedric van Putten 49*5234fe38SCedric van Putten class InspectorProxy { 50*5234fe38SCedric van Putten /** Root of the project used for relative to absolute source path conversion. */ 51*5234fe38SCedric van Putten _projectRoot: string; 52*5234fe38SCedric van Putten /** Maps device ID to Device instance. */ 53*5234fe38SCedric van Putten _devices: Map<number, Device>; 54*5234fe38SCedric van Putten /** Internal counter for device IDs -- just gets incremented for each new device. */ 55*5234fe38SCedric van Putten _deviceCounter: number = 0; 56*5234fe38SCedric van Putten 57*5234fe38SCedric van Putten /** 58*5234fe38SCedric van Putten * We store server's address with port (like '127.0.0.1:8081') to be able to build URLs 59*5234fe38SCedric van Putten * (devtoolsFrontendUrl and webSocketDebuggerUrl) for page descriptions. These URLs are used 60*5234fe38SCedric van Putten * by debugger to know where to connect. 61*5234fe38SCedric van Putten */ 62*5234fe38SCedric van Putten _serverAddressWithPort: string = ''; 63*5234fe38SCedric van Putten 6433643b60SEvan Bacon constructor(projectRoot: string); 65*5234fe38SCedric van Putten 66*5234fe38SCedric van Putten /** 67*5234fe38SCedric van Putten * Process HTTP request sent to server. We only respond to 2 HTTP requests: 68*5234fe38SCedric van Putten * 1. /json/version returns Chrome debugger protocol version that we use 69*5234fe38SCedric van Putten * 2. /json and /json/list returns list of page descriptions (list of inspectable apps). 70*5234fe38SCedric van Putten * This list is combined from all the connected devices. 71*5234fe38SCedric van Putten */ 72*5234fe38SCedric van Putten processRequest(req: HttpRequest, res: HttpResponse, next: Middleware): void; 73*5234fe38SCedric van Putten 74*5234fe38SCedric van Putten /** Adds websocket listeners to the provided HTTP/HTTPS server. */ 75*5234fe38SCedric van Putten createWebSocketListeners(server: HttpServer | HttpsServer): Record<string, WS.Server>; 76*5234fe38SCedric van Putten 77*5234fe38SCedric van Putten /** Converts page information received from device into PageDescription object that is sent to debugger. */ 78*5234fe38SCedric van Putten _buildPageDescription(deviceId: number, device: Device, page: Page): PageDescription; 79*5234fe38SCedric van Putten 80*5234fe38SCedric van Putten /** 81*5234fe38SCedric van Putten * Sends object as response to HTTP request. 82*5234fe38SCedric van Putten * Just serializes object using JSON and sets required headers. 83*5234fe38SCedric van Putten */ 84*5234fe38SCedric van Putten _sendJsonResponse(response: ServerResponse, object: any): void; 85*5234fe38SCedric van Putten 86*5234fe38SCedric van Putten /** 87*5234fe38SCedric van Putten * Adds websocket handler for device connections. 88*5234fe38SCedric van Putten * Device connects to /inspector/device and passes device and app names as 89*5234fe38SCedric van Putten * HTTP GET params. 90*5234fe38SCedric van Putten * For each new websocket connection we parse device and app names and create 91*5234fe38SCedric van Putten * new instance of Device class. 92*5234fe38SCedric van Putten */ 93*5234fe38SCedric van Putten _createDeviceConnectionWSServer(): WS.Server; 94*5234fe38SCedric van Putten 95*5234fe38SCedric van Putten /** 96*5234fe38SCedric van Putten * Returns websocket handler for debugger connections. 97*5234fe38SCedric van Putten * Debugger connects to webSocketDebuggerUrl that we return as part of page description 98*5234fe38SCedric van Putten * in /json response. 99*5234fe38SCedric van Putten * When debugger connects we try to parse device and page IDs from the query and pass 100*5234fe38SCedric van Putten * websocket object to corresponding Device instance. 101*5234fe38SCedric van Putten */ 102*5234fe38SCedric van Putten _createDebuggerConnectionWSServer(): WS.Server; 103*5234fe38SCedric van Putten } 104*5234fe38SCedric van Putten 105*5234fe38SCedric van Putten class Device { 106*5234fe38SCedric van Putten /** ID of the device. */ 107*5234fe38SCedric van Putten _id: number; 108*5234fe38SCedric van Putten /** Name of the device. */ 109*5234fe38SCedric van Putten _name: string; 110*5234fe38SCedric van Putten /** Package name of the app. */ 111*5234fe38SCedric van Putten _app: string; 112*5234fe38SCedric van Putten /** Stores socket connection between Inspector Proxy and device. */ 113*5234fe38SCedric van Putten _deviceSocket: WS; 114*5234fe38SCedric van Putten /** Stores last list of device's pages. */ 115*5234fe38SCedric van Putten _pages: Page[]; 116*5234fe38SCedric van Putten /** Stores information about currently connected debugger (if any). */ 117*5234fe38SCedric van Putten _debuggerConnection: DebuggerInfo | null = null; 118*5234fe38SCedric van Putten /** Whether we are in the middle of a reload in the REACT_NATIVE_RELOADABLE_PAGE. */ 119*5234fe38SCedric van Putten _isReloading: boolean = false; 120*5234fe38SCedric van Putten /** The previous "GetPages" message, for deduplication in debug logs. */ 121*5234fe38SCedric van Putten _lastGetPagesMessage: string = ''; 122*5234fe38SCedric van Putten /** Mapping built from scriptParsed events and used to fetch file content in `Debugger.getScriptSource`. */ 123*5234fe38SCedric van Putten _scriptIdToSourcePathMapping: Map<string, string>; 124*5234fe38SCedric van Putten /** Root of the project used for relative to absolute source path conversion. */ 125*5234fe38SCedric van Putten _projectRoot: string; 126*5234fe38SCedric van Putten 127*5234fe38SCedric van Putten /** 128*5234fe38SCedric van Putten * Last known Page ID of the React Native page. 129*5234fe38SCedric van Putten * This is used by debugger connections that don't have PageID specified 130*5234fe38SCedric van Putten * (and will interact with the latest React Native page). 131*5234fe38SCedric van Putten */ 132*5234fe38SCedric van Putten _lastConnectedReactNativePage: Page | null = null; 133*5234fe38SCedric van Putten 134*5234fe38SCedric van Putten constructor(id: number, name: string, app: string, socket: WS, projectRoot: string); 135*5234fe38SCedric van Putten 136*5234fe38SCedric van Putten getName(): string; 137*5234fe38SCedric van Putten getPagesList(): Page[]; 138*5234fe38SCedric van Putten 139*5234fe38SCedric van Putten /** 140*5234fe38SCedric van Putten * Handles new debugger connection to this device: 141*5234fe38SCedric van Putten * 1. Sends connect event to device 142*5234fe38SCedric van Putten * 2. Forwards all messages from the debugger to device as wrappedEvent 143*5234fe38SCedric van Putten * 3. Sends disconnect event to device when debugger connection socket closes. 144*5234fe38SCedric van Putten */ 145*5234fe38SCedric van Putten handleDebuggerConnection(socket: WS, pageId: string): void; 146*5234fe38SCedric van Putten 147*5234fe38SCedric van Putten /** 148*5234fe38SCedric van Putten * Handles messages received from device: 149*5234fe38SCedric van Putten * 1. For getPages responses updates local _pages list. 150*5234fe38SCedric van Putten * 2. All other messages are forwarded to debugger as wrappedEvent. 151*5234fe38SCedric van Putten * 152*5234fe38SCedric van Putten * In the future more logic will be added to this method for modifying 153*5234fe38SCedric van Putten * some of the messages (like updating messages with source maps and file locations). 154*5234fe38SCedric van Putten */ 155*5234fe38SCedric van Putten _handleMessageFromDevice(message: MessageFromDevice): void; 156*5234fe38SCedric van Putten 157*5234fe38SCedric van Putten /** Sends single message to device. */ 158*5234fe38SCedric van Putten _sendMessageToDevice(message: MessageToDevice): void; 159*5234fe38SCedric van Putten 160*5234fe38SCedric van Putten /** Sends 'getPages' request to device every PAGES_POLLING_INTERVAL milliseconds.*/ 161*5234fe38SCedric van Putten _setPagesPolling(): void; 162*5234fe38SCedric van Putten 163*5234fe38SCedric van Putten /** Allows to make changes in incoming message from device. */ 164*5234fe38SCedric van Putten _processMessageFromDevice( 165*5234fe38SCedric van Putten payload: { method: string; params: { sourceMapURL: string; url: string } }, 166*5234fe38SCedric van Putten debuggerInfo: DebuggerInfo 167*5234fe38SCedric van Putten ): void; 168*5234fe38SCedric van Putten 169*5234fe38SCedric van Putten /** Allows to make changes in incoming messages from debugger. */ 170*5234fe38SCedric van Putten _interceptMessageFromDebugger( 171*5234fe38SCedric van Putten request: DebuggerRequest, 172*5234fe38SCedric van Putten debuggerInfo: DebuggerInfo, 173*5234fe38SCedric van Putten socket: WS 174*5234fe38SCedric van Putten ): DebuggerResponse | null; 175*5234fe38SCedric van Putten 176*5234fe38SCedric van Putten // _newReactNativePage 177*5234fe38SCedric van Putten // _processDebuggerSetBreakpointByUrl 178*5234fe38SCedric van Putten // _processDebuggerGetScriptSource 179*5234fe38SCedric van Putten // _mapToDevicePageId 18033643b60SEvan Bacon } 18133643b60SEvan Bacon} 182