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.
Files changed (45) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/build/lib/protocol/index.d.ts +13 -8
  3. package/build/lib/protocol/index.d.ts.map +1 -1
  4. package/build/lib/protocol/index.js +17 -12
  5. package/build/lib/protocol/index.js.map +1 -1
  6. package/build/lib/rpc/index.d.ts +2 -3
  7. package/build/lib/rpc/index.d.ts.map +1 -1
  8. package/build/lib/rpc/index.js +2 -2
  9. package/build/lib/rpc/index.js.map +1 -1
  10. package/build/lib/rpc/remote-messages.d.ts +62 -41
  11. package/build/lib/rpc/remote-messages.d.ts.map +1 -1
  12. package/build/lib/rpc/remote-messages.js +56 -41
  13. package/build/lib/rpc/remote-messages.js.map +1 -1
  14. package/build/lib/rpc/rpc-client-real-device.d.ts +26 -8
  15. package/build/lib/rpc/rpc-client-real-device.d.ts.map +1 -1
  16. package/build/lib/rpc/rpc-client-real-device.js +21 -16
  17. package/build/lib/rpc/rpc-client-real-device.js.map +1 -1
  18. package/build/lib/rpc/rpc-client-simulator.d.ts +36 -28
  19. package/build/lib/rpc/rpc-client-simulator.d.ts.map +1 -1
  20. package/build/lib/rpc/rpc-client-simulator.js +39 -36
  21. package/build/lib/rpc/rpc-client-simulator.js.map +1 -1
  22. package/build/lib/rpc/rpc-client.d.ts +278 -189
  23. package/build/lib/rpc/rpc-client.d.ts.map +1 -1
  24. package/build/lib/rpc/rpc-client.js +222 -178
  25. package/build/lib/rpc/rpc-client.js.map +1 -1
  26. package/build/lib/rpc/rpc-message-handler.d.ts +32 -39
  27. package/build/lib/rpc/rpc-message-handler.d.ts.map +1 -1
  28. package/build/lib/rpc/rpc-message-handler.js +39 -53
  29. package/build/lib/rpc/rpc-message-handler.js.map +1 -1
  30. package/build/lib/types.d.ts +28 -0
  31. package/build/lib/types.d.ts.map +1 -1
  32. package/build/test/functional/safari-e2e-specs.js +5 -1
  33. package/build/test/functional/safari-e2e-specs.js.map +1 -1
  34. package/build/tsconfig.tsbuildinfo +1 -1
  35. package/lib/protocol/{index.js → index.ts} +29 -17
  36. package/lib/rpc/index.ts +2 -0
  37. package/lib/rpc/{remote-messages.js → remote-messages.ts} +73 -59
  38. package/lib/rpc/rpc-client-real-device.ts +68 -0
  39. package/lib/rpc/{rpc-client-simulator.js → rpc-client-simulator.ts} +54 -57
  40. package/lib/rpc/{rpc-client.js → rpc-client.ts} +368 -284
  41. package/lib/rpc/{rpc-message-handler.js → rpc-message-handler.ts} +73 -64
  42. package/lib/types.ts +31 -0
  43. package/package.json +1 -1
  44. package/lib/rpc/index.js +0 -4
  45. package/lib/rpc/rpc-client-real-device.js +0 -64
@@ -8,6 +8,19 @@ import { util, timing } from '@appium/support';
8
8
  import { EventEmitter } from 'node:events';
9
9
  import AsyncLock from 'async-lock';
10
10
  import { convertJavascriptEvaluationResult } from '../utils';
11
+ import type { StringRecord } from '@appium/types';
12
+ import type {
13
+ AppIdKey,
14
+ PageIdKey,
15
+ TargetId,
16
+ TargetInfo,
17
+ ProvisionalTargetInfo,
18
+ RemoteCommandOpts,
19
+ RemoteCommand,
20
+ RawRemoteCommand,
21
+ RpcClientOptions,
22
+ RemoteCommandId,
23
+ } from '../types';
11
24
 
12
25
  const DATA_LOG_LENGTH = {length: 200};
13
26
  const MIN_WAIT_FOR_TARGET_TIMEOUT_MS = 30000;
@@ -24,76 +37,72 @@ export const NEW_APP_CONNECTED_ERROR = 'New application has connected';
24
37
  export const EMPTY_PAGE_DICTIONARY_ERROR = 'Empty page dictionary received';
25
38
  const ON_PAGE_INITIALIZED_EVENT = 'onPageInitialized';
26
39
 
40
+ /**
41
+ * Details about a pending page target notification.
42
+ */
43
+ interface PendingPageTargetDetails {
44
+ appIdKey: AppIdKey;
45
+ pageIdKey: PageIdKey;
46
+ pageReadinessDetector?: PageReadinessDetector;
47
+ }
27
48
 
