Socket
Socket
Sign inDemoInstall

metro-inspector-proxy

Package Overview
Dependencies
Maintainers
2
Versions
70
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

metro-inspector-proxy - npm Package Compare versions

Comparing version 0.73.3 to 0.73.5

2

package.json
{
"name": "metro-inspector-proxy",
"version": "0.73.3",
"version": "0.73.5",
"description": "🚇 Inspector proxy for React Native and dev tools integration.",

@@ -5,0 +5,0 @@ "main": "src/index.js",

@@ -11,8 +11,7 @@ /**

*/
"use strict";
const { runInspectorProxy } = require("./index");
const yargs = require("yargs");
const argv = yargs

@@ -19,0 +18,0 @@ .option("port", {

"use strict";
var fs = _interopRequireWildcard(require("fs"));
var http = _interopRequireWildcard(require("http"));
var path = _interopRequireWildcard(require("path"));
var _ws = _interopRequireDefault(require("ws"));
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
function _getRequireWildcardCache(nodeInterop) {

@@ -23,3 +18,2 @@ if (typeof WeakMap !== "function") return null;

}
function _interopRequireWildcard(obj, nodeInterop) {

@@ -57,3 +51,2 @@ if (!nodeInterop && obj && obj.__esModule) {

}
/**

@@ -69,11 +62,14 @@ * Copyright (c) Meta Platforms, Inc. and affiliates.

*/
const debug = require("debug")("Metro:InspectorProxy");
const PAGES_POLLING_INTERVAL = 1000;
const PAGES_POLLING_INTERVAL = 1000; // Android's stock emulator and other emulators such as genymotion use a standard localhost alias.
// Android's stock emulator and other emulators such as genymotion use a standard localhost alias.
const EMULATOR_LOCALHOST_ADDRESSES = ["10.0.2.2", "10.0.3.2"];
const EMULATOR_LOCALHOST_ADDRESSES = ["10.0.2.2", "10.0.3.2"]; // Prefix for script URLs that are alphanumeric IDs. See comment in _processMessageFromDevice method for
// Prefix for script URLs that are alphanumeric IDs. See comment in _processMessageFromDevice method for
// more details.
const FILE_PREFIX = "file://";
const REACT_NATIVE_RELOADABLE_PAGE_ID = "-1";
/**

@@ -83,21 +79,31 @@ * Device class represents single device connection to Inspector Proxy. Each device

*/
class Device {
// ID of the device.
// Name of the device.
// Package name of the app.
// Stores socket connection between Inspector Proxy and device.
// Stores last list of device's pages.
// Stores information about currently connected debugger (if any).
_debuggerConnection = null; // Last known Page ID of the React Native page.
_debuggerConnection = null;
// Last known Page ID of the React Native page.
// This is used by debugger connections that don't have PageID specified
// (and will interact with the latest React Native page).
_lastConnectedReactNativePage = null;
_lastConnectedReactNativePage = null; // Whether we are in the middle of a reload in the REACT_NATIVE_RELOADABLE_PAGE.
// Whether we are in the middle of a reload in the REACT_NATIVE_RELOADABLE_PAGE.
_isReloading = false;
_isReloading = false; // The previous "GetPages" message, for deduplication in debug logs.
// The previous "GetPages" message, for deduplication in debug logs.
_lastGetPagesMessage = "";
_lastGetPagesMessage = ""; // Mapping built from scriptParsed events and used to fetch file content in `Debugger.getScriptSource`.
// Mapping built from scriptParsed events and used to fetch file content in `Debugger.getScriptSource`.
_scriptIdToSourcePathMapping = new Map();
_scriptIdToSourcePathMapping = new Map(); // Root of the project used for relative to absolute source path conversion.
// Root of the project used for relative to absolute source path conversion.

@@ -111,6 +117,4 @@ constructor(id, name, app, socket, projectRoot) {

this._projectRoot = projectRoot;
this._deviceSocket.on("message", (message) => {
const parsedMessage = JSON.parse(message);
if (parsedMessage.event === "getPages") {

@@ -128,6 +132,4 @@ // There's a 'getPages' message every second, so only show them if they change

}
this._handleMessageFromDevice(parsedMessage);
});
this._deviceSocket.on("close", () => {

@@ -137,14 +139,10 @@ // Device disconnected - close debugger connection.

this._debuggerConnection.socket.close();
this._debuggerConnection = null;
}
});
this._setPagesPolling();
}
getName() {
return this._name;
}
getPagesList() {

@@ -162,7 +160,8 @@ if (this._lastConnectedReactNativePage) {

}
} // Handles new debugger connection to this device:
}
// Handles new debugger connection to this device:
// 1. Sends connect event to device
// 2. Forwards all messages from the debugger to device as wrappedEvent
// 3. Sends disconnect event to device when debugger connection socket closes.
handleDebuggerConnection(socket, pageId) {

@@ -172,6 +171,4 @@ // Disconnect current debugger if we already have debugger connected.

this._debuggerConnection.socket.close();
this._debuggerConnection = null;
}
const debuggerInfo = {

@@ -184,3 +181,2 @@ socket,

debug(`Got new debugger connection for page ${pageId} of ${this._name}`);
this._sendMessageToDevice({

@@ -192,7 +188,5 @@ event: "connect",

});
socket.on("message", (message) => {
debug("(Debugger) -> (Proxy) (Device): " + message);
const debuggerRequest = JSON.parse(message);
const handled = this._interceptMessageFromDebugger(

@@ -203,3 +197,2 @@ debuggerRequest,

);
if (!handled) {

@@ -217,3 +210,2 @@ this._sendMessageToDevice({

debug(`Debugger for page ${pageId} and ${this._name} disconnected.`);
this._sendMessageToDevice({

@@ -225,7 +217,5 @@ event: "disconnect",

});
this._debuggerConnection = null;
});
const sendFunc = socket.send;
socket.send = function (message) {

@@ -235,3 +225,5 @@ debug("(Debugger) <- (Proxy) (Device): " + message);

};
} // Handles messages received from device:
}
// Handles messages received from device:
// 1. For getPages responses updates local _pages list.

@@ -243,6 +235,7 @@ // 2. All other messages are forwarded to debugger as wrappedEvent.

// locations).
_handleMessageFromDevice(message) {
if (message.event === "getPages") {
this._pages = message.payload; // Check if device have new React Native page.
this._pages = message.payload;
// Check if device have new React Native page.
// There is usually no more than 2-3 pages per device so this operation

@@ -252,7 +245,5 @@ // is not expensive.

// created instead of manually checking this on every getPages result.
for (let i = 0; i < this._pages.length; ++i) {
if (this._pages[i].title.indexOf("React") >= 0) {
var _this$_lastConnectedR;
if (

@@ -266,3 +257,2 @@ this._pages[i].id !=

this._newReactNativePage(this._pages[i]);
break;

@@ -279,3 +269,2 @@ }

: null;
if (debuggerSocket && debuggerSocket.readyState === _ws.default.OPEN) {

@@ -297,7 +286,8 @@ if (

return;
} // FIXME: Is it possible that we received message for pageID that does not
}
// FIXME: Is it possible that we received message for pageID that does not
// correspond to current debugger connection?
const debuggerSocket = this._debuggerConnection.socket;
if (

@@ -310,5 +300,3 @@ debuggerSocket == null ||

}
const parsedPayload = JSON.parse(message.payload.wrappedEvent);
if (this._debuggerConnection) {

@@ -318,8 +306,8 @@ // Wrapping just to make flow happy :)

}
const messageToSend = JSON.stringify(parsedPayload);
debuggerSocket.send(messageToSend);
}
} // Sends single message to device.
}
// Sends single message to device.
_sendMessageToDevice(message) {

@@ -330,7 +318,7 @@ try {

}
this._deviceSocket.send(JSON.stringify(message));
} catch (error) {}
} // Sends 'getPages' request to device every PAGES_POLLING_INTERVAL milliseconds.
}
// Sends 'getPages' request to device every PAGES_POLLING_INTERVAL milliseconds.
_setPagesPolling() {

@@ -344,9 +332,8 @@ setInterval(

);
} // We received new React Native Page ID.
}
// We received new React Native Page ID.
_newReactNativePage(page) {
var _this$_lastConnectedR2;
debug(`React Native page updated to ${page.id}`);
if (

@@ -362,3 +349,2 @@ this._debuggerConnection == null ||

}
const oldPageId =

@@ -370,3 +356,5 @@ (_this$_lastConnectedR2 = this._lastConnectedReactNativePage) === null ||

this._lastConnectedReactNativePage = page;
this._isReloading = true; // We already had a debugger connected to React Native page and a
this._isReloading = true;
// We already had a debugger connected to React Native page and a
// new one appeared - in this case we need to emulate execution context

@@ -384,3 +372,2 @@ // detroy and resend Debugger.enable and Runtime.enable commands to new

}
this._sendMessageToDevice({

@@ -392,3 +379,2 @@ event: "connect",

});
const toSend = [

@@ -404,3 +390,2 @@ {

];
for (const message of toSend) {

@@ -415,4 +400,5 @@ this._sendMessageToDevice({

}
} // Allows to make changes in incoming message from device.
}
// Allows to make changes in incoming message from device.
_processMessageFromDevice(payload, debuggerInfo) {

@@ -422,7 +408,5 @@ // Replace Android addresses for scriptParsed event.

const params = payload.params || {};
if ("sourceMapURL" in params) {
for (let i = 0; i < EMULATOR_LOCALHOST_ADDRESSES.length; ++i) {
const address = EMULATOR_LOCALHOST_ADDRESSES[i];
if (params.sourceMapURL.indexOf(address) >= 0) {

@@ -437,7 +421,5 @@ payload.params.sourceMapURL = params.sourceMapURL.replace(

}
if ("url" in params) {
for (let i = 0; i < EMULATOR_LOCALHOST_ADDRESSES.length; ++i) {
const address = EMULATOR_LOCALHOST_ADDRESSES[i];
if (params.url.indexOf(address) >= 0) {

@@ -447,12 +429,14 @@ payload.params.url = params.url.replace(address, "localhost");

}
} // Chrome doesn't download source maps if URL param is not a valid
}
// Chrome doesn't download source maps if URL param is not a valid
// URL. Some frameworks pass alphanumeric script ID instead of URL which causes
// Chrome to not download source maps. In this case we want to prepend script ID
// with 'file://' prefix.
if (payload.params.url.match(/^[0-9a-z]+$/)) {
payload.params.url = FILE_PREFIX + payload.params.url;
debuggerInfo.prependedFilePrefix = true;
} // $FlowFixMe[prop-missing]
}
// $FlowFixMe[prop-missing]
if (params.scriptId != null) {

@@ -462,3 +446,2 @@ this._scriptIdToSourcePathMapping.set(params.scriptId, params.url);

}
if (debuggerInfo.pageId == REACT_NATIVE_RELOADABLE_PAGE_ID) {

@@ -470,3 +453,2 @@ // Chrome won't use the source map unless it appears to be new.

}
if (payload.params.url) {

@@ -478,3 +460,2 @@ payload.params.url +=

}
if (

@@ -491,3 +472,5 @@ payload.method === "Runtime.executionContextCreated" &&

})
); // The VM starts in a paused mode. Ask it to resume.
);
// The VM starts in a paused mode. Ask it to resume.
// Note that if setting breakpoints in early initialization functions,

@@ -499,3 +482,2 @@ // there's a currently race condition between these functions executing

// at its convenience.
this._sendMessageToDevice({

@@ -511,9 +493,9 @@ event: "wrappedEvent",

});
this._isReloading = false;
}
} // Allows to make changes in incoming messages from debugger. Returns a boolean
}
// Allows to make changes in incoming messages from debugger. Returns a boolean
// indicating whether the message has been handled locally (i.e. does not need
// to be forwarded to the target).
_interceptMessageFromDebugger(req, debuggerInfo, socket) {

@@ -524,9 +506,6 @@ if (req.method === "Debugger.setBreakpointByUrl") {

this._processDebuggerGetScriptSource(req, socket);
return true;
}
return false;
}
_processDebuggerSetBreakpointByUrl(req, debuggerInfo) {

@@ -540,3 +519,2 @@ // If we replaced Android emulator's address to localhost we need to change it back.

);
if (

@@ -552,6 +530,6 @@ req.params.url &&

}
if (req.params.urlRegex) {
req.params.urlRegex = req.params.urlRegex.replace(
/localhost/g, // $FlowFixMe[incompatible-call]
/localhost/g,
// $FlowFixMe[incompatible-call]
debuggerInfo.originalSourceURLAddress

@@ -562,6 +540,4 @@ );

}
_processDebuggerGetScriptSource(req, socket) {
let scriptSource = `Source for script with id '${req.params.scriptId}' was not found.`;
const sendResponse = () => {

@@ -578,14 +554,10 @@ const result = {

};
const pathToSource = this._scriptIdToSourcePathMapping.get(
req.params.scriptId
);
if (pathToSource) {
let pathIsURL = false;
try {
pathIsURL = new URL(pathToSource).hostname == "localhost";
} catch {}
if (pathIsURL) {

@@ -595,3 +567,2 @@ http

const { statusCode } = httpResponse;
if (statusCode == 200) {

@@ -625,3 +596,2 @@ httpResponse.setEncoding("utf8");

}
sendResponse();

@@ -631,3 +601,2 @@ }

}
_mapToDevicePageId(pageId) {

@@ -644,3 +613,2 @@ if (

}
module.exports = Device;

@@ -11,20 +11,17 @@ /**

*/
"use strict";
const InspectorProxy = require("./InspectorProxy");
const { parse } = require("url"); // Runs new HTTP Server and attaches Inspector Proxy to it.
const { parse } = require("url");
// Runs new HTTP Server and attaches Inspector Proxy to it.
// Requires are inlined here because we don't want to import them
// when someone needs only InspectorProxy instance (without starting
// new HTTP server).
function runInspectorProxy(port, projectRoot) {
const inspectorProxy = new InspectorProxy(projectRoot);
const app = require("connect")(); // $FlowFixMe[method-unbinding] added when improving typing for this parameters
const app = require("connect")();
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
app.use(inspectorProxy.processRequest.bind(inspectorProxy));
const httpServer = require("http").createServer(app);
httpServer.listen(port, "127.0.0.1", () => {

@@ -35,3 +32,2 @@ const websocketEndpoints =

const { pathname } = parse(request.url);
if (pathname != null && websocketEndpoints[pathname]) {

@@ -52,3 +48,2 @@ websocketEndpoints[pathname].handleUpgrade(

}
module.exports = {

@@ -55,0 +50,0 @@ InspectorProxy,

@@ -11,12 +11,9 @@ /**

*/
"use strict";
const Device = require("./Device");
const debug = require("debug")("Metro:InspectorProxy");
const url = require("url");
const WS = require("ws");
const WS_DEVICE_URL = "/inspector/device";

@@ -28,24 +25,27 @@ const WS_DEBUGGER_URL = "/inspector/debug";

const INTERNAL_ERROR_CODE = 1011;
/**
* Main Inspector Proxy class that connects JavaScript VM inside Android/iOS apps and JS debugger.
*/
class InspectorProxy {
// Root of the project used for relative to absolute source path conversion.
// Maps device ID to Device instance.
// Internal counter for device IDs -- just gets incremented for each new device.
_deviceCounter = 0; // We store server's address with port (like '127.0.0.1:8081') to be able to build URLs
_deviceCounter = 0;
// We store server's address with port (like '127.0.0.1:8081') to be able to build URLs
// (devtoolsFrontendUrl and webSocketDebuggerUrl) for page descriptions. These URLs are used
// by debugger to know where to connect.
_serverAddressWithPort = "";
constructor(projectRoot) {
this._projectRoot = projectRoot;
this._devices = new Map();
} // Process HTTP request sent to server. We only respond to 2 HTTP requests:
}
// Process HTTP request sent to server. We only respond to 2 HTTP requests:
// 1. /json/version returns Chrome debugger protocol version that we use
// 2. /json and /json/list returns list of page descriptions (list of inspectable apps).
// This list is combined from all the connected devices.
processRequest(request, response, next) {

@@ -65,3 +65,2 @@ if (

});
this._sendJsonResponse(response, result);

@@ -76,7 +75,7 @@ } else if (request.url === PAGES_LIST_JSON_VERSION_URL) {

}
} // Adds websocket listeners to the provided HTTP/HTTPS server.
}
// Adds websocket listeners to the provided HTTP/HTTPS server.
createWebSocketListeners(server) {
const { port } = server.address();
if (server.address().family === "IPv6") {

@@ -87,3 +86,2 @@ this._serverAddressWithPort = `[::1]:${port}`;

}
return {

@@ -93,5 +91,6 @@ [WS_DEVICE_URL]: this._createDeviceConnectionWSServer(),

};
} // Converts page information received from device into PageDescription object
}
// Converts page information received from device into PageDescription object
// that is sent to debugger.
_buildPageDescription(deviceId, device, page) {

@@ -113,5 +112,6 @@ const debuggerUrl = `${this._serverAddressWithPort}${WS_DEBUGGER_URL}?device=${deviceId}&page=${page.id}`;

};
} // Sends object as response to HTTP request.
}
// Sends object as response to HTTP request.
// Just serializes object using JSON and sets required headers.
_sendJsonResponse(response, object) {

@@ -126,3 +126,5 @@ const data = JSON.stringify(object, null, 2);

response.end(data);
} // Adds websocket handler for device connections.
}
// Adds websocket handler for device connections.
// Device connects to /inspector/device and passes device and app names as

@@ -132,3 +134,2 @@ // HTTP GET params.

// new instance of Device class.
_createDeviceConnectionWSServer() {

@@ -138,4 +139,4 @@ const wss = new WS.Server({

perMessageDeflate: true,
}); // $FlowFixMe[value-as-type]
});
// $FlowFixMe[value-as-type]
wss.on("connection", async (socket, req) => {

@@ -147,3 +148,2 @@ try {

const deviceId = this._deviceCounter++;
this._devices.set(

@@ -153,7 +153,5 @@ deviceId,

);
debug(`Got new connection: device=${deviceName}, app=${appName}`);
socket.on("close", () => {
this._devices.delete(deviceId);
debug(`Device ${deviceName} disconnected.`);

@@ -163,3 +161,2 @@ });

var _e$toString;
console.error("error", e);

@@ -176,3 +173,5 @@ socket.close(

return wss;
} // Returns websocket handler for debugger connections.
}
// Returns websocket handler for debugger connections.
// Debugger connects to webSocketDebuggerUrl that we return as part of page description

@@ -182,3 +181,2 @@ // in /json response.

// websocket object to corresponding Device instance.
_createDebuggerConnectionWSServer() {

@@ -188,4 +186,4 @@ const wss = new WS.Server({

perMessageDeflate: false,
}); // $FlowFixMe[value-as-type]
});
// $FlowFixMe[value-as-type]
wss.on("connection", async (socket, req) => {

@@ -196,17 +194,12 @@ try {

const pageId = query.page;
if (deviceId == null || pageId == null) {
throw new Error("Incorrect URL - must provide device and page IDs");
}
const device = this._devices.get(parseInt(deviceId, 10));
if (device == null) {
throw new Error("Unknown device with ID " + deviceId);
}
device.handleDebuggerConnection(socket, pageId);
} catch (e) {
var _e$toString2;
console.error(e);

@@ -226,3 +219,2 @@ socket.close(

}
module.exports = InspectorProxy;

@@ -11,4 +11,7 @@ /**

*/
"use strict"; // Page information received from the device. New page is created for
"use strict";
// Page information received from the device. New page is created for
// each new instance of VM and can appear when user reloads React Native
// application.

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc