UNPKG

appium-remote-debugger

Version:
193 lines 7.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.RpcClientRealDeviceShim = void 0; const logger_1 = require("../logger"); const rpc_client_1 = require("./rpc-client"); const lodash_1 = __importDefault(require("lodash")); /** * RPC client implementation for iOS 18+ real devices using the WebInspector shim. * This client uses the `com.apple.webinspector.shim.remote` service via RemoteXPC * tunneling, which is required for iOS 18 and later where the traditional * Web Inspector service is no longer available. * * Extends RpcClient to provide device-specific connection handling using * the appium-ios-remotexpc library. */ class RpcClientRealDeviceShim extends rpc_client_1.RpcClient { webInspectorService; remoteXPC; messageListenerTask; isListening = false; /** * Creates a new RpcClientRealDeviceShim instance. * * @param opts - Options for configuring the shim RPC client. */ constructor(opts) { super(opts); } /** * Connects to the WebInspector shim service on an iOS 18+ real device. * Uses the RemoteXPC tunnel to establish a connection to the * `com.apple.webinspector.shim.remote` service. */ async connect() { if (this.isConnected) { return; } logger_1.log.debug(`Connecting to WebInspector shim service for device ${this.udid}`); const { Services } = await import('appium-ios-remotexpc'); const result = await Services.startWebInspectorService(this.udid); this.webInspectorService = result.webInspectorService; this.remoteXPC = result.remoteXPC; this.startMessageListener(); this.isConnected = true; logger_1.log.debug('Successfully connected to WebInspector shim service'); } /** * Disconnects from the WebInspector shim service. * Closes the service connection and cleans up resources. */ async disconnect() { if (!this.isConnected) { return; } logger_1.log.debug('Disconnecting from WebInspector shim service'); await super.disconnect(); // Stop the message listener this.isListening = false; if (this.webInspectorService) { try { await this.webInspectorService.stopListeningAsync(); } catch (err) { logger_1.log.warn('Error while stopping shim message listener', err); await this.webInspectorService.close(); this.webInspectorService = undefined; } } // Wait for the listener task to complete if (this.messageListenerTask) { try { await this.messageListenerTask; } catch { // Ignore errors during shutdown } } // Close the connections if (this.webInspectorService) { await this.webInspectorService.close(); this.webInspectorService = undefined; } if (this.remoteXPC) { await this.remoteXPC.close(); this.remoteXPC = undefined; } this.isConnected = false; logger_1.log.debug('Disconnected from WebInspector shim service'); } /** * Sends a command message to the WebInspector shim service. * Translates the RemoteCommand format to the shim service format. * * @param cmd - The command to send to the device. */ async sendMessage(cmd) { if (!this.webInspectorService) { throw new Error('WebInspector shim service is not initialized. Is the client connected?'); } const selector = cmd.__selector; const args = this.translateArguments(cmd.__argument); logger_1.log.debug(`Sending message via shim: ${selector}`); await this.webInspectorService.sendMessage(selector, args); } /** * Receives data from the WebInspector shim service and handles it. * This method is called by the message listener when a message is received. * * @param data - The data received from the service. */ async receive(data) { if (this.isConnected && data) { await this.messageHandler.handleMessage(data); } } /** * Starts the background message listener that receives messages from * the WebInspector shim service and forwards them to the message handler. */ startMessageListener() { if (this.isListening || !this.webInspectorService) { return; } this.isListening = true; const service = this.webInspectorService; this.messageListenerTask = (async () => { try { for await (const message of service.listenMessage()) { if (!this.isListening) { break; } // Convert the message to the expected format const convertedMessage = this.convertMessage(message); await this.receive(convertedMessage); } } catch (err) { if (this.isListening) { logger_1.log.error('Error in shim message listener', err); } } finally { this.isListening = false; } })(); } /** * Converts a message from the WebInspector shim format to the format * expected by the message handler. * * @param message - The message from the shim service. * @returns The converted message in the expected format. */ convertMessage(message) { const converted = { __selector: message.__selector, }; // Convert buffer data to strings where necessary if (lodash_1.default.isPlainObject(message.__argument)) { const args = { ...message.__argument }; // Handle WIRMessageDataKey and WIRSocketDataKey which may be buffers for (const key of ['WIRMessageDataKey', 'WIRSocketDataKey', 'WIRDestinationKey']) { if (args[key] !== undefined && Buffer.isBuffer(args[key])) { args[key] = args[key].toString('utf8'); } } converted.__argument = args; } return converted; } /** * Translates command arguments from the RemoteCommand format to the * format expected by the WebInspector shim service. * * @param args - The arguments from the RemoteCommand. * @returns The translated arguments for the shim service. */ translateArguments(args) { if (!lodash_1.default.isPlainObject(args)) { return {}; } const translated = { ...args }; // Remove the connection identifier key as it will be added by the shim service delete translated.WIRConnectionIdentifierKey; return translated; } } exports.RpcClientRealDeviceShim = RpcClientRealDeviceShim; exports.default = RpcClientRealDeviceShim; //# sourceMappingURL=rpc-client-real-device-shim.js.map