appium-remote-debugger 15.2.13 → 15.3.0
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/CHANGELOG.md +12 -0
- package/build/lib/protocol/index.d.ts +13 -8
- package/build/lib/protocol/index.d.ts.map +1 -1
- package/build/lib/protocol/index.js +17 -12
- package/build/lib/protocol/index.js.map +1 -1
- package/build/lib/rpc/index.d.ts +2 -3
- package/build/lib/rpc/index.d.ts.map +1 -1
- package/build/lib/rpc/index.js +2 -2
- package/build/lib/rpc/index.js.map +1 -1
- package/build/lib/rpc/remote-messages.d.ts +62 -41
- package/build/lib/rpc/remote-messages.d.ts.map +1 -1
- package/build/lib/rpc/remote-messages.js +56 -41
- package/build/lib/rpc/remote-messages.js.map +1 -1
- package/build/lib/rpc/rpc-client-real-device.d.ts +26 -8
- package/build/lib/rpc/rpc-client-real-device.d.ts.map +1 -1
- package/build/lib/rpc/rpc-client-real-device.js +21 -16
- package/build/lib/rpc/rpc-client-real-device.js.map +1 -1
- package/build/lib/rpc/rpc-client-simulator.d.ts +36 -28
- package/build/lib/rpc/rpc-client-simulator.d.ts.map +1 -1
- package/build/lib/rpc/rpc-client-simulator.js +39 -36
- package/build/lib/rpc/rpc-client-simulator.js.map +1 -1
- package/build/lib/rpc/rpc-client.d.ts +278 -189
- package/build/lib/rpc/rpc-client.d.ts.map +1 -1
- package/build/lib/rpc/rpc-client.js +222 -178
- package/build/lib/rpc/rpc-client.js.map +1 -1
- package/build/lib/rpc/rpc-message-handler.d.ts +32 -39
- package/build/lib/rpc/rpc-message-handler.d.ts.map +1 -1
- package/build/lib/rpc/rpc-message-handler.js +39 -53
- package/build/lib/rpc/rpc-message-handler.js.map +1 -1
- package/build/lib/types.d.ts +28 -0
- package/build/lib/types.d.ts.map +1 -1
- package/build/test/functional/safari-e2e-specs.js +5 -1
- package/build/test/functional/safari-e2e-specs.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/lib/protocol/{index.js → index.ts} +29 -17
- package/lib/rpc/index.ts +2 -0
- package/lib/rpc/{remote-messages.js → remote-messages.ts} +73 -59
- package/lib/rpc/rpc-client-real-device.ts +68 -0
- package/lib/rpc/{rpc-client-simulator.js → rpc-client-simulator.ts} +54 -57
- package/lib/rpc/{rpc-client.js → rpc-client.ts} +368 -284
- package/lib/rpc/{rpc-message-handler.js → rpc-message-handler.ts} +73 -64
- package/lib/types.ts +31 -0
- package/package.json +1 -1
- package/lib/rpc/index.js +0 -4
- package/lib/rpc/rpc-client-real-device.js +0 -64
|
@@ -28,57 +28,43 @@ const NO_TARGET_PRESENT_YET_ERRORS = [
|
|
|
28
28
|
exports.NEW_APP_CONNECTED_ERROR = 'New application has connected';
|
|
29
29
|
exports.EMPTY_PAGE_DICTIONARY_ERROR = 'Empty page dictionary received';
|
|
30
30
|
const ON_PAGE_INITIALIZED_EVENT = 'onPageInitialized';
|
|
31
|
+
/**
|
|
32
|
+
* Base class for RPC clients that communicate with the Web Inspector.
|
|
33
|
+
* Provides functionality for managing targets, sending commands, and handling
|
|
34
|
+
* page initialization. Subclasses must implement device-specific connection logic.
|
|
35
|
+
*/
|
|
31
36
|
class RpcClient {
|
|
32
|
-
/** @type {RpcMessageHandler|undefined} */
|
|
33
37
|
messageHandler;
|
|
34
|
-
/** @type {RemoteMessages|undefined} */
|
|
35
38
|
remoteMessages;
|
|
36
|
-
/** @type {boolean} */
|
|
37
39
|
connected;
|
|
38
|
-
/** @type {boolean} */
|
|
39
40
|
isSafari;
|
|
40
|
-
/** @type {string} */
|
|
41
41
|
connId;
|
|
42
|
-
/** @type {string} */
|
|
43
42
|
senderId;
|
|
44
|
-
/** @type {number} */
|
|
45
43
|
msgId;
|
|
46
|
-
/** @type {string|undefined} */
|
|
47
44
|
udid;
|
|
48
|
-
/** @type {boolean|undefined} */
|
|
49
45
|
logAllCommunication;
|
|
50
|
-
/** @type {boolean|undefined} */
|
|
51
46
|
logAllCommunicationHexDump;
|
|
52
|
-
/** @type {number|undefined} */
|
|
53
47
|
socketChunkSize;
|
|
54
|
-
/** @type {number|undefined} */
|
|
55
48
|
webInspectorMaxFrameLength;
|
|
56
|
-
/** @type {boolean|undefined} */
|
|
57
49
|
fullPageInitialization;
|
|
58
|
-
/** @type {string|undefined} */
|
|
59
50
|
bundleId;
|
|
60
|
-
/** @type {number | undefined} */
|
|
61
51
|
pageLoadTimeoutMs;
|
|
62
|
-
/** @type {string} */
|
|
63
52
|
platformVersion;
|
|
64
|
-
/** @type {string[]} */
|
|
65
53
|
_contexts;
|
|
66
|
-
/** @type {AppToTargetsMap} */
|
|
67
54
|
_targets;
|
|
68
|
-
/** @type {EventEmitter} */
|
|
69
55
|
_targetSubscriptions;
|
|
70
|
-
/** @type {PendingPageTargetDetails | undefined} */
|
|
71
56
|
_pendingTargetNotification;
|
|
72
|
-
/** @type {number} */
|
|
73
57
|
_targetCreationTimeoutMs;
|
|
58
|
+
_provisionedPages;
|
|
59
|
+
_pageSelectionLock;
|
|
60
|
+
_pageSelectionMonitor;
|
|
74
61
|
/**
|
|
75
|
-
*
|
|
76
|
-
* @param {RpcClientOptions} [opts={}]
|
|
62
|
+
* @param opts - Options for configuring the RPC client.
|
|
77
63
|
*/
|
|
78
64
|
constructor(opts = {}) {
|
|
79
65
|
const { bundleId, platformVersion = '', isSafari = true, logAllCommunication = false, logAllCommunicationHexDump = false, webInspectorMaxFrameLength, socketChunkSize, fullPageInitialization = false, udid, pageLoadTimeoutMs, targetCreationTimeoutMs = DEFAULT_TARGET_CREATION_TIMEOUT_MS, } = opts;
|
|
80
66
|
this.isSafari = isSafari;
|
|
81
|
-
this.
|
|
67
|
+
this.connected = false;
|
|
82
68
|
this.connId = support_1.util.uuidV4();
|
|
83
69
|
this.senderId = support_1.util.uuidV4();
|
|
84
70
|
this.msgId = 0;
|
|
@@ -108,72 +94,137 @@ class RpcClient {
|
|
|
108
94
|
this.messageHandler.on('Heap.garbageCollected', this.onGarbageCollected.bind(this));
|
|
109
95
|
}
|
|
110
96
|
/**
|
|
111
|
-
*
|
|
97
|
+
* Gets the list of execution context IDs.
|
|
98
|
+
*
|
|
99
|
+
* @returns Array of execution context IDs.
|
|
112
100
|
*/
|
|
113
101
|
get contexts() {
|
|
114
102
|
return this._contexts;
|
|
115
103
|
}
|
|
116
104
|
/**
|
|
117
|
-
*
|
|
105
|
+
* Gets the mapping of applications to their pages and targets.
|
|
106
|
+
*
|
|
107
|
+
* @returns The targets mapping structure.
|
|
118
108
|
*/
|
|
119
109
|
get targets() {
|
|
120
110
|
return this._targets;
|
|
121
111
|
}
|
|
122
112
|
/**
|
|
123
|
-
*
|
|
113
|
+
* Gets whether the client is currently connected.
|
|
114
|
+
*
|
|
115
|
+
* @returns True if connected, false otherwise.
|
|
124
116
|
*/
|
|
125
117
|
get isConnected() {
|
|
126
118
|
return this.connected;
|
|
127
119
|
}
|
|
128
120
|
/**
|
|
129
|
-
*
|
|
121
|
+
* Sets the connection status.
|
|
122
|
+
*
|
|
123
|
+
* @param connected - The connection status to set.
|
|
130
124
|
*/
|
|
131
125
|
set isConnected(connected) {
|
|
132
126
|
this.connected = !!connected;
|
|
133
127
|
}
|
|
134
128
|
/**
|
|
135
|
-
*
|
|
129
|
+
* Gets the event emitter for target subscriptions.
|
|
130
|
+
*
|
|
131
|
+
* @returns The target subscriptions event emitter.
|
|
136
132
|
*/
|
|
137
133
|
get targetSubscriptions() {
|
|
138
134
|
return this._targetSubscriptions;
|
|
139
135
|
}
|
|
140
136
|
/**
|
|
137
|
+
* Registers an event listener on the message handler.
|
|
138
|
+
*
|
|
139
|
+
* Supported events include:
|
|
140
|
+
*
|
|
141
|
+
* **RPC-level events:**
|
|
142
|
+
* - `_rpc_reportSetup:` - Emitted when the debugger setup is reported
|
|
143
|
+
* - `_rpc_reportConnectedApplicationList:` - Emitted when the list of connected applications is reported
|
|
144
|
+
* - `_rpc_forwardGetListing:` - Emitted when an application sends a page listing
|
|
145
|
+
* - `_rpc_applicationConnected:` - Emitted when a new application connects
|
|
146
|
+
* - `_rpc_applicationDisconnected:` - Emitted when an application disconnects
|
|
147
|
+
* - `_rpc_applicationUpdated:` - Emitted when an application is updated
|
|
148
|
+
* - `_rpc_reportConnectedDriverList:` - Emitted when the list of connected drivers is reported
|
|
149
|
+
* - `_rpc_reportCurrentState:` - Emitted when the current state is reported
|
|
141
150
|
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
151
|
+
* **Target events:**
|
|
152
|
+
* - `Target.targetCreated` - Emitted when a new target is created (args: error, appIdKey, targetInfo)
|
|
153
|
+
* - `Target.targetDestroyed` - Emitted when a target is destroyed (args: error, appIdKey, targetInfo)
|
|
154
|
+
* - `Target.didCommitProvisionalTarget` - Emitted when a provisional target commits (args: error, appIdKey, provisionalTargetInfo)
|
|
155
|
+
*
|
|
156
|
+
* **Page events:**
|
|
157
|
+
* - `Page.frameStoppedLoading` - Emitted when a frame stops loading
|
|
158
|
+
* - `Page.frameNavigated` - Emitted when a frame navigates
|
|
159
|
+
* - `Page.frameDetached` - Emitted when a frame is detached
|
|
160
|
+
* - `Page.loadEventFired` - Emitted when the page load event fires
|
|
161
|
+
*
|
|
162
|
+
* **Runtime events:**
|
|
163
|
+
* - `Runtime.executionContextCreated` - Emitted when an execution context is created (args: error, context)
|
|
164
|
+
*
|
|
165
|
+
* **Console events:**
|
|
166
|
+
* - `Console.messageAdded` - Emitted when a console message is added (args: error, message)
|
|
167
|
+
* - `Console.messageRepeatCountUpdated` - Emitted when a console message repeat count is updated
|
|
168
|
+
* - `ConsoleEvent` - Aggregate event for all Console.* events (args: error, params, methodName)
|
|
169
|
+
*
|
|
170
|
+
* **Network events:**
|
|
171
|
+
* - `NetworkEvent` - Aggregate event for all Network.* events (args: error, params, methodName)
|
|
172
|
+
*
|
|
173
|
+
* **Timeline events:**
|
|
174
|
+
* - `Timeline.eventRecorded` - Emitted when a timeline event is recorded (args: error, record)
|
|
175
|
+
*
|
|
176
|
+
* **Heap events:**
|
|
177
|
+
* - `Heap.garbageCollected` - Emitted when garbage collection occurs
|
|
178
|
+
*
|
|
179
|
+
* **Message ID events:**
|
|
180
|
+
* - Any numeric string (message ID) - Emitted for command responses (args: error, result)
|
|
181
|
+
*
|
|
182
|
+
* @param event - The event name to listen for.
|
|
183
|
+
* @param listener - The listener function to call when the event is emitted.
|
|
184
|
+
* The listener receives (error, ...args) where error may be null/undefined.
|
|
185
|
+
* @returns This instance for method chaining.
|
|
145
186
|
*/
|
|
146
187
|
on(event, listener) {
|
|
147
|
-
// @ts-ignore messageHandler must be defined here
|
|
148
188
|
this.messageHandler.on(event, listener);
|
|
149
189
|
return this;
|
|
150
190
|
}
|
|
151
191
|
/**
|
|
192
|
+
* Registers a one-time event listener on the message handler.
|
|
193
|
+
* The listener will be automatically removed after being called once.
|
|
152
194
|
*
|
|
153
|
-
* @
|
|
154
|
-
*
|
|
155
|
-
* @
|
|
195
|
+
* See {@link RpcClient.on} for a list of supported events.
|
|
196
|
+
*
|
|
197
|
+
* @param event - The event name to listen for.
|
|
198
|
+
* @param listener - The listener function to call when the event is emitted.
|
|
199
|
+
* The listener receives (error, ...args) where error may be null/undefined.
|
|
200
|
+
* @returns This instance for method chaining.
|
|
156
201
|
*/
|
|
157
202
|
once(event, listener) {
|
|
158
|
-
// @ts-ignore messageHandler must be defined here
|
|
159
203
|
this.messageHandler.once(event, listener);
|
|
160
204
|
return this;
|
|
161
205
|
}
|
|
162
206
|
/**
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
* @
|
|
207
|
+
* Removes an event listener from the message handler.
|
|
208
|
+
*
|
|
209
|
+
* See {@link RpcClient.on} for a list of supported events.
|
|
210
|
+
*
|
|
211
|
+
* @param event - The event name to stop listening for.
|
|
212
|
+
* @param listener - The listener function to remove.
|
|
213
|
+
* @returns This instance for method chaining.
|
|
166
214
|
*/
|
|
167
215
|
off(event, listener) {
|
|
168
|
-
// @ts-ignore messageHandler must be defined here
|
|
169
216
|
this.messageHandler.off(event, listener);
|
|
170
217
|
return this;
|
|
171
218
|
}
|
|
172
219
|
/**
|
|
220
|
+
* Waits for a target to be created for the specified app and page.
|
|
221
|
+
* If the target already exists, returns it immediately. Otherwise,
|
|
222
|
+
* waits up to the configured timeout for the target to be created.
|
|
173
223
|
*
|
|
174
|
-
* @param
|
|
175
|
-
* @param
|
|
176
|
-
* @returns
|
|
224
|
+
* @param appIdKey - The application identifier key.
|
|
225
|
+
* @param pageIdKey - The page identifier key.
|
|
226
|
+
* @returns A promise that resolves to the target ID if found, undefined otherwise.
|
|
227
|
+
* @throws Error if no target is found after the timeout.
|
|
177
228
|
*/
|
|
178
229
|
async waitForTarget(appIdKey, pageIdKey) {
|
|
179
230
|
let target = this.getTarget(appIdKey, pageIdKey);
|
|
@@ -203,11 +254,14 @@ class RpcClient {
|
|
|
203
254
|
}
|
|
204
255
|
}
|
|
205
256
|
/**
|
|
257
|
+
* Sends a command to the remote debugger with automatic retry logic
|
|
258
|
+
* for target-related errors. Handles cases where targets are not yet
|
|
259
|
+
* available or not supported.
|
|
206
260
|
*
|
|
207
|
-
* @param
|
|
208
|
-
* @param
|
|
209
|
-
* @param
|
|
210
|
-
* @returns
|
|
261
|
+
* @param command - The command name to send.
|
|
262
|
+
* @param opts - Options for the command.
|
|
263
|
+
* @param waitForResponse - Whether to wait for a response. Defaults to true.
|
|
264
|
+
* @returns A promise that resolves to the command result or options.
|
|
211
265
|
*/
|
|
212
266
|
async send(command, opts, waitForResponse = true) {
|
|
213
267
|
const timer = new support_1.timing.Timer().start();
|
|
@@ -221,7 +275,7 @@ class RpcClient {
|
|
|
221
275
|
return await this.sendToDevice(command, opts, waitForResponse);
|
|
222
276
|
}
|
|
223
277
|
else if (appIdKey && NO_TARGET_PRESENT_YET_ERRORS.some((error) => messageLc.includes(error))) {
|
|
224
|
-
await this.waitForTarget(appIdKey,
|
|
278
|
+
await this.waitForTarget(appIdKey, pageIdKey);
|
|
225
279
|
return await this.sendToDevice(command, opts, waitForResponse);
|
|
226
280
|
}
|
|
227
281
|
throw err;
|
|
@@ -231,14 +285,17 @@ class RpcClient {
|
|
|
231
285
|
}
|
|
232
286
|
}
|
|
233
287
|
/**
|
|
288
|
+
* Sends a command directly to the device, handling message routing,
|
|
289
|
+
* response waiting, and error handling.
|
|
234
290
|
*
|
|
235
|
-
* @template
|
|
236
|
-
* @param
|
|
237
|
-
* @param
|
|
238
|
-
* @param
|
|
239
|
-
* @returns
|
|
291
|
+
* @template TWaitForResponse - Whether to wait for a response.
|
|
292
|
+
* @param command - The command name to send.
|
|
293
|
+
* @param opts - Options for the command.
|
|
294
|
+
* @param waitForResponse - Whether to wait for a response. Defaults to true.
|
|
295
|
+
* @returns A promise that resolves based on waitForResponse:
|
|
296
|
+
* - If true: resolves to the response value
|
|
297
|
+
* - If false: resolves to the full options object
|
|
240
298
|
*/
|
|
241
|
-
// @ts-ignore Compiler issue
|
|
242
299
|
async sendToDevice(command, opts, waitForResponse = true) {
|
|
243
300
|
return await new bluebird_1.default(async (resolve, reject) => {
|
|
244
301
|
// promise to be resolved whenever remote debugger
|
|
@@ -248,7 +305,6 @@ class RpcClient {
|
|
|
248
305
|
// for target-base communication, everything is wrapped up
|
|
249
306
|
const wrapperMsgId = this.msgId++;
|
|
250
307
|
// acknowledge wrapper message
|
|
251
|
-
// @ts-ignore messageHandler must be defined
|
|
252
308
|
this.messageHandler.on(wrapperMsgId.toString(), function (err) {
|
|
253
309
|
if (err) {
|
|
254
310
|
reject(err);
|
|
@@ -258,24 +314,20 @@ class RpcClient {
|
|
|
258
314
|
const pageIdKey = opts.pageIdKey;
|
|
259
315
|
const targetId = opts.targetId ?? this.getTarget(appIdKey, pageIdKey);
|
|
260
316
|
// retrieve the correct command to send
|
|
261
|
-
/** @type {import('../types').RemoteCommandOpts} */
|
|
262
317
|
const fullOpts = lodash_1.default.defaults({
|
|
263
318
|
connId: this.connId,
|
|
264
319
|
senderId: this.senderId,
|
|
265
320
|
targetId,
|
|
266
|
-
id: msgId,
|
|
321
|
+
id: msgId.toString(),
|
|
267
322
|
}, opts);
|
|
268
|
-
/** @type {import('../types').RawRemoteCommand} */
|
|
269
323
|
let cmd;
|
|
270
324
|
try {
|
|
271
|
-
// @ts-ignore remoteMessages must be defined
|
|
272
325
|
cmd = this.remoteMessages.getRemoteCommand(command, fullOpts);
|
|
273
326
|
}
|
|
274
327
|
catch (err) {
|
|
275
328
|
logger_1.log.error(err);
|
|
276
329
|
return reject(err);
|
|
277
330
|
}
|
|
278
|
-
/** @type {import('../types').RemoteCommand} */
|
|
279
331
|
const finalCommand = {
|
|
280
332
|
__argument: lodash_1.default.omit(cmd.__argument, ['WIRSocketDataKey']),
|
|
281
333
|
__selector: cmd.__selector,
|
|
@@ -283,19 +335,18 @@ class RpcClient {
|
|
|
283
335
|
const hasSocketData = lodash_1.default.isPlainObject(cmd.__argument?.WIRSocketDataKey);
|
|
284
336
|
if (hasSocketData) {
|
|
285
337
|
// make sure the message being sent has all the information that is needed
|
|
286
|
-
|
|
287
|
-
if (lodash_1.default.
|
|
288
|
-
//
|
|
289
|
-
|
|
338
|
+
const socketData = cmd.__argument.WIRSocketDataKey;
|
|
339
|
+
if (!lodash_1.default.isInteger(socketData.id)) {
|
|
340
|
+
// ! This must be a number
|
|
341
|
+
socketData.id = wrapperMsgId;
|
|
290
342
|
}
|
|
291
|
-
finalCommand.__argument.WIRSocketDataKey = Buffer.from(JSON.stringify(
|
|
343
|
+
finalCommand.__argument.WIRSocketDataKey = Buffer.from(JSON.stringify(socketData));
|
|
292
344
|
}
|
|
293
345
|
let messageHandled = true;
|
|
294
346
|
if (!waitForResponse) {
|
|
295
347
|
// the promise will be resolved as soon as the socket has been sent
|
|
296
348
|
messageHandled = false;
|
|
297
349
|
// do not log receipts
|
|
298
|
-
// @ts-ignore messageHandler must be defined
|
|
299
350
|
this.messageHandler.once(msgId.toString(), (err) => {
|
|
300
351
|
if (err) {
|
|
301
352
|
// we are not waiting for this, and if it errors it is most likely
|
|
@@ -303,26 +354,22 @@ class RpcClient {
|
|
|
303
354
|
logger_1.log.error(`Received error from send that is not being waited for (id: ${msgId}): ` +
|
|
304
355
|
lodash_1.default.truncate(JSON.stringify(err), DATA_LOG_LENGTH));
|
|
305
356
|
// reject, though it is very rare that this will be triggered, since
|
|
306
|
-
// the promise is resolved
|
|
357
|
+
// the promise is resolved directly after send. On the off chance,
|
|
307
358
|
// though, it will alert of a protocol change.
|
|
308
359
|
reject(err);
|
|
309
360
|
}
|
|
310
361
|
});
|
|
311
|
-
// @ts-ignore messageHandler must be defined
|
|
312
362
|
}
|
|
313
|
-
else if (this.messageHandler.
|
|
314
|
-
// @ts-ignore messageHandler must be defined
|
|
363
|
+
else if (this.messageHandler.listenerCount(cmd.__selector)) {
|
|
315
364
|
this.messageHandler.prependOnceListener(cmd.__selector, (err, ...args) => {
|
|
316
365
|
if (err) {
|
|
317
366
|
return reject(err);
|
|
318
367
|
}
|
|
319
368
|
logger_1.log.debug(`Received response from send (id: ${msgId}): '${lodash_1.default.truncate(JSON.stringify(args), DATA_LOG_LENGTH)}'`);
|
|
320
|
-
// @ts-ignore This is ok
|
|
321
369
|
resolve(args);
|
|
322
370
|
});
|
|
323
371
|
}
|
|
324
372
|
else if (hasSocketData) {
|
|
325
|
-
// @ts-ignore messageHandler must be defined
|
|
326
373
|
this.messageHandler.once(msgId.toString(), (err, value) => {
|
|
327
374
|
if (err) {
|
|
328
375
|
return reject(new Error(`Remote debugger error with code '${err.code}': ${err.message}`));
|
|
@@ -346,7 +393,6 @@ class RpcClient {
|
|
|
346
393
|
if (!messageHandled) {
|
|
347
394
|
// There are no handlers waiting for a response before resolving,
|
|
348
395
|
// and no errors sending the message over the socket, so resolve
|
|
349
|
-
// @ts-ignore This is ok
|
|
350
396
|
resolve(fullOpts);
|
|
351
397
|
}
|
|
352
398
|
}
|
|
@@ -355,34 +401,47 @@ class RpcClient {
|
|
|
355
401
|
}
|
|
356
402
|
});
|
|
357
403
|
}
|
|
404
|
+
/**
|
|
405
|
+
* Connects to the remote debugger. Must be implemented by subclasses.
|
|
406
|
+
*
|
|
407
|
+
* @throws Error indicating that subclasses must implement this method.
|
|
408
|
+
*/
|
|
358
409
|
async connect() {
|
|
359
410
|
throw new Error(`Sub-classes need to implement a 'connect' function`);
|
|
360
411
|
}
|
|
412
|
+
/**
|
|
413
|
+
* Disconnects from the remote debugger and cleans up event listeners.
|
|
414
|
+
*/
|
|
361
415
|
async disconnect() {
|
|
362
|
-
this.messageHandler
|
|
416
|
+
this.messageHandler.removeAllListeners();
|
|
363
417
|
}
|
|
364
418
|
/**
|
|
365
|
-
*
|
|
366
|
-
*
|
|
419
|
+
* Sends a message to the device. Must be implemented by subclasses.
|
|
420
|
+
*
|
|
421
|
+
* @param _command - The command to send.
|
|
422
|
+
* @throws Error indicating that subclasses must implement this method.
|
|
367
423
|
*/
|
|
368
424
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
369
|
-
async sendMessage(
|
|
425
|
+
async sendMessage(_command) {
|
|
370
426
|
throw new Error(`Sub-classes need to implement a 'sendMessage' function`);
|
|
371
427
|
}
|
|
372
428
|
/**
|
|
373
|
-
*
|
|
374
|
-
*
|
|
429
|
+
* Receives data from the device. Must be implemented by subclasses.
|
|
430
|
+
*
|
|
431
|
+
* @param _data - The data received from the device.
|
|
432
|
+
* @throws Error indicating that subclasses must implement this method.
|
|
375
433
|
*/
|
|
376
434
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
377
|
-
async receive(
|
|
435
|
+
async receive(_data) {
|
|
378
436
|
throw new Error(`Sub-classes need to implement a 'receive' function`);
|
|
379
437
|
}
|
|
380
438
|
/**
|
|
439
|
+
* Handles the creation of a new target for an application and page.
|
|
440
|
+
* Initializes the page and waits for readiness if configured.
|
|
381
441
|
*
|
|
382
|
-
* @param
|
|
383
|
-
* @param
|
|
384
|
-
* @param
|
|
385
|
-
* @returns {Promise<void>}
|
|
442
|
+
* @param err - Error if one occurred, undefined otherwise.
|
|
443
|
+
* @param app - The application identifier key.
|
|
444
|
+
* @param targetInfo - Information about the created target.
|
|
386
445
|
*/
|
|
387
446
|
async addTarget(err, app, targetInfo) {
|
|
388
447
|
if (lodash_1.default.isNil(targetInfo?.targetId)) {
|
|
@@ -444,7 +503,8 @@ class RpcClient {
|
|
|
444
503
|
}
|
|
445
504
|
logger_1.log.debug(`Target created for app '${appIdKey}' and page '${pageIdKey}': ${JSON.stringify(targetInfo)}`);
|
|
446
505
|
if (lodash_1.default.has(this.targets[appIdKey], pageIdKey)) {
|
|
447
|
-
|
|
506
|
+
const existingTarget = this.targets[appIdKey][pageIdKey];
|
|
507
|
+
logger_1.log.debug(`There is already a target for this app and page ('${existingTarget}'). ` +
|
|
448
508
|
`This might cause problems`);
|
|
449
509
|
}
|
|
450
510
|
this.targets[appIdKey][pageIdKey] = targetInfo.targetId;
|
|
@@ -491,11 +551,11 @@ class RpcClient {
|
|
|
491
551
|
}
|
|
492
552
|
}
|
|
493
553
|
/**
|
|
554
|
+
* Handles updates to provisional targets when they commit.
|
|
494
555
|
*
|
|
495
|
-
* @param
|
|
496
|
-
* @param
|
|
497
|
-
* @param
|
|
498
|
-
* @returns {Promise<void>}
|
|
556
|
+
* @param err - Error if one occurred, undefined otherwise.
|
|
557
|
+
* @param app - The application identifier key.
|
|
558
|
+
* @param targetInfo - Information about the provisional target update.
|
|
499
559
|
*/
|
|
500
560
|
async updateTarget(err, app, targetInfo) {
|
|
501
561
|
const { oldTargetId, newTargetId, } = targetInfo;
|
|
@@ -512,11 +572,11 @@ class RpcClient {
|
|
|
512
572
|
};
|
|
513
573
|
}
|
|
514
574
|
/**
|
|
575
|
+
* Handles the destruction of a target, including cleanup of provisional targets.
|
|
515
576
|
*
|
|
516
|
-
* @param
|
|
517
|
-
* @param
|
|
518
|
-
* @param
|
|
519
|
-
* @returns {Promise<void>}
|
|
577
|
+
* @param err - Error if one occurred, undefined otherwise.
|
|
578
|
+
* @param app - The application identifier key.
|
|
579
|
+
* @param targetInfo - Information about the destroyed target.
|
|
520
580
|
*/
|
|
521
581
|
async removeTarget(err, app, targetInfo) {
|
|
522
582
|
if (lodash_1.default.isNil(targetInfo?.targetId)) {
|
|
@@ -552,21 +612,27 @@ class RpcClient {
|
|
|
552
612
|
logger_1.log.debug(`Target '${targetInfo.targetId}' deleted for app '${app}', but no such target exists`);
|
|
553
613
|
}
|
|
554
614
|
/**
|
|
555
|
-
*
|
|
556
|
-
*
|
|
557
|
-
* @
|
|
615
|
+
* Gets the target ID for a specific app and page combination.
|
|
616
|
+
*
|
|
617
|
+
* @param appIdKey - The application identifier key.
|
|
618
|
+
* @param pageIdKey - The page identifier key.
|
|
619
|
+
* @returns The target ID if found, undefined otherwise.
|
|
558
620
|
*/
|
|
559
621
|
getTarget(appIdKey, pageIdKey) {
|
|
560
622
|
if (!appIdKey || !pageIdKey) {
|
|
561
623
|
return;
|
|
562
624
|
}
|
|
563
|
-
|
|
625
|
+
const target = this.targets[appIdKey]?.[pageIdKey];
|
|
626
|
+
return target && typeof target === 'string' ? target : undefined;
|
|
564
627
|
}
|
|
565
628
|
/**
|
|
566
|
-
*
|
|
567
|
-
*
|
|
568
|
-
*
|
|
569
|
-
*
|
|
629
|
+
* Selects a page within an application, setting up the Web Inspector session
|
|
630
|
+
* and waiting for the page to be initialized. Mimics the steps that Desktop
|
|
631
|
+
* Safari uses to initialize a Web Inspector session.
|
|
632
|
+
*
|
|
633
|
+
* @param appIdKey - The application identifier key.
|
|
634
|
+
* @param pageIdKey - The page identifier key.
|
|
635
|
+
* @param pageReadinessDetector - Optional detector for determining when the page is ready.
|
|
570
636
|
*/
|
|
571
637
|
async selectPage(appIdKey, pageIdKey, pageReadinessDetector) {
|
|
572
638
|
await this._pageSelectionLock.acquire(toPageSelectionKey(appIdKey, pageIdKey), async () => {
|
|
@@ -598,20 +664,18 @@ class RpcClient {
|
|
|
598
664
|
const msLeft = Math.max(timeoutMs - Math.trunc(timer.getDuration().asMilliSeconds), 1000);
|
|
599
665
|
logger_1.log.debug(`Waiting up to ${msLeft}ms for page '${pageIdKey}' of app '${appIdKey}' to be selected`);
|
|
600
666
|
await new Promise((resolve) => {
|
|
601
|
-
const onPageInitialized = (
|
|
602
|
-
/** @type {import("../types").AppIdKey} */ notifiedAppIdKey,
|
|
603
|
-
/** @type {import("../types").PageIdKey} */ notifiedPageIdKey) => {
|
|
667
|
+
const onPageInitialized = (notifiedAppIdKey, notifiedPageIdKey) => {
|
|
604
668
|
const timeoutHandler = setTimeout(() => {
|
|
605
669
|
this._pageSelectionMonitor.off(ON_PAGE_INITIALIZED_EVENT, onPageInitialized);
|
|
606
670
|
logger_1.log.warn(`Page '${pageIdKey}' for app '${appIdKey}' has not been selected ` +
|
|
607
671
|
`within ${timer.getDuration().asMilliSeconds}ms. Continuing anyway`);
|
|
608
|
-
resolve(
|
|
672
|
+
resolve();
|
|
609
673
|
}, msLeft);
|
|
610
674
|
if (notifiedAppIdKey === appIdKey && notifiedPageIdKey === pageIdKey) {
|
|
611
675
|
clearTimeout(timeoutHandler);
|
|
612
676
|
this._pageSelectionMonitor.off(ON_PAGE_INITIALIZED_EVENT, onPageInitialized);
|
|
613
677
|
logger_1.log.debug(`Selected the page ${pageIdKey}@${appIdKey} after ${timer.getDuration().asMilliSeconds}ms`);
|
|
614
|
-
resolve(
|
|
678
|
+
resolve();
|
|
615
679
|
}
|
|
616
680
|
else {
|
|
617
681
|
logger_1.log.debug(`Got notified that page ${notifiedPageIdKey}@${notifiedAppIdKey} is initialized, ` +
|
|
@@ -623,13 +687,15 @@ class RpcClient {
|
|
|
623
687
|
});
|
|
624
688
|
}
|
|
625
689
|
/**
|
|
626
|
-
*
|
|
627
|
-
*
|
|
690
|
+
* Initializes a page by enabling various Web Inspector domains.
|
|
691
|
+
* Can perform either simple or full initialization based on configuration.
|
|
692
|
+
* Mimics the steps that Desktop Safari Develop tools uses to initialize
|
|
693
|
+
* a Web Inspector session.
|
|
628
694
|
*
|
|
629
|
-
* @param
|
|
630
|
-
* @param
|
|
631
|
-
* @param
|
|
632
|
-
* @returns
|
|
695
|
+
* @param appIdKey - The application identifier key.
|
|
696
|
+
* @param pageIdKey - The page identifier key.
|
|
697
|
+
* @param targetId - Optional target ID. If not provided, will be retrieved from the targets map.
|
|
698
|
+
* @returns A promise that resolves to true if initialization succeeded, false otherwise.
|
|
633
699
|
*/
|
|
634
700
|
async _initializePage(appIdKey, pageIdKey, targetId) {
|
|
635
701
|
const sendOpts = {
|
|
@@ -725,7 +791,7 @@ class RpcClient {
|
|
|
725
791
|
try {
|
|
726
792
|
const res = await this.send(domain, opts);
|
|
727
793
|
if (domain === 'Console.getLoggingChannels') {
|
|
728
|
-
for (const source of (res?.channels || []).map((
|
|
794
|
+
for (const source of (res?.channels || []).map((entry) => entry.source)) {
|
|
729
795
|
try {
|
|
730
796
|
await this.send('Console.setLoggingChannelLevel', Object.assign({
|
|
731
797
|
source,
|
|
@@ -753,9 +819,13 @@ class RpcClient {
|
|
|
753
819
|
return true;
|
|
754
820
|
}
|
|
755
821
|
/**
|
|
822
|
+
* Connects to a specific application and returns its page dictionary.
|
|
756
823
|
*
|
|
757
|
-
* @param
|
|
758
|
-
* @returns
|
|
824
|
+
* @param appIdKey - The application identifier key to connect to.
|
|
825
|
+
* @returns A promise that resolves to a tuple containing the connected app ID key
|
|
826
|
+
* and the page dictionary.
|
|
827
|
+
* @throws Error if a new application connects during the process or if the page
|
|
828
|
+
* dictionary is empty.
|
|
759
829
|
*/
|
|
760
830
|
async selectApp(appIdKey) {
|
|
761
831
|
return await new bluebird_1.default((resolve, reject) => {
|
|
@@ -777,7 +847,7 @@ class RpcClient {
|
|
|
777
847
|
}
|
|
778
848
|
reject(new Error(exports.NEW_APP_CONNECTED_ERROR));
|
|
779
849
|
};
|
|
780
|
-
this.messageHandler
|
|
850
|
+
this.messageHandler.prependOnceListener('_rpc_applicationConnected:', onAppChange);
|
|
781
851
|
// do the actual connecting to the app
|
|
782
852
|
(async () => {
|
|
783
853
|
try {
|
|
@@ -796,15 +866,16 @@ class RpcClient {
|
|
|
796
866
|
reject(err);
|
|
797
867
|
}
|
|
798
868
|
finally {
|
|
799
|
-
this.messageHandler
|
|
869
|
+
this.messageHandler.off('_rpc_applicationConnected:', onAppChange);
|
|
800
870
|
}
|
|
801
871
|
})();
|
|
802
872
|
});
|
|
803
873
|
}
|
|
804
874
|
/**
|
|
875
|
+
* Handles execution context creation events by storing the context ID.
|
|
805
876
|
*
|
|
806
|
-
* @param
|
|
807
|
-
* @param
|
|
877
|
+
* @param err - Error if one occurred, undefined otherwise.
|
|
878
|
+
* @param context - The execution context information.
|
|
808
879
|
*/
|
|
809
880
|
onExecutionContextCreated(err, context) {
|
|
810
881
|
// { id: 2, isPageContext: true, name: '', frameId: '0.1' }
|
|
@@ -813,27 +884,29 @@ class RpcClient {
|
|
|
813
884
|
this.contexts.push(context.id);
|
|
814
885
|
}
|
|
815
886
|
/**
|
|
816
|
-
*
|
|
887
|
+
* Handles garbage collection events by logging them.
|
|
888
|
+
* Garbage collection can affect operation timing.
|
|
817
889
|
*/
|
|
818
890
|
onGarbageCollected() {
|
|
819
|
-
// just want to log that this is happening, as it can affect
|
|
891
|
+
// just want to log that this is happening, as it can affect operation
|
|
820
892
|
logger_1.log.debug(`Web Inspector garbage collected`);
|
|
821
893
|
}
|
|
822
894
|
/**
|
|
895
|
+
* Handles script parsing events by logging script information.
|
|
823
896
|
*
|
|
824
|
-
* @param
|
|
825
|
-
* @param
|
|
897
|
+
* @param err - Error if one occurred, undefined otherwise.
|
|
898
|
+
* @param scriptInfo - Information about the parsed script.
|
|
826
899
|
*/
|
|
827
900
|
onScriptParsed(err, scriptInfo) {
|
|
828
901
|
// { scriptId: '13', url: '', startLine: 0, startColumn: 0, endLine: 82, endColumn: 3 }
|
|
829
902
|
logger_1.log.debug(`Script parsed: ${JSON.stringify(scriptInfo)}`);
|
|
830
903
|
}
|
|
831
904
|
/**
|
|
905
|
+
* Resumes a paused target.
|
|
832
906
|
*
|
|
833
|
-
* @param
|
|
834
|
-
* @param
|
|
835
|
-
* @param
|
|
836
|
-
* @returns {Promise<void>}
|
|
907
|
+
* @param appIdKey - The application identifier key.
|
|
908
|
+
* @param pageIdKey - The page identifier key.
|
|
909
|
+
* @param targetId - The target ID to resume.
|
|
837
910
|
*/
|
|
838
911
|
async _resumeTarget(appIdKey, pageIdKey, targetId) {
|
|
839
912
|
try {
|
|
@@ -849,12 +922,13 @@ class RpcClient {
|
|
|
849
922
|
}
|
|
850
923
|
}
|
|
851
924
|
/**
|
|
925
|
+
* Waits for a page to be ready by periodically checking the document readyState.
|
|
926
|
+
* Uses the provided readiness detector to determine when the page is ready.
|
|
852
927
|
*
|
|
853
|
-
* @param
|
|
854
|
-
* @param
|
|
855
|
-
* @param
|
|
856
|
-
* @param
|
|
857
|
-
* @returns {Promise<void>}
|
|
928
|
+
* @param appIdKey - The application identifier key.
|
|
929
|
+
* @param pageIdKey - The page identifier key.
|
|
930
|
+
* @param targetId - The target ID.
|
|
931
|
+
* @param pageReadinessDetector - The detector for determining page readiness.
|
|
858
932
|
*/
|
|
859
933
|
async _waitForPageReadiness(appIdKey, pageIdKey, targetId, pageReadinessDetector) {
|
|
860
934
|
if (!pageReadinessDetector) {
|
|
@@ -863,7 +937,6 @@ class RpcClient {
|
|
|
863
937
|
logger_1.log.debug(`Waiting up to ${pageReadinessDetector.timeoutMs}ms for page readiness`);
|
|
864
938
|
const timer = new support_1.timing.Timer().start();
|
|
865
939
|
while (pageReadinessDetector.timeoutMs - timer.getDuration().asMilliSeconds > 0) {
|
|
866
|
-
/** @type {string} */
|
|
867
940
|
let readyState;
|
|
868
941
|
try {
|
|
869
942
|
const commandTimeoutMs = Math.max(100, Math.trunc((pageReadinessDetector.timeoutMs - timer.getDuration().asMilliSeconds) * 0.8));
|
|
@@ -891,10 +964,12 @@ class RpcClient {
|
|
|
891
964
|
`${timer.getDuration().asMilliSeconds}ms. Continuing anyway`);
|
|
892
965
|
}
|
|
893
966
|
/**
|
|
967
|
+
* Waits for a page to be initialized by acquiring locks on both the page
|
|
968
|
+
* target lock and the page selection lock.
|
|
894
969
|
*
|
|
895
|
-
* @param
|
|
896
|
-
* @param
|
|
897
|
-
* @
|
|
970
|
+
* @param appIdKey - The application identifier key.
|
|
971
|
+
* @param pageIdKey - The page identifier key.
|
|
972
|
+
* @throws Error if no targets are found for the application.
|
|
898
973
|
*/
|
|
899
974
|
async waitForPage(appIdKey, pageIdKey) {
|
|
900
975
|
const appTargetsMap = this.targets[appIdKey];
|
|
@@ -913,14 +988,15 @@ class RpcClient {
|
|
|
913
988
|
}
|
|
914
989
|
}
|
|
915
990
|
/**
|
|
916
|
-
*
|
|
991
|
+
* Gets the pending target details if there is a pending request for the given app.
|
|
992
|
+
* Filters out non-page target types (e.g., 'frame').
|
|
917
993
|
*
|
|
918
|
-
* @param
|
|
919
|
-
* @param
|
|
920
|
-
* @returns
|
|
994
|
+
* @param appId - The application identifier key.
|
|
995
|
+
* @param targetInfo - Information about the target.
|
|
996
|
+
* @returns The pending page target details if there's a match, undefined otherwise.
|
|
921
997
|
*/
|
|
922
998
|
_getPendingPageTargetDetails(appId, targetInfo) {
|
|
923
|
-
const logInfo = (
|
|
999
|
+
const logInfo = (message) => void logger_1.log.info(`Skipping 'Target.targetCreated' event ${message} for app '${appId}': ${JSON.stringify(targetInfo)}`);
|
|
924
1000
|
if (!this._pendingTargetNotification) {
|
|
925
1001
|
return logInfo('with no pending request');
|
|
926
1002
|
}
|
|
@@ -938,45 +1014,13 @@ class RpcClient {
|
|
|
938
1014
|
}
|
|
939
1015
|
exports.RpcClient = RpcClient;
|
|
940
1016
|
/**
|
|
1017
|
+
* Creates a unique key for page selection based on app and page IDs.
|
|
941
1018
|
*
|
|
942
|
-
* @param
|
|
943
|
-
* @param
|
|
944
|
-
* @returns
|
|
1019
|
+
* @param appIdKey - The application identifier key.
|
|
1020
|
+
* @param pageIdKey - The page identifier key.
|
|
1021
|
+
* @returns A string key combining both identifiers.
|
|
945
1022
|
*/
|
|
946
1023
|
function toPageSelectionKey(appIdKey, pageIdKey) {
|
|
947
1024
|
return `${appIdKey}:${pageIdKey}`;
|
|
948
1025
|
}
|
|
949
|
-
exports.default = RpcClient;
|
|
950
|
-
/**
|
|
951
|
-
* @typedef {Object} RpcClientOptions
|
|
952
|
-
* @property {string} [bundleId]
|
|
953
|
-
* @property {string} [platformVersion='']
|
|
954
|
-
* @property {boolean} [isSafari=true]
|
|
955
|
-
* @property {boolean} [logAllCommunication=false]
|
|
956
|
-
* @property {boolean} [logAllCommunicationHexDump=false]
|
|
957
|
-
* @property {number} [webInspectorMaxFrameLength]
|
|
958
|
-
* @property {number} [socketChunkSize]
|
|
959
|
-
* @property {boolean} [fullPageInitialization=false]
|
|
960
|
-
* @property {number} [pageLoadTimeoutMs]
|
|
961
|
-
* @property {string} [udid]
|
|
962
|
-
* @property {number} [targetCreationTimeoutMs]
|
|
963
|
-
*/
|
|
964
|
-
/**
|
|
965
|
-
* @typedef {Object} PendingPageTargetDetails
|
|
966
|
-
* @property {import('../types').AppIdKey} appIdKey
|
|
967
|
-
* @property {import('../types').PageIdKey} pageIdKey
|
|
968
|
-
* @property {PageReadinessDetector | undefined} pageReadinessDetector
|
|
969
|
-
*/
|
|
970
|
-
/**
|
|
971
|
-
* @typedef {{[key: import('../types').PageIdKey]: import('../types').TargetId}} PageDict
|
|
972
|
-
*/
|
|
973
|
-
/**
|
|
974
|
-
* @typedef {PageDict & {provisional?: import('../types').ProvisionalTargetInfo, lock: AsyncLock}} PagesToTargets
|
|
975
|
-
* @typedef {{[key: import('../types').AppIdKey]: PagesToTargets}} AppToTargetsMap
|
|
976
|
-
*/
|
|
977
|
-
/**
|
|
978
|
-
* @typedef {Object} PageReadinessDetector
|
|
979
|
-
* @property {number} timeoutMs
|
|
980
|
-
* @property {(readyState: string) => boolean} readinessDetector
|
|
981
|
-
*/
|
|
982
1026
|
//# sourceMappingURL=rpc-client.js.map
|