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