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
@@ -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.isConnected = false;
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
- * @returns {string[]}
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
- * @returns {AppToTargetsMap}
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
- * @returns {boolean}
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
- * @param {boolean} connected
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
- * @returns {EventEmitter}
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
- * @param {string} event
143
- * @param {Function} listener
144
- * @returns {this}
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
- * @param {string} event
154
- * @param {Function} listener
155
- * @returns {this}
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
- * @param {string} event
164
- * @param {Function} listener
165
- * @returns {this}
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 {import('../types').AppIdKey} appIdKey
175
- * @param {import('../types').PageIdKey} pageIdKey
176
- * @returns {Promise<import('../types').TargetId | undefined>}
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 {string} command
208
- * @param {import('../types').RemoteCommandOpts} opts
209
- * @param {boolean} [waitForResponse=true]
210
- * @returns {Promise<any>}
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, /** @type {import('../types').PageIdKey} */ (pageIdKey));
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 {boolean} TWaitForResponse
236
- * @param {string} command
237
- * @param {import('../types').RemoteCommandOpts} opts
238
- * @param {TWaitForResponse} [waitForResponse=true]
239
- * @returns {Promise<TWaitForResponse extends true ? import('../types').RemoteCommandOpts : any>}
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
- // @ts-ignore We have asserted it's a plain object above
287
- if (lodash_1.default.isNil(cmd.__argument.WIRSocketDataKey.id)) {
288
- // @ts-ignore We have already asserted it's a plain object above
289
- cmd.__argument.WIRSocketDataKey.id = wrapperMsgId;
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(cmd.__argument.WIRSocketDataKey));
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 directlty after send. On the off chance,
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.listeners(cmd.__selector).length) {
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?.removeAllListeners();
416
+ this.messageHandler.removeAllListeners();
363
417
  }
364
418
  /**
365
- * @param {import('../types').RemoteCommand} command
366
- * @returns {Promise<void>}
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(command) {
425
+ async sendMessage(_command) {
370
426
  throw new Error(`Sub-classes need to implement a 'sendMessage' function`);
371
427
  }
372
428
  /**
373
- * @param {any} data
374
- * @returns {Promise<void>}
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(data) {
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 {Error | undefined} err
383
- * @param {import('../types').AppIdKey} app
384
- * @param {import('../types').TargetInfo} targetInfo
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
- logger_1.log.debug(`There is already a target for this app and page ('${this.targets[appIdKey][pageIdKey]}'). ` +
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 {Error | undefined} err
496
- * @param {import('../types').AppIdKey} app
497
- * @param {import('../types').ProvisionalTargetInfo} targetInfo
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 {Error | undefined} err
517
- * @param {import('../types').AppIdKey} app
518
- * @param {import('../types').TargetInfo} targetInfo
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
- * @param {import('../types').AppIdKey} [appIdKey]
556
- * @param {import('../types').PageIdKey} [pageIdKey]
557
- * @returns {string | undefined}
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
- return this.targets[appIdKey]?.[pageIdKey];
625
+ const target = this.targets[appIdKey]?.[pageIdKey];
626
+ return target && typeof target === 'string' ? target : undefined;
564
627
  }
565
628
  /**
566
- * @param {import('../types').AppIdKey} appIdKey
567
- * @param {import('../types').PageIdKey} pageIdKey
568
- * @param {PageReadinessDetector} [pageReadinessDetector]
569
- * @returns {Promise<void>}
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(false);
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(true);
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
- * Mimic every step that Desktop Safari Develop tools uses to initialize a
627
- * Web Inspector session
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 {import('../types').AppIdKey} appIdKey
630
- * @param {import('../types').PageIdKey} pageIdKey
631
- * @param {import('../types').TargetId} [targetId]
632
- * @returns {Promise<boolean>}
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((/** @type {{ source: any; }} */ entry) => entry.source)) {
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 {import('../types').AppIdKey} appIdKey
758
- * @returns {Promise<[string, Record<string, any>]>}
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?.prependOnceListener('_rpc_applicationConnected:', onAppChange);
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?.off('_rpc_applicationConnected:', onAppChange);
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 {Error?} err
807
- * @param {Record<string, any>} context
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
- * @returns {void}
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 opertion
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 {Error?} err
825
- * @param {Record<string, any>} scriptInfo
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 {import('../types').AppIdKey} appIdKey
834
- * @param {import('../types').PageIdKey} pageIdKey
835
- * @param {import('../types').TargetId} targetId
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 {import('../types').AppIdKey} appIdKey
854
- * @param {import('../types').PageIdKey} pageIdKey
855
- * @param {import('../types').TargetId} targetId
856
- * @param {PageReadinessDetector} [pageReadinessDetector]
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 {import('../types').AppIdKey} appIdKey
896
- * @param {import('../types').PageIdKey} pageIdKey
897
- * @returns {Promise<void>}
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
- * Get the pending target details if there is a pending request.
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 {import('../types').AppIdKey} appId
919
- * @param {import('../types').TargetInfo} targetInfo
920
- * @returns {PendingPageTargetDetails | undefined}
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 = (/** @type {string} */ message) => void logger_1.log.info(`Skipping 'Target.targetCreated' event ${message} for app '${appId}': ${JSON.stringify(targetInfo)}`);
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 {import('../types').AppIdKey} appIdKey
943
- * @param {import('../types').PageIdKey} pageIdKey
944
- * @returns {string}
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