appium-ios-remotexpc 5.2.1 → 5.2.2

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 CHANGED
@@ -1,3 +1,9 @@
1
+ ## [5.2.2](https://github.com/appium/appium-ios-remotexpc/compare/v5.2.1...v5.2.2) (2026-06-24)
2
+
3
+ ### Miscellaneous Chores
4
+
5
+ * Add scripts to typescript checks ([#253](https://github.com/appium/appium-ios-remotexpc/issues/253)) ([f06a206](https://github.com/appium/appium-ios-remotexpc/commit/f06a2060a41f834ca049cb31d9f453c12d9fc013))
6
+
1
7
  ## [5.2.1](https://github.com/appium/appium-ios-remotexpc/compare/v5.2.0...v5.2.1) (2026-06-24)
2
8
 
3
9
  ### Miscellaneous Chores
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appium-ios-remotexpc",
3
- "version": "5.2.1",
3
+ "version": "5.2.2",
4
4
  "main": "build/src/index.js",
5
5
  "types": "build/src/index.d.ts",
6
6
  "type": "module",
@@ -16,6 +16,10 @@ export function startTimeoutProgressLogger({
16
16
  let timer = null;
17
17
  let isStopped = false;
18
18
 
19
+ /**
20
+ * @param {string} status
21
+ * @param {boolean} isComplete
22
+ */
19
23
  const logProgress = (status, isComplete = false) => {
20
24
  const elapsedMs = performance.now() - startedAt;
21
25
  const boundedElapsedMs = Math.min(elapsedMs, timeoutMs);
@@ -28,6 +32,10 @@ export function startTimeoutProgressLogger({
28
32
  );
29
33
  };
30
34
 
35
+ /**
36
+ * @param {string} status
37
+ * @param {boolean} isComplete
38
+ */
31
39
  const stop = (status, isComplete = false) => {
32
40
  if (isStopped) {
33
41
  return;
@@ -35,7 +35,7 @@ export async function refreshServiceCatalog(udid, entry, log) {
35
35
  * @template T
36
36
  * @param {object} opts
37
37
  * @param {import('appium-ios-remotexpc').TunnelRegistryServer | null} opts.registryServer
38
- * @param {T} opts.result
38
+ * @param {T & { tunnel: { Address: string, RsdPort?: number } }} opts.result
39
39
  * @param {(result: T) => string} opts.getUdid
40
40
  * @param {(result: T, existing: import('appium-ios-remotexpc').TunnelRegistryEntry | undefined, now: number) => import('appium-ios-remotexpc').TunnelRegistryEntry} opts.buildEntry
41
41
  * @param {{info: (message: string) => void, warn: (message: string) => void}} opts.log
@@ -20,12 +20,23 @@ const log = logger.getLogger('AppleTVPairing');
20
20
  const APPLETV_PAIRING_DISCOVERY_PROGRESS_INTERVAL_MS = 1000;
21
21
  const APPLETV_PAIRING_DISCOVERY_PROGRESS_BAR_WIDTH = 24;
22
22
 
23
+ /**
24
+ * @param {import('appium-ios-remotexpc').AppleTVPairingService} pairingService
25
+ * @param {number} timeoutMs
26
+ * @returns {{ startedAt: number, promise: Promise<AppleTVDevice[]> }}
27
+ */
23
28
  function discoverAppleTVPairingDevices(pairingService, timeoutMs) {
24
29
  const startedAt = performance.now();
25
30
  const promise = pairingService.discoverDevices({ timeoutMs });
26
31
  return { startedAt, promise };
27
32
  }
28
33
 
34
+ /**
35
+ *
36
+ * @param {{ startedAt: number, promise: Promise<AppleTVDevice[]> }} discovery
37
+ * @param {number} timeoutMs
38
+ * @returns {Promise<AppleTVDevice[]>}
39
+ */
29
40
  async function waitForAppleTVPairingDiscovery(discovery, timeoutMs) {
30
41
  const progress = startTimeoutProgressLogger({
31
42
  log,
@@ -48,6 +59,12 @@ async function waitForAppleTVPairingDiscovery(discovery, timeoutMs) {
48
59
  }
49
60
  }
50
61
 
62
+ /**
63
+ * @param {import('appium-ios-remotexpc').AppleTVPairingService} pairingService
64
+ * @param {string} deviceSelector
65
+ * @param {number} discoveryTimeoutMs
66
+ * @returns {Promise<{ success: boolean, deviceId: string, pairingFile?: string, error?: Error | null }>}
67
+ */
51
68
  async function discoverAndPairWithProgress(
52
69
  pairingService,
53
70
  deviceSelector,
@@ -124,3 +141,7 @@ async function main() {
124
141
  }
125
142
 
126
143
  await main();
144
+
145
+ /**
146
+ * @typedef {import('appium-ios-remotexpc').AppleTVDevice} AppleTVDevice
147
+ */
@@ -39,6 +39,7 @@ const APPLETV_TUNNEL_DISCOVERY_PROGRESS_BAR_WIDTH = 24;
39
39
  /** @type {import('appium-ios-remotexpc').TunnelRegistryServer | null} */
40
40
  let registryServer = null;
41
41
 
42
+ /** @type {(() => void)[]} */
42
43
  const registryWatcherStops = [];
43
44
  /** @type {Map<string, Promise<void>>} */
44
45
  const reconnectingByUdid = new Map();
@@ -48,6 +49,10 @@ const establishedTunnelsByUdid = new Map();
48
49
  /** @type {Map<string, () => void>} */
49
50
  const lifecycleWatchStopByUdid = new Map();
50
51
 
52
+ /**
53
+ * @param {import('appium-ios-tuntap').TunnelConnection} tunnelConnection
54
+ * @returns {Promise<void>}
55
+ */
51
56
  async function closeTunnelQuietly(tunnelConnection) {
52
57
  try {
53
58
  await tunnelConnection.closer();
@@ -56,6 +61,10 @@ async function closeTunnelQuietly(tunnelConnection) {
56
61
  }
57
62
  }
58
63
 
64
+ /**
65
+ * @param {string} udid
66
+ * @returns {void}
67
+ */
59
68
  function stopLifecycleWatch(udid) {
60
69
  const stop = lifecycleWatchStopByUdid.get(udid);
61
70
  if (stop) {
@@ -68,7 +77,7 @@ function stopLifecycleWatch(udid) {
68
77
  *
69
78
  * @param {import('appium-ios-remotexpc').AppleTVTunnelService} tunnelService
70
79
  * @param {number} timeoutMs
71
- * @returns {Promise<number, Promise<AppleTVDevice[]>>}
80
+ * @returns {{ startedAt: number, promise: Promise<AppleTVDevice[]> }}
72
81
  */
73
82
  function startDevicesDiscovery(tunnelService, timeoutMs) {
74
83
  const startedAt = performance.now();
@@ -78,7 +87,7 @@ function startDevicesDiscovery(tunnelService, timeoutMs) {
78
87
 
79
88
  /**
80
89
  *
81
- * @param {Promise<AppleTVDevice[]>} discovery
90
+ * @param {{ startedAt: number, promise: Promise<AppleTVDevice[]> }} discovery
82
91
  * @param {number} timeoutMs
83
92
  * @returns {Promise<AppleTVDevice[]>}
84
93
  */
@@ -109,9 +118,13 @@ async function waitForDevicesDiscovery(discovery, timeoutMs) {
109
118
  * @returns {boolean}
110
119
  */
111
120
  function isNoDevicesFoundError(err) {
112
- return err instanceof Error && ('code' in err && err.code === 'NO_DEVICES') || err.message?.includes('No devices found');
121
+ return err instanceof Error && ('code' in err && err.code === 'NO_DEVICES') || /** @type {Error} */ (err).message?.includes('No devices found');
113
122
  }
114
123
 
124
+ /**
125
+ * @param {AppleTvEstablishedTunnel} result
126
+ * @returns {void}
127
+ */
115
128
  function registerEstablishedTunnel(result) {
116
129
  const udid = result.device.identifier;
117
130
  stopLifecycleWatch(udid);
@@ -125,11 +138,15 @@ function registerEstablishedTunnel(result) {
125
138
  }
126
139
  establishedTunnelsByUdid.set(udid, result);
127
140
  }
141
+
142
+ /** @type {import('appium-ios-remotexpc').AppleTVTunnelService | null} */
128
143
  let tunnelService = null;
129
144
 
130
145
  /**
131
- * @param {object} registry
146
+ * @param {import('appium-ios-remotexpc').TunnelRegistry} registry
132
147
  * @param {AppleTvEstablishedTunnel[]} successfulResults
148
+ * @param {object} callbacks
149
+ * @param {function({ udid: string, address: string }): Promise<void>} [callbacks.onTunnelDead]
133
150
  */
134
151
  function attachAppleTvTunnelRegistryLifecycleWatch(registry, successfulResults, callbacks = {}) {
135
152
  for (const result of successfulResults) {
@@ -163,7 +180,14 @@ function attachAppleTvTunnelRegistryLifecycleWatch(registry, successfulResults,
163
180
  );
164
181
  }
165
182
 
183
+ /**
184
+ * @returns {void}
185
+ */
166
186
  function setupCleanupHandlers() {
187
+ /**
188
+ * @param {string} signal
189
+ * @returns {Promise<void>}
190
+ */
167
191
  const cleanup = async (signal) => {
168
192
  log.warn(`\nCleaning up (${signal})...`);
169
193
 
@@ -230,6 +254,7 @@ function setupCleanupHandlers() {
230
254
  */
231
255
  async function establishOneTunnel(startResult) {
232
256
  const { tcpSocket, psk, device } = startResult;
257
+ /** @type {{ notify: ((reason: string) => void) | null }} */
233
258
  const lifecycle = { notify: null };
234
259
 
235
260
  const tunnelConnection = await TunnelManager.getTunnelPsk(
@@ -247,6 +272,10 @@ async function establishOneTunnel(startResult) {
247
272
  RsdPort: tunnelConnection.RsdPort,
248
273
  },
249
274
  tunnelConnection,
275
+ /**
276
+ * @param {(reason: string) => void} handler
277
+ * @returns {void}
278
+ */
250
279
  registerOnDead: (handler) => {
251
280
  lifecycle.notify = handler;
252
281
  },
@@ -256,6 +285,12 @@ async function establishOneTunnel(startResult) {
256
285
  return result;
257
286
  }
258
287
 
288
+ /**
289
+ * @param {AppleTvEstablishedTunnel} result
290
+ * @param {import('appium-ios-remotexpc').TunnelRegistryEntry | undefined} existing
291
+ * @param {number} now
292
+ * @returns {import('appium-ios-remotexpc').TunnelRegistryEntry}
293
+ */
259
294
  function buildAppleTvTunnelEntry(result, existing, now) {
260
295
  const udid = result.device.identifier;
261
296
  const entry = {
@@ -272,6 +307,11 @@ function buildAppleTvTunnelEntry(result, existing, now) {
272
307
  return entry;
273
308
  }
274
309
 
310
+ /**
311
+ *
312
+ * @param {AppleTvEstablishedTunnel} result
313
+ * @returns {Promise<boolean>}
314
+ */
275
315
  async function publishDiscoveredTunnelEntry(result) {
276
316
  return await publishTunnelRegistryEntry({
277
317
  registryServer,
@@ -282,6 +322,16 @@ async function publishDiscoveredTunnelEntry(result) {
282
322
  });
283
323
  }
284
324
 
325
+ /**
326
+ *
327
+ * @param {object} opts
328
+ * @param {string} opts.udid
329
+ * @param {number} opts.maxRetries
330
+ * @param {import('appium-ios-remotexpc').AppleTVTunnelService} opts.tunnelService
331
+ * @param {function(string): Promise<void>} opts.reconnectTunnelByUdid
332
+ * @param {number} opts.discoveryTimeoutMs
333
+ * @returns {Promise<void>}
334
+ */
285
335
  async function runAppleTvReconnectAttempts({
286
336
  udid,
287
337
  maxRetries,
@@ -327,6 +377,14 @@ async function runAppleTvReconnectAttempts({
327
377
  log.error(`Reconnect retries exhausted for ${udid}`);
328
378
  }
329
379
 
380
+ /**
381
+ *
382
+ * @param {object} opts
383
+ * @param {number} opts.reconnectRetries
384
+ * @param {import('appium-ios-remotexpc').AppleTVTunnelService} opts.tunnelService
385
+ * @param {number} opts.discoveryTimeoutMs
386
+ * @returns {function(string): Promise<void>}
387
+ */
330
388
  function createAppleTvReconnectTunnelByUdid({
331
389
  reconnectRetries,
332
390
  tunnelService,
@@ -565,3 +623,7 @@ await main();
565
623
  * @property {import('appium-ios-tuntap').TunnelConnection} tunnelConnection
566
624
  * @property {(handler: (reason: string) => void) => void} registerOnDead
567
625
  */
626
+
627
+ /**
628
+ * @typedef {import('appium-ios-remotexpc').AppleTVDevice} AppleTVDevice
629
+ */
@@ -40,6 +40,10 @@ const establishedTunnelsByUdid = new Map();
40
40
  /** @type {Map<string, () => void>} */
41
41
  const lifecycleWatchStopByUdid = new Map();
42
42
 
43
+ /**
44
+ * @param {import('appium-ios-tuntap').TunnelConnection} tunnelConnection
45
+ * @returns {Promise<void>}
46
+ */
43
47
  async function closeTunnelQuietly(tunnelConnection) {
44
48
  try {
45
49
  await tunnelConnection.closer();
@@ -48,6 +52,10 @@ async function closeTunnelQuietly(tunnelConnection) {
48
52
  }
49
53
  }
50
54
 
55
+ /**
56
+ * @param {string} udid
57
+ * @returns {void}
58
+ */
51
59
  function stopLifecycleWatch(udid) {
52
60
  const stop = lifecycleWatchStopByUdid.get(udid);
53
61
  if (stop) {
@@ -56,6 +64,10 @@ function stopLifecycleWatch(udid) {
56
64
  }
57
65
  }
58
66
 
67
+ /**
68
+ * @param {TunnelCreationSuccessResult} result
69
+ * @returns {void}
70
+ */
59
71
  function registerEstablishedTunnel(result) {
60
72
  const udid = result.device.Properties.SerialNumber;
61
73
  stopLifecycleWatch(udid);
@@ -70,6 +82,11 @@ function registerEstablishedTunnel(result) {
70
82
  establishedTunnelsByUdid.set(udid, result);
71
83
  }
72
84
 
85
+ /**
86
+ *
87
+ * @param {string} connectionType
88
+ * @returns {number}
89
+ */
73
90
  function connectionTypeRank(connectionType) {
74
91
  if (connectionType === 'USB') {
75
92
  return 0;
@@ -104,15 +121,25 @@ function dedupeDevicesByUdid(devices) {
104
121
  return [...byUdid.values()];
105
122
  }
106
123
 
124
+ /**
125
+ *
126
+ * @param {TunnelCreationSuccessResult} result
127
+ * @param {import('appium-ios-remotexpc').TunnelRegistryEntry | undefined} existing
128
+ * @param {number} now
129
+ * @returns {import('appium-ios-remotexpc').TunnelRegistryEntry}
130
+ */
107
131
  function buildTunnelEntry(result, existing, now) {
108
132
  const udid = result.device.Properties.SerialNumber;
133
+ /** @type {import('appium-ios-remotexpc').TunnelRegistryEntry} */
109
134
  const entry = {
110
135
  udid,
111
136
  deviceId: result.device.DeviceID,
112
137
  address: result.tunnel.Address,
113
138
  rsdPort: result.tunnel.RsdPort,
114
139
  services: {},
140
+ // @ts-expect-error - connectionType is not typed
115
141
  connectionType: result.device.Properties.ConnectionType,
142
+ // @ts-expect-error - productId is not typed
116
143
  productId: result.device.Properties.ProductID,
117
144
  createdAt: existing?.createdAt ?? now,
118
145
  lastUpdated: now,
@@ -120,6 +147,10 @@ function buildTunnelEntry(result, existing, now) {
120
147
  return entry;
121
148
  }
122
149
 
150
+ /**
151
+ * @param {TunnelCreationSuccessResult} result
152
+ * @returns {Promise<boolean>}
153
+ */
123
154
  async function publishDiscoveredTunnelEntry(result) {
124
155
  return await publishTunnelRegistryEntry({
125
156
  registryServer,
@@ -130,6 +161,7 @@ async function publishDiscoveredTunnelEntry(result) {
130
161
  });
131
162
  }
132
163
 
164
+ /** @type {(() => void)[]} */
133
165
  const registryWatcherStops = [];
134
166
  /** @type {Map<string, Promise<void>>} */
135
167
  const reconnectingByUdid = new Map();
@@ -137,8 +169,11 @@ const reconnectingByUdid = new Map();
137
169
  /**
138
170
  * When the native forwarder exits, drop the UDID from the HTTP registry and tear down state.
139
171
  *
140
- * @param {object} registry
172
+ * @param {import('appium-ios-remotexpc').TunnelRegistry} registry
141
173
  * @param {TunnelCreationSuccessResult[]} successful
174
+ * @param {object} callbacks
175
+ * @param {function({ udid: string, address: string }): Promise<void>} [callbacks.onTunnelDead]
176
+ * @returns {void}
142
177
  */
143
178
  function attachTunnelRegistryLifecycleWatch(registry, successful, callbacks = {}) {
144
179
  for (const result of successful) {
@@ -172,6 +207,15 @@ function attachTunnelRegistryLifecycleWatch(registry, successful, callbacks = {}
172
207
  );
173
208
  }
174
209
 
210
+ /**
211
+ *
212
+ * @param {object} opts
213
+ * @param {string} opts.udid
214
+ * @param {number} opts.maxRetries
215
+ * @param {import('appium-ios-remotexpc').UsbmuxDevice} opts.device
216
+ * @param {function(string): Promise<void>} opts.reconnectTunnelByUdid
217
+ * @returns {Promise<void>}
218
+ */
175
219
  async function runReconnectAttempts({
176
220
  udid,
177
221
  maxRetries,
@@ -193,6 +237,7 @@ async function runReconnectAttempts({
193
237
  registryServer.getRegistry(),
194
238
  [result],
195
239
  {
240
+
196
241
  onTunnelDead: async ({ udid: droppedUdid }) => {
197
242
  await reconnectTunnelByUdid(droppedUdid);
198
243
  },
@@ -211,6 +256,12 @@ async function runReconnectAttempts({
211
256
  log.error(`Reconnect retries exhausted for ${udid}`);
212
257
  }
213
258
 
259
+ /**
260
+ * @param {object} opts
261
+ * @param {number} opts.reconnectRetries
262
+ * @param {Map<string, import('appium-ios-remotexpc').UsbmuxDevice>} opts.devicesByUdid
263
+ * @returns {function(string): Promise<void>}
264
+ */
214
265
  function createReconnectTunnelByUdid({
215
266
  reconnectRetries,
216
267
  devicesByUdid,
@@ -245,7 +296,14 @@ function createReconnectTunnelByUdid({
245
296
  };
246
297
  }
247
298
 
299
+ /**
300
+ * @returns {void}
301
+ */
248
302
  function setupCleanupHandlers() {
303
+ /**
304
+ * @param {string} signal
305
+ * @returns {Promise<void>}
306
+ */
249
307
  const cleanup = async (signal) => {
250
308
  log.warn(`\nCleaning up (${signal})...`);
251
309
 
@@ -298,6 +356,10 @@ function setupCleanupHandlers() {
298
356
  });
299
357
  }
300
358
 
359
+ /**
360
+ * @param {import('appium-ios-remotexpc').UsbmuxDevice} device
361
+ * @returns {Promise<TunnelCreationSuccessResult>}
362
+ */
301
363
  async function createTunnelForDevice(device) {
302
364
  const udid = device.Properties.SerialNumber;
303
365
 
@@ -323,6 +385,7 @@ async function createTunnelForDevice(device) {
323
385
  log.info('CoreDeviceProxy started successfully');
324
386
 
325
387
  log.info(`Creating tunnel...`);
388
+ /** @type {{ notify: ((reason: string) => void) | null }} */
326
389
  const lifecycle = { notify: null };
327
390
  const tunnelConnection = await TunnelManager.getTunnel(
328
391
  socket,
@@ -339,7 +402,9 @@ async function createTunnelForDevice(device) {
339
402
  log.info(` Tunnel Address: ${tunnelConnection.Address}`);
340
403
  log.info(` Tunnel RsdPort: ${tunnelConnection.RsdPort}`);
341
404
 
405
+ /** @type {TunnelCreationSuccessResult} */
342
406
  const result = {
407
+ // @ts-expect-error - device is not typed
343
408
  device,
344
409
  tunnel: {
345
410
  Address: tunnelConnection.Address,
@@ -347,6 +412,10 @@ async function createTunnelForDevice(device) {
347
412
  },
348
413
  success: true,
349
414
  tunnelConnection,
415
+ /**
416
+ * @param {(reason: string) => void} handler
417
+ * @returns {void}
418
+ */
350
419
  registerOnDead: (handler) => {
351
420
  lifecycle.notify = handler;
352
421
  },
@@ -358,6 +427,7 @@ async function createTunnelForDevice(device) {
358
427
  const errorMessage = `Failed to create tunnel for device ${udid}: ${error}`;
359
428
  log.error(`❌ ${errorMessage}`);
360
429
  return {
430
+ // @ts-expect-error - device is not typed
361
431
  device,
362
432
  tunnel: { Address: '', RsdPort: 0 },
363
433
  success: false,
@@ -366,6 +436,9 @@ async function createTunnelForDevice(device) {
366
436
  }
367
437
  }
368
438
 
439
+ /**
440
+ * @returns {Promise<void>}
441
+ */
369
442
  async function main() {
370
443
  setupCleanupHandlers();
371
444
 
@@ -492,6 +565,10 @@ async function main() {
492
565
  registryServer.getRegistry(),
493
566
  [result],
494
567
  {
568
+ /**
569
+ * @param {{ udid: string }} ctx
570
+ * @returns {Promise<void>}
571
+ */
495
572
  onTunnelDead: async ({ udid }) => {
496
573
  await reconnectTunnelByUdid(udid);
497
574
  },
@@ -554,8 +631,9 @@ await main();
554
631
  * Successful tunnel row (USB lockdown + CoreDeviceProxy) used for the registry and lifecycle watch.
555
632
  *
556
633
  * @typedef {object} TunnelCreationSuccessResult
557
- * @property {{ Properties: { SerialNumber: string }, DeviceID: number }} device
634
+ * @property {{ Properties: { SerialNumber: string, [key: string]: unknown }, DeviceID: number }} device
558
635
  * @property {{ Address: string, RsdPort?: number }} tunnel
559
636
  * @property {import('appium-ios-tuntap').TunnelConnection} tunnelConnection
560
637
  * @property {(handler: (reason: string) => void) => void} registerOnDead
638
+ * @property {boolean} success
561
639
  */