execbro 2.3.0 → 2.6.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 (596) hide show
  1. package/README.md +2 -0
  2. package/build/__tests__/helpers/fake-cdp-server.d.ts +0 -12
  3. package/build/__tests__/helpers/fake-cdp-server.d.ts.map +1 -1
  4. package/build/__tests__/helpers/fake-cdp-server.js +0 -22
  5. package/build/__tests__/helpers/fake-cdp-server.js.map +1 -1
  6. package/build/__tests__/helpers/tap-test-helpers.d.ts +0 -23
  7. package/build/__tests__/helpers/tap-test-helpers.d.ts.map +1 -1
  8. package/build/__tests__/helpers/tap-test-helpers.js +2 -29
  9. package/build/__tests__/helpers/tap-test-helpers.js.map +1 -1
  10. package/build/__tests__/integration/auto-reconnect.test.js +4 -14
  11. package/build/__tests__/integration/auto-reconnect.test.js.map +1 -1
  12. package/build/__tests__/integration/connection-health.test.js +1 -10
  13. package/build/__tests__/integration/connection-health.test.js.map +1 -1
  14. package/build/__tests__/integration/execute-in-app.test.js +1 -11
  15. package/build/__tests__/integration/execute-in-app.test.js.map +1 -1
  16. package/build/__tests__/integration/native-logs.test.js +0 -5
  17. package/build/__tests__/integration/native-logs.test.js.map +1 -1
  18. package/build/__tests__/integration/per-call-timeout.test.js +1 -5
  19. package/build/__tests__/integration/per-call-timeout.test.js.map +1 -1
  20. package/build/__tests__/integration/socket-close-midflight.test.js +2 -12
  21. package/build/__tests__/integration/socket-close-midflight.test.js.map +1 -1
  22. package/build/__tests__/integration/stale-target-probe.test.js +2 -17
  23. package/build/__tests__/integration/stale-target-probe.test.js.map +1 -1
  24. package/build/__tests__/integration/tap-strategies.test.js +2 -36
  25. package/build/__tests__/integration/tap-strategies.test.js.map +1 -1
  26. package/build/__tests__/integration/text-input.test.js +0 -39
  27. package/build/__tests__/integration/text-input.test.js.map +1 -1
  28. package/build/__tests__/integration/tools.test.js +1 -7
  29. package/build/__tests__/integration/tools.test.js.map +1 -1
  30. package/build/__tests__/setup.js +0 -2
  31. package/build/__tests__/setup.js.map +1 -1
  32. package/build/__tests__/unit/androidEmulatorAvds.test.js +0 -10
  33. package/build/__tests__/unit/androidEmulatorAvds.test.js.map +1 -1
  34. package/build/__tests__/unit/androidPinch.test.d.ts +2 -0
  35. package/build/__tests__/unit/androidPinch.test.d.ts.map +1 -0
  36. package/build/__tests__/unit/androidPinch.test.js +119 -0
  37. package/build/__tests__/unit/androidPinch.test.js.map +1 -0
  38. package/build/__tests__/unit/appContext.test.js +0 -2
  39. package/build/__tests__/unit/appContext.test.js.map +1 -1
  40. package/build/__tests__/unit/appRequest.test.js +0 -3
  41. package/build/__tests__/unit/appRequest.test.js.map +1 -1
  42. package/build/__tests__/unit/buildToken.test.js +0 -6
  43. package/build/__tests__/unit/buildToken.test.js.map +1 -1
  44. package/build/__tests__/unit/burst.test.js +0 -1
  45. package/build/__tests__/unit/burst.test.js.map +1 -1
  46. package/build/__tests__/unit/clearLogsNative.test.js +0 -8
  47. package/build/__tests__/unit/clearLogsNative.test.js.map +1 -1
  48. package/build/__tests__/unit/componentFormatNaming.test.js +0 -13
  49. package/build/__tests__/unit/componentFormatNaming.test.js.map +1 -1
  50. package/build/__tests__/unit/componentSource.test.js +0 -4
  51. package/build/__tests__/unit/componentSource.test.js.map +1 -1
  52. package/build/__tests__/unit/componentSourceResolve.test.js +0 -1
  53. package/build/__tests__/unit/componentSourceResolve.test.js.map +1 -1
  54. package/build/__tests__/unit/contextWiring.test.js +0 -3
  55. package/build/__tests__/unit/contextWiring.test.js.map +1 -1
  56. package/build/__tests__/unit/coordinateMiss.test.js +0 -16
  57. package/build/__tests__/unit/coordinateMiss.test.js.map +1 -1
  58. package/build/__tests__/unit/deviceDiscovery.test.js +0 -4
  59. package/build/__tests__/unit/deviceDiscovery.test.js.map +1 -1
  60. package/build/__tests__/unit/deviceResolution.test.js +0 -2
  61. package/build/__tests__/unit/deviceResolution.test.js.map +1 -1
  62. package/build/__tests__/unit/deviceResolver.test.js +0 -1
  63. package/build/__tests__/unit/deviceResolver.test.js.map +1 -1
  64. package/build/__tests__/unit/deviceResolverStaleRetry.test.js +0 -8
  65. package/build/__tests__/unit/deviceResolverStaleRetry.test.js.map +1 -1
  66. package/build/__tests__/unit/emulatorBridge.test.d.ts +2 -0
  67. package/build/__tests__/unit/emulatorBridge.test.d.ts.map +1 -0
  68. package/build/__tests__/unit/emulatorBridge.test.js +76 -0
  69. package/build/__tests__/unit/emulatorBridge.test.js.map +1 -0
  70. package/build/__tests__/unit/emulatorGrpc.test.d.ts +2 -0
  71. package/build/__tests__/unit/emulatorGrpc.test.d.ts.map +1 -0
  72. package/build/__tests__/unit/emulatorGrpc.test.js +33 -0
  73. package/build/__tests__/unit/emulatorGrpc.test.js.map +1 -0
  74. package/build/__tests__/unit/epochRender.test.js +0 -3
  75. package/build/__tests__/unit/epochRender.test.js.map +1 -1
  76. package/build/__tests__/unit/errorCategorization.test.js +0 -18
  77. package/build/__tests__/unit/errorCategorization.test.js.map +1 -1
  78. package/build/__tests__/unit/exec.test.js +0 -1
  79. package/build/__tests__/unit/exec.test.js.map +1 -1
  80. package/build/__tests__/unit/executor.test.js +0 -15
  81. package/build/__tests__/unit/executor.test.js.map +1 -1
  82. package/build/__tests__/unit/findElementTreeReuse.test.js +0 -12
  83. package/build/__tests__/unit/findElementTreeReuse.test.js.map +1 -1
  84. package/build/__tests__/unit/fingerprint.test.js +0 -4
  85. package/build/__tests__/unit/fingerprint.test.js.map +1 -1
  86. package/build/__tests__/unit/focusedInputTools.test.js +0 -4
  87. package/build/__tests__/unit/focusedInputTools.test.js.map +1 -1
  88. package/build/__tests__/unit/getLogsEmptyContract.test.js +0 -30
  89. package/build/__tests__/unit/getLogsEmptyContract.test.js.map +1 -1
  90. package/build/__tests__/unit/getLogsNativeSource.test.js +0 -22
  91. package/build/__tests__/unit/getLogsNativeSource.test.js.map +1 -1
  92. package/build/__tests__/unit/graphqlOperation.test.d.ts +2 -0
  93. package/build/__tests__/unit/graphqlOperation.test.d.ts.map +1 -0
  94. package/build/__tests__/unit/graphqlOperation.test.js +107 -0
  95. package/build/__tests__/unit/graphqlOperation.test.js.map +1 -0
  96. package/build/__tests__/unit/inputTarget.test.js +0 -16
  97. package/build/__tests__/unit/inputTarget.test.js.map +1 -1
  98. package/build/__tests__/unit/inputTextTool.test.js +0 -3
  99. package/build/__tests__/unit/inputTextTool.test.js.map +1 -1
  100. package/build/__tests__/unit/jsLogEvents.test.js +0 -3
  101. package/build/__tests__/unit/jsLogEvents.test.js.map +1 -1
  102. package/build/__tests__/unit/keyboardMetrics.test.js +0 -2
  103. package/build/__tests__/unit/keyboardMetrics.test.js.map +1 -1
  104. package/build/__tests__/unit/keyboardRaise.test.js +0 -2
  105. package/build/__tests__/unit/keyboardRaise.test.js.map +1 -1
  106. package/build/__tests__/unit/licenseGrace.test.js +1 -4
  107. package/build/__tests__/unit/licenseGrace.test.js.map +1 -1
  108. package/build/__tests__/unit/logEventFormat.test.js +0 -27
  109. package/build/__tests__/unit/logEventFormat.test.js.map +1 -1
  110. package/build/__tests__/unit/logEvents.test.js +1 -5
  111. package/build/__tests__/unit/logEvents.test.js.map +1 -1
  112. package/build/__tests__/unit/logGrouping.test.js +0 -2
  113. package/build/__tests__/unit/logGrouping.test.js.map +1 -1
  114. package/build/__tests__/unit/logOwnership.test.js +0 -11
  115. package/build/__tests__/unit/logOwnership.test.js.map +1 -1
  116. package/build/__tests__/unit/logRelevance.test.js +0 -6
  117. package/build/__tests__/unit/logRelevance.test.js.map +1 -1
  118. package/build/__tests__/unit/logSourceAndroid.test.js +0 -4
  119. package/build/__tests__/unit/logSourceAndroid.test.js.map +1 -1
  120. package/build/__tests__/unit/logSourceIos.test.js +0 -5
  121. package/build/__tests__/unit/logSourceIos.test.js.map +1 -1
  122. package/build/__tests__/unit/logs.test.js +0 -1
  123. package/build/__tests__/unit/logs.test.js.map +1 -1
  124. package/build/__tests__/unit/measureComponentTools.test.js +0 -1
  125. package/build/__tests__/unit/measureComponentTools.test.js.map +1 -1
  126. package/build/__tests__/unit/metro.test.js +0 -2
  127. package/build/__tests__/unit/metro.test.js.map +1 -1
  128. package/build/__tests__/unit/nativeInputValue.test.js +0 -4
  129. package/build/__tests__/unit/nativeInputValue.test.js.map +1 -1
  130. package/build/__tests__/unit/nativeLogs.test.js +1 -23
  131. package/build/__tests__/unit/nativeLogs.test.js.map +1 -1
  132. package/build/__tests__/unit/navigateVerify.test.js +0 -6
  133. package/build/__tests__/unit/navigateVerify.test.js.map +1 -1
  134. package/build/__tests__/unit/navigation.test.js +0 -5
  135. package/build/__tests__/unit/navigation.test.js.map +1 -1
  136. package/build/__tests__/unit/networkInterceptor.test.js +0 -2
  137. package/build/__tests__/unit/networkInterceptor.test.js.map +1 -1
  138. package/build/__tests__/unit/overlayGuard.test.d.ts +2 -0
  139. package/build/__tests__/unit/overlayGuard.test.d.ts.map +1 -0
  140. package/build/__tests__/unit/overlayGuard.test.js +123 -0
  141. package/build/__tests__/unit/overlayGuard.test.js.map +1 -0
  142. package/build/__tests__/unit/pendingExecutions.test.js +0 -6
  143. package/build/__tests__/unit/pendingExecutions.test.js.map +1 -1
  144. package/build/__tests__/unit/pinchGeometry.test.d.ts +2 -0
  145. package/build/__tests__/unit/pinchGeometry.test.d.ts.map +1 -0
  146. package/build/__tests__/unit/pinchGeometry.test.js +139 -0
  147. package/build/__tests__/unit/pinchGeometry.test.js.map +1 -0
  148. package/build/__tests__/unit/pinchTool.test.d.ts +2 -0
  149. package/build/__tests__/unit/pinchTool.test.d.ts.map +1 -0
  150. package/build/__tests__/unit/pinchTool.test.js +39 -0
  151. package/build/__tests__/unit/pinchTool.test.js.map +1 -0
  152. package/build/__tests__/unit/probe.test.js +0 -1
  153. package/build/__tests__/unit/probe.test.js.map +1 -1
  154. package/build/__tests__/unit/projectMemory.test.js +6 -7
  155. package/build/__tests__/unit/projectMemory.test.js.map +1 -1
  156. package/build/__tests__/unit/projectStore.test.js +2 -2
  157. package/build/__tests__/unit/projectStore.test.js.map +1 -1
  158. package/build/__tests__/unit/reloadAppAutoConnect.test.js +0 -9
  159. package/build/__tests__/unit/reloadAppAutoConnect.test.js.map +1 -1
  160. package/build/__tests__/unit/rnGlobalsBootstrap.test.js +3 -3
  161. package/build/__tests__/unit/rnGlobalsBootstrap.test.js.map +1 -1
  162. package/build/__tests__/unit/routeHistory.test.d.ts +2 -0
  163. package/build/__tests__/unit/routeHistory.test.d.ts.map +1 -0
  164. package/build/__tests__/unit/routeHistory.test.js +136 -0
  165. package/build/__tests__/unit/routeHistory.test.js.map +1 -0
  166. package/build/__tests__/unit/screenSpace.test.js +30 -13
  167. package/build/__tests__/unit/screenSpace.test.js.map +1 -1
  168. package/build/__tests__/unit/screenState.test.js +3 -6
  169. package/build/__tests__/unit/screenState.test.js.map +1 -1
  170. package/build/__tests__/unit/screenStateInputValue.test.js +0 -7
  171. package/build/__tests__/unit/screenStateInputValue.test.js.map +1 -1
  172. package/build/__tests__/unit/screenStateKeyboard.test.js +0 -1
  173. package/build/__tests__/unit/screenStateKeyboard.test.js.map +1 -1
  174. package/build/__tests__/unit/screenshot-diff.test.js +0 -6
  175. package/build/__tests__/unit/screenshot-diff.test.js.map +1 -1
  176. package/build/__tests__/unit/sdkMirrorPoller.test.js +0 -8
  177. package/build/__tests__/unit/sdkMirrorPoller.test.js.map +1 -1
  178. package/build/__tests__/unit/settleAndDiff.test.js +0 -11
  179. package/build/__tests__/unit/settleAndDiff.test.js.map +1 -1
  180. package/build/__tests__/unit/signedVerdict.test.js +0 -5
  181. package/build/__tests__/unit/signedVerdict.test.js.map +1 -1
  182. package/build/__tests__/unit/swipeDirection.test.js +6 -9
  183. package/build/__tests__/unit/swipeDirection.test.js.map +1 -1
  184. package/build/__tests__/unit/symbolicate.test.js +0 -1
  185. package/build/__tests__/unit/symbolicate.test.js.map +1 -1
  186. package/build/__tests__/unit/tap.test.js +2 -40
  187. package/build/__tests__/unit/tap.test.js.map +1 -1
  188. package/build/__tests__/unit/textEntry.test.js +0 -27
  189. package/build/__tests__/unit/textEntry.test.js.map +1 -1
  190. package/build/__tests__/unit/toolDescriptions.test.js +0 -40
  191. package/build/__tests__/unit/toolDescriptions.test.js.map +1 -1
  192. package/build/__tests__/unit/toolsJson.test.js +0 -10
  193. package/build/__tests__/unit/toolsJson.test.js.map +1 -1
  194. package/build/__tests__/unit/transportClassify.test.js +0 -20
  195. package/build/__tests__/unit/transportClassify.test.js.map +1 -1
  196. package/build/__tests__/unit/truncate.test.js +0 -5
  197. package/build/__tests__/unit/truncate.test.js.map +1 -1
  198. package/build/__tests__/unit/usageCache.test.js +0 -3
  199. package/build/__tests__/unit/usageCache.test.js.map +1 -1
  200. package/build/__tests__/unit/usageGate.test.js +1 -1
  201. package/build/__tests__/unit/usageGate.test.js.map +1 -1
  202. package/build/__tests__/unit/visibility.test.js +0 -8
  203. package/build/__tests__/unit/visibility.test.js.map +1 -1
  204. package/build/__tests__/unit/waitForMeasurements.test.js +2 -7
  205. package/build/__tests__/unit/waitForMeasurements.test.js.map +1 -1
  206. package/build/bin.js +0 -18
  207. package/build/bin.js.map +1 -1
  208. package/build/core/android.d.ts +0 -120
  209. package/build/core/android.d.ts.map +1 -1
  210. package/build/core/android.js +4 -259
  211. package/build/core/android.js.map +1 -1
  212. package/build/core/androidPinch.d.ts +21 -0
  213. package/build/core/androidPinch.d.ts.map +1 -0
  214. package/build/core/androidPinch.js +72 -0
  215. package/build/core/androidPinch.js.map +1 -0
  216. package/build/core/androidSystemBars.d.ts +7 -0
  217. package/build/core/androidSystemBars.d.ts.map +1 -0
  218. package/build/core/androidSystemBars.js +21 -0
  219. package/build/core/androidSystemBars.js.map +1 -0
  220. package/build/core/appContext.d.ts +0 -50
  221. package/build/core/appContext.d.ts.map +1 -1
  222. package/build/core/appContext.js +0 -50
  223. package/build/core/appContext.js.map +1 -1
  224. package/build/core/appDetection.d.ts +0 -16
  225. package/build/core/appDetection.d.ts.map +1 -1
  226. package/build/core/appDetection.js +0 -36
  227. package/build/core/appDetection.js.map +1 -1
  228. package/build/core/appRequest.d.ts +0 -12
  229. package/build/core/appRequest.d.ts.map +1 -1
  230. package/build/core/appRequest.js +0 -15
  231. package/build/core/appRequest.js.map +1 -1
  232. package/build/core/bufferConfig.d.ts +0 -2
  233. package/build/core/bufferConfig.d.ts.map +1 -1
  234. package/build/core/bufferConfig.js +0 -2
  235. package/build/core/bufferConfig.js.map +1 -1
  236. package/build/core/bundle.js +0 -40
  237. package/build/core/bundle.js.map +1 -1
  238. package/build/core/componentSearch.d.ts +0 -19
  239. package/build/core/componentSearch.d.ts.map +1 -1
  240. package/build/core/componentSearch.js +1 -22
  241. package/build/core/componentSearch.js.map +1 -1
  242. package/build/core/componentSource.d.ts +0 -21
  243. package/build/core/componentSource.d.ts.map +1 -1
  244. package/build/core/componentSource.js +0 -27
  245. package/build/core/componentSource.js.map +1 -1
  246. package/build/core/componentTree.js +0 -12
  247. package/build/core/componentTree.js.map +1 -1
  248. package/build/core/config.d.ts +0 -8
  249. package/build/core/config.d.ts.map +1 -1
  250. package/build/core/config.js +0 -10
  251. package/build/core/config.js.map +1 -1
  252. package/build/core/connection.d.ts +0 -76
  253. package/build/core/connection.d.ts.map +1 -1
  254. package/build/core/connection.js +15 -340
  255. package/build/core/connection.js.map +1 -1
  256. package/build/core/connectionState.d.ts +0 -78
  257. package/build/core/connectionState.d.ts.map +1 -1
  258. package/build/core/connectionState.js +4 -92
  259. package/build/core/connectionState.js.map +1 -1
  260. package/build/core/debugGlobals.js +0 -68
  261. package/build/core/debugGlobals.js.map +1 -1
  262. package/build/core/deviceDiscovery.d.ts +0 -5
  263. package/build/core/deviceDiscovery.d.ts.map +1 -1
  264. package/build/core/deviceDiscovery.js +0 -12
  265. package/build/core/deviceDiscovery.js.map +1 -1
  266. package/build/core/deviceResolver.d.ts +0 -10
  267. package/build/core/deviceResolver.d.ts.map +1 -1
  268. package/build/core/deviceResolver.js +0 -54
  269. package/build/core/deviceResolver.js.map +1 -1
  270. package/build/core/emulatorBridge.d.ts +17 -0
  271. package/build/core/emulatorBridge.d.ts.map +1 -0
  272. package/build/core/emulatorBridge.js +78 -0
  273. package/build/core/emulatorBridge.js.map +1 -0
  274. package/build/core/emulatorGrpc.d.ts +14 -0
  275. package/build/core/emulatorGrpc.d.ts.map +1 -0
  276. package/build/core/emulatorGrpc.js +93 -0
  277. package/build/core/emulatorGrpc.js.map +1 -0
  278. package/build/core/epochRender.d.ts +0 -12
  279. package/build/core/epochRender.d.ts.map +1 -1
  280. package/build/core/epochRender.js +0 -12
  281. package/build/core/epochRender.js.map +1 -1
  282. package/build/core/errorScreenParser.d.ts +0 -21
  283. package/build/core/errorScreenParser.d.ts.map +1 -1
  284. package/build/core/errorScreenParser.js +0 -50
  285. package/build/core/errorScreenParser.js.map +1 -1
  286. package/build/core/errors.d.ts +0 -15
  287. package/build/core/errors.d.ts.map +1 -1
  288. package/build/core/errors.js +0 -15
  289. package/build/core/errors.js.map +1 -1
  290. package/build/core/escapeNonAscii.d.ts +0 -9
  291. package/build/core/escapeNonAscii.d.ts.map +1 -1
  292. package/build/core/escapeNonAscii.js +1 -43
  293. package/build/core/escapeNonAscii.js.map +1 -1
  294. package/build/core/exec.d.ts +0 -12
  295. package/build/core/exec.d.ts.map +1 -1
  296. package/build/core/exec.js +2 -14
  297. package/build/core/exec.js.map +1 -1
  298. package/build/core/executor.js +0 -3
  299. package/build/core/executor.js.map +1 -1
  300. package/build/core/fastRefreshRecorder.js +0 -4
  301. package/build/core/fastRefreshRecorder.js.map +1 -1
  302. package/build/core/fastRefreshTools.js +0 -3
  303. package/build/core/fastRefreshTools.js.map +1 -1
  304. package/build/core/feedback.js +0 -4
  305. package/build/core/feedback.js.map +1 -1
  306. package/build/core/graphqlOperation.d.ts +13 -0
  307. package/build/core/graphqlOperation.d.ts.map +1 -0
  308. package/build/core/graphqlOperation.js +80 -0
  309. package/build/core/graphqlOperation.js.map +1 -0
  310. package/build/core/guides.d.ts +0 -10
  311. package/build/core/guides.d.ts.map +1 -1
  312. package/build/core/guides.js +18 -12
  313. package/build/core/guides.js.map +1 -1
  314. package/build/core/iconSemantics.d.ts +0 -15
  315. package/build/core/iconSemantics.d.ts.map +1 -1
  316. package/build/core/iconSemantics.js +0 -31
  317. package/build/core/iconSemantics.js.map +1 -1
  318. package/build/core/index.d.ts +2 -0
  319. package/build/core/index.d.ts.map +1 -1
  320. package/build/core/index.js +5 -47
  321. package/build/core/index.js.map +1 -1
  322. package/build/core/injected/visibility.d.ts +0 -30
  323. package/build/core/injected/visibility.d.ts.map +1 -1
  324. package/build/core/injected/visibility.js +0 -33
  325. package/build/core/injected/visibility.js.map +1 -1
  326. package/build/core/injectedFilters.d.ts +0 -28
  327. package/build/core/injectedFilters.d.ts.map +1 -1
  328. package/build/core/injectedFilters.js +0 -28
  329. package/build/core/injectedFilters.js.map +1 -1
  330. package/build/core/inputTarget.d.ts +1 -34
  331. package/build/core/inputTarget.d.ts.map +1 -1
  332. package/build/core/inputTarget.js +0 -26
  333. package/build/core/inputTarget.js.map +1 -1
  334. package/build/core/inputTargetTools.d.ts +0 -7
  335. package/build/core/inputTargetTools.d.ts.map +1 -1
  336. package/build/core/inputTargetTools.js +0 -7
  337. package/build/core/inputTargetTools.js.map +1 -1
  338. package/build/core/inspector.d.ts +0 -33
  339. package/build/core/inspector.d.ts.map +1 -1
  340. package/build/core/inspector.js +27 -40
  341. package/build/core/inspector.js.map +1 -1
  342. package/build/core/ios.d.ts +11 -122
  343. package/build/core/ios.d.ts.map +1 -1
  344. package/build/core/ios.js +32 -299
  345. package/build/core/ios.js.map +1 -1
  346. package/build/core/jsExecute.d.ts +0 -100
  347. package/build/core/jsExecute.d.ts.map +1 -1
  348. package/build/core/jsExecute.js +0 -261
  349. package/build/core/jsExecute.js.map +1 -1
  350. package/build/core/jsLogEvents.d.ts +0 -8
  351. package/build/core/jsLogEvents.d.ts.map +1 -1
  352. package/build/core/jsLogEvents.js +0 -8
  353. package/build/core/jsLogEvents.js.map +1 -1
  354. package/build/core/keyboardMetrics.js +0 -8
  355. package/build/core/keyboardMetrics.js.map +1 -1
  356. package/build/core/keyboardRaise.d.ts +0 -8
  357. package/build/core/keyboardRaise.d.ts.map +1 -1
  358. package/build/core/keyboardRaise.js +0 -23
  359. package/build/core/keyboardRaise.js.map +1 -1
  360. package/build/core/license.d.ts +0 -4
  361. package/build/core/license.d.ts.map +1 -1
  362. package/build/core/license.js +2 -55
  363. package/build/core/license.js.map +1 -1
  364. package/build/core/logDiagnosis.d.ts +0 -34
  365. package/build/core/logDiagnosis.d.ts.map +1 -1
  366. package/build/core/logDiagnosis.js +0 -15
  367. package/build/core/logDiagnosis.js.map +1 -1
  368. package/build/core/logEventFormat.d.ts +0 -1
  369. package/build/core/logEventFormat.d.ts.map +1 -1
  370. package/build/core/logEventFormat.js +0 -30
  371. package/build/core/logEventFormat.js.map +1 -1
  372. package/build/core/logEvents.d.ts +0 -54
  373. package/build/core/logEvents.d.ts.map +1 -1
  374. package/build/core/logEvents.js +0 -37
  375. package/build/core/logEvents.js.map +1 -1
  376. package/build/core/logGrouping.d.ts +0 -9
  377. package/build/core/logGrouping.d.ts.map +1 -1
  378. package/build/core/logGrouping.js +0 -17
  379. package/build/core/logGrouping.js.map +1 -1
  380. package/build/core/logOwnership.d.ts +0 -17
  381. package/build/core/logOwnership.d.ts.map +1 -1
  382. package/build/core/logOwnership.js +0 -34
  383. package/build/core/logOwnership.js.map +1 -1
  384. package/build/core/logRelevance.d.ts +0 -8
  385. package/build/core/logRelevance.d.ts.map +1 -1
  386. package/build/core/logRelevance.js +0 -15
  387. package/build/core/logRelevance.js.map +1 -1
  388. package/build/core/logSourceAndroid.d.ts +0 -8
  389. package/build/core/logSourceAndroid.d.ts.map +1 -1
  390. package/build/core/logSourceAndroid.js +0 -11
  391. package/build/core/logSourceAndroid.js.map +1 -1
  392. package/build/core/logSourceIos.d.ts +0 -8
  393. package/build/core/logSourceIos.d.ts.map +1 -1
  394. package/build/core/logSourceIos.js +1 -24
  395. package/build/core/logSourceIos.js.map +1 -1
  396. package/build/core/logbox.d.ts +0 -30
  397. package/build/core/logbox.d.ts.map +1 -1
  398. package/build/core/logbox.js +0 -43
  399. package/build/core/logbox.js.map +1 -1
  400. package/build/core/logs.d.ts +0 -6
  401. package/build/core/logs.d.ts.map +1 -1
  402. package/build/core/logs.js +0 -19
  403. package/build/core/logs.js.map +1 -1
  404. package/build/core/metro.d.ts +0 -14
  405. package/build/core/metro.d.ts.map +1 -1
  406. package/build/core/metro.js +1 -32
  407. package/build/core/metro.js.map +1 -1
  408. package/build/core/moduleRegistry.d.ts +0 -17
  409. package/build/core/moduleRegistry.d.ts.map +1 -1
  410. package/build/core/moduleRegistry.js +0 -17
  411. package/build/core/moduleRegistry.js.map +1 -1
  412. package/build/core/nativeInputValue.d.ts +0 -29
  413. package/build/core/nativeInputValue.d.ts.map +1 -1
  414. package/build/core/nativeInputValue.js +0 -18
  415. package/build/core/nativeInputValue.js.map +1 -1
  416. package/build/core/nativeLogs.d.ts +0 -47
  417. package/build/core/nativeLogs.d.ts.map +1 -1
  418. package/build/core/nativeLogs.js +0 -82
  419. package/build/core/nativeLogs.js.map +1 -1
  420. package/build/core/nativeOnlyHints.js +0 -4
  421. package/build/core/nativeOnlyHints.js.map +1 -1
  422. package/build/core/navigate.d.ts +0 -15
  423. package/build/core/navigate.d.ts.map +1 -1
  424. package/build/core/navigate.js +0 -22
  425. package/build/core/navigate.js.map +1 -1
  426. package/build/core/navigation.d.ts +0 -60
  427. package/build/core/navigation.d.ts.map +1 -1
  428. package/build/core/navigation.js +0 -60
  429. package/build/core/navigation.js.map +1 -1
  430. package/build/core/network.d.ts +0 -5
  431. package/build/core/network.d.ts.map +1 -1
  432. package/build/core/network.js +10 -31
  433. package/build/core/network.js.map +1 -1
  434. package/build/core/networkInterceptor.d.ts +0 -19
  435. package/build/core/networkInterceptor.d.ts.map +1 -1
  436. package/build/core/networkInterceptor.js +3 -27
  437. package/build/core/networkInterceptor.js.map +1 -1
  438. package/build/core/ocr.d.ts +0 -31
  439. package/build/core/ocr.d.ts.map +1 -1
  440. package/build/core/ocr.js +1 -71
  441. package/build/core/ocr.js.map +1 -1
  442. package/build/core/paths.js +0 -1
  443. package/build/core/paths.js.map +1 -1
  444. package/build/core/pinchGeometry.d.ts +28 -0
  445. package/build/core/pinchGeometry.d.ts.map +1 -0
  446. package/build/core/pinchGeometry.js +97 -0
  447. package/build/core/pinchGeometry.js.map +1 -0
  448. package/build/core/pinchThresholds.d.ts +14 -0
  449. package/build/core/pinchThresholds.d.ts.map +1 -0
  450. package/build/core/pinchThresholds.js +9 -0
  451. package/build/core/pinchThresholds.js.map +1 -0
  452. package/build/core/posthog.js +0 -7
  453. package/build/core/posthog.js.map +1 -1
  454. package/build/core/pressables.d.ts +0 -25
  455. package/build/core/pressables.d.ts.map +1 -1
  456. package/build/core/pressables.js +0 -37
  457. package/build/core/pressables.js.map +1 -1
  458. package/build/core/probe.d.ts +0 -9
  459. package/build/core/probe.d.ts.map +1 -1
  460. package/build/core/probe.js +0 -9
  461. package/build/core/probe.js.map +1 -1
  462. package/build/core/projectMemory.d.ts +0 -2
  463. package/build/core/projectMemory.d.ts.map +1 -1
  464. package/build/core/projectMemory.js +2 -7
  465. package/build/core/projectMemory.js.map +1 -1
  466. package/build/core/projectStore.d.ts +0 -18
  467. package/build/core/projectStore.d.ts.map +1 -1
  468. package/build/core/projectStore.js +0 -19
  469. package/build/core/projectStore.js.map +1 -1
  470. package/build/core/promiseHandles.d.ts +0 -13
  471. package/build/core/promiseHandles.d.ts.map +1 -1
  472. package/build/core/promiseHandles.js +0 -25
  473. package/build/core/promiseHandles.js.map +1 -1
  474. package/build/core/register.js +0 -44
  475. package/build/core/register.js.map +1 -1
  476. package/build/core/rnGlobalsBootstrap.d.ts +0 -13
  477. package/build/core/rnGlobalsBootstrap.d.ts.map +1 -1
  478. package/build/core/rnGlobalsBootstrap.js +0 -22
  479. package/build/core/rnGlobalsBootstrap.js.map +1 -1
  480. package/build/core/routeHistory.d.ts +24 -0
  481. package/build/core/routeHistory.d.ts.map +1 -0
  482. package/build/core/routeHistory.js +186 -0
  483. package/build/core/routeHistory.js.map +1 -0
  484. package/build/core/screenLayout.d.ts +0 -11
  485. package/build/core/screenLayout.d.ts.map +1 -1
  486. package/build/core/screenLayout.js +24 -61
  487. package/build/core/screenLayout.js.map +1 -1
  488. package/build/core/screenSpace.d.ts +32 -44
  489. package/build/core/screenSpace.d.ts.map +1 -1
  490. package/build/core/screenSpace.js +80 -46
  491. package/build/core/screenSpace.js.map +1 -1
  492. package/build/core/screenSpaceDevice.d.ts +0 -8
  493. package/build/core/screenSpaceDevice.d.ts.map +1 -1
  494. package/build/core/screenSpaceDevice.js +31 -14
  495. package/build/core/screenSpaceDevice.js.map +1 -1
  496. package/build/core/screenState.d.ts +2 -74
  497. package/build/core/screenState.d.ts.map +1 -1
  498. package/build/core/screenState.js +204 -145
  499. package/build/core/screenState.js.map +1 -1
  500. package/build/core/sdkBridge.js +0 -8
  501. package/build/core/sdkBridge.js.map +1 -1
  502. package/build/core/sdkMirrorPoller.d.ts +0 -18
  503. package/build/core/sdkMirrorPoller.d.ts.map +1 -1
  504. package/build/core/sdkMirrorPoller.js +0 -47
  505. package/build/core/sdkMirrorPoller.js.map +1 -1
  506. package/build/core/selectionBuffer.d.ts +0 -6
  507. package/build/core/selectionBuffer.d.ts.map +1 -1
  508. package/build/core/selectionBuffer.js +0 -5
  509. package/build/core/selectionBuffer.js.map +1 -1
  510. package/build/core/signedVerdict.js +0 -6
  511. package/build/core/signedVerdict.js.map +1 -1
  512. package/build/core/state.d.ts +0 -31
  513. package/build/core/state.d.ts.map +1 -1
  514. package/build/core/state.js +0 -57
  515. package/build/core/state.js.map +1 -1
  516. package/build/core/symbolicate.js +0 -11
  517. package/build/core/symbolicate.js.map +1 -1
  518. package/build/core/telemetry.d.ts +0 -47
  519. package/build/core/telemetry.d.ts.map +1 -1
  520. package/build/core/telemetry.js +1 -173
  521. package/build/core/telemetry.js.map +1 -1
  522. package/build/core/textEntry.d.ts +0 -34
  523. package/build/core/textEntry.d.ts.map +1 -1
  524. package/build/core/textEntry.js +0 -73
  525. package/build/core/textEntry.js.map +1 -1
  526. package/build/core/toolHelpers.d.ts +0 -12
  527. package/build/core/toolHelpers.d.ts.map +1 -1
  528. package/build/core/toolHelpers.js +2 -47
  529. package/build/core/toolHelpers.js.map +1 -1
  530. package/build/core/truncate.d.ts +0 -17
  531. package/build/core/truncate.d.ts.map +1 -1
  532. package/build/core/truncate.js +0 -32
  533. package/build/core/truncate.js.map +1 -1
  534. package/build/core/types.d.ts +0 -6
  535. package/build/core/types.d.ts.map +1 -1
  536. package/build/core/usageCache.js +0 -12
  537. package/build/core/usageCache.js.map +1 -1
  538. package/build/index.js +0 -18
  539. package/build/index.js.map +1 -1
  540. package/build/pro/coordinateMiss.d.ts +0 -30
  541. package/build/pro/coordinateMiss.d.ts.map +1 -1
  542. package/build/pro/coordinateMiss.js +0 -17
  543. package/build/pro/coordinateMiss.js.map +1 -1
  544. package/build/pro/overlayGuard.d.ts +27 -0
  545. package/build/pro/overlayGuard.d.ts.map +1 -0
  546. package/build/pro/overlayGuard.js +103 -0
  547. package/build/pro/overlayGuard.js.map +1 -0
  548. package/build/pro/screenshot-diff.js +4 -6
  549. package/build/pro/screenshot-diff.js.map +1 -1
  550. package/build/pro/tap.d.ts +0 -55
  551. package/build/pro/tap.d.ts.map +1 -1
  552. package/build/pro/tap.js +72 -316
  553. package/build/pro/tap.js.map +1 -1
  554. package/build/pro/usageGate.js +0 -9
  555. package/build/pro/usageGate.js.map +1 -1
  556. package/build/pro/usageNotifications.js +1 -13
  557. package/build/pro/usageNotifications.js.map +1 -1
  558. package/build/pro/verifyAction.d.ts +0 -13
  559. package/build/pro/verifyAction.d.ts.map +1 -1
  560. package/build/pro/verifyAction.js +0 -45
  561. package/build/pro/verifyAction.js.map +1 -1
  562. package/build/tools/_deviceArg.d.ts +0 -35
  563. package/build/tools/_deviceArg.d.ts.map +1 -1
  564. package/build/tools/_deviceArg.js +0 -35
  565. package/build/tools/_deviceArg.js.map +1 -1
  566. package/build/tools/accountTools.js +2 -2
  567. package/build/tools/accountTools.js.map +1 -1
  568. package/build/tools/bundleTools.js +0 -16
  569. package/build/tools/bundleTools.js.map +1 -1
  570. package/build/tools/componentTools.d.ts.map +1 -1
  571. package/build/tools/componentTools.js +47 -53
  572. package/build/tools/componentTools.js.map +1 -1
  573. package/build/tools/connectionTools.js +1 -59
  574. package/build/tools/connectionTools.js.map +1 -1
  575. package/build/tools/deviceTools.js +0 -11
  576. package/build/tools/deviceTools.js.map +1 -1
  577. package/build/tools/executionTools.js +1 -18
  578. package/build/tools/executionTools.js.map +1 -1
  579. package/build/tools/interactionTools.d.ts +0 -10
  580. package/build/tools/interactionTools.d.ts.map +1 -1
  581. package/build/tools/interactionTools.js +190 -59
  582. package/build/tools/interactionTools.js.map +1 -1
  583. package/build/tools/logTools.d.ts +0 -13
  584. package/build/tools/logTools.d.ts.map +1 -1
  585. package/build/tools/logTools.js +2 -126
  586. package/build/tools/logTools.js.map +1 -1
  587. package/build/tools/metaTools.js +0 -10
  588. package/build/tools/metaTools.js.map +1 -1
  589. package/build/tools/networkTools.js +4 -43
  590. package/build/tools/networkTools.js.map +1 -1
  591. package/build/tools/requestTools.js +0 -5
  592. package/build/tools/requestTools.js.map +1 -1
  593. package/build/tools/screenshotTools.js +5 -111
  594. package/build/tools/screenshotTools.js.map +1 -1
  595. package/package.json +2 -2
  596. package/tools.json +16 -1
package/build/core/ios.js CHANGED
@@ -8,13 +8,9 @@ import { getActiveSimulatorUdid } from "./state.js";
8
8
  import { notifyDriverMissing } from "./logbox.js";
9
9
  import { execAsync } from "./exec.js";
10
10
  const execFileAsync = promisify(execFile);
11
- // simctl command timeout in milliseconds
12
11
  const SIMCTL_TIMEOUT = 30000;
13
- // IDB command timeout in milliseconds
14
12
  const IDB_TIMEOUT = 30000;
15
- // AXe command timeout in milliseconds
16
13
  const AXE_TIMEOUT = 30000;
17
- // Valid button types for IDB ui button command
18
14
  export const IOS_BUTTON_TYPES = [
19
15
  "HOME",
20
16
  "LOCK",
@@ -22,35 +18,19 @@ export const IOS_BUTTON_TYPES = [
22
18
  "SIRI",
23
19
  "APPLE_PAY",
24
20
  ];
25
- // Track connected IDB simulators to avoid redundant connect calls
26
21
  const connectedIdbSimulators = new Set();
27
- /**
28
- * Get the IDB executable path
29
- * Supports IDB_PATH environment variable for custom installations
30
- */
31
22
  function getIdbPath() {
32
23
  return process.env.IDB_PATH || "idb";
33
24
  }
34
- /**
35
- * Get the active iOS UI driver.
36
- * Defaults to AXe; set IOS_DRIVER=idb in MCP server env to use IDB instead.
37
- */
38
25
  function getIosDriver() {
39
26
  const driver = process.env.IOS_DRIVER?.toLowerCase();
40
27
  if (driver === "idb")
41
28
  return "idb";
42
29
  return "axe";
43
30
  }
44
- /**
45
- * Get the AXe executable path.
46
- * Supports AXE_PATH environment variable for custom installations.
47
- */
48
31
  function getAxePath() {
49
32
  return process.env.AXE_PATH || "axe";
50
33
  }
51
- /**
52
- * Run AXe command with execFile (no shell) for proper argument handling.
53
- */
54
34
  async function runAxe(...args) {
55
35
  const axePath = getAxePath();
56
36
  const { stdout, stderr } = await execFileAsync(axePath, args, {
@@ -61,10 +41,6 @@ async function runAxe(...args) {
61
41
  stderr: stderr.trim(),
62
42
  };
63
43
  }
64
- /**
65
- * Run IDB command with execFile (no shell) for proper argument handling
66
- * This matches the original ios-simulator-mcp implementation
67
- */
68
44
  async function runIdb(...args) {
69
45
  const idbPath = getIdbPath();
70
46
  const { stdout, stderr } = await execFileAsync(idbPath, args, {
@@ -75,9 +51,6 @@ async function runIdb(...args) {
75
51
  stderr: stderr.trim(),
76
52
  };
77
53
  }
78
- /**
79
- * Check if IDB is available
80
- */
81
54
  export async function isIdbAvailable() {
82
55
  try {
83
56
  await runIdb("--help");
@@ -87,9 +60,6 @@ export async function isIdbAvailable() {
87
60
  return false;
88
61
  }
89
62
  }
90
- /**
91
- * Check if AXe CLI is available
92
- */
93
63
  export async function isAxeAvailable() {
94
64
  try {
95
65
  await runAxe("--version");
@@ -99,17 +69,10 @@ export async function isAxeAvailable() {
99
69
  return false;
100
70
  }
101
71
  }
102
- /**
103
- * Check if the active iOS UI driver is available.
104
- * Used by tap.ts for early fail-fast checks.
105
- */
106
72
  export async function isUiDriverAvailable() {
107
73
  const driver = getIosDriver();
108
74
  return driver === "axe" ? isAxeAvailable() : isIdbAvailable();
109
75
  }
110
- /**
111
- * Get install instructions for the active iOS UI driver.
112
- */
113
76
  export function getUiDriverInstallHint() {
114
77
  const driver = getIosDriver();
115
78
  if (driver === "axe") {
@@ -117,9 +80,6 @@ export function getUiDriverInstallHint() {
117
80
  }
118
81
  return "IDB is not installed. Install with: brew install idb-companion\nTip: Consider switching to AXe (recommended) — brew install cameroncooke/axe/axe and set IOS_DRIVER=axe";
119
82
  }
120
- /**
121
- * Map IDB button names (uppercase/underscores) to AXe button names (lowercase/hyphens).
122
- */
123
83
  function toAxeButtonName(idbButton) {
124
84
  const map = {
125
85
  HOME: "home",
@@ -130,12 +90,7 @@ function toAxeButtonName(idbButton) {
130
90
  };
131
91
  return map[idbButton];
132
92
  }
133
- /**
134
- * Ensure IDB is connected to the specified simulator
135
- * IDB requires `idb connect <UDID>` before any UI commands work
136
- */
137
93
  async function ensureIdbConnected(udid) {
138
- // Skip if already connected in this session
139
94
  if (connectedIdbSimulators.has(udid)) {
140
95
  return { success: true };
141
96
  }
@@ -146,7 +101,6 @@ async function ensureIdbConnected(udid) {
146
101
  }
147
102
  catch (error) {
148
103
  const errorMessage = error instanceof Error ? error.message : String(error);
149
- // "Already connected" is not an error
150
104
  if (errorMessage.includes("already connected") ||
151
105
  errorMessage.includes("Connected")) {
152
106
  connectedIdbSimulators.add(udid);
@@ -158,12 +112,6 @@ async function ensureIdbConnected(udid) {
158
112
  };
159
113
  }
160
114
  }
161
- /**
162
- * Shared pre-flight check for all UI driver functions:
163
- * 1. Check driver availability
164
- * 2. Resolve target simulator UDID
165
- * 3. Ensure IDB connection (IDB only)
166
- */
167
115
  async function ensureUiDriverReady(udid) {
168
116
  const driver = getIosDriver();
169
117
  if (driver === "axe") {
@@ -205,19 +153,11 @@ async function ensureUiDriverReady(udid) {
205
153
  }
206
154
  return { ready: true, driver, targetUdid };
207
155
  }
208
- // Shared message for callers that hit a missing-Xcode preflight. Includes
209
- // the actual install command so the agent (or user) can act without guessing.
210
156
  export const XCODE_MISSING_ERROR = "Xcode command line tools are not installed or not selected. " +
211
157
  "Run `xcode-select --install` for the CLT, or install full Xcode from the App Store " +
212
158
  "and run `sudo xcode-select -s /Applications/Xcode.app/Contents/Developer`. " +
213
159
  "simctl ships with full Xcode (the standalone CLT does not include it).";
214
- // Memoized result — Xcode CLT presence does not change mid-session in
215
- // practice, and `xcrun simctl help` adds 50–200 ms per ios_* call without
216
- // caching. Re-resolve once per process; restart the MCP server after install.
217
160
  let simctlAvailableCache = null;
218
- /**
219
- * Check if simctl is available (cached for the lifetime of the process).
220
- */
221
161
  export async function isSimctlAvailable() {
222
162
  if (simctlAvailableCache)
223
163
  return simctlAvailableCache;
@@ -232,15 +172,9 @@ export async function isSimctlAvailable() {
232
172
  })();
233
173
  return simctlAvailableCache;
234
174
  }
235
- /**
236
- * Reset the cached simctl availability check. Test-only.
237
- */
238
175
  export function resetSimctlAvailableCache() {
239
176
  simctlAvailableCache = null;
240
177
  }
241
- /**
242
- * List iOS simulators
243
- */
244
178
  export async function listIOSSimulators(onlyBooted = false) {
245
179
  try {
246
180
  const simctlAvailable = await isSimctlAvailable();
@@ -255,7 +189,6 @@ export async function listIOSSimulators(onlyBooted = false) {
255
189
  });
256
190
  const data = JSON.parse(stdout);
257
191
  const simulators = [];
258
- // Parse devices from each runtime
259
192
  for (const [runtime, devices] of Object.entries(data.devices)) {
260
193
  if (!Array.isArray(devices))
261
194
  continue;
@@ -264,7 +197,6 @@ export async function listIOSSimulators(onlyBooted = false) {
264
197
  continue;
265
198
  if (onlyBooted && device.state !== "Booted")
266
199
  continue;
267
- // Extract iOS version from runtime string
268
200
  const runtimeMatch = runtime.match(/iOS[- ](\d+[.-]\d+)/i);
269
201
  const runtimeVersion = runtimeMatch
270
202
  ? `iOS ${runtimeMatch[1].replace("-", ".")}`
@@ -288,7 +220,6 @@ export async function listIOSSimulators(onlyBooted = false) {
288
220
  simulators: [],
289
221
  };
290
222
  }
291
- // Sort: Booted first, then by name
292
223
  simulators.sort((a, b) => {
293
224
  if (a.state === "Booted" && b.state !== "Booted")
294
225
  return -1;
@@ -315,11 +246,6 @@ export async function listIOSSimulators(onlyBooted = false) {
315
246
  };
316
247
  }
317
248
  }
318
- /**
319
- * Build the actionable "no booted simulator" error message. Lists up to 6
320
- * recently-available shutdown simulators by name+UDID and points the agent
321
- * at `ios_boot_simulator`, so it can recover instead of giving up.
322
- */
323
249
  export async function buildNoSimulatorError() {
324
250
  const base = "No iOS simulator is currently running. " +
325
251
  "Boot one with `ios_boot_simulator` (pass udid or name), " +
@@ -350,9 +276,6 @@ export async function buildNoSimulatorError() {
350
276
  return base;
351
277
  }
352
278
  }
353
- /**
354
- * Get the booted simulator UDID
355
- */
356
279
  export async function getBootedSimulatorUdid() {
357
280
  try {
358
281
  const { stdout } = await execAsync("xcrun simctl list devices booted -j", {
@@ -374,10 +297,6 @@ export async function getBootedSimulatorUdid() {
374
297
  return null;
375
298
  }
376
299
  }
377
- /**
378
- * Find a booted simulator's UDID by its device name
379
- * Matches Metro's deviceName against simulator names from simctl
380
- */
381
300
  export async function findSimulatorByName(deviceName) {
382
301
  try {
383
302
  const { stdout } = await execAsync("xcrun simctl list devices booted -j", {
@@ -392,11 +311,9 @@ export async function findSimulatorByName(deviceName) {
392
311
  if (device.state !== "Booted")
393
312
  continue;
394
313
  const normalizedSimName = device.name.toLowerCase().trim();
395
- // Exact match
396
314
  if (normalizedSimName === normalizedDeviceName) {
397
315
  return device.udid;
398
316
  }
399
- // Partial match (deviceName contains simulator name or vice versa)
400
317
  if (normalizedSimName.includes(normalizedDeviceName) ||
401
318
  normalizedDeviceName.includes(normalizedSimName)) {
402
319
  return device.udid;
@@ -409,28 +326,16 @@ export async function findSimulatorByName(deviceName) {
409
326
  return null;
410
327
  }
411
328
  }
412
- /**
413
- * Get the active simulator UDID (Metro-connected) or fall back to first booted simulator
414
- * This enables automatic device scoping based on Metro connection
415
- */
416
329
  export async function getActiveOrBootedSimulatorUdid() {
417
- // First, check if there's an active Metro-connected simulator
418
330
  const activeUdid = getActiveSimulatorUdid();
419
331
  if (activeUdid) {
420
332
  return activeUdid;
421
333
  }
422
- // Fall back to first booted simulator
423
334
  return getBootedSimulatorUdid();
424
335
  }
425
- /**
426
- * Build device selector for simctl command
427
- */
428
336
  function buildDeviceArg(udid) {
429
337
  return udid || "booted";
430
338
  }
431
- /**
432
- * Take a screenshot from an iOS simulator
433
- */
434
339
  export async function iosScreenshot(outputPath, udid) {
435
340
  try {
436
341
  const simctlAvailable = await isSimctlAvailable();
@@ -440,7 +345,6 @@ export async function iosScreenshot(outputPath, udid) {
440
345
  error: XCODE_MISSING_ERROR,
441
346
  };
442
347
  }
443
- // Resolve target UDID (prefer Metro-connected simulator)
444
348
  const targetUdid = udid || (await getActiveOrBootedSimulatorUdid());
445
349
  if (!targetUdid) {
446
350
  return {
@@ -448,14 +352,11 @@ export async function iosScreenshot(outputPath, udid) {
448
352
  error: await buildNoSimulatorError(),
449
353
  };
450
354
  }
451
- // Generate output path if not provided
452
355
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
453
356
  const finalOutputPath = outputPath || path.join(os.tmpdir(), `ios-screenshot-${timestamp}.png`);
454
357
  await execAsync(`xcrun simctl io ${targetUdid} screenshot "${finalOutputPath}"`, {
455
358
  timeout: SIMCTL_TIMEOUT,
456
359
  });
457
- // Resize image if needed (API limit: 2000px max for multi-image requests)
458
- // Return scale factor so AI can convert image coords to device coords
459
360
  const MAX_DIMENSION = 2000;
460
361
  const image = sharp(finalOutputPath);
461
362
  const metadata = await image.metadata();
@@ -464,7 +365,6 @@ export async function iosScreenshot(outputPath, udid) {
464
365
  let imageData;
465
366
  let scaleFactor = 1;
466
367
  if (originalWidth > MAX_DIMENSION || originalHeight > MAX_DIMENSION) {
467
- // Calculate scale to fit within MAX_DIMENSION
468
368
  scaleFactor = Math.max(originalWidth, originalHeight) / MAX_DIMENSION;
469
369
  imageData = await image
470
370
  .resize(MAX_DIMENSION, MAX_DIMENSION, {
@@ -493,9 +393,6 @@ export async function iosScreenshot(outputPath, udid) {
493
393
  };
494
394
  }
495
395
  }
496
- /**
497
- * Install an app on an iOS simulator
498
- */
499
396
  export async function iosInstallApp(appPath, udid) {
500
397
  try {
501
398
  const simctlAvailable = await isSimctlAvailable();
@@ -505,14 +402,12 @@ export async function iosInstallApp(appPath, udid) {
505
402
  error: XCODE_MISSING_ERROR,
506
403
  };
507
404
  }
508
- // Verify app exists
509
405
  if (!existsSync(appPath)) {
510
406
  return {
511
407
  success: false,
512
408
  error: `App bundle not found: ${appPath}`,
513
409
  };
514
410
  }
515
- // Resolve target UDID (prefer Metro-connected simulator)
516
411
  const targetUdid = udid || (await getActiveOrBootedSimulatorUdid());
517
412
  if (!targetUdid) {
518
413
  return {
@@ -521,7 +416,7 @@ export async function iosInstallApp(appPath, udid) {
521
416
  };
522
417
  }
523
418
  await execAsync(`xcrun simctl install ${targetUdid} "${appPath}"`, {
524
- timeout: 120000, // 2 minute timeout for install
419
+ timeout: 120000,
525
420
  });
526
421
  return {
527
422
  success: true,
@@ -535,9 +430,6 @@ export async function iosInstallApp(appPath, udid) {
535
430
  };
536
431
  }
537
432
  }
538
- /**
539
- * Launch an app on an iOS simulator
540
- */
541
433
  export async function iosLaunchApp(bundleId, udid) {
542
434
  try {
543
435
  const simctlAvailable = await isSimctlAvailable();
@@ -547,7 +439,6 @@ export async function iosLaunchApp(bundleId, udid) {
547
439
  error: XCODE_MISSING_ERROR,
548
440
  };
549
441
  }
550
- // Resolve target UDID (prefer Metro-connected simulator)
551
442
  const targetUdid = udid || (await getActiveOrBootedSimulatorUdid());
552
443
  if (!targetUdid) {
553
444
  return {
@@ -576,9 +467,6 @@ export async function iosLaunchApp(bundleId, udid) {
576
467
  };
577
468
  }
578
469
  }
579
- /**
580
- * Open a URL in the iOS simulator
581
- */
582
470
  export async function iosOpenUrl(url, udid) {
583
471
  try {
584
472
  const simctlAvailable = await isSimctlAvailable();
@@ -588,7 +476,6 @@ export async function iosOpenUrl(url, udid) {
588
476
  error: XCODE_MISSING_ERROR,
589
477
  };
590
478
  }
591
- // Resolve target UDID (prefer Metro-connected simulator)
592
479
  const targetUdid = udid || (await getActiveOrBootedSimulatorUdid());
593
480
  if (!targetUdid) {
594
481
  return {
@@ -618,9 +505,6 @@ export async function iosOpenUrl(url, udid) {
618
505
  };
619
506
  }
620
507
  }
621
- /**
622
- * Terminate an app on an iOS simulator
623
- */
624
508
  export async function iosTerminateApp(bundleId, udid) {
625
509
  try {
626
510
  const simctlAvailable = await isSimctlAvailable();
@@ -630,7 +514,6 @@ export async function iosTerminateApp(bundleId, udid) {
630
514
  error: XCODE_MISSING_ERROR,
631
515
  };
632
516
  }
633
- // Resolve target UDID (prefer Metro-connected simulator)
634
517
  const targetUdid = udid || (await getActiveOrBootedSimulatorUdid());
635
518
  if (!targetUdid) {
636
519
  return {
@@ -653,9 +536,6 @@ export async function iosTerminateApp(bundleId, udid) {
653
536
  };
654
537
  }
655
538
  }
656
- /**
657
- * Boot an iOS simulator
658
- */
659
539
  export async function iosBootSimulator(udid) {
660
540
  try {
661
541
  const simctlAvailable = await isSimctlAvailable();
@@ -666,14 +546,10 @@ export async function iosBootSimulator(udid) {
666
546
  };
667
547
  }
668
548
  await execAsync(`xcrun simctl boot ${udid}`, {
669
- timeout: 60000, // 1 minute timeout for boot
549
+ timeout: 60000,
670
550
  });
671
- // Open Simulator app
672
551
  await execAsync("open -a Simulator", { timeout: 10000 }).catch(() => {
673
- // Ignore if Simulator app doesn't open
674
552
  });
675
- // The device inventory just changed — drop the cached copy so the next
676
- // resolve/tap sees this simulator as booted without waiting out the TTL.
677
553
  const { resetDeviceDiscoveryCache } = await import("./deviceDiscovery.js");
678
554
  resetDeviceDiscoveryCache();
679
555
  return {
@@ -683,7 +559,6 @@ export async function iosBootSimulator(udid) {
683
559
  }
684
560
  catch (error) {
685
561
  const errorMessage = error instanceof Error ? error.message : String(error);
686
- // Already booted is not an error
687
562
  if (errorMessage.includes("Unable to boot device in current state: Booted")) {
688
563
  return {
689
564
  success: true,
@@ -696,16 +571,6 @@ export async function iosBootSimulator(udid) {
696
571
  };
697
572
  }
698
573
  }
699
- // ============================================================================
700
- // iOS UI Interaction Tools
701
- // These tools require an iOS UI driver to be installed:
702
- // Recommended: AXe — brew install cameroncooke/axe/axe
703
- // Alternative: IDB — brew install idb-companion
704
- // AXe is the default driver. Set IOS_DRIVER=idb to fall back to IDB.
705
- // ============================================================================
706
- /**
707
- * Tap at coordinates on an iOS simulator using IDB
708
- */
709
574
  export async function iosTap(x, y, options) {
710
575
  try {
711
576
  const preflight = await ensureUiDriverReady(options?.udid);
@@ -716,7 +581,6 @@ export async function iosTap(x, y, options) {
716
581
  const yRounded = Math.round(y);
717
582
  if (driver === "axe") {
718
583
  if (options?.duration !== undefined) {
719
- // Long press: use axe touch with delay (convert ms to seconds)
720
584
  const delaySecs = String(options.duration / 1000);
721
585
  await runAxe("touch", "-x", String(xRounded), "-y", String(yRounded), "--down", "--up", "--delay", delaySecs, "--udid", targetUdid);
722
586
  }
@@ -746,9 +610,6 @@ export async function iosTap(x, y, options) {
746
610
  };
747
611
  }
748
612
  }
749
- /**
750
- * Swipe gesture on an iOS simulator
751
- */
752
613
  export async function iosSwipe(startX, startY, endX, endY, options) {
753
614
  try {
754
615
  const preflight = await ensureUiDriverReady(options?.udid);
@@ -772,7 +633,6 @@ export async function iosSwipe(startX, startY, endX, endY, options) {
772
633
  String(y2),
773
634
  ];
774
635
  if (options?.duration !== undefined) {
775
- // Both axe and idb expect duration in seconds (matches the MCP tool schema).
776
636
  args.push("--duration", String(options.duration));
777
637
  }
778
638
  if (options?.delta !== undefined) {
@@ -806,9 +666,6 @@ export async function iosSwipe(startX, startY, endX, endY, options) {
806
666
  };
807
667
  }
808
668
  }
809
- /**
810
- * Input text into the active field on an iOS simulator
811
- */
812
669
  export async function iosInputText(text, udid) {
813
670
  try {
814
671
  const preflight = await ensureUiDriverReady(udid);
@@ -816,12 +673,6 @@ export async function iosInputText(text, udid) {
816
673
  return preflight.error;
817
674
  const { driver, targetUdid } = preflight;
818
675
  if (driver === "axe") {
819
- // Submit one HID event per chunk instead of AXe's default 200. Text
820
- // arriving out of order has been observed on device ("Alice" landing as
821
- // "Aicel", "CASEB" as "CSEBA" — in both cases the second keystroke last),
822
- // and a single large submission is the plausible cause. Measured at the
823
- // same latency as plain `axe type` (1432ms vs 1519ms for 19 chars), so
824
- // the serialisation is free.
825
676
  await runAxe("batch", "--udid", targetUdid, "--type-chunk-size", "1", "--step", `type '${text.replace(/'/g, "'\\''")}'`);
826
677
  }
827
678
  else {
@@ -841,12 +692,8 @@ export async function iosInputText(text, udid) {
841
692
  };
842
693
  }
843
694
  }
844
- /**
845
- * Press a hardware button on an iOS simulator
846
- */
847
695
  export async function iosButton(button, options) {
848
696
  try {
849
- // Validate button type
850
697
  if (!IOS_BUTTON_TYPES.includes(button)) {
851
698
  return {
852
699
  success: false,
@@ -887,9 +734,6 @@ export async function iosButton(button, options) {
887
734
  };
888
735
  }
889
736
  }
890
- /**
891
- * Send a key event to an iOS simulator
892
- */
893
737
  export async function iosKeyEvent(keycode, options) {
894
738
  try {
895
739
  const preflight = await ensureUiDriverReady(options?.udid);
@@ -926,9 +770,6 @@ export async function iosKeyEvent(keycode, options) {
926
770
  };
927
771
  }
928
772
  }
929
- /**
930
- * Send a sequence of key events to an iOS simulator
931
- */
932
773
  export async function iosKeySequence(keycodes, udid) {
933
774
  try {
934
775
  if (!keycodes || keycodes.length === 0) {
@@ -968,9 +809,6 @@ export async function iosKeySequence(keycodes, udid) {
968
809
  };
969
810
  }
970
811
  }
971
- /**
972
- * Format accessibility tree for human-readable output
973
- */
974
812
  function formatAccessibilityTree(elements, depth = 0) {
975
813
  const lines = [];
976
814
  const indent = " ".repeat(depth);
@@ -997,25 +835,15 @@ function formatAccessibilityTree(elements, depth = 0) {
997
835
  }
998
836
  return lines.join("\n");
999
837
  }
1000
- /**
1001
- * Probe the iOS root accessibility element for the top safe-area inset in points.
1002
- * React-native-screens modal/sheet presentations cause measureInWindow to return y
1003
- * relative to the content origin (below the inset) instead of the window. Knowing
1004
- * the inset lets us detect and correct the shifted coordinate space.
1005
- *
1006
- * Cached so repeated taps don't each incur a describe-ui call. The probe costs
1007
- * ~200ms and ran on essentially every fiber+native tap under the old 5s TTL;
1008
- * the inset itself only changes on rotation, so a longer TTL is safe (a rotation
1009
- * mid-session costs at most one stale correction until the entry expires).
1010
- */
1011
838
  const SAFE_AREA_CACHE_TTL_MS = 30_000;
1012
839
  const safeAreaTopCache = new Map();
1013
- export async function getIOSSafeAreaTop(udid) {
840
+ async function probeIOSRootFrame(udid) {
1014
841
  const cacheKey = udid || "__default__";
1015
842
  const cached = safeAreaTopCache.get(cacheKey);
1016
843
  if (cached && cached.expires > Date.now())
1017
- return cached.value;
844
+ return cached;
1018
845
  let value = 0;
846
+ let screen = null;
1019
847
  try {
1020
848
  const preflight = await ensureUiDriverReady(udid);
1021
849
  if (preflight.ready) {
@@ -1033,17 +861,25 @@ export async function getIOSSafeAreaTop(udid) {
1033
861
  const y = root?.frame?.y;
1034
862
  if (typeof y === "number" && y > 0)
1035
863
  value = Math.round(y);
864
+ const w = root?.frame?.width;
865
+ const h = root?.frame?.height;
866
+ if (typeof w === "number" && w > 0 && typeof h === "number" && h > 0) {
867
+ screen = { width: w, height: h };
868
+ }
1036
869
  }
1037
870
  }
1038
871
  catch {
1039
- // Fall through: value stays 0 (no correction)
1040
872
  }
1041
- safeAreaTopCache.set(cacheKey, { value, expires: Date.now() + SAFE_AREA_CACHE_TTL_MS });
1042
- return value;
873
+ const entry = { value, screen, expires: Date.now() + SAFE_AREA_CACHE_TTL_MS };
874
+ safeAreaTopCache.set(cacheKey, entry);
875
+ return entry;
876
+ }
877
+ export async function getIOSSafeAreaTop(udid) {
878
+ return (await probeIOSRootFrame(udid)).value;
879
+ }
880
+ export async function getIOSScreenPointSize(udid) {
881
+ return (await probeIOSRootFrame(udid)).screen;
1043
882
  }
1044
- /**
1045
- * Get accessibility info for the entire screen
1046
- */
1047
883
  export async function iosDescribeAll(udid) {
1048
884
  try {
1049
885
  const preflight = await ensureUiDriverReady(udid);
@@ -1061,9 +897,7 @@ export async function iosDescribeAll(udid) {
1061
897
  throw new Error(result.stderr);
1062
898
  stdout = result.stdout;
1063
899
  }
1064
- // Parse JSON response
1065
900
  const elements = JSON.parse(stdout);
1066
- // Format for human-readable output
1067
901
  const formatted = formatAccessibilityTree(elements);
1068
902
  const header = 'Note: All coordinates are in iOS points. Use tap(text="...") for tapping — do not pass these coordinates to tap(x, y) directly.\n\n';
1069
903
  return {
@@ -1079,27 +913,8 @@ export async function iosDescribeAll(udid) {
1079
913
  };
1080
914
  }
1081
915
  }
1082
- /**
1083
- * I1 (2026-05-16): when a native iOS system sheet (auth prompt, permission request,
1084
- * Safari sign-in sheet) is on screen, the RN app's fiber tree still describes the
1085
- * underlying screen — but those pressables aren't interactable until the sheet is
1086
- * dismissed. Detect the overlay so the screenshot output can warn the agent and
1087
- * point it at tap(x, y, native=true) for the overlay's actual buttons.
1088
- *
1089
- * Heuristic: scan the top three levels of the AXe accessibility tree for an
1090
- * element whose type matches an iOS sheet/alert role, OR whose AXLabel matches a
1091
- * well-known system-overlay phrase. False positives are rare because the patterns
1092
- * are specific to system-presented sheets — RN content rarely uses these labels.
1093
- */
1094
916
  const IOS_SYSTEM_OVERLAY_TYPES = /^(Alert|ActionSheet|Sheet|Dialog|SystemDialog)$/i;
1095
917
  const IOS_SYSTEM_OVERLAY_LABEL_PATTERNS = /Wants to Use|Sign in to|Sign In to|Allow .* to (use|access)|Open in Safari|Continue with|Use Face ID|Use Touch ID|Use Passcode/i;
1096
- /**
1097
- * Telltale of an RN accessibilityLabel rollup vs a system-presented sheet:
1098
- * a rollup concatenates multiple child labels with commas / newlines (3+ separators),
1099
- * while a system sheet's label is a single sentence. Without this guard the label
1100
- * patterns above false-positive on screens like gifted.co's login wrapper whose
1101
- * rollup happens to contain "Continue with" amongst many other phrases.
1102
- */
1103
918
  function looksLikeRNLabelRollup(label) {
1104
919
  const commas = (label.match(/,/g) ?? []).length;
1105
920
  const newlines = (label.match(/\n/g) ?? []).length;
@@ -1146,9 +961,6 @@ export function formatIOSSystemOverlayWarning(detection) {
1146
961
  `React Native screen UNDERNEATH and may be stale. For overlay buttons, take the coordinates ` +
1147
962
  `from the screenshot and call tap(x, y, native=true, platform="ios").`);
1148
963
  }
1149
- /**
1150
- * Get accessibility info at a specific point
1151
- */
1152
964
  export async function iosDescribePoint(x, y, udid) {
1153
965
  try {
1154
966
  const preflight = await ensureUiDriverReady(udid);
@@ -1168,7 +980,6 @@ export async function iosDescribePoint(x, y, udid) {
1168
980
  throw new Error(result.stderr);
1169
981
  stdout = result.stdout;
1170
982
  }
1171
- // Parse JSON response - may be single element or array
1172
983
  let element;
1173
984
  try {
1174
985
  const parsed = JSON.parse(stdout);
@@ -1181,7 +992,6 @@ export async function iosDescribePoint(x, y, udid) {
1181
992
  elements: [],
1182
993
  };
1183
994
  }
1184
- // Format for human-readable output
1185
995
  const parts = [];
1186
996
  if (element.type)
1187
997
  parts.push(`Type: ${element.type}`);
@@ -1211,9 +1021,6 @@ export async function iosDescribePoint(x, y, udid) {
1211
1021
  };
1212
1022
  }
1213
1023
  }
1214
- /**
1215
- * Helper to flatten nested accessibility elements
1216
- */
1217
1024
  function flattenElements(elements) {
1218
1025
  const result = [];
1219
1026
  for (const el of elements) {
@@ -1224,10 +1031,6 @@ function flattenElements(elements) {
1224
1031
  }
1225
1032
  return result;
1226
1033
  }
1227
- /**
1228
- * Tap an element by its accessibility label using IDB
1229
- * This simplifies the workflow: no need to manually find coordinates
1230
- */
1231
1034
  export async function iosTapElement(options) {
1232
1035
  try {
1233
1036
  const { label, labelContains, index = 0, duration, udid } = options;
@@ -1237,7 +1040,6 @@ export async function iosTapElement(options) {
1237
1040
  error: "Either 'label' (exact match) or 'labelContains' (partial match) is required",
1238
1041
  };
1239
1042
  }
1240
- // Get all accessibility elements
1241
1043
  const describeResult = await iosDescribeAll(udid);
1242
1044
  if (!describeResult.success || !describeResult.elements) {
1243
1045
  return {
@@ -1245,7 +1047,6 @@ export async function iosTapElement(options) {
1245
1047
  error: describeResult.error || "Failed to get accessibility elements",
1246
1048
  };
1247
1049
  }
1248
- // Flatten the tree and find matching elements
1249
1050
  const allElements = flattenElements(describeResult.elements);
1250
1051
  const matches = allElements.filter((el) => {
1251
1052
  if (!el.AXLabel)
@@ -1265,7 +1066,6 @@ export async function iosTapElement(options) {
1265
1066
  error: `No element found with ${searchTerm}`,
1266
1067
  };
1267
1068
  }
1268
- // Select element by index (default 0 = first match)
1269
1069
  if (index >= matches.length) {
1270
1070
  return {
1271
1071
  success: false,
@@ -1273,17 +1073,14 @@ export async function iosTapElement(options) {
1273
1073
  };
1274
1074
  }
1275
1075
  const element = matches[index];
1276
- // Check if element has frame coordinates
1277
1076
  if (!element.frame) {
1278
1077
  return {
1279
1078
  success: false,
1280
1079
  error: `Element "${element.AXLabel}" has no frame coordinates`,
1281
1080
  };
1282
1081
  }
1283
- // Calculate center
1284
1082
  const centerX = Math.round(element.frame.x + element.frame.width / 2);
1285
1083
  const centerY = Math.round(element.frame.y + element.frame.height / 2);
1286
- // Tap at center
1287
1084
  const tapResult = await iosTap(centerX, centerY, { duration, udid });
1288
1085
  if (tapResult.success) {
1289
1086
  return {
@@ -1300,9 +1097,6 @@ export async function iosTapElement(options) {
1300
1097
  };
1301
1098
  }
1302
1099
  }
1303
- /**
1304
- * Parse IDB accessibility output into simplified element array
1305
- */
1306
1100
  function parseIdbAccessibilityForFindElement(output) {
1307
1101
  const elements = [];
1308
1102
  try {
@@ -1355,14 +1149,9 @@ function parseIdbAccessibilityForFindElement(output) {
1355
1149
  }
1356
1150
  }
1357
1151
  catch {
1358
- // If JSON parsing fails, return empty array
1359
1152
  }
1360
1153
  return elements;
1361
1154
  }
1362
- /**
1363
- * Check if an element's center is within the screen viewport.
1364
- * Filters out off-screen elements in scroll views, horizontal pagers, etc.
1365
- */
1366
1155
  function isIOSElementInViewport(element, screenWidth, screenHeight) {
1367
1156
  const { center } = element;
1368
1157
  return (center.x >= 0 &&
@@ -1370,10 +1159,6 @@ function isIOSElementInViewport(element, screenWidth, screenHeight) {
1370
1159
  center.y >= 0 &&
1371
1160
  center.y <= screenHeight);
1372
1161
  }
1373
- /**
1374
- * Extract screen dimensions from the parsed UI tree.
1375
- * The root Application element's frame represents the screen size.
1376
- */
1377
1162
  function extractIOSScreenSize(rawOutput) {
1378
1163
  try {
1379
1164
  const data = JSON.parse(rawOutput);
@@ -1384,13 +1169,9 @@ function extractIOSScreenSize(rawOutput) {
1384
1169
  }
1385
1170
  }
1386
1171
  catch {
1387
- // Fall through
1388
1172
  }
1389
1173
  return null;
1390
1174
  }
1391
- /**
1392
- * Match iOS element against find options
1393
- */
1394
1175
  function matchesIOSFindElement(element, options) {
1395
1176
  if (options.label !== undefined) {
1396
1177
  if (element.label !== options.label)
@@ -1424,9 +1205,6 @@ function matchesIOSFindElement(element, options) {
1424
1205
  }
1425
1206
  return true;
1426
1207
  }
1427
- /**
1428
- * Get UI accessibility tree from iOS simulator using IDB (for find_element)
1429
- */
1430
1208
  export async function iosGetUITree(udid) {
1431
1209
  try {
1432
1210
  const preflight = await ensureUiDriverReady(udid);
@@ -1456,20 +1234,6 @@ export async function iosGetUITree(udid) {
1456
1234
  };
1457
1235
  }
1458
1236
  }
1459
- /**
1460
- * Bug 3 (2026-05-16): RN rolls up child Text labels into a parent View's
1461
- * accessibilityLabel when the parent is marked `accessible`. Result: a screen-root
1462
- * View whose label is a 200+ char concatenation of every visible text. `labelContains`
1463
- * matches it, frame is the whole screen, centroid happens to land on whatever button
1464
- * sits near the middle — and the tap silently lands on the wrong target.
1465
- *
1466
- * Heuristic filter: an element is an aggregator wrapper when EITHER
1467
- * (a) its label is much longer than the query AND contains concatenation hints
1468
- * (newlines, commas, semicolons — RN rollup typically joins with `, ` or `\n`)
1469
- * (b) its frame covers >50% of the viewport (a real button never does this)
1470
- *
1471
- * Both heuristics together catch the gifted.co LoginScreen wrapper case the doc cites.
1472
- */
1473
1237
  function isAggregatorWrapper(element, query, screenSize) {
1474
1238
  const labelLen = element.label?.length ?? 0;
1475
1239
  const queryLen = query.length;
@@ -1487,15 +1251,6 @@ function isAggregatorWrapper(element, query, screenSize) {
1487
1251
  }
1488
1252
  return isLongConcatenatedLabel || isFullScreenFrame;
1489
1253
  }
1490
- /**
1491
- * Find element(s) in the iOS UI tree matching the given criteria.
1492
- *
1493
- * `tree` lets a caller reuse an accessibility dump it already has. Each fetch
1494
- * shells out to `axe describe-ui` (~210ms measured 2026-08-01) and the tap
1495
- * strategy runs two or three passes with different predicates, so re-fetching
1496
- * per pass was costing 400-600ms per accessibility tap for a tree that cannot
1497
- * change between passes.
1498
- */
1499
1254
  export async function iosFindElement(options, udid, tree) {
1500
1255
  try {
1501
1256
  if (!options.label &&
@@ -1519,8 +1274,6 @@ export async function iosFindElement(options, udid, tree) {
1519
1274
  error: treeResult.error,
1520
1275
  };
1521
1276
  }
1522
- // Filter to viewport-visible elements first, then match criteria.
1523
- // This prevents selecting off-screen duplicates in scroll views / pagers.
1524
1277
  const screenSize = treeResult.rawOutput
1525
1278
  ? extractIOSScreenSize(treeResult.rawOutput)
1526
1279
  : null;
@@ -1528,21 +1281,9 @@ export async function iosFindElement(options, udid, tree) {
1528
1281
  ? treeResult.elements.filter((el) => isIOSElementInViewport(el, screenSize.width, screenSize.height))
1529
1282
  : treeResult.elements;
1530
1283
  let matches = visibleElements.filter((el) => matchesIOSFindElement(el, options));
1531
- // If no visible matches, fall back to all elements (element may be partially visible
1532
- // or the screen size extraction failed)
1533
1284
  if (matches.length === 0) {
1534
1285
  matches = treeResult.elements.filter((el) => matchesIOSFindElement(el, options));
1535
1286
  }
1536
- // Bug 3 (2026-05-16): when matching by partial string (labelContains / identifierContains),
1537
- // drop aggregator-wrapper elements that match only because their label rolls up every
1538
- // visible child's text. Without this filter, tapping such a wrapper's centroid
1539
- // silently lands on the wrong on-screen element.
1540
- //
1541
- // When ALL matches are aggregator wrappers (the common case — RN often rolls labels
1542
- // up to a single accessible wrapper, leaving no individual element with a matching
1543
- // label), refuse with a clear error pointing the agent at component= or coordinates.
1544
- // This is safer than silently tapping the wrapper centroid (which lands wherever the
1545
- // wrapper's geometric center happens to be — usually NOT on the requested button).
1546
1287
  if (options.labelContains !== undefined && matches.length > 0) {
1547
1288
  const filtered = matches.filter((el) => !isAggregatorWrapper(el, options.labelContains, screenSize));
1548
1289
  if (filtered.length > 0) {
@@ -1608,20 +1349,11 @@ export async function iosFindElement(options, udid, tree) {
1608
1349
  };
1609
1350
  }
1610
1351
  }
1611
- // DPR cache: UDID → device pixel ratio (never changes during session)
1612
1352
  const dprCache = new Map();
1613
- /**
1614
- * Pure calculation: DPR = screenshotPixelWidth / screenPointWidth.
1615
- * Exported for unit testing.
1616
- */
1353
+ const pixelSizeCache = new Map();
1617
1354
  export function calculateDPR(screenshotPixelWidth, screenPointWidth) {
1618
1355
  return Math.round(screenshotPixelWidth / screenPointWidth);
1619
1356
  }
1620
- /**
1621
- * Query the actual device pixel ratio for an iOS simulator.
1622
- * Uses screenshot pixel width vs accessibility root frame width.
1623
- * Result is cached per UDID for the session.
1624
- */
1625
1357
  export async function getDevicePixelRatio(udid, pixelDims) {
1626
1358
  const cacheKey = udid || "__default__";
1627
1359
  const cached = dprCache.get(cacheKey);
@@ -1634,14 +1366,10 @@ export async function getDevicePixelRatio(udid, pixelDims) {
1634
1366
  let pixelWidth;
1635
1367
  let pixelHeight;
1636
1368
  if (pixelDims && pixelDims.width > 0 && pixelDims.height > 0) {
1637
- // The caller already captured a frame this turn (tap always does) — reuse
1638
- // its dimensions instead of shelling out for a screenshot of our own, which
1639
- // measured ~230ms of the ~470ms first-call cost.
1640
1369
  pixelWidth = pixelDims.width;
1641
1370
  pixelHeight = pixelDims.height;
1642
1371
  }
1643
1372
  else {
1644
- // Take screenshot to get pixel dimensions
1645
1373
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
1646
1374
  const screenshotPath = path.join(os.tmpdir(), `ios-dpr-screenshot-${timestamp}.png`);
1647
1375
  await execAsync(`xcrun simctl io ${resolvedUdid} screenshot "${screenshotPath}"`, {
@@ -1651,7 +1379,9 @@ export async function getDevicePixelRatio(udid, pixelDims) {
1651
1379
  pixelWidth = metadata.width;
1652
1380
  pixelHeight = metadata.height;
1653
1381
  }
1654
- // Try accessibility tree for exact DPR, fall back to inference
1382
+ if (pixelWidth > 0 && pixelHeight > 0) {
1383
+ pixelSizeCache.set(cacheKey, { width: pixelWidth, height: pixelHeight });
1384
+ }
1655
1385
  const driver = getIosDriver();
1656
1386
  const driverOk = driver === "axe" ? await isAxeAvailable() : await isIdbAvailable();
1657
1387
  if (driverOk) {
@@ -1674,22 +1404,25 @@ export async function getDevicePixelRatio(udid, pixelDims) {
1674
1404
  }
1675
1405
  }
1676
1406
  catch {
1677
- // driver failed — fall through to inference
1678
1407
  }
1679
1408
  }
1680
- // Fallback: infer DPR from screenshot pixel dimensions
1681
1409
  const { inferIOSDevicePixelRatio } = await import("./ocr.js");
1682
1410
  const dpr = inferIOSDevicePixelRatio(pixelWidth, pixelHeight);
1683
1411
  dprCache.set(cacheKey, dpr);
1684
1412
  return dpr;
1685
1413
  }
1686
- /** Clear DPR cache (for testing or when devices change) */
1414
+ export async function getIOSScreenPixelSize(udid, pixelDims) {
1415
+ const cacheKey = udid || "__default__";
1416
+ const cached = pixelSizeCache.get(cacheKey);
1417
+ if (cached)
1418
+ return cached;
1419
+ await getDevicePixelRatio(udid, pixelDims).catch(() => undefined);
1420
+ return pixelSizeCache.get(cacheKey) ?? null;
1421
+ }
1687
1422
  export function clearDPRCache() {
1688
1423
  dprCache.clear();
1424
+ pixelSizeCache.clear();
1689
1425
  }
1690
- /**
1691
- * Wait for element to appear on iOS screen with polling
1692
- */
1693
1426
  export async function iosWaitForElement(options, udid) {
1694
1427
  const timeoutMs = options.timeoutMs ?? 10000;
1695
1428
  const pollIntervalMs = options.pollIntervalMs ?? 500;