@react-native/dev-middleware 0.73.0 → 0.73.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -3
- package/dist/createDevMiddleware.d.ts +53 -0
- package/dist/createDevMiddleware.js +53 -6
- package/dist/createDevMiddleware.js.flow +44 -15
- package/dist/index.d.ts +12 -0
- package/dist/index.flow.d.ts +14 -0
- package/dist/index.flow.js.flow +5 -2
- package/dist/index.js +14 -1
- package/dist/index.js.flow +1 -9
- package/dist/inspector-proxy/Device.d.ts +90 -0
- package/dist/inspector-proxy/Device.js +708 -0
- package/dist/inspector-proxy/Device.js.flow +97 -0
- package/dist/inspector-proxy/DeviceEventReporter.d.ts +53 -0
- package/dist/inspector-proxy/DeviceEventReporter.js +162 -0
- package/dist/inspector-proxy/DeviceEventReporter.js.flow +65 -0
- package/dist/inspector-proxy/InspectorProxy.d.ts +61 -0
- package/dist/inspector-proxy/InspectorProxy.js +212 -0
- package/dist/inspector-proxy/InspectorProxy.js.flow +65 -0
- package/dist/inspector-proxy/types.d.ts +78 -0
- package/dist/inspector-proxy/types.js +1 -0
- package/dist/inspector-proxy/types.js.flow +135 -0
- package/dist/middleware/openDebuggerMiddleware.d.ts +37 -0
- package/dist/middleware/openDebuggerMiddleware.js +78 -24
- package/dist/middleware/openDebuggerMiddleware.js.flow +15 -64
- package/dist/types/BrowserLauncher.d.ts +27 -0
- package/dist/types/BrowserLauncher.js +1 -0
- package/dist/types/BrowserLauncher.js.flow +31 -0
- package/dist/types/EventReporter.d.ts +69 -0
- package/dist/types/EventReporter.js +1 -0
- package/dist/types/EventReporter.js.flow +81 -0
- package/dist/types/Experiments.d.ts +25 -0
- package/dist/types/Experiments.js +1 -0
- package/dist/types/Experiments.js.flow +27 -0
- package/dist/types/Logger.d.ts +16 -0
- package/dist/utils/DefaultBrowserLauncher.d.ts +18 -0
- package/dist/utils/DefaultBrowserLauncher.js +77 -0
- package/dist/utils/DefaultBrowserLauncher.js.flow +20 -0
- package/dist/utils/getDevToolsFrontendUrl.d.ts +21 -0
- package/dist/utils/getDevToolsFrontendUrl.js +48 -0
- package/dist/utils/getDevToolsFrontendUrl.js.flow +21 -0
- package/package.json +6 -1
- package/dist/utils/getDevServerUrl.js +0 -37
- package/dist/utils/getDevServerUrl.js.flow +0 -32
- package/dist/utils/launchChromeDevTools.js +0 -44
- package/dist/utils/launchChromeDevTools.js.flow +0 -38
- package/dist/utils/launchDebuggerAppWindow.js +0 -58
- package/dist/utils/launchDebuggerAppWindow.js.flow +0 -56
- package/dist/utils/queryInspectorTargets.js +0 -35
- package/dist/utils/queryInspectorTargets.js.flow +0 -40
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
* @flow strict-local
|
|
8
|
+
* @format
|
|
9
|
+
* @oncall react_native
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type {
|
|
13
|
+
DebuggerRequest,
|
|
14
|
+
GetScriptSourceRequest,
|
|
15
|
+
MessageFromDevice,
|
|
16
|
+
MessageToDevice,
|
|
17
|
+
Page,
|
|
18
|
+
SetBreakpointByUrlRequest,
|
|
19
|
+
} from "./types";
|
|
20
|
+
|
|
21
|
+
import DeviceEventReporter from "./DeviceEventReporter";
|
|
22
|
+
import WS from "ws";
|
|
23
|
+
import type { EventReporter } from "../types/EventReporter";
|
|
24
|
+
|
|
25
|
+
type DebuggerInfo = {
|
|
26
|
+
// Debugger web socket connection
|
|
27
|
+
socket: WS,
|
|
28
|
+
// If we replaced address (like '10.0.2.2') to localhost we need to store original
|
|
29
|
+
// address because Chrome uses URL or urlRegex params (instead of scriptId) to set breakpoints.
|
|
30
|
+
originalSourceURLAddress?: string,
|
|
31
|
+
prependedFilePrefix: boolean,
|
|
32
|
+
pageId: string,
|
|
33
|
+
userAgent: string | null,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Device class represents single device connection to Inspector Proxy. Each device
|
|
38
|
+
* can have multiple inspectable pages.
|
|
39
|
+
*/
|
|
40
|
+
declare export default class Device {
|
|
41
|
+
_id: string;
|
|
42
|
+
_name: string;
|
|
43
|
+
_app: string;
|
|
44
|
+
_deviceSocket: WS;
|
|
45
|
+
_pages: Array<Page>;
|
|
46
|
+
_debuggerConnection: ?DebuggerInfo;
|
|
47
|
+
_lastConnectedReactNativePage: ?Page;
|
|
48
|
+
_isReloading: boolean;
|
|
49
|
+
_lastGetPagesMessage: string;
|
|
50
|
+
_scriptIdToSourcePathMapping: Map<string, string>;
|
|
51
|
+
_projectRoot: string;
|
|
52
|
+
_deviceEventReporter: ?DeviceEventReporter;
|
|
53
|
+
constructor(
|
|
54
|
+
id: string,
|
|
55
|
+
name: string,
|
|
56
|
+
app: string,
|
|
57
|
+
socket: WS,
|
|
58
|
+
projectRoot: string,
|
|
59
|
+
eventReporter: ?EventReporter
|
|
60
|
+
): void;
|
|
61
|
+
getName(): string;
|
|
62
|
+
getApp(): string;
|
|
63
|
+
getPagesList(): Array<Page>;
|
|
64
|
+
handleDebuggerConnection(
|
|
65
|
+
socket: WS,
|
|
66
|
+
pageId: string,
|
|
67
|
+
metadata: $ReadOnly<{
|
|
68
|
+
userAgent: string | null,
|
|
69
|
+
}>
|
|
70
|
+
): void;
|
|
71
|
+
handleDuplicateDeviceConnection(newDevice: Device): void;
|
|
72
|
+
_handleMessageFromDevice(message: MessageFromDevice): void;
|
|
73
|
+
_sendMessageToDevice(message: MessageToDevice): void;
|
|
74
|
+
_setPagesPolling(): void;
|
|
75
|
+
_newReactNativePage(page: Page): void;
|
|
76
|
+
_processMessageFromDevice(
|
|
77
|
+
payload: { method: string, params: { sourceMapURL: string, url: string } },
|
|
78
|
+
debuggerInfo: DebuggerInfo
|
|
79
|
+
): void;
|
|
80
|
+
_interceptMessageFromDebugger(
|
|
81
|
+
req: DebuggerRequest,
|
|
82
|
+
debuggerInfo: DebuggerInfo,
|
|
83
|
+
socket: WS
|
|
84
|
+
): boolean;
|
|
85
|
+
_processDebuggerSetBreakpointByUrl(
|
|
86
|
+
req: SetBreakpointByUrlRequest,
|
|
87
|
+
debuggerInfo: DebuggerInfo
|
|
88
|
+
): void;
|
|
89
|
+
_processDebuggerGetScriptSource(
|
|
90
|
+
req: GetScriptSourceRequest,
|
|
91
|
+
socket: WS
|
|
92
|
+
): void;
|
|
93
|
+
_mapToDevicePageId(pageId: string): string;
|
|
94
|
+
_tryParseHTTPURL(url: string): ?URL;
|
|
95
|
+
_fetchText(url: URL): Promise<string>;
|
|
96
|
+
_sendErrorToDebugger(message: string): void;
|
|
97
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*
|
|
8
|
+
* @format
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { EventReporter } from "../types/EventReporter";
|
|
12
|
+
import TTLCache from "@isaacs/ttlcache";
|
|
13
|
+
type PendingCommand = {
|
|
14
|
+
method: string;
|
|
15
|
+
requestOrigin: "proxy" | "debugger";
|
|
16
|
+
requestTime: number;
|
|
17
|
+
metadata: RequestMetadata;
|
|
18
|
+
};
|
|
19
|
+
type DeviceMetadata = Readonly<{
|
|
20
|
+
appId: string;
|
|
21
|
+
deviceId: string;
|
|
22
|
+
deviceName: string;
|
|
23
|
+
}>;
|
|
24
|
+
type RequestMetadata = Readonly<{
|
|
25
|
+
pageId: string | null;
|
|
26
|
+
frontendUserAgent: string | null;
|
|
27
|
+
}>;
|
|
28
|
+
declare class DeviceEventReporter {
|
|
29
|
+
_eventReporter: EventReporter;
|
|
30
|
+
_pendingCommands: TTLCache<number, PendingCommand>;
|
|
31
|
+
_metadata: DeviceMetadata;
|
|
32
|
+
constructor(eventReporter: EventReporter, metadata: DeviceMetadata);
|
|
33
|
+
logRequest(
|
|
34
|
+
req: Readonly<{ id: number; method: string }>,
|
|
35
|
+
origin: "debugger" | "proxy",
|
|
36
|
+
metadata: RequestMetadata
|
|
37
|
+
): void;
|
|
38
|
+
logResponse(
|
|
39
|
+
res: Readonly<{ id: number; error?: { message: string; data?: unknown } }>,
|
|
40
|
+
origin: "device" | "proxy",
|
|
41
|
+
metadata: Readonly<{
|
|
42
|
+
pageId: string | null;
|
|
43
|
+
frontendUserAgent: string | null;
|
|
44
|
+
}>
|
|
45
|
+
): void;
|
|
46
|
+
logConnection(
|
|
47
|
+
connectedEntity: "debugger",
|
|
48
|
+
metadata: Readonly<{ pageId: string; frontendUserAgent: string | null }>
|
|
49
|
+
): void;
|
|
50
|
+
logDisconnection(disconnectedEntity: "device" | "debugger"): void;
|
|
51
|
+
_logExpiredCommand(pendingCommand: PendingCommand): void;
|
|
52
|
+
}
|
|
53
|
+
export default DeviceEventReporter;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true,
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _ttlcache = _interopRequireDefault(require("@isaacs/ttlcache"));
|
|
8
|
+
function _interopRequireDefault(obj) {
|
|
9
|
+
return obj && obj.__esModule ? obj : { default: obj };
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
13
|
+
*
|
|
14
|
+
* This source code is licensed under the MIT license found in the
|
|
15
|
+
* LICENSE file in the root directory of this source tree.
|
|
16
|
+
*
|
|
17
|
+
*
|
|
18
|
+
* @format
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
class DeviceEventReporter {
|
|
22
|
+
_pendingCommands = new _ttlcache.default({
|
|
23
|
+
ttl: 10000,
|
|
24
|
+
dispose: (command, id, reason) => {
|
|
25
|
+
if (reason === "delete" || reason === "set") {
|
|
26
|
+
// TODO: Report clobbering ('set') using a dedicated error code
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
this._logExpiredCommand(command);
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
constructor(eventReporter, metadata) {
|
|
33
|
+
this._eventReporter = eventReporter;
|
|
34
|
+
this._metadata = metadata;
|
|
35
|
+
}
|
|
36
|
+
logRequest(req, origin, metadata) {
|
|
37
|
+
this._pendingCommands.set(req.id, {
|
|
38
|
+
method: req.method,
|
|
39
|
+
requestOrigin: origin,
|
|
40
|
+
requestTime: Date.now(),
|
|
41
|
+
metadata,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
logResponse(res, origin, metadata) {
|
|
45
|
+
const pendingCommand = this._pendingCommands.get(res.id);
|
|
46
|
+
if (!pendingCommand) {
|
|
47
|
+
this._eventReporter.logEvent({
|
|
48
|
+
type: "debugger_command",
|
|
49
|
+
protocol: "CDP",
|
|
50
|
+
requestOrigin: null,
|
|
51
|
+
method: null,
|
|
52
|
+
status: "coded_error",
|
|
53
|
+
errorCode: "UNMATCHED_REQUEST_ID",
|
|
54
|
+
responseOrigin: "proxy",
|
|
55
|
+
timeSinceStart: null,
|
|
56
|
+
appId: this._metadata.appId,
|
|
57
|
+
deviceId: this._metadata.deviceId,
|
|
58
|
+
deviceName: this._metadata.deviceName,
|
|
59
|
+
pageId: metadata.pageId,
|
|
60
|
+
frontendUserAgent: metadata.frontendUserAgent,
|
|
61
|
+
});
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const timeSinceStart = Date.now() - pendingCommand.requestTime;
|
|
65
|
+
this._pendingCommands.delete(res.id);
|
|
66
|
+
if (res.error) {
|
|
67
|
+
let { message } = res.error;
|
|
68
|
+
if ("data" in res.error) {
|
|
69
|
+
message += ` (${String(res.error.data)})`;
|
|
70
|
+
}
|
|
71
|
+
this._eventReporter.logEvent({
|
|
72
|
+
type: "debugger_command",
|
|
73
|
+
requestOrigin: pendingCommand.requestOrigin,
|
|
74
|
+
method: pendingCommand.method,
|
|
75
|
+
protocol: "CDP",
|
|
76
|
+
status: "coded_error",
|
|
77
|
+
errorCode: "PROTOCOL_ERROR",
|
|
78
|
+
errorDetails: message,
|
|
79
|
+
responseOrigin: origin,
|
|
80
|
+
timeSinceStart,
|
|
81
|
+
appId: this._metadata.appId,
|
|
82
|
+
deviceId: this._metadata.deviceId,
|
|
83
|
+
deviceName: this._metadata.deviceName,
|
|
84
|
+
pageId: pendingCommand.metadata.pageId,
|
|
85
|
+
frontendUserAgent: pendingCommand.metadata.frontendUserAgent,
|
|
86
|
+
});
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
this._eventReporter.logEvent({
|
|
90
|
+
type: "debugger_command",
|
|
91
|
+
protocol: "CDP",
|
|
92
|
+
requestOrigin: pendingCommand.requestOrigin,
|
|
93
|
+
method: pendingCommand.method,
|
|
94
|
+
status: "success",
|
|
95
|
+
responseOrigin: origin,
|
|
96
|
+
timeSinceStart,
|
|
97
|
+
appId: this._metadata.appId,
|
|
98
|
+
deviceId: this._metadata.deviceId,
|
|
99
|
+
deviceName: this._metadata.deviceName,
|
|
100
|
+
pageId: pendingCommand.metadata.pageId,
|
|
101
|
+
frontendUserAgent: pendingCommand.metadata.frontendUserAgent,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
logConnection(connectedEntity, metadata) {
|
|
105
|
+
this._eventReporter.logEvent({
|
|
106
|
+
type: "connect_debugger_frontend",
|
|
107
|
+
status: "success",
|
|
108
|
+
appId: this._metadata.appId,
|
|
109
|
+
deviceName: this._metadata.deviceName,
|
|
110
|
+
deviceId: this._metadata.deviceId,
|
|
111
|
+
pageId: metadata.pageId,
|
|
112
|
+
frontendUserAgent: metadata.frontendUserAgent,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
logDisconnection(disconnectedEntity) {
|
|
116
|
+
const eventReporter = this._eventReporter;
|
|
117
|
+
if (!eventReporter) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const errorCode =
|
|
121
|
+
disconnectedEntity === "device"
|
|
122
|
+
? "DEVICE_DISCONNECTED"
|
|
123
|
+
: "DEBUGGER_DISCONNECTED";
|
|
124
|
+
for (const pendingCommand of this._pendingCommands.values()) {
|
|
125
|
+
this._eventReporter.logEvent({
|
|
126
|
+
type: "debugger_command",
|
|
127
|
+
protocol: "CDP",
|
|
128
|
+
requestOrigin: pendingCommand.requestOrigin,
|
|
129
|
+
method: pendingCommand.method,
|
|
130
|
+
status: "coded_error",
|
|
131
|
+
errorCode,
|
|
132
|
+
responseOrigin: "proxy",
|
|
133
|
+
timeSinceStart: Date.now() - pendingCommand.requestTime,
|
|
134
|
+
appId: this._metadata.appId,
|
|
135
|
+
deviceId: this._metadata.deviceId,
|
|
136
|
+
deviceName: this._metadata.deviceName,
|
|
137
|
+
pageId: pendingCommand.metadata.pageId,
|
|
138
|
+
frontendUserAgent: pendingCommand.metadata.frontendUserAgent,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
this._pendingCommands.clear();
|
|
142
|
+
}
|
|
143
|
+
_logExpiredCommand(pendingCommand) {
|
|
144
|
+
this._eventReporter.logEvent({
|
|
145
|
+
type: "debugger_command",
|
|
146
|
+
protocol: "CDP",
|
|
147
|
+
requestOrigin: pendingCommand.requestOrigin,
|
|
148
|
+
method: pendingCommand.method,
|
|
149
|
+
status: "coded_error",
|
|
150
|
+
errorCode: "TIMED_OUT",
|
|
151
|
+
responseOrigin: "proxy",
|
|
152
|
+
timeSinceStart: Date.now() - pendingCommand.requestTime,
|
|
153
|
+
appId: this._metadata.appId,
|
|
154
|
+
deviceId: this._metadata.deviceId,
|
|
155
|
+
deviceName: this._metadata.deviceName,
|
|
156
|
+
pageId: pendingCommand.metadata.pageId,
|
|
157
|
+
frontendUserAgent: pendingCommand.metadata.frontendUserAgent,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
var _default = DeviceEventReporter;
|
|
162
|
+
exports.default = _default;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
* @flow strict-local
|
|
8
|
+
* @format
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { EventReporter } from "../types/EventReporter";
|
|
12
|
+
import TTLCache from "@isaacs/ttlcache";
|
|
13
|
+
|
|
14
|
+
type PendingCommand = {
|
|
15
|
+
method: string,
|
|
16
|
+
requestOrigin: "proxy" | "debugger",
|
|
17
|
+
requestTime: number,
|
|
18
|
+
metadata: RequestMetadata,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type DeviceMetadata = $ReadOnly<{
|
|
22
|
+
appId: string,
|
|
23
|
+
deviceId: string,
|
|
24
|
+
deviceName: string,
|
|
25
|
+
}>;
|
|
26
|
+
|
|
27
|
+
type RequestMetadata = $ReadOnly<{
|
|
28
|
+
pageId: string | null,
|
|
29
|
+
frontendUserAgent: string | null,
|
|
30
|
+
}>;
|
|
31
|
+
|
|
32
|
+
declare class DeviceEventReporter {
|
|
33
|
+
_eventReporter: EventReporter;
|
|
34
|
+
_pendingCommands: TTLCache<number, PendingCommand>;
|
|
35
|
+
_metadata: DeviceMetadata;
|
|
36
|
+
constructor(eventReporter: EventReporter, metadata: DeviceMetadata): void;
|
|
37
|
+
logRequest(
|
|
38
|
+
req: $ReadOnly<{ id: number, method: string, ... }>,
|
|
39
|
+
origin: "debugger" | "proxy",
|
|
40
|
+
metadata: RequestMetadata
|
|
41
|
+
): void;
|
|
42
|
+
logResponse(
|
|
43
|
+
res: $ReadOnly<{
|
|
44
|
+
id: number,
|
|
45
|
+
error?: { message: string, data?: mixed },
|
|
46
|
+
...
|
|
47
|
+
}>,
|
|
48
|
+
origin: "device" | "proxy",
|
|
49
|
+
metadata: $ReadOnly<{
|
|
50
|
+
pageId: string | null,
|
|
51
|
+
frontendUserAgent: string | null,
|
|
52
|
+
}>
|
|
53
|
+
): void;
|
|
54
|
+
logConnection(
|
|
55
|
+
connectedEntity: "debugger",
|
|
56
|
+
metadata: $ReadOnly<{
|
|
57
|
+
pageId: string,
|
|
58
|
+
frontendUserAgent: string | null,
|
|
59
|
+
}>
|
|
60
|
+
): void;
|
|
61
|
+
logDisconnection(disconnectedEntity: "device" | "debugger"): void;
|
|
62
|
+
_logExpiredCommand(pendingCommand: PendingCommand): void;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
declare export default DeviceEventReporter;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*
|
|
8
|
+
* @format
|
|
9
|
+
* @oncall react_native
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type {
|
|
13
|
+
JsonPagesListResponse,
|
|
14
|
+
JsonVersionResponse,
|
|
15
|
+
Page,
|
|
16
|
+
PageDescription,
|
|
17
|
+
} from "./types";
|
|
18
|
+
import type { EventReporter } from "../types/EventReporter";
|
|
19
|
+
import type { Experiments } from "../types/Experiments";
|
|
20
|
+
import type { IncomingMessage, ServerResponse } from "http";
|
|
21
|
+
import WS from "ws";
|
|
22
|
+
import Device from "./Device";
|
|
23
|
+
export interface InspectorProxyQueries {
|
|
24
|
+
getPageDescriptions(): Array<PageDescription>;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Main Inspector Proxy class that connects JavaScript VM inside Android/iOS apps and JS debugger.
|
|
28
|
+
*/
|
|
29
|
+
declare class InspectorProxy implements InspectorProxyQueries {
|
|
30
|
+
_projectRoot: string;
|
|
31
|
+
_serverBaseUrl: string;
|
|
32
|
+
_devices: Map<string, Device>;
|
|
33
|
+
_deviceCounter: number;
|
|
34
|
+
_eventReporter: null | undefined | EventReporter;
|
|
35
|
+
_experiments: Experiments;
|
|
36
|
+
constructor(
|
|
37
|
+
projectRoot: string,
|
|
38
|
+
serverBaseUrl: string,
|
|
39
|
+
eventReporter: null | undefined | EventReporter,
|
|
40
|
+
experiments: Experiments
|
|
41
|
+
);
|
|
42
|
+
getPageDescriptions(): Array<PageDescription>;
|
|
43
|
+
processRequest(
|
|
44
|
+
request: IncomingMessage,
|
|
45
|
+
response: ServerResponse,
|
|
46
|
+
next: ($$PARAM_0$$: null | undefined | Error) => unknown
|
|
47
|
+
): void;
|
|
48
|
+
createWebSocketListeners(): { [path: string]: WS.Server };
|
|
49
|
+
_buildPageDescription(
|
|
50
|
+
deviceId: string,
|
|
51
|
+
device: Device,
|
|
52
|
+
page: Page
|
|
53
|
+
): PageDescription;
|
|
54
|
+
_sendJsonResponse(
|
|
55
|
+
response: ServerResponse,
|
|
56
|
+
object: JsonPagesListResponse | JsonVersionResponse
|
|
57
|
+
): void;
|
|
58
|
+
_createDeviceConnectionWSServer(): ws$WebSocketServer;
|
|
59
|
+
_createDebuggerConnectionWSServer(): ws$WebSocketServer;
|
|
60
|
+
}
|
|
61
|
+
export default InspectorProxy;
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true,
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _url = _interopRequireDefault(require("url"));
|
|
8
|
+
var _ws = _interopRequireDefault(require("ws"));
|
|
9
|
+
var _Device = _interopRequireDefault(require("./Device"));
|
|
10
|
+
function _interopRequireDefault(obj) {
|
|
11
|
+
return obj && obj.__esModule ? obj : { default: obj };
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
15
|
+
*
|
|
16
|
+
* This source code is licensed under the MIT license found in the
|
|
17
|
+
* LICENSE file in the root directory of this source tree.
|
|
18
|
+
*
|
|
19
|
+
*
|
|
20
|
+
* @format
|
|
21
|
+
* @oncall react_native
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const debug = require("debug")("Metro:InspectorProxy");
|
|
25
|
+
const WS_DEVICE_URL = "/inspector/device";
|
|
26
|
+
const WS_DEBUGGER_URL = "/inspector/debug";
|
|
27
|
+
const PAGES_LIST_JSON_URL = "/json";
|
|
28
|
+
const PAGES_LIST_JSON_URL_2 = "/json/list";
|
|
29
|
+
const PAGES_LIST_JSON_VERSION_URL = "/json/version";
|
|
30
|
+
const INTERNAL_ERROR_CODE = 1011;
|
|
31
|
+
/**
|
|
32
|
+
* Main Inspector Proxy class that connects JavaScript VM inside Android/iOS apps and JS debugger.
|
|
33
|
+
*/
|
|
34
|
+
class InspectorProxy {
|
|
35
|
+
// Root of the project used for relative to absolute source path conversion.
|
|
36
|
+
|
|
37
|
+
/** The base URL to the dev server from the developer machine. */
|
|
38
|
+
|
|
39
|
+
// Maps device ID to Device instance.
|
|
40
|
+
|
|
41
|
+
// Internal counter for device IDs -- just gets incremented for each new device.
|
|
42
|
+
_deviceCounter = 0;
|
|
43
|
+
constructor(projectRoot, serverBaseUrl, eventReporter, experiments) {
|
|
44
|
+
this._projectRoot = projectRoot;
|
|
45
|
+
this._serverBaseUrl = serverBaseUrl;
|
|
46
|
+
this._devices = new Map();
|
|
47
|
+
this._eventReporter = eventReporter;
|
|
48
|
+
this._experiments = experiments;
|
|
49
|
+
}
|
|
50
|
+
getPageDescriptions() {
|
|
51
|
+
// Build list of pages from all devices.
|
|
52
|
+
let result = [];
|
|
53
|
+
Array.from(this._devices.entries()).forEach(([deviceId, device]) => {
|
|
54
|
+
result = result.concat(
|
|
55
|
+
device
|
|
56
|
+
.getPagesList()
|
|
57
|
+
.map((page) => this._buildPageDescription(deviceId, device, page))
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Process HTTP request sent to server. We only respond to 2 HTTP requests:
|
|
64
|
+
// 1. /json/version returns Chrome debugger protocol version that we use
|
|
65
|
+
// 2. /json and /json/list returns list of page descriptions (list of inspectable apps).
|
|
66
|
+
// This list is combined from all the connected devices.
|
|
67
|
+
processRequest(request, response, next) {
|
|
68
|
+
if (
|
|
69
|
+
request.url === PAGES_LIST_JSON_URL ||
|
|
70
|
+
request.url === PAGES_LIST_JSON_URL_2
|
|
71
|
+
) {
|
|
72
|
+
this._sendJsonResponse(response, this.getPageDescriptions());
|
|
73
|
+
} else if (request.url === PAGES_LIST_JSON_VERSION_URL) {
|
|
74
|
+
this._sendJsonResponse(response, {
|
|
75
|
+
Browser: "Mobile JavaScript",
|
|
76
|
+
"Protocol-Version": "1.1",
|
|
77
|
+
});
|
|
78
|
+
} else {
|
|
79
|
+
next();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
createWebSocketListeners() {
|
|
83
|
+
return {
|
|
84
|
+
[WS_DEVICE_URL]: this._createDeviceConnectionWSServer(),
|
|
85
|
+
[WS_DEBUGGER_URL]: this._createDebuggerConnectionWSServer(),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Converts page information received from device into PageDescription object
|
|
90
|
+
// that is sent to debugger.
|
|
91
|
+
_buildPageDescription(deviceId, device, page) {
|
|
92
|
+
const { host, protocol } = new URL(this._serverBaseUrl);
|
|
93
|
+
const webSocketScheme = protocol === "https:" ? "wss" : "ws";
|
|
94
|
+
const webSocketUrlWithoutProtocol = `${host}${WS_DEBUGGER_URL}?device=${deviceId}&page=${page.id}`;
|
|
95
|
+
// For now, `/json/list` returns the legacy built-in `devtools://` URL, to
|
|
96
|
+
// preserve existing handling by Flipper. This may return a placeholder in
|
|
97
|
+
// future -- please use the `/open-debugger` endpoint.
|
|
98
|
+
const devtoolsFrontendUrl =
|
|
99
|
+
`devtools://devtools/bundled/js_app.html?experiments=true&v8only=true&${webSocketScheme}=` +
|
|
100
|
+
encodeURIComponent(webSocketUrlWithoutProtocol);
|
|
101
|
+
return {
|
|
102
|
+
id: `${deviceId}-${page.id}`,
|
|
103
|
+
description: page.app,
|
|
104
|
+
title: page.title,
|
|
105
|
+
faviconUrl: "https://reactjs.org/favicon.ico",
|
|
106
|
+
devtoolsFrontendUrl,
|
|
107
|
+
type: "node",
|
|
108
|
+
webSocketDebuggerUrl: `${webSocketScheme}://${webSocketUrlWithoutProtocol}`,
|
|
109
|
+
vm: page.vm,
|
|
110
|
+
deviceName: device.getName(),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Sends object as response to HTTP request.
|
|
115
|
+
// Just serializes object using JSON and sets required headers.
|
|
116
|
+
_sendJsonResponse(response, object) {
|
|
117
|
+
const data = JSON.stringify(object, null, 2);
|
|
118
|
+
response.writeHead(200, {
|
|
119
|
+
"Content-Type": "application/json; charset=UTF-8",
|
|
120
|
+
"Cache-Control": "no-cache",
|
|
121
|
+
"Content-Length": data.length.toString(),
|
|
122
|
+
Connection: "close",
|
|
123
|
+
});
|
|
124
|
+
response.end(data);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Adds websocket handler for device connections.
|
|
128
|
+
// Device connects to /inspector/device and passes device and app names as
|
|
129
|
+
// HTTP GET params.
|
|
130
|
+
// For each new websocket connection we parse device and app names and create
|
|
131
|
+
// new instance of Device class.
|
|
132
|
+
_createDeviceConnectionWSServer() {
|
|
133
|
+
const wss = new _ws.default.Server({
|
|
134
|
+
noServer: true,
|
|
135
|
+
perMessageDeflate: true,
|
|
136
|
+
});
|
|
137
|
+
// $FlowFixMe[value-as-type]
|
|
138
|
+
wss.on("connection", async (socket, req) => {
|
|
139
|
+
try {
|
|
140
|
+
const fallbackDeviceId = String(this._deviceCounter++);
|
|
141
|
+
const query = _url.default.parse(req.url || "", true).query || {};
|
|
142
|
+
const deviceId = query.device || fallbackDeviceId;
|
|
143
|
+
const deviceName = query.name || "Unknown";
|
|
144
|
+
const appName = query.app || "Unknown";
|
|
145
|
+
const oldDevice = this._devices.get(deviceId);
|
|
146
|
+
const newDevice = new _Device.default(
|
|
147
|
+
deviceId,
|
|
148
|
+
deviceName,
|
|
149
|
+
appName,
|
|
150
|
+
socket,
|
|
151
|
+
this._projectRoot,
|
|
152
|
+
this._eventReporter
|
|
153
|
+
);
|
|
154
|
+
if (oldDevice) {
|
|
155
|
+
oldDevice.handleDuplicateDeviceConnection(newDevice);
|
|
156
|
+
}
|
|
157
|
+
this._devices.set(deviceId, newDevice);
|
|
158
|
+
debug(
|
|
159
|
+
`Got new connection: name=${deviceName}, app=${appName}, device=${deviceId}`
|
|
160
|
+
);
|
|
161
|
+
socket.on("close", () => {
|
|
162
|
+
this._devices.delete(deviceId);
|
|
163
|
+
debug(`Device ${deviceName} disconnected.`);
|
|
164
|
+
});
|
|
165
|
+
} catch (e) {
|
|
166
|
+
console.error("error", e);
|
|
167
|
+
socket.close(INTERNAL_ERROR_CODE, e?.toString() ?? "Unknown error");
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
return wss;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Returns websocket handler for debugger connections.
|
|
174
|
+
// Debugger connects to webSocketDebuggerUrl that we return as part of page description
|
|
175
|
+
// in /json response.
|
|
176
|
+
// When debugger connects we try to parse device and page IDs from the query and pass
|
|
177
|
+
// websocket object to corresponding Device instance.
|
|
178
|
+
_createDebuggerConnectionWSServer() {
|
|
179
|
+
const wss = new _ws.default.Server({
|
|
180
|
+
noServer: true,
|
|
181
|
+
perMessageDeflate: false,
|
|
182
|
+
});
|
|
183
|
+
// $FlowFixMe[value-as-type]
|
|
184
|
+
wss.on("connection", async (socket, req) => {
|
|
185
|
+
try {
|
|
186
|
+
const query = _url.default.parse(req.url || "", true).query || {};
|
|
187
|
+
const deviceId = query.device;
|
|
188
|
+
const pageId = query.page;
|
|
189
|
+
if (deviceId == null || pageId == null) {
|
|
190
|
+
throw new Error("Incorrect URL - must provide device and page IDs");
|
|
191
|
+
}
|
|
192
|
+
const device = this._devices.get(deviceId);
|
|
193
|
+
if (device == null) {
|
|
194
|
+
throw new Error("Unknown device with ID " + deviceId);
|
|
195
|
+
}
|
|
196
|
+
device.handleDebuggerConnection(socket, pageId, {
|
|
197
|
+
userAgent: req.headers["user-agent"] ?? null,
|
|
198
|
+
});
|
|
199
|
+
} catch (e) {
|
|
200
|
+
console.error(e);
|
|
201
|
+
socket.close(INTERNAL_ERROR_CODE, e?.toString() ?? "Unknown error");
|
|
202
|
+
this._eventReporter?.logEvent({
|
|
203
|
+
type: "connect_debugger_frontend",
|
|
204
|
+
status: "error",
|
|
205
|
+
error: e,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
return wss;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
exports.default = InspectorProxy;
|