28
- export class RpcClient {
29
- /** @type {RpcMessageHandler|undefined} */
30
- messageHandler;
31
-
32
- /** @type {RemoteMessages|undefined} */
33
- remoteMessages;
34
-
35
- /** @type {boolean} */
36
- connected;
37
-
38
- /** @type {boolean} */
39
- isSafari;
40
-
41
- /** @type {string} */
42
- connId;
43
-
44
- /** @type {string} */
45
- senderId;
46
-
47
- /** @type {number} */
48
- msgId;
49
-
50
- /** @type {string|undefined} */
51
- udid;
52
-
53
- /** @type {boolean|undefined} */
54
- logAllCommunication;
55
-
56
- /** @type {boolean|undefined} */
57
- logAllCommunicationHexDump;
58
-
59
- /** @type {number|undefined} */
60
- socketChunkSize;
61
-
62
- /** @type {number|undefined} */
63
- webInspectorMaxFrameLength;
64
-
65
- /** @type {boolean|undefined} */
66
- fullPageInitialization;
67
-
68
- /** @type {string|undefined} */
69
- bundleId;
70
-
71
- /** @type {number | undefined} */
72
- pageLoadTimeoutMs;
73
-
74
- /** @type {string} */
75
- platformVersion;
76
-
77
- /** @type {string[]} */
78
- _contexts;
79
-
80
- /** @type {AppToTargetsMap} */
81
- _targets;
49
+ /**
50
+ * Pages to targets mapping with optional provisional target info and lock.
51
+ */
52
+ interface PagesToTargets {
53
+ [key: string]: TargetId | ProvisionalTargetInfo | AsyncLock | undefined;
54
+ provisional?: ProvisionalTargetInfo;
55
+ lock: AsyncLock;
56
+ }
82
57
 
83
- /** @type {EventEmitter} */
84
- _targetSubscriptions;
58
+ /**
59
+ * Mapping of application IDs to their pages and targets.
60
+ */
61
+ type AppToTargetsMap = Record<AppIdKey, PagesToTargets>;
85
62
 
86
- /** @type {PendingPageTargetDetails | undefined} */
87
- _pendingTargetNotification;
63
+ /**
64
+ * Detector for determining when a page is ready.
65
+ */
66
+ interface PageReadinessDetector {
67
+ timeoutMs: number;
68
+ readinessDetector: (readyState: string) => boolean;
69
+ }
88
70
 
89
- /** @type {number} */
90
- _targetCreationTimeoutMs;
71
+ /**
72
+ * Base class for RPC clients that communicate with the Web Inspector.
73
+ * Provides functionality for managing targets, sending commands, and handling
74
+ * page initialization. Subclasses must implement device-specific connection logic.
75
+ */
76
+ export class RpcClient {
77
+ protected readonly messageHandler: RpcMessageHandler;
78
+ protected readonly remoteMessages: RemoteMessages;
79
+ protected connected: boolean;
80
+ protected readonly isSafari: boolean;
81
+ protected readonly connId: string;
82
+ protected readonly senderId: string;
83
+ protected msgId: number;
84
+ protected readonly udid?: string;
85
+ protected readonly logAllCommunication?: boolean;
86
+ protected readonly logAllCommunicationHexDump?: boolean;
87
+ protected readonly socketChunkSize?: number;
88
+ protected readonly webInspectorMaxFrameLength?: number;
89
+ protected readonly fullPageInitialization?: boolean;
90
+ protected readonly bundleId?: string;
91
+ protected readonly pageLoadTimeoutMs?: number;
92
+ protected readonly platformVersion: string;
93
+ protected readonly _contexts: number[];
94
+ protected readonly _targets: AppToTargetsMap;
95
+ protected readonly _targetSubscriptions: EventEmitter;
96
+ protected _pendingTargetNotification?: PendingPageTargetDetails;
97
+ protected readonly _targetCreationTimeoutMs: number;
98
+ protected readonly _provisionedPages: Set<PageIdKey>;
99
+ protected readonly _pageSelectionLock: AsyncLock;
100
+ protected readonly _pageSelectionMonitor: EventEmitter;
91
101
 
92
102
  /**
93
- *
94
- * @param {RpcClientOptions} [opts={}]
103
+ * @param opts - Options for configuring the RPC client.
95
104
  */
96
- constructor (opts = {}) {
105
+ constructor(opts: RpcClientOptions = {}) {
97
106
  const {
98
107
  bundleId,
99
108
  platformVersion = '',
@@ -110,7 +119,7 @@ export class RpcClient {
110
119
 
111
120
  this.isSafari = isSafari;
112
121
 
113
- this.isConnected = false;
122
+ this.connected = false;
114
123
  this.connId = util.uuidV4();
115
124
  this.senderId = util.uuidV4();
116
125
  this.msgId = 0;
@@ -148,82 +157,147 @@ export class RpcClient {
148
157
  }
149
158
 
150
159
  /**
151
- * @returns {string[]}
160
+ * Gets the list of execution context IDs.
161
+ *
162
+ * @returns Array of execution context IDs.
152
163
  */
153
- get contexts () {
164
+ get contexts(): number[] {
154
165
  return this._contexts;
155
166
  }
156
167
 
157
168
  /**
158
- * @returns {AppToTargetsMap}
169
+ * Gets the mapping of applications to their pages and targets.
170
+ *
171
+ * @returns The targets mapping structure.
159
172
  */
160
- get targets () {
173
+ get targets(): AppToTargetsMap {
161
174
  return this._targets;
162
175
  }
163
176
 
164
177
  /**
165
- * @returns {boolean}
178
+ * Gets whether the client is currently connected.
179
+ *
180
+ * @returns True if connected, false otherwise.
166
181
  */
167
- get isConnected () {
182
+ get isConnected(): boolean {
168
183
  return this.connected;
169
184
  }
170
185
 
171
186
  /**
172
- * @param {boolean} connected
187
+ * Sets the connection status.
188
+ *
189
+ * @param connected - The connection status to set.
173
190
  */
174
- set isConnected (connected) {
191
+ set isConnected(connected: boolean) {
175
192
  this.connected = !!connected;
176
193
  }
177
194
 
178
195
  /**
179
- * @returns {EventEmitter}
196
+ * Gets the event emitter for target subscriptions.
197
+ *
198
+ * @returns The target subscriptions event emitter.
180
199
  */
181
- get targetSubscriptions() {
200
+ get targetSubscriptions(): EventEmitter {
182
201
  return this._targetSubscriptions;
183
202
  }
184
203
 
185
204
  /**
205
+ * Registers an event listener on the message handler.
206
+ *
207
+ * Supported events include:
208
+ *
209
+ * **RPC-level events:**
210
+ * - `_rpc_reportSetup:` - Emitted when the debugger setup is reported
211
+ * - `_rpc_reportConnectedApplicationList:` - Emitted when the list of connected applications is reported
212
+ * - `_rpc_forwardGetListing:` - Emitted when an application sends a page listing
213
+ * - `_rpc_applicationConnected:` - Emitted when a new application connects
214
+ * - `_rpc_applicationDisconnected:` - Emitted when an application disconnects
215
+ * - `_rpc_applicationUpdated:` - Emitted when an application is updated
216
+ * - `_rpc_reportConnectedDriverList:` - Emitted when the list of connected drivers is reported
217
+ * - `_rpc_reportCurrentState:` - Emitted when the current state is reported
218
+ *
219
+ * **Target events:**
220
+ * - `Target.targetCreated` - Emitted when a new target is created (args: error, appIdKey, targetInfo)
221
+ * - `Target.targetDestroyed` - Emitted when a target is destroyed (args: error, appIdKey, targetInfo)
222
+ * - `Target.didCommitProvisionalTarget` - Emitted when a provisional target commits (args: error, appIdKey, provisionalTargetInfo)
186
223
  *
187
- * @param {string} event
188
- * @param {Function} listener
189
- * @returns {this}
224
+ * **Page events:**
225
+ * - `Page.frameStoppedLoading` - Emitted when a frame stops loading
226
+ * - `Page.frameNavigated` - Emitted when a frame navigates
227
+ * - `Page.frameDetached` - Emitted when a frame is detached
228
+ * - `Page.loadEventFired` - Emitted when the page load event fires
229
+ *
230
+ * **Runtime events:**
231
+ * - `Runtime.executionContextCreated` - Emitted when an execution context is created (args: error, context)
232
+ *
233
+ * **Console events:**
234
+ * - `Console.messageAdded` - Emitted when a console message is added (args: error, message)
235
+ * - `Console.messageRepeatCountUpdated` - Emitted when a console message repeat count is updated
236
+ * - `ConsoleEvent` - Aggregate event for all Console.* events (args: error, params, methodName)
237
+ *
238
+ * **Network events:**
239
+ * - `NetworkEvent` - Aggregate event for all Network.* events (args: error, params, methodName)
240
+ *
241
+ * **Timeline events:**
242
+ * - `Timeline.eventRecorded` - Emitted when a timeline event is recorded (args: error, record)
243
+ *
244
+ * **Heap events:**
245
+ * - `Heap.garbageCollected` - Emitted when garbage collection occurs
246
+ *
247
+ * **Message ID events:**
248
+ * - Any numeric string (message ID) - Emitted for command responses (args: error, result)
249
+ *
250
+ * @param event - The event name to listen for.
251
+ * @param listener - The listener function to call when the event is emitted.
252
+ * The listener receives (error, ...args) where error may be null/undefined.
253
+ * @returns This instance for method chaining.
190
254
  */
191
- on (event, listener) {
192
- // @ts-ignore messageHandler must be defined here
255
+ on(event: string, listener: (...args: any[]) => void): this {
193
256
  this.messageHandler.on(event, listener);
194
257
  return this;
195
258
  }
196
259
 
197
260
  /**
261
+ * Registers a one-time event listener on the message handler.
262
+ * The listener will be automatically removed after being called once.
263
+ *
264
+ * See {@link RpcClient.on} for a list of supported events.
198
265
  *
199
- * @param {string} event
200
- * @param {Function} listener
201
- * @returns {this}
266
+ * @param event - The event name to listen for.
267
+ * @param listener - The listener function to call when the event is emitted.
268
+ * The listener receives (error, ...args) where error may be null/undefined.
269
+ * @returns This instance for method chaining.
202
270
  */
203
- once (event, listener) {
204
- // @ts-ignore messageHandler must be defined here
271
+ once(event: string, listener: (...args: any[]) => void): this {
205
272
  this.messageHandler.once(event, listener);
206
273
  return this;
207
274
  }
208
275
 
209
276
  /**
210
- * @param {string} event
211
- * @param {Function} listener
212
- * @returns {this}
277
+ * Removes an event listener from the message handler.
278
+ *
279
+ * See {@link RpcClient.on} for a list of supported events.
280
+ *
281
+ * @param event - The event name to stop listening for.
282
+ * @param listener - The listener function to remove.
283
+ * @returns This instance for method chaining.
213
284
  */
214
- off (event, listener) {
215
- // @ts-ignore messageHandler must be defined here
285
+ off(event: string, listener: (...args: any[]) => void): this {
216
286
  this.messageHandler.off(event, listener);
217
287
  return this;
218
288
  }
219
289
 
220
290
  /**
291
+ * Waits for a target to be created for the specified app and page.
292
+ * If the target already exists, returns it immediately. Otherwise,
293
+ * waits up to the configured timeout for the target to be created.
221
294
  *
222
- * @param {import('../types').AppIdKey} appIdKey
223
- * @param {import('../types').PageIdKey} pageIdKey
224
- * @returns {Promise<import('../types').TargetId | undefined>}
295
+ * @param appIdKey - The application identifier key.
296
+ * @param pageIdKey - The page identifier key.
297
+ * @returns A promise that resolves to the target ID if found, undefined otherwise.
298
+ * @throws Error if no target is found after the timeout.
225
299
  */
226
- async waitForTarget (appIdKey, pageIdKey) {
300
+ async waitForTarget(appIdKey: AppIdKey, pageIdKey: PageIdKey): Promise<TargetId | undefined> {
227
301
  let target = this.getTarget(appIdKey, pageIdKey);
228
302
  if (target) {
229
303
  log.debug(
@@ -247,7 +321,7 @@ export class RpcClient {
247
321
  intervalMs: WAIT_FOR_TARGET_INTERVAL_MS,
248
322
  });
249
323
  return target;
250
- } catch (err) {
324
+ } catch (err: any) {
251
325
  if (!err.message.includes('Condition unmet')) {
252
326
  throw err;
253
327
  }
@@ -258,17 +332,20 @@ export class RpcClient {
258
332
  }
259
333
 
260
334
  /**
335
+ * Sends a command to the remote debugger with automatic retry logic
336
+ * for target-related errors. Handles cases where targets are not yet
337
+ * available or not supported.
261
338
  *
262
- * @param {string} command
263
- * @param {import('../types').RemoteCommandOpts} opts
264
- * @param {boolean} [waitForResponse=true]
265
- * @returns {Promise<any>}
339
+ * @param command - The command name to send.
340
+ * @param opts - Options for the command.
341
+ * @param waitForResponse - Whether to wait for a response. Defaults to true.
342
+ * @returns A promise that resolves to the command result or options.
266
343
  */
267
- async send (command, opts, waitForResponse = true) {
344
+ async send(command: string, opts: RemoteCommandOpts, waitForResponse: boolean = true): Promise<any> {
268
345
  const timer = new timing.Timer().start();
269
346
  try {
270
347
  return await this.sendToDevice(command, opts, waitForResponse);
271
- } catch (err) {
348
+ } catch (err: any) {
272
349
  const {
273
350
  appIdKey,
274
351
  pageIdKey
@@ -277,7 +354,7 @@ export class RpcClient {
277
354
  if (messageLc.includes(NO_TARGET_SUPPORTED_ERROR)) {
278
355
  return await this.sendToDevice(command, opts, waitForResponse);
279
356
  } else if (appIdKey && NO_TARGET_PRESENT_YET_ERRORS.some((error) => messageLc.includes(error))) {
280
- await this.waitForTarget(appIdKey, /** @type {import('../types').PageIdKey} */ (pageIdKey));
357
+ await this.waitForTarget(appIdKey, pageIdKey as PageIdKey);
281
358
  return await this.sendToDevice(command, opts, waitForResponse);
282
359
  }
283
360
  throw err;
@@ -287,16 +364,23 @@ export class RpcClient {
287
364
  }
288
365
 
289
366
  /**
367
+ * Sends a command directly to the device, handling message routing,
368
+ * response waiting, and error handling.
290
369
  *
291
- * @template {boolean} TWaitForResponse
292
- * @param {string} command
293
- * @param {import('../types').RemoteCommandOpts} opts
294
- * @param {TWaitForResponse} [waitForResponse=true]
295
- * @returns {Promise<TWaitForResponse extends true ? import('../types').RemoteCommandOpts : any>}
370
+ * @template TWaitForResponse - Whether to wait for a response.
371
+ * @param command - The command name to send.
372
+ * @param opts - Options for the command.
373
+ * @param waitForResponse - Whether to wait for a response. Defaults to true.
374
+ * @returns A promise that resolves based on waitForResponse:
375
+ * - If true: resolves to the response value
376
+ * - If false: resolves to the full options object
296
377
  */
297
- // @ts-ignore Compiler issue
298
- async sendToDevice (command, opts, waitForResponse = true) {
299
- return await new B(async (resolve, reject) => {
378
+ async sendToDevice<TWaitForResponse extends boolean = true>(
379
+ command: string,
380
+ opts: RemoteCommandOpts,
381
+ waitForResponse: TWaitForResponse = true as TWaitForResponse
382
+ ): Promise<TWaitForResponse extends true ? any : RemoteCommandOpts> {
383
+ return await new B<any>(async (resolve, reject) => {
300
384
  // promise to be resolved whenever remote debugger
301
385
  // replies to our request
302
386
 
@@ -305,8 +389,7 @@ export class RpcClient {
305
389
  // for target-base communication, everything is wrapped up
306
390
  const wrapperMsgId = this.msgId++;
307
391
  // acknowledge wrapper message
308
- // @ts-ignore messageHandler must be defined
309
- this.messageHandler.on(wrapperMsgId.toString(), function (err) {
392
+ this.messageHandler.on(wrapperMsgId.toString(), function (err: Error | null) {
310
393
  if (err) {
311
394
  reject(err);
312
395
  }
@@ -317,38 +400,34 @@ export class RpcClient {
317
400
  const targetId = opts.targetId ?? this.getTarget(appIdKey, pageIdKey);
318
401
 
319
402
  // retrieve the correct command to send
320
- /** @type {import('../types').RemoteCommandOpts} */
321
- const fullOpts = _.defaults({
403
+ const fullOpts: RemoteCommandOpts & RemoteCommandId = _.defaults({
322
404
  connId: this.connId,
323
405
  senderId: this.senderId,
324
406
  targetId,
325
- id: msgId,
407
+ id: msgId.toString(),
326
408
  }, opts);
327
- /** @type {import('../types').RawRemoteCommand} */
328
- let cmd;
409
+ let cmd: RawRemoteCommand;
329
410
  try {
330
- // @ts-ignore remoteMessages must be defined
331
411
  cmd = this.remoteMessages.getRemoteCommand(command, fullOpts);
332
- } catch (err) {
412
+ } catch (err: any) {
333
413
  log.error(err);
334
414
  return reject(err);
335
415
  }
336
416
 
337
- /** @type {import('../types').RemoteCommand} */
338
- const finalCommand = {
339
- __argument: _.omit(cmd.__argument, ['WIRSocketDataKey']),
417
+ const finalCommand: RemoteCommand = {
418
+ __argument: _.omit(cmd.__argument, ['WIRSocketDataKey']) as any,
340
419
  __selector: cmd.__selector,
341
420
  };
342
421
 
343
422
  const hasSocketData = _.isPlainObject(cmd.__argument?.WIRSocketDataKey);
344
423
  if (hasSocketData) {
345
424
  // make sure the message being sent has all the information that is needed
346
- // @ts-ignore We have asserted it's a plain object above
347
- if (_.isNil(cmd.__argument.WIRSocketDataKey.id)) {
348
- // @ts-ignore We have already asserted it's a plain object above
349
- cmd.__argument.WIRSocketDataKey.id = wrapperMsgId;
425
+ const socketData = cmd.__argument.WIRSocketDataKey as StringRecord;
426
+ if (!_.isInteger(socketData.id)) {
427
+ // ! This must be a number
428
+ socketData.id = wrapperMsgId;
350
429
  }
351
- finalCommand.__argument.WIRSocketDataKey = Buffer.from(JSON.stringify(cmd.__argument.WIRSocketDataKey));
430
+ finalCommand.__argument.WIRSocketDataKey = Buffer.from(JSON.stringify(socketData));
352
431
  }
353
432
 
354
433
  let messageHandled = true;
@@ -356,8 +435,7 @@ export class RpcClient {
356
435
  // the promise will be resolved as soon as the socket has been sent
357
436
  messageHandled = false;
358
437
  // do not log receipts
359
- // @ts-ignore messageHandler must be defined
360
- this.messageHandler.once(msgId.toString(), (err) => {
438
+ this.messageHandler.once(msgId.toString(), (err: Error | null) => {
361
439
  if (err) {
362
440
  // we are not waiting for this, and if it errors it is most likely
363
441
  // a protocol change. Log and check during testing
@@ -366,27 +444,23 @@ export class RpcClient {
366
444
  _.truncate(JSON.stringify(err), DATA_LOG_LENGTH)
367
445
  );
368
446
  // reject, though it is very rare that this will be triggered, since
369
- // the promise is resolved directlty after send. On the off chance,
447
+ // the promise is resolved directly after send. On the off chance,
370
448
  // though, it will alert of a protocol change.
371
449
  reject(err);
372
450
  }
373
451
  });
374
- // @ts-ignore messageHandler must be defined
375
- } else if (this.messageHandler.listeners(cmd.__selector).length) {
376
- // @ts-ignore messageHandler must be defined
377
- this.messageHandler.prependOnceListener(cmd.__selector, (err, ...args) => {
452
+ } else if (this.messageHandler.listenerCount(cmd.__selector)) {
453
+ this.messageHandler.prependOnceListener(cmd.__selector, (err: Error | null, ...args: any[]) => {
378
454
  if (err) {
379
455
  return reject(err);
380
456
  }
381
457
  log.debug(`Received response from send (id: ${msgId}): '${_.truncate(JSON.stringify(args), DATA_LOG_LENGTH)}'`);
382
- // @ts-ignore This is ok
383
458
  resolve(args);
384
459
  });
385
460
  } else if (hasSocketData) {
386
- // @ts-ignore messageHandler must be defined
387
- this.messageHandler.once(msgId.toString(), (err, value) => {
461
+ this.messageHandler.once(msgId.toString(), (err: Error | null, value: any) => {
388
462
  if (err) {
389
- return reject(new Error(`Remote debugger error with code '${err.code}': ${err.message}`));
463
+ return reject(new Error(`Remote debugger error with code '${(err as any).code}': ${err.message}`));
390
464
  }
391
465
  log.debug(`Received data response from send (id: ${msgId}): '${_.truncate(JSON.stringify(value), DATA_LOG_LENGTH)}'`);
392
466
  resolve(value);
@@ -407,8 +481,7 @@ export class RpcClient {
407
481
  if (!messageHandled) {
408
482
  // There are no handlers waiting for a response before resolving,
409
483
  // and no errors sending the message over the socket, so resolve
410
- // @ts-ignore This is ok
411
- resolve(fullOpts);
484
+ resolve(fullOpts as any);
412
485
  }
413
486
  } catch (err) {
414
487
  return reject(err);
@@ -416,40 +489,53 @@ export class RpcClient {
416
489
  });
417
490
  }
418
491
 
419
- async connect () {
492
+ /**
493
+ * Connects to the remote debugger. Must be implemented by subclasses.
494
+ *
495
+ * @throws Error indicating that subclasses must implement this method.
496
+ */
497
+ async connect(): Promise<void> {
420
498
  throw new Error(`Sub-classes need to implement a 'connect' function`);
421
499
  }
422
500
 
423
- async disconnect () {
424
- this.messageHandler?.removeAllListeners();
501
+ /**
502
+ * Disconnects from the remote debugger and cleans up event listeners.
503
+ */
504
+ async disconnect(): Promise<void> {
505
+ this.messageHandler.removeAllListeners();
425
506
  }
426
507
 
427
508
  /**
428
- * @param {import('../types').RemoteCommand} command
429
- * @returns {Promise<void>}
509
+ * Sends a message to the device. Must be implemented by subclasses.
510
+ *
511
+ * @param _command - The command to send.
512
+ * @throws Error indicating that subclasses must implement this method.
430
513
  */
431
514
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
432
- async sendMessage (command) {
515
+ async sendMessage(_command: RemoteCommand): Promise<void> {
433
516
  throw new Error(`Sub-classes need to implement a 'sendMessage' function`);
434
517
  }
435
518
 
436
519
  /**
437
- * @param {any} data
438
- * @returns {Promise<void>}
520
+ * Receives data from the device. Must be implemented by subclasses.
521
+ *
522
+ * @param _data - The data received from the device.
523
+ * @throws Error indicating that subclasses must implement this method.
439
524
  */
440
525
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
441
- async receive (data) {
526
+ async receive(_data: any): Promise<void> {
442
527
  throw new Error(`Sub-classes need to implement a 'receive' function`);
443
528
  }
444
529
 
445
530
  /**
531
+ * Handles the creation of a new target for an application and page.
532
+ * Initializes the page and waits for readiness if configured.
446
533
  *
447
- * @param {Error | undefined} err
448
- * @param {import('../types').AppIdKey} app
449
- * @param {import('../types').TargetInfo} targetInfo
450
- * @returns {Promise<void>}
534
+ * @param err - Error if one occurred, undefined otherwise.
535
+ * @param app - The application identifier key.
536
+ * @param targetInfo - Information about the created target.
451
537
  */
452
- async addTarget (err, app, targetInfo) {
538
+ async addTarget(err: Error | undefined, app: AppIdKey, targetInfo: TargetInfo): Promise<void> {
453
539
  if (_.isNil(targetInfo?.targetId)) {
454
540
  log.info(`Received 'Target.targetCreated' event for app '${app}' with no target. Skipping`);
455
541
  return;
@@ -468,11 +554,11 @@ export class RpcClient {
468
554
  if (!_.isPlainObject(this.targets[appIdKey])) {
469
555
  this.targets[appIdKey] = {
470
556
  lock: new AsyncLock({maxOccupationTime: this._targetCreationTimeoutMs}),
471
- };
557
+ } as PagesToTargets;
472
558
  }
473
559
  const timer = new timing.Timer().start();
474
560
 
475
- const adjustPageReadinessDetector = () => {
561
+ const adjustPageReadinessDetector = (): PageReadinessDetector | undefined => {
476
562
  if (!pageReadinessDetector) {
477
563
  return;
478
564
  }
@@ -516,7 +602,7 @@ export class RpcClient {
516
602
  );
517
603
  }
518
604
  });
519
- } catch (e) {
605
+ } catch (e: any) {
520
606
  log.warn(
521
607
  `Cannot complete the initialization of the provisional target '${targetInfo.targetId}' ` +
522
608
  `after ${timer.getDuration().asMilliSeconds}ms: ${e.message}`
@@ -527,8 +613,9 @@ export class RpcClient {
527
613
 
528
614
  log.debug(`Target created for app '${appIdKey}' and page '${pageIdKey}': ${JSON.stringify(targetInfo)}`);
529
615
  if (_.has(this.targets[appIdKey], pageIdKey)) {
616
+ const existingTarget = this.targets[appIdKey][pageIdKey] as TargetId;
530
617
  log.debug(
531
- `There is already a target for this app and page ('${this.targets[appIdKey][pageIdKey]}'). ` +
618
+ `There is already a target for this app and page ('${existingTarget}'). ` +
532
619
  `This might cause problems`
533
620
  );
534
621
  }
@@ -540,7 +627,7 @@ export class RpcClient {
540
627
  appIdKey,
541
628
  pageIdKey,
542
629
  });
543
- } catch (e) {
630
+ } catch (e: any) {
544
631
  log.debug(
545
632
  `Cannot setup pause on start for app '${appIdKey}' and page '${pageIdKey}': ${e.message}`
546
633
  );
@@ -567,7 +654,7 @@ export class RpcClient {
567
654
  );
568
655
  }
569
656
  });
570
- } catch (e) {
657
+ } catch (e: any) {
571
658
  log.warn(e.message);
572
659
  } finally {
573
660
  // Target creation is happening after provisioning,
@@ -578,13 +665,13 @@ export class RpcClient {
578
665
  }
579
666
 
580
667
  /**
668
+ * Handles updates to provisional targets when they commit.
581
669
  *
582
- * @param {Error | undefined} err
583
- * @param {import('../types').AppIdKey} app
584
- * @param {import('../types').ProvisionalTargetInfo} targetInfo
585
- * @returns {Promise<void>}
670
+ * @param err - Error if one occurred, undefined otherwise.
671
+ * @param app - The application identifier key.
672
+ * @param targetInfo - Information about the provisional target update.
586
673
  */
587
- async updateTarget (err, app, targetInfo) {
674
+ async updateTarget(err: Error | undefined, app: AppIdKey, targetInfo: ProvisionalTargetInfo): Promise<void> {
588
675
  const {
589
676
  oldTargetId,
590
677
  newTargetId,
@@ -605,13 +692,13 @@ export class RpcClient {
605
692
  }
606
693
 
607
694
  /**
695
+ * Handles the destruction of a target, including cleanup of provisional targets.
608
696
  *
609
- * @param {Error | undefined} err
610
- * @param {import('../types').AppIdKey} app
611
- * @param {import('../types').TargetInfo} targetInfo
612
- * @returns {Promise<void>}
697
+ * @param err - Error if one occurred, undefined otherwise.
698
+ * @param app - The application identifier key.
699
+ * @param targetInfo - Information about the destroyed target.
613
700
  */
614
- async removeTarget (err, app, targetInfo) {
701
+ async removeTarget(err: Error | undefined, app: AppIdKey, targetInfo: TargetInfo): Promise<void> {
615
702
  if (_.isNil(targetInfo?.targetId)) {
616
703
  log.debug(`Received 'Target.targetDestroyed' event with no target. Skipping`);
617
704
  return;
@@ -656,24 +743,34 @@ export class RpcClient {
656
743
  }
657
744
 
658
745
  /**
659
- * @param {import('../types').AppIdKey} [appIdKey]
660
- * @param {import('../types').PageIdKey} [pageIdKey]
661
- * @returns {string | undefined}
746
+ * Gets the target ID for a specific app and page combination.
747
+ *
748
+ * @param appIdKey - The application identifier key.
749
+ * @param pageIdKey - The page identifier key.
750
+ * @returns The target ID if found, undefined otherwise.
662
751
  */
663
- getTarget (appIdKey, pageIdKey) {
752
+ getTarget(appIdKey?: AppIdKey, pageIdKey?: PageIdKey): TargetId | undefined {
664
753
  if (!appIdKey || !pageIdKey) {
665
754
  return;
666
755
  }
667
- return this.targets[appIdKey]?.[pageIdKey];
756
+ const target = this.targets[appIdKey]?.[pageIdKey];
757
+ return target && typeof target === 'string' ? target : undefined;
668
758
  }
669
759
 
670
760
  /**
671
- * @param {import('../types').AppIdKey} appIdKey
672
- * @param {import('../types').PageIdKey} pageIdKey
673
- * @param {PageReadinessDetector} [pageReadinessDetector]
674
- * @returns {Promise<void>}
761
+ * Selects a page within an application, setting up the Web Inspector session
762
+ * and waiting for the page to be initialized. Mimics the steps that Desktop
763
+ * Safari uses to initialize a Web Inspector session.
764
+ *
765
+ * @param appIdKey - The application identifier key.
766
+ * @param pageIdKey - The page identifier key.
767
+ * @param pageReadinessDetector - Optional detector for determining when the page is ready.
675
768
  */
676
- async selectPage (appIdKey, pageIdKey, pageReadinessDetector) {
769
+ async selectPage(
770
+ appIdKey: AppIdKey,
771
+ pageIdKey: PageIdKey,
772
+ pageReadinessDetector?: PageReadinessDetector
773
+ ): Promise<void> {
677
774
  await this._pageSelectionLock.acquire(toPageSelectionKey(appIdKey, pageIdKey), async () => {
678
775
  this._pendingTargetNotification = {appIdKey, pageIdKey, pageReadinessDetector};
679
776
  this._provisionedPages.clear();
@@ -710,10 +807,10 @@ export class RpcClient {
710
807
  log.debug(
711
808
  `Waiting up to ${msLeft}ms for page '${pageIdKey}' of app '${appIdKey}' to be selected`
712
809
  );
713
- await new Promise((resolve) => {
810
+ await new Promise<void>((resolve) => {
714
811
  const onPageInitialized = (
715
- /** @type {import("../types").AppIdKey} */ notifiedAppIdKey,
716
- /** @type {import("../types").PageIdKey} */ notifiedPageIdKey
812
+ notifiedAppIdKey: AppIdKey,
813
+ notifiedPageIdKey: PageIdKey
717
814
  ) => {
718
815
  const timeoutHandler = setTimeout(() => {
719
816
  this._pageSelectionMonitor.off(ON_PAGE_INITIALIZED_EVENT, onPageInitialized);
@@ -721,7 +818,7 @@ export class RpcClient {
721
818
  `Page '${pageIdKey}' for app '${appIdKey}' has not been selected ` +
722
819
  `within ${timer.getDuration().asMilliSeconds}ms. Continuing anyway`
723
820
  );
724
- resolve(false);
821
+ resolve();
725
822
  }, msLeft);
726
823
 
727
824
  if (notifiedAppIdKey === appIdKey && notifiedPageIdKey === pageIdKey) {
@@ -730,7 +827,7 @@ export class RpcClient {
730
827
  log.debug(
731
828
  `Selected the page ${pageIdKey}@${appIdKey} after ${timer.getDuration().asMilliSeconds}ms`
732
829
  );
733
- resolve(true);
830
+ resolve();
734
831
  } else {
735
832
  log.debug(
736
833
  `Got notified that page ${notifiedPageIdKey}@${notifiedAppIdKey} is initialized, ` +
@@ -745,16 +842,22 @@ export class RpcClient {
745
842
  }
746
843
 
747
844
  /**
748
- * Mimic every step that Desktop Safari Develop tools uses to initialize a
749
- * Web Inspector session
845
+ * Initializes a page by enabling various Web Inspector domains.
846
+ * Can perform either simple or full initialization based on configuration.
847
+ * Mimics the steps that Desktop Safari Develop tools uses to initialize
848
+ * a Web Inspector session.
750
849
  *
751
- * @param {import('../types').AppIdKey} appIdKey
752
- * @param {import('../types').PageIdKey} pageIdKey
753
- * @param {import('../types').TargetId} [targetId]
754
- * @returns {Promise<boolean>}
850
+ * @param appIdKey - The application identifier key.
851
+ * @param pageIdKey - The page identifier key.
852
+ * @param targetId - Optional target ID. If not provided, will be retrieved from the targets map.
853
+ * @returns A promise that resolves to true if initialization succeeded, false otherwise.
755
854
  */
756
- async _initializePage (appIdKey, pageIdKey, targetId) {
757
- const sendOpts = {
855
+ private async _initializePage(
856
+ appIdKey: AppIdKey,
857
+ pageIdKey: PageIdKey,
858
+ targetId?: TargetId
859
+ ): Promise<boolean> {
860
+ const sendOpts: RemoteCommandOpts = {
758
861
  appIdKey,
759
862
  pageIdKey,
760
863
  targetId,
@@ -778,7 +881,7 @@ export class RpcClient {
778
881
  ]) {
779
882
  try {
780
883
  await this.send(domain, sendOpts);
781
- } catch (err) {
884
+ } catch (err: any) {
782
885
  log.info(`Cannot enable domain '${domain}' during initialization: ${err.message}`);
783
886
  if (MISSING_TARGET_ERROR_PATTERN.test(err.message)) {
784
887
  return false;
@@ -793,7 +896,7 @@ export class RpcClient {
793
896
  }
794
897
 
795
898
  // The sequence of commands here is important
796
- const domainsToOptsMap = {
899
+ const domainsToOptsMap: Record<string, RemoteCommandOpts> = {
797
900
  'Inspector.enable': sendOpts,
798
901
  'Page.enable': sendOpts,
799
902
  'Runtime.enable': sendOpts,
@@ -855,13 +958,13 @@ export class RpcClient {
855
958
  try {
856
959
  const res = await this.send(domain, opts);
857
960
  if (domain === 'Console.getLoggingChannels') {
858
- for (const source of (res?.channels || []).map((/** @type {{ source: any; }} */ entry) => entry.source)) {
961
+ for (const source of (res?.channels || []).map((entry: { source: any }) => entry.source)) {
859
962
  try {
860
963
  await this.send('Console.setLoggingChannelLevel', Object.assign({
861
964
  source,
862
965
  level: 'verbose',
863
966
  }, sendOpts));
864
- } catch (err) {
967
+ } catch (err: any) {
865
968
  log.info(`Cannot set logging channel level for '${source}': ${err.message}`);
866
969
  if (MISSING_TARGET_ERROR_PATTERN.test(err.message)) {
867
970
  return false;
@@ -869,7 +972,7 @@ export class RpcClient {
869
972
  }
870
973
  }
871
974
  }
872
- } catch (err) {
975
+ } catch (err: any) {
873
976
  log.info(`Cannot enable domain '${domain}' during full initialization: ${err.message}`);
874
977
  if (MISSING_TARGET_ERROR_PATTERN.test(err.message)) {
875
978
  return false;
@@ -884,16 +987,20 @@ export class RpcClient {
884
987
  }
885
988
 
886
989
  /**
990
+ * Connects to a specific application and returns its page dictionary.
887
991
  *
888
- * @param {import('../types').AppIdKey} appIdKey
889
- * @returns {Promise<[string, Record<string, any>]>}
992
+ * @param appIdKey - The application identifier key to connect to.
993
+ * @returns A promise that resolves to a tuple containing the connected app ID key
994
+ * and the page dictionary.
995
+ * @throws Error if a new application connects during the process or if the page
996
+ * dictionary is empty.
890
997
  */
891
- async selectApp (appIdKey) {
892
- return await new B((resolve, reject) => {
998
+ async selectApp(appIdKey: AppIdKey): Promise<[string, StringRecord]> {
999
+ return await new B<[string, StringRecord]>((resolve, reject) => {
893
1000
  // local callback, temporarily added as callback to
894
1001
  // `_rpc_applicationConnected:` remote debugger response
895
1002
  // to handle the initial connection
896
- const onAppChange = (err, dict) => {
1003
+ const onAppChange = (err: Error | null, dict: StringRecord) => {
897
1004
  if (err) {
898
1005
  return reject(err);
899
1006
  }
@@ -910,7 +1017,7 @@ export class RpcClient {
910
1017
 
911
1018
  reject(new Error(NEW_APP_CONNECTED_ERROR));
912
1019
  };
913
- this.messageHandler?.prependOnceListener('_rpc_applicationConnected:', onAppChange);
1020
+ this.messageHandler.prependOnceListener('_rpc_applicationConnected:', onAppChange);
914
1021
 
915
1022
  // do the actual connecting to the app
916
1023
  (async () => {
@@ -923,22 +1030,23 @@ export class RpcClient {
923
1030
  } else {
924
1031
  resolve([connectedAppIdKey, pageDict]);
925
1032
  }
926
- } catch (err) {
1033
+ } catch (err: any) {
927
1034
  log.warn(`Unable to connect to the app: ${err.message}`);
928
1035
  reject(err);
929
1036
  } finally {
930
- this.messageHandler?.off('_rpc_applicationConnected:', onAppChange);
1037
+ this.messageHandler.off('_rpc_applicationConnected:', onAppChange);
931
1038
  }
932
1039
  })();
933
1040
  });
934
1041
  }
935
1042
 
936
1043
  /**
1044
+ * Handles execution context creation events by storing the context ID.
937
1045
  *
938
- * @param {Error?} err
939
- * @param {Record<string, any>} context
1046
+ * @param err - Error if one occurred, undefined otherwise.
1047
+ * @param context - The execution context information.
940
1048
  */
941
- onExecutionContextCreated (err, context) {
1049
+ onExecutionContextCreated(err: Error | undefined, context: { id: number }): void {
942
1050
  // { id: 2, isPageContext: true, name: '', frameId: '0.1' }
943
1051
  // right now we have no way to map contexts to apps/pages
944
1052
  // so just store
@@ -946,31 +1054,33 @@ export class RpcClient {
946
1054
  }
947
1055
 
948
1056
  /**
949
- * @returns {void}
1057
+ * Handles garbage collection events by logging them.
1058
+ * Garbage collection can affect operation timing.
950
1059
  */
951
- onGarbageCollected () {
952
- // just want to log that this is happening, as it can affect opertion
1060
+ onGarbageCollected(): void {
1061
+ // just want to log that this is happening, as it can affect operation
953
1062
  log.debug(`Web Inspector garbage collected`);
954
1063
  }
955
1064
 
956
1065
  /**
1066
+ * Handles script parsing events by logging script information.
957
1067
  *
958
- * @param {Error?} err
959
- * @param {Record<string, any>} scriptInfo
1068
+ * @param err - Error if one occurred, undefined otherwise.
1069
+ * @param scriptInfo - Information about the parsed script.
960
1070
  */
961
- onScriptParsed (err, scriptInfo) {
1071
+ onScriptParsed(err: Error | undefined, scriptInfo: StringRecord): void {
962
1072
  // { scriptId: '13', url: '', startLine: 0, startColumn: 0, endLine: 82, endColumn: 3 }
963
1073
  log.debug(`Script parsed: ${JSON.stringify(scriptInfo)}`);
964
1074
  }
965
1075
 
966
1076
  /**
1077
+ * Resumes a paused target.
967
1078
  *
968
- * @param {import('../types').AppIdKey} appIdKey
969
- * @param {import('../types').PageIdKey} pageIdKey
970
- * @param {import('../types').TargetId} targetId
971
- * @returns {Promise<void>}
1079
+ * @param appIdKey - The application identifier key.
1080
+ * @param pageIdKey - The page identifier key.
1081
+ * @param targetId - The target ID to resume.
972
1082
  */
973
- async _resumeTarget (appIdKey, pageIdKey, targetId) {
1083
+ private async _resumeTarget(appIdKey: AppIdKey, pageIdKey: PageIdKey, targetId: TargetId): Promise<void> {
974
1084
  try {
975
1085
  await this.send('Target.resume', {
976
1086
  appIdKey,
@@ -978,20 +1088,26 @@ export class RpcClient {
978
1088
  targetId,
979
1089
  });
980
1090
  log.debug(`Successfully resumed the target ${targetId}@${appIdKey}`);
981
- } catch (e) {
1091
+ } catch (e: any) {
982
1092
  log.warn(`Could not resume the target ${targetId}@${appIdKey}: ${e.message}`);
983
1093
  }
984
1094
  }
985
1095
 
986
1096
  /**
1097
+ * Waits for a page to be ready by periodically checking the document readyState.
1098
+ * Uses the provided readiness detector to determine when the page is ready.
987
1099
  *
988
- * @param {import('../types').AppIdKey} appIdKey
989
- * @param {import('../types').PageIdKey} pageIdKey
990
- * @param {import('../types').TargetId} targetId
991
- * @param {PageReadinessDetector} [pageReadinessDetector]
992
- * @returns {Promise<void>}
1100
+ * @param appIdKey - The application identifier key.
1101
+ * @param pageIdKey - The page identifier key.
1102
+ * @param targetId - The target ID.
1103
+ * @param pageReadinessDetector - The detector for determining page readiness.
993
1104
  */
994
- async _waitForPageReadiness(appIdKey, pageIdKey, targetId, pageReadinessDetector) {
1105
+ private async _waitForPageReadiness(
1106
+ appIdKey: AppIdKey,
1107
+ pageIdKey: PageIdKey,
1108
+ targetId: TargetId,
1109
+ pageReadinessDetector?: PageReadinessDetector
1110
+ ): Promise<void> {
995
1111
  if (!pageReadinessDetector) {
996
1112
  return;
997
1113
  }
@@ -999,8 +1115,7 @@ export class RpcClient {
999
1115
  log.debug(`Waiting up to ${pageReadinessDetector.timeoutMs}ms for page readiness`);
1000
1116
  const timer = new timing.Timer().start();
1001
1117
  while (pageReadinessDetector.timeoutMs - timer.getDuration().asMilliSeconds > 0) {
1002
- /** @type {string} */
1003
- let readyState;
1118
+ let readyState: string;
1004
1119
  try {
1005
1120
  const commandTimeoutMs = Math.max(
1006
1121
  100,
@@ -1014,7 +1129,7 @@ export class RpcClient {
1014
1129
  targetId,
1015
1130
  })).timeout(commandTimeoutMs);
1016
1131
  readyState = convertJavascriptEvaluationResult(rawResult);
1017
- } catch (e) {
1132
+ } catch (e: any) {
1018
1133
  log.debug(`Cannot determine page readiness: ${e.message}`);
1019
1134
  continue;
1020
1135
  }
@@ -1034,12 +1149,14 @@ export class RpcClient {
1034
1149
  }
1035
1150
 
1036
1151
  /**
1152
+ * Waits for a page to be initialized by acquiring locks on both the page
1153
+ * target lock and the page selection lock.
1037
1154
  *
1038
- * @param {import('../types').AppIdKey} appIdKey
1039
- * @param {import('../types').PageIdKey} pageIdKey
1040
- * @returns {Promise<void>}
1155
+ * @param appIdKey - The application identifier key.
1156
+ * @param pageIdKey - The page identifier key.
1157
+ * @throws Error if no targets are found for the application.
1041
1158
  */
1042
- async waitForPage (appIdKey, pageIdKey) {
1159
+ async waitForPage(appIdKey: AppIdKey, pageIdKey: PageIdKey): Promise<void> {
1043
1160
  const appTargetsMap = this.targets[appIdKey];
1044
1161
  if (!appTargetsMap) {
1045
1162
  throw new Error(`No targets found for app '${appIdKey}'`);
@@ -1057,16 +1174,21 @@ export class RpcClient {
1057
1174
  }
1058
1175
 
1059
1176
  /**
1060
- * Get the pending target details if there is a pending request.
1177
+ * Gets the pending target details if there is a pending request for the given app.
1178
+ * Filters out non-page target types (e.g., 'frame').
1061
1179
  *
1062
- * @param {import('../types').AppIdKey} appId
1063
- * @param {import('../types').TargetInfo} targetInfo
1064
- * @returns {PendingPageTargetDetails | undefined}
1180
+ * @param appId - The application identifier key.
1181
+ * @param targetInfo - Information about the target.
1182
+ * @returns The pending page target details if there's a match, undefined otherwise.
1065
1183
  */
1066
- _getPendingPageTargetDetails(appId, targetInfo) {
1067
- const logInfo = (/** @type {string} */ message) => void log.info(
1068
- `Skipping 'Target.targetCreated' event ${message} for app '${appId}': ${JSON.stringify(targetInfo)}`
1069
- );
1184
+ private _getPendingPageTargetDetails(
1185
+ appId: AppIdKey,
1186
+ targetInfo: TargetInfo
1187
+ ): PendingPageTargetDetails | undefined {
1188
+ const logInfo = (message: string): undefined =>
1189
+ void log.info(
1190
+ `Skipping 'Target.targetCreated' event ${message} for app '${appId}': ${JSON.stringify(targetInfo)}`
1191
+ );
1070
1192
  if (!this._pendingTargetNotification) {
1071
1193
  return logInfo('with no pending request');
1072
1194
  }
@@ -1085,50 +1207,12 @@ export class RpcClient {
1085
1207
  }
1086
1208
 
1087
1209
  /**
1210
+ * Creates a unique key for page selection based on app and page IDs.
1088
1211
  *
1089
- * @param {import('../types').AppIdKey} appIdKey
1090
- * @param {import('../types').PageIdKey} pageIdKey
1091
- * @returns {string}
1212
+ * @param appIdKey - The application identifier key.
1213
+ * @param pageIdKey - The page identifier key.
1214
+ * @returns A string key combining both identifiers.
1092
1215
  */
1093
- function toPageSelectionKey(appIdKey, pageIdKey) {
1216
+ function toPageSelectionKey(appIdKey: AppIdKey, pageIdKey: PageIdKey): string {
1094
1217
  return `${appIdKey}:${pageIdKey}`;
1095
1218
  }
1096
-
1097
- export default RpcClient;
1098
-
1099
- /**
1100
- * @typedef {Object} RpcClientOptions
1101
- * @property {string} [bundleId]
1102
- * @property {string} [platformVersion='']
1103
- * @property {boolean} [isSafari=true]
1104
- * @property {boolean} [logAllCommunication=false]
1105
- * @property {boolean} [logAllCommunicationHexDump=false]
1106
- * @property {number} [webInspectorMaxFrameLength]
1107
- * @property {number} [socketChunkSize]
1108
- * @property {boolean} [fullPageInitialization=false]
1109
- * @property {number} [pageLoadTimeoutMs]
1110
- * @property {string} [udid]
1111
- * @property {number} [targetCreationTimeoutMs]
1112
- */
1113
-
1114
- /**
1115
- * @typedef {Object} PendingPageTargetDetails
1116
- * @property {import('../types').AppIdKey} appIdKey
1117
- * @property {import('../types').PageIdKey} pageIdKey
1118
- * @property {PageReadinessDetector | undefined} pageReadinessDetector
1119
- */
1120
-
1121
- /**
1122
- * @typedef {{[key: import('../types').PageIdKey]: import('../types').TargetId}} PageDict
1123
- */
1124
-
1125
- /**
1126
- * @typedef {PageDict & {provisional?: import('../types').ProvisionalTargetInfo, lock: AsyncLock}} PagesToTargets
1127
- * @typedef {{[key: import('../types').AppIdKey]: PagesToTargets}} AppToTargetsMap
1128
- */
1129
-
1130
- /**
1131
- * @typedef {Object} PageReadinessDetector
1132
- * @property {number} timeoutMs
1133
- * @property {(readyState: string) => boolean} readinessDetector
1134
- */