react-native-ai-debugger 2.4.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.
- package/README.md +2 -0
- package/build/__tests__/helpers/fake-cdp-server.d.ts +0 -12
- package/build/__tests__/helpers/fake-cdp-server.d.ts.map +1 -1
- package/build/__tests__/helpers/fake-cdp-server.js +0 -22
- package/build/__tests__/helpers/fake-cdp-server.js.map +1 -1
- package/build/__tests__/helpers/tap-test-helpers.d.ts +0 -23
- package/build/__tests__/helpers/tap-test-helpers.d.ts.map +1 -1
- package/build/__tests__/helpers/tap-test-helpers.js +2 -29
- package/build/__tests__/helpers/tap-test-helpers.js.map +1 -1
- package/build/__tests__/integration/auto-reconnect.test.js +4 -14
- package/build/__tests__/integration/auto-reconnect.test.js.map +1 -1
- package/build/__tests__/integration/connection-health.test.js +1 -10
- package/build/__tests__/integration/connection-health.test.js.map +1 -1
- package/build/__tests__/integration/execute-in-app.test.js +1 -11
- package/build/__tests__/integration/execute-in-app.test.js.map +1 -1
- package/build/__tests__/integration/native-logs.test.js +0 -5
- package/build/__tests__/integration/native-logs.test.js.map +1 -1
- package/build/__tests__/integration/per-call-timeout.test.js +1 -5
- package/build/__tests__/integration/per-call-timeout.test.js.map +1 -1
- package/build/__tests__/integration/socket-close-midflight.test.js +2 -12
- package/build/__tests__/integration/socket-close-midflight.test.js.map +1 -1
- package/build/__tests__/integration/stale-target-probe.test.js +2 -17
- package/build/__tests__/integration/stale-target-probe.test.js.map +1 -1
- package/build/__tests__/integration/tap-strategies.test.js +2 -36
- package/build/__tests__/integration/tap-strategies.test.js.map +1 -1
- package/build/__tests__/integration/text-input.test.js +0 -39
- package/build/__tests__/integration/text-input.test.js.map +1 -1
- package/build/__tests__/integration/tools.test.js +1 -7
- package/build/__tests__/integration/tools.test.js.map +1 -1
- package/build/__tests__/setup.js +0 -2
- package/build/__tests__/setup.js.map +1 -1
- package/build/__tests__/unit/androidEmulatorAvds.test.js +0 -10
- package/build/__tests__/unit/androidEmulatorAvds.test.js.map +1 -1
- package/build/__tests__/unit/androidPinch.test.d.ts +2 -0
- package/build/__tests__/unit/androidPinch.test.d.ts.map +1 -0
- package/build/__tests__/unit/androidPinch.test.js +119 -0
- package/build/__tests__/unit/androidPinch.test.js.map +1 -0
- package/build/__tests__/unit/appContext.test.js +0 -2
- package/build/__tests__/unit/appContext.test.js.map +1 -1
- package/build/__tests__/unit/appRequest.test.js +0 -3
- package/build/__tests__/unit/appRequest.test.js.map +1 -1
- package/build/__tests__/unit/buildToken.test.js +0 -6
- package/build/__tests__/unit/buildToken.test.js.map +1 -1
- package/build/__tests__/unit/burst.test.js +0 -1
- package/build/__tests__/unit/burst.test.js.map +1 -1
- package/build/__tests__/unit/clearLogsNative.test.js +0 -8
- package/build/__tests__/unit/clearLogsNative.test.js.map +1 -1
- package/build/__tests__/unit/componentFormatNaming.test.js +0 -13
- package/build/__tests__/unit/componentFormatNaming.test.js.map +1 -1
- package/build/__tests__/unit/componentSource.test.js +0 -4
- package/build/__tests__/unit/componentSource.test.js.map +1 -1
- package/build/__tests__/unit/componentSourceResolve.test.js +0 -1
- package/build/__tests__/unit/componentSourceResolve.test.js.map +1 -1
- package/build/__tests__/unit/contextWiring.test.js +0 -3
- package/build/__tests__/unit/contextWiring.test.js.map +1 -1
- package/build/__tests__/unit/coordinateMiss.test.js +0 -16
- package/build/__tests__/unit/coordinateMiss.test.js.map +1 -1
- package/build/__tests__/unit/deviceDiscovery.test.js +0 -4
- package/build/__tests__/unit/deviceDiscovery.test.js.map +1 -1
- package/build/__tests__/unit/deviceResolution.test.js +0 -2
- package/build/__tests__/unit/deviceResolution.test.js.map +1 -1
- package/build/__tests__/unit/deviceResolver.test.js +0 -1
- package/build/__tests__/unit/deviceResolver.test.js.map +1 -1
- package/build/__tests__/unit/deviceResolverStaleRetry.test.js +0 -8
- package/build/__tests__/unit/deviceResolverStaleRetry.test.js.map +1 -1
- package/build/__tests__/unit/emulatorBridge.test.d.ts +2 -0
- package/build/__tests__/unit/emulatorBridge.test.d.ts.map +1 -0
- package/build/__tests__/unit/emulatorBridge.test.js +76 -0
- package/build/__tests__/unit/emulatorBridge.test.js.map +1 -0
- package/build/__tests__/unit/emulatorGrpc.test.d.ts +2 -0
- package/build/__tests__/unit/emulatorGrpc.test.d.ts.map +1 -0
- package/build/__tests__/unit/emulatorGrpc.test.js +33 -0
- package/build/__tests__/unit/emulatorGrpc.test.js.map +1 -0
- package/build/__tests__/unit/epochRender.test.js +0 -3
- package/build/__tests__/unit/epochRender.test.js.map +1 -1
- package/build/__tests__/unit/errorCategorization.test.js +0 -18
- package/build/__tests__/unit/errorCategorization.test.js.map +1 -1
- package/build/__tests__/unit/exec.test.js +0 -1
- package/build/__tests__/unit/exec.test.js.map +1 -1
- package/build/__tests__/unit/executor.test.js +0 -15
- package/build/__tests__/unit/executor.test.js.map +1 -1
- package/build/__tests__/unit/findElementTreeReuse.test.js +0 -12
- package/build/__tests__/unit/findElementTreeReuse.test.js.map +1 -1
- package/build/__tests__/unit/fingerprint.test.js +0 -4
- package/build/__tests__/unit/fingerprint.test.js.map +1 -1
- package/build/__tests__/unit/focusedInputTools.test.js +0 -4
- package/build/__tests__/unit/focusedInputTools.test.js.map +1 -1
- package/build/__tests__/unit/getLogsEmptyContract.test.js +0 -30
- package/build/__tests__/unit/getLogsEmptyContract.test.js.map +1 -1
- package/build/__tests__/unit/getLogsNativeSource.test.js +0 -22
- package/build/__tests__/unit/getLogsNativeSource.test.js.map +1 -1
- package/build/__tests__/unit/graphqlOperation.test.d.ts +2 -0
- package/build/__tests__/unit/graphqlOperation.test.d.ts.map +1 -0
- package/build/__tests__/unit/graphqlOperation.test.js +107 -0
- package/build/__tests__/unit/graphqlOperation.test.js.map +1 -0
- package/build/__tests__/unit/inputTarget.test.js +0 -16
- package/build/__tests__/unit/inputTarget.test.js.map +1 -1
- package/build/__tests__/unit/inputTextTool.test.js +0 -3
- package/build/__tests__/unit/inputTextTool.test.js.map +1 -1
- package/build/__tests__/unit/jsLogEvents.test.js +0 -3
- package/build/__tests__/unit/jsLogEvents.test.js.map +1 -1
- package/build/__tests__/unit/keyboardMetrics.test.js +0 -2
- package/build/__tests__/unit/keyboardMetrics.test.js.map +1 -1
- package/build/__tests__/unit/keyboardRaise.test.js +0 -2
- package/build/__tests__/unit/keyboardRaise.test.js.map +1 -1
- package/build/__tests__/unit/licenseGrace.test.js +1 -4
- package/build/__tests__/unit/licenseGrace.test.js.map +1 -1
- package/build/__tests__/unit/logEventFormat.test.js +0 -27
- package/build/__tests__/unit/logEventFormat.test.js.map +1 -1
- package/build/__tests__/unit/logEvents.test.js +1 -5
- package/build/__tests__/unit/logEvents.test.js.map +1 -1
- package/build/__tests__/unit/logGrouping.test.js +0 -2
- package/build/__tests__/unit/logGrouping.test.js.map +1 -1
- package/build/__tests__/unit/logOwnership.test.js +0 -11
- package/build/__tests__/unit/logOwnership.test.js.map +1 -1
- package/build/__tests__/unit/logRelevance.test.js +0 -6
- package/build/__tests__/unit/logRelevance.test.js.map +1 -1
- package/build/__tests__/unit/logSourceAndroid.test.js +0 -4
- package/build/__tests__/unit/logSourceAndroid.test.js.map +1 -1
- package/build/__tests__/unit/logSourceIos.test.js +0 -5
- package/build/__tests__/unit/logSourceIos.test.js.map +1 -1
- package/build/__tests__/unit/logs.test.js +0 -1
- package/build/__tests__/unit/logs.test.js.map +1 -1
- package/build/__tests__/unit/measureComponentTools.test.js +0 -1
- package/build/__tests__/unit/measureComponentTools.test.js.map +1 -1
- package/build/__tests__/unit/metro.test.js +0 -2
- package/build/__tests__/unit/metro.test.js.map +1 -1
- package/build/__tests__/unit/nativeInputValue.test.js +0 -4
- package/build/__tests__/unit/nativeInputValue.test.js.map +1 -1
- package/build/__tests__/unit/nativeLogs.test.js +1 -23
- package/build/__tests__/unit/nativeLogs.test.js.map +1 -1
- package/build/__tests__/unit/navigateVerify.test.js +0 -6
- package/build/__tests__/unit/navigateVerify.test.js.map +1 -1
- package/build/__tests__/unit/navigation.test.js +0 -5
- package/build/__tests__/unit/navigation.test.js.map +1 -1
- package/build/__tests__/unit/networkInterceptor.test.js +0 -2
- package/build/__tests__/unit/networkInterceptor.test.js.map +1 -1
- package/build/__tests__/unit/overlayGuard.test.js +0 -17
- package/build/__tests__/unit/overlayGuard.test.js.map +1 -1
- package/build/__tests__/unit/pendingExecutions.test.js +0 -6
- package/build/__tests__/unit/pendingExecutions.test.js.map +1 -1
- package/build/__tests__/unit/pinchGeometry.test.d.ts +2 -0
- package/build/__tests__/unit/pinchGeometry.test.d.ts.map +1 -0
- package/build/__tests__/unit/pinchGeometry.test.js +139 -0
- package/build/__tests__/unit/pinchGeometry.test.js.map +1 -0
- package/build/__tests__/unit/pinchTool.test.d.ts +2 -0
- package/build/__tests__/unit/pinchTool.test.d.ts.map +1 -0
- package/build/__tests__/unit/pinchTool.test.js +39 -0
- package/build/__tests__/unit/pinchTool.test.js.map +1 -0
- package/build/__tests__/unit/probe.test.js +0 -1
- package/build/__tests__/unit/probe.test.js.map +1 -1
- package/build/__tests__/unit/projectMemory.test.js +6 -7
- package/build/__tests__/unit/projectMemory.test.js.map +1 -1
- package/build/__tests__/unit/projectStore.test.js +2 -2
- package/build/__tests__/unit/projectStore.test.js.map +1 -1
- package/build/__tests__/unit/reloadAppAutoConnect.test.js +0 -9
- package/build/__tests__/unit/reloadAppAutoConnect.test.js.map +1 -1
- package/build/__tests__/unit/rnGlobalsBootstrap.test.js +3 -3
- package/build/__tests__/unit/rnGlobalsBootstrap.test.js.map +1 -1
- package/build/__tests__/unit/routeHistory.test.d.ts +2 -0
- package/build/__tests__/unit/routeHistory.test.d.ts.map +1 -0
- package/build/__tests__/unit/routeHistory.test.js +136 -0
- package/build/__tests__/unit/routeHistory.test.js.map +1 -0
- package/build/__tests__/unit/screenSpace.test.js +0 -16
- package/build/__tests__/unit/screenSpace.test.js.map +1 -1
- package/build/__tests__/unit/screenState.test.js +3 -6
- package/build/__tests__/unit/screenState.test.js.map +1 -1
- package/build/__tests__/unit/screenStateInputValue.test.js +0 -7
- package/build/__tests__/unit/screenStateInputValue.test.js.map +1 -1
- package/build/__tests__/unit/screenStateKeyboard.test.js +0 -1
- package/build/__tests__/unit/screenStateKeyboard.test.js.map +1 -1
- package/build/__tests__/unit/screenshot-diff.test.js +0 -6
- package/build/__tests__/unit/screenshot-diff.test.js.map +1 -1
- package/build/__tests__/unit/sdkMirrorPoller.test.js +0 -8
- package/build/__tests__/unit/sdkMirrorPoller.test.js.map +1 -1
- package/build/__tests__/unit/settleAndDiff.test.js +0 -11
- package/build/__tests__/unit/settleAndDiff.test.js.map +1 -1
- package/build/__tests__/unit/signedVerdict.test.js +0 -5
- package/build/__tests__/unit/signedVerdict.test.js.map +1 -1
- package/build/__tests__/unit/swipeDirection.test.js +6 -9
- package/build/__tests__/unit/swipeDirection.test.js.map +1 -1
- package/build/__tests__/unit/symbolicate.test.js +0 -1
- package/build/__tests__/unit/symbolicate.test.js.map +1 -1
- package/build/__tests__/unit/tap.test.js +0 -38
- package/build/__tests__/unit/tap.test.js.map +1 -1
- package/build/__tests__/unit/textEntry.test.js +0 -27
- package/build/__tests__/unit/textEntry.test.js.map +1 -1
- package/build/__tests__/unit/toolDescriptions.test.js +0 -40
- package/build/__tests__/unit/toolDescriptions.test.js.map +1 -1
- package/build/__tests__/unit/toolsJson.test.js +0 -10
- package/build/__tests__/unit/toolsJson.test.js.map +1 -1
- package/build/__tests__/unit/transportClassify.test.js +0 -20
- package/build/__tests__/unit/transportClassify.test.js.map +1 -1
- package/build/__tests__/unit/truncate.test.js +0 -5
- package/build/__tests__/unit/truncate.test.js.map +1 -1
- package/build/__tests__/unit/usageCache.test.js +0 -3
- package/build/__tests__/unit/usageCache.test.js.map +1 -1
- package/build/__tests__/unit/usageGate.test.js +1 -1
- package/build/__tests__/unit/usageGate.test.js.map +1 -1
- package/build/__tests__/unit/visibility.test.js +0 -8
- package/build/__tests__/unit/visibility.test.js.map +1 -1
- package/build/__tests__/unit/waitForMeasurements.test.js +2 -7
- package/build/__tests__/unit/waitForMeasurements.test.js.map +1 -1
- package/build/bin.js +0 -18
- package/build/bin.js.map +1 -1
- package/build/core/android.d.ts +0 -120
- package/build/core/android.d.ts.map +1 -1
- package/build/core/android.js +4 -259
- package/build/core/android.js.map +1 -1
- package/build/core/androidPinch.d.ts +21 -0
- package/build/core/androidPinch.d.ts.map +1 -0
- package/build/core/androidPinch.js +72 -0
- package/build/core/androidPinch.js.map +1 -0
- package/build/core/androidSystemBars.d.ts +7 -0
- package/build/core/androidSystemBars.d.ts.map +1 -0
- package/build/core/androidSystemBars.js +21 -0
- package/build/core/androidSystemBars.js.map +1 -0
- package/build/core/appContext.d.ts +0 -50
- package/build/core/appContext.d.ts.map +1 -1
- package/build/core/appContext.js +0 -50
- package/build/core/appContext.js.map +1 -1
- package/build/core/appDetection.d.ts +0 -16
- package/build/core/appDetection.d.ts.map +1 -1
- package/build/core/appDetection.js +0 -36
- package/build/core/appDetection.js.map +1 -1
- package/build/core/appRequest.d.ts +0 -12
- package/build/core/appRequest.d.ts.map +1 -1
- package/build/core/appRequest.js +0 -15
- package/build/core/appRequest.js.map +1 -1
- package/build/core/bufferConfig.d.ts +0 -2
- package/build/core/bufferConfig.d.ts.map +1 -1
- package/build/core/bufferConfig.js +0 -2
- package/build/core/bufferConfig.js.map +1 -1
- package/build/core/bundle.js +0 -40
- package/build/core/bundle.js.map +1 -1
- package/build/core/componentSearch.d.ts +0 -19
- package/build/core/componentSearch.d.ts.map +1 -1
- package/build/core/componentSearch.js +1 -22
- package/build/core/componentSearch.js.map +1 -1
- package/build/core/componentSource.d.ts +0 -21
- package/build/core/componentSource.d.ts.map +1 -1
- package/build/core/componentSource.js +0 -27
- package/build/core/componentSource.js.map +1 -1
- package/build/core/componentTree.js +0 -12
- package/build/core/componentTree.js.map +1 -1
- package/build/core/config.d.ts +0 -8
- package/build/core/config.d.ts.map +1 -1
- package/build/core/config.js +0 -10
- package/build/core/config.js.map +1 -1
- package/build/core/connection.d.ts +0 -76
- package/build/core/connection.d.ts.map +1 -1
- package/build/core/connection.js +15 -340
- package/build/core/connection.js.map +1 -1
- package/build/core/connectionState.d.ts +0 -78
- package/build/core/connectionState.d.ts.map +1 -1
- package/build/core/connectionState.js +4 -92
- package/build/core/connectionState.js.map +1 -1
- package/build/core/debugGlobals.js +0 -68
- package/build/core/debugGlobals.js.map +1 -1
- package/build/core/deviceDiscovery.d.ts +0 -5
- package/build/core/deviceDiscovery.d.ts.map +1 -1
- package/build/core/deviceDiscovery.js +0 -12
- package/build/core/deviceDiscovery.js.map +1 -1
- package/build/core/deviceResolver.d.ts +0 -10
- package/build/core/deviceResolver.d.ts.map +1 -1
- package/build/core/deviceResolver.js +0 -54
- package/build/core/deviceResolver.js.map +1 -1
- package/build/core/emulatorBridge.d.ts +17 -0
- package/build/core/emulatorBridge.d.ts.map +1 -0
- package/build/core/emulatorBridge.js +78 -0
- package/build/core/emulatorBridge.js.map +1 -0
- package/build/core/emulatorGrpc.d.ts +14 -0
- package/build/core/emulatorGrpc.d.ts.map +1 -0
- package/build/core/emulatorGrpc.js +93 -0
- package/build/core/emulatorGrpc.js.map +1 -0
- package/build/core/epochRender.d.ts +0 -12
- package/build/core/epochRender.d.ts.map +1 -1
- package/build/core/epochRender.js +0 -12
- package/build/core/epochRender.js.map +1 -1
- package/build/core/errorScreenParser.d.ts +0 -21
- package/build/core/errorScreenParser.d.ts.map +1 -1
- package/build/core/errorScreenParser.js +0 -50
- package/build/core/errorScreenParser.js.map +1 -1
- package/build/core/errors.d.ts +0 -15
- package/build/core/errors.d.ts.map +1 -1
- package/build/core/errors.js +0 -15
- package/build/core/errors.js.map +1 -1
- package/build/core/escapeNonAscii.d.ts +0 -9
- package/build/core/escapeNonAscii.d.ts.map +1 -1
- package/build/core/escapeNonAscii.js +1 -43
- package/build/core/escapeNonAscii.js.map +1 -1
- package/build/core/exec.d.ts +0 -12
- package/build/core/exec.d.ts.map +1 -1
- package/build/core/exec.js +2 -14
- package/build/core/exec.js.map +1 -1
- package/build/core/executor.js +0 -3
- package/build/core/executor.js.map +1 -1
- package/build/core/fastRefreshRecorder.js +0 -4
- package/build/core/fastRefreshRecorder.js.map +1 -1
- package/build/core/fastRefreshTools.js +0 -3
- package/build/core/fastRefreshTools.js.map +1 -1
- package/build/core/feedback.js +0 -4
- package/build/core/feedback.js.map +1 -1
- package/build/core/graphqlOperation.d.ts +13 -0
- package/build/core/graphqlOperation.d.ts.map +1 -0
- package/build/core/graphqlOperation.js +80 -0
- package/build/core/graphqlOperation.js.map +1 -0
- package/build/core/guides.d.ts +0 -10
- package/build/core/guides.d.ts.map +1 -1
- package/build/core/guides.js +18 -12
- package/build/core/guides.js.map +1 -1
- package/build/core/iconSemantics.d.ts +0 -15
- package/build/core/iconSemantics.d.ts.map +1 -1
- package/build/core/iconSemantics.js +0 -31
- package/build/core/iconSemantics.js.map +1 -1
- package/build/core/index.d.ts +2 -0
- package/build/core/index.d.ts.map +1 -1
- package/build/core/index.js +5 -47
- package/build/core/index.js.map +1 -1
- package/build/core/injected/visibility.d.ts +0 -30
- package/build/core/injected/visibility.d.ts.map +1 -1
- package/build/core/injected/visibility.js +0 -33
- package/build/core/injected/visibility.js.map +1 -1
- package/build/core/injectedFilters.d.ts +0 -28
- package/build/core/injectedFilters.d.ts.map +1 -1
- package/build/core/injectedFilters.js +0 -28
- package/build/core/injectedFilters.js.map +1 -1
- package/build/core/inputTarget.d.ts +1 -34
- package/build/core/inputTarget.d.ts.map +1 -1
- package/build/core/inputTarget.js +0 -26
- package/build/core/inputTarget.js.map +1 -1
- package/build/core/inputTargetTools.d.ts +0 -7
- package/build/core/inputTargetTools.d.ts.map +1 -1
- package/build/core/inputTargetTools.js +0 -7
- package/build/core/inputTargetTools.js.map +1 -1
- package/build/core/inspector.d.ts +0 -33
- package/build/core/inspector.d.ts.map +1 -1
- package/build/core/inspector.js +0 -36
- package/build/core/inspector.js.map +1 -1
- package/build/core/ios.d.ts +0 -127
- package/build/core/ios.d.ts.map +1 -1
- package/build/core/ios.js +2 -313
- package/build/core/ios.js.map +1 -1
- package/build/core/jsExecute.d.ts +0 -100
- package/build/core/jsExecute.d.ts.map +1 -1
- package/build/core/jsExecute.js +0 -261
- package/build/core/jsExecute.js.map +1 -1
- package/build/core/jsLogEvents.d.ts +0 -8
- package/build/core/jsLogEvents.d.ts.map +1 -1
- package/build/core/jsLogEvents.js +0 -8
- package/build/core/jsLogEvents.js.map +1 -1
- package/build/core/keyboardMetrics.js +0 -8
- package/build/core/keyboardMetrics.js.map +1 -1
- package/build/core/keyboardRaise.d.ts +0 -8
- package/build/core/keyboardRaise.d.ts.map +1 -1
- package/build/core/keyboardRaise.js +0 -23
- package/build/core/keyboardRaise.js.map +1 -1
- package/build/core/license.d.ts +0 -4
- package/build/core/license.d.ts.map +1 -1
- package/build/core/license.js +2 -55
- package/build/core/license.js.map +1 -1
- package/build/core/logDiagnosis.d.ts +0 -34
- package/build/core/logDiagnosis.d.ts.map +1 -1
- package/build/core/logDiagnosis.js +0 -15
- package/build/core/logDiagnosis.js.map +1 -1
- package/build/core/logEventFormat.d.ts +0 -1
- package/build/core/logEventFormat.d.ts.map +1 -1
- package/build/core/logEventFormat.js +0 -30
- package/build/core/logEventFormat.js.map +1 -1
- package/build/core/logEvents.d.ts +0 -54
- package/build/core/logEvents.d.ts.map +1 -1
- package/build/core/logEvents.js +0 -37
- package/build/core/logEvents.js.map +1 -1
- package/build/core/logGrouping.d.ts +0 -9
- package/build/core/logGrouping.d.ts.map +1 -1
- package/build/core/logGrouping.js +0 -17
- package/build/core/logGrouping.js.map +1 -1
- package/build/core/logOwnership.d.ts +0 -17
- package/build/core/logOwnership.d.ts.map +1 -1
- package/build/core/logOwnership.js +0 -34
- package/build/core/logOwnership.js.map +1 -1
- package/build/core/logRelevance.d.ts +0 -8
- package/build/core/logRelevance.d.ts.map +1 -1
- package/build/core/logRelevance.js +0 -15
- package/build/core/logRelevance.js.map +1 -1
- package/build/core/logSourceAndroid.d.ts +0 -8
- package/build/core/logSourceAndroid.d.ts.map +1 -1
- package/build/core/logSourceAndroid.js +0 -11
- package/build/core/logSourceAndroid.js.map +1 -1
- package/build/core/logSourceIos.d.ts +0 -8
- package/build/core/logSourceIos.d.ts.map +1 -1
- package/build/core/logSourceIos.js +1 -24
- package/build/core/logSourceIos.js.map +1 -1
- package/build/core/logbox.d.ts +0 -30
- package/build/core/logbox.d.ts.map +1 -1
- package/build/core/logbox.js +0 -43
- package/build/core/logbox.js.map +1 -1
- package/build/core/logs.d.ts +0 -6
- package/build/core/logs.d.ts.map +1 -1
- package/build/core/logs.js +0 -19
- package/build/core/logs.js.map +1 -1
- package/build/core/metro.d.ts +0 -14
- package/build/core/metro.d.ts.map +1 -1
- package/build/core/metro.js +1 -32
- package/build/core/metro.js.map +1 -1
- package/build/core/moduleRegistry.d.ts +0 -17
- package/build/core/moduleRegistry.d.ts.map +1 -1
- package/build/core/moduleRegistry.js +0 -17
- package/build/core/moduleRegistry.js.map +1 -1
- package/build/core/nativeInputValue.d.ts +0 -29
- package/build/core/nativeInputValue.d.ts.map +1 -1
- package/build/core/nativeInputValue.js +0 -18
- package/build/core/nativeInputValue.js.map +1 -1
- package/build/core/nativeLogs.d.ts +0 -47
- package/build/core/nativeLogs.d.ts.map +1 -1
- package/build/core/nativeLogs.js +0 -82
- package/build/core/nativeLogs.js.map +1 -1
- package/build/core/nativeOnlyHints.js +0 -4
- package/build/core/nativeOnlyHints.js.map +1 -1
- package/build/core/navigate.d.ts +0 -15
- package/build/core/navigate.d.ts.map +1 -1
- package/build/core/navigate.js +0 -22
- package/build/core/navigate.js.map +1 -1
- package/build/core/navigation.d.ts +0 -60
- package/build/core/navigation.d.ts.map +1 -1
- package/build/core/navigation.js +0 -60
- package/build/core/navigation.js.map +1 -1
- package/build/core/network.d.ts +0 -5
- package/build/core/network.d.ts.map +1 -1
- package/build/core/network.js +10 -31
- package/build/core/network.js.map +1 -1
- package/build/core/networkInterceptor.d.ts +0 -19
- package/build/core/networkInterceptor.d.ts.map +1 -1
- package/build/core/networkInterceptor.js +3 -27
- package/build/core/networkInterceptor.js.map +1 -1
- package/build/core/ocr.d.ts +0 -31
- package/build/core/ocr.d.ts.map +1 -1
- package/build/core/ocr.js +1 -71
- package/build/core/ocr.js.map +1 -1
- package/build/core/paths.js +0 -1
- package/build/core/paths.js.map +1 -1
- package/build/core/pinchGeometry.d.ts +28 -0
- package/build/core/pinchGeometry.d.ts.map +1 -0
- package/build/core/pinchGeometry.js +97 -0
- package/build/core/pinchGeometry.js.map +1 -0
- package/build/core/pinchThresholds.d.ts +14 -0
- package/build/core/pinchThresholds.d.ts.map +1 -0
- package/build/core/pinchThresholds.js +9 -0
- package/build/core/pinchThresholds.js.map +1 -0
- package/build/core/posthog.js +0 -7
- package/build/core/posthog.js.map +1 -1
- package/build/core/pressables.d.ts +0 -25
- package/build/core/pressables.d.ts.map +1 -1
- package/build/core/pressables.js +0 -37
- package/build/core/pressables.js.map +1 -1
- package/build/core/probe.d.ts +0 -9
- package/build/core/probe.d.ts.map +1 -1
- package/build/core/probe.js +0 -9
- package/build/core/probe.js.map +1 -1
- package/build/core/projectMemory.d.ts +0 -2
- package/build/core/projectMemory.d.ts.map +1 -1
- package/build/core/projectMemory.js +2 -7
- package/build/core/projectMemory.js.map +1 -1
- package/build/core/projectStore.d.ts +0 -18
- package/build/core/projectStore.d.ts.map +1 -1
- package/build/core/projectStore.js +0 -19
- package/build/core/projectStore.js.map +1 -1
- package/build/core/promiseHandles.d.ts +0 -13
- package/build/core/promiseHandles.d.ts.map +1 -1
- package/build/core/promiseHandles.js +0 -25
- package/build/core/promiseHandles.js.map +1 -1
- package/build/core/register.js +0 -44
- package/build/core/register.js.map +1 -1
- package/build/core/rnGlobalsBootstrap.d.ts +0 -13
- package/build/core/rnGlobalsBootstrap.d.ts.map +1 -1
- package/build/core/rnGlobalsBootstrap.js +0 -22
- package/build/core/rnGlobalsBootstrap.js.map +1 -1
- package/build/core/routeHistory.d.ts +24 -0
- package/build/core/routeHistory.d.ts.map +1 -0
- package/build/core/routeHistory.js +186 -0
- package/build/core/routeHistory.js.map +1 -0
- package/build/core/screenLayout.d.ts +0 -11
- package/build/core/screenLayout.d.ts.map +1 -1
- package/build/core/screenLayout.js +1 -60
- package/build/core/screenLayout.js.map +1 -1
- package/build/core/screenSpace.d.ts +0 -107
- package/build/core/screenSpace.d.ts.map +1 -1
- package/build/core/screenSpace.js +0 -103
- package/build/core/screenSpace.js.map +1 -1
- package/build/core/screenSpaceDevice.d.ts +0 -8
- package/build/core/screenSpaceDevice.d.ts.map +1 -1
- package/build/core/screenSpaceDevice.js +0 -23
- package/build/core/screenSpaceDevice.js.map +1 -1
- package/build/core/screenState.d.ts +0 -73
- package/build/core/screenState.d.ts.map +1 -1
- package/build/core/screenState.js +6 -115
- package/build/core/screenState.js.map +1 -1
- package/build/core/sdkBridge.js +0 -8
- package/build/core/sdkBridge.js.map +1 -1
- package/build/core/sdkMirrorPoller.d.ts +0 -18
- package/build/core/sdkMirrorPoller.d.ts.map +1 -1
- package/build/core/sdkMirrorPoller.js +0 -47
- package/build/core/sdkMirrorPoller.js.map +1 -1
- package/build/core/selectionBuffer.d.ts +0 -6
- package/build/core/selectionBuffer.d.ts.map +1 -1
- package/build/core/selectionBuffer.js +0 -5
- package/build/core/selectionBuffer.js.map +1 -1
- package/build/core/signedVerdict.js +0 -6
- package/build/core/signedVerdict.js.map +1 -1
- package/build/core/state.d.ts +0 -31
- package/build/core/state.d.ts.map +1 -1
- package/build/core/state.js +0 -57
- package/build/core/state.js.map +1 -1
- package/build/core/symbolicate.js +0 -11
- package/build/core/symbolicate.js.map +1 -1
- package/build/core/telemetry.d.ts +0 -47
- package/build/core/telemetry.d.ts.map +1 -1
- package/build/core/telemetry.js +1 -173
- package/build/core/telemetry.js.map +1 -1
- package/build/core/textEntry.d.ts +0 -34
- package/build/core/textEntry.d.ts.map +1 -1
- package/build/core/textEntry.js +0 -73
- package/build/core/textEntry.js.map +1 -1
- package/build/core/toolHelpers.d.ts +0 -12
- package/build/core/toolHelpers.d.ts.map +1 -1
- package/build/core/toolHelpers.js +2 -47
- package/build/core/toolHelpers.js.map +1 -1
- package/build/core/truncate.d.ts +0 -17
- package/build/core/truncate.d.ts.map +1 -1
- package/build/core/truncate.js +0 -32
- package/build/core/truncate.js.map +1 -1
- package/build/core/types.d.ts +0 -6
- package/build/core/types.d.ts.map +1 -1
- package/build/core/usageCache.js +0 -12
- package/build/core/usageCache.js.map +1 -1
- package/build/index.js +0 -18
- package/build/index.js.map +1 -1
- package/build/pro/coordinateMiss.d.ts +0 -30
- package/build/pro/coordinateMiss.d.ts.map +1 -1
- package/build/pro/coordinateMiss.js +0 -17
- package/build/pro/coordinateMiss.js.map +1 -1
- package/build/pro/overlayGuard.d.ts +2 -40
- package/build/pro/overlayGuard.d.ts.map +1 -1
- package/build/pro/overlayGuard.js +0 -63
- package/build/pro/overlayGuard.js.map +1 -1
- package/build/pro/screenshot-diff.js +4 -6
- package/build/pro/screenshot-diff.js.map +1 -1
- package/build/pro/tap.d.ts +0 -55
- package/build/pro/tap.d.ts.map +1 -1
- package/build/pro/tap.js +7 -330
- package/build/pro/tap.js.map +1 -1
- package/build/pro/usageGate.js +0 -9
- package/build/pro/usageGate.js.map +1 -1
- package/build/pro/usageNotifications.js +1 -13
- package/build/pro/usageNotifications.js.map +1 -1
- package/build/pro/verifyAction.d.ts +0 -13
- package/build/pro/verifyAction.d.ts.map +1 -1
- package/build/pro/verifyAction.js +0 -45
- package/build/pro/verifyAction.js.map +1 -1
- package/build/tools/_deviceArg.d.ts +0 -35
- package/build/tools/_deviceArg.d.ts.map +1 -1
- package/build/tools/_deviceArg.js +0 -35
- package/build/tools/_deviceArg.js.map +1 -1
- package/build/tools/accountTools.js +2 -2
- package/build/tools/accountTools.js.map +1 -1
- package/build/tools/bundleTools.js +0 -16
- package/build/tools/bundleTools.js.map +1 -1
- package/build/tools/componentTools.d.ts.map +1 -1
- package/build/tools/componentTools.js +17 -45
- package/build/tools/componentTools.js.map +1 -1
- package/build/tools/connectionTools.js +1 -59
- package/build/tools/connectionTools.js.map +1 -1
- package/build/tools/deviceTools.js +0 -11
- package/build/tools/deviceTools.js.map +1 -1
- package/build/tools/executionTools.js +1 -18
- package/build/tools/executionTools.js.map +1 -1
- package/build/tools/interactionTools.d.ts +0 -10
- package/build/tools/interactionTools.d.ts.map +1 -1
- package/build/tools/interactionTools.js +190 -59
- package/build/tools/interactionTools.js.map +1 -1
- package/build/tools/logTools.d.ts +0 -13
- package/build/tools/logTools.d.ts.map +1 -1
- package/build/tools/logTools.js +2 -126
- package/build/tools/logTools.js.map +1 -1
- package/build/tools/metaTools.js +0 -10
- package/build/tools/metaTools.js.map +1 -1
- package/build/tools/networkTools.js +4 -43
- package/build/tools/networkTools.js.map +1 -1
- package/build/tools/requestTools.js +0 -5
- package/build/tools/requestTools.js.map +1 -1
- package/build/tools/screenshotTools.js +5 -111
- package/build/tools/screenshotTools.js.map +1 -1
- package/package.json +2 -2
- 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,
|
|
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,
|
|
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,8 @@ 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
|
-
/**
|
|
1014
|
-
* The root element's frame carries both the inset and the screen's point size, so the
|
|
1015
|
-
* one describe-ui call answers both. Splitting them into two probes would double the
|
|
1016
|
-
* ~200ms cost for callers (the layout tools) that need the size to derive the
|
|
1017
|
-
* point -> delivered-pixel scale.
|
|
1018
|
-
*/
|
|
1019
840
|
async function probeIOSRootFrame(udid) {
|
|
1020
841
|
const cacheKey = udid || "__default__";
|
|
1021
842
|
const cached = safeAreaTopCache.get(cacheKey);
|
|
@@ -1048,7 +869,6 @@ async function probeIOSRootFrame(udid) {
|
|
|
1048
869
|
}
|
|
1049
870
|
}
|
|
1050
871
|
catch {
|
|
1051
|
-
// Fall through: value stays 0 (no correction)
|
|
1052
872
|
}
|
|
1053
873
|
const entry = { value, screen, expires: Date.now() + SAFE_AREA_CACHE_TTL_MS };
|
|
1054
874
|
safeAreaTopCache.set(cacheKey, entry);
|
|
@@ -1057,13 +877,9 @@ async function probeIOSRootFrame(udid) {
|
|
|
1057
877
|
export async function getIOSSafeAreaTop(udid) {
|
|
1058
878
|
return (await probeIOSRootFrame(udid)).value;
|
|
1059
879
|
}
|
|
1060
|
-
/** Screen size in points, or null when the UI driver could not be reached. */
|
|
1061
880
|
export async function getIOSScreenPointSize(udid) {
|
|
1062
881
|
return (await probeIOSRootFrame(udid)).screen;
|
|
1063
882
|
}
|
|
1064
|
-
/**
|
|
1065
|
-
* Get accessibility info for the entire screen
|
|
1066
|
-
*/
|
|
1067
883
|
export async function iosDescribeAll(udid) {
|
|
1068
884
|
try {
|
|
1069
885
|
const preflight = await ensureUiDriverReady(udid);
|
|
@@ -1081,9 +897,7 @@ export async function iosDescribeAll(udid) {
|
|
|
1081
897
|
throw new Error(result.stderr);
|
|
1082
898
|
stdout = result.stdout;
|
|
1083
899
|
}
|
|
1084
|
-
// Parse JSON response
|
|
1085
900
|
const elements = JSON.parse(stdout);
|
|
1086
|
-
// Format for human-readable output
|
|
1087
901
|
const formatted = formatAccessibilityTree(elements);
|
|
1088
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';
|
|
1089
903
|
return {
|
|
@@ -1099,27 +913,8 @@ export async function iosDescribeAll(udid) {
|
|
|
1099
913
|
};
|
|
1100
914
|
}
|
|
1101
915
|
}
|
|
1102
|
-
/**
|
|
1103
|
-
* I1 (2026-05-16): when a native iOS system sheet (auth prompt, permission request,
|
|
1104
|
-
* Safari sign-in sheet) is on screen, the RN app's fiber tree still describes the
|
|
1105
|
-
* underlying screen — but those pressables aren't interactable until the sheet is
|
|
1106
|
-
* dismissed. Detect the overlay so the screenshot output can warn the agent and
|
|
1107
|
-
* point it at tap(x, y, native=true) for the overlay's actual buttons.
|
|
1108
|
-
*
|
|
1109
|
-
* Heuristic: scan the top three levels of the AXe accessibility tree for an
|
|
1110
|
-
* element whose type matches an iOS sheet/alert role, OR whose AXLabel matches a
|
|
1111
|
-
* well-known system-overlay phrase. False positives are rare because the patterns
|
|
1112
|
-
* are specific to system-presented sheets — RN content rarely uses these labels.
|
|
1113
|
-
*/
|
|
1114
916
|
const IOS_SYSTEM_OVERLAY_TYPES = /^(Alert|ActionSheet|Sheet|Dialog|SystemDialog)$/i;
|
|
1115
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;
|
|
1116
|
-
/**
|
|
1117
|
-
* Telltale of an RN accessibilityLabel rollup vs a system-presented sheet:
|
|
1118
|
-
* a rollup concatenates multiple child labels with commas / newlines (3+ separators),
|
|
1119
|
-
* while a system sheet's label is a single sentence. Without this guard the label
|
|
1120
|
-
* patterns above false-positive on screens like gifted.co's login wrapper whose
|
|
1121
|
-
* rollup happens to contain "Continue with" amongst many other phrases.
|
|
1122
|
-
*/
|
|
1123
918
|
function looksLikeRNLabelRollup(label) {
|
|
1124
919
|
const commas = (label.match(/,/g) ?? []).length;
|
|
1125
920
|
const newlines = (label.match(/\n/g) ?? []).length;
|
|
@@ -1166,9 +961,6 @@ export function formatIOSSystemOverlayWarning(detection) {
|
|
|
1166
961
|
`React Native screen UNDERNEATH and may be stale. For overlay buttons, take the coordinates ` +
|
|
1167
962
|
`from the screenshot and call tap(x, y, native=true, platform="ios").`);
|
|
1168
963
|
}
|
|
1169
|
-
/**
|
|
1170
|
-
* Get accessibility info at a specific point
|
|
1171
|
-
*/
|
|
1172
964
|
export async function iosDescribePoint(x, y, udid) {
|
|
1173
965
|
try {
|
|
1174
966
|
const preflight = await ensureUiDriverReady(udid);
|
|
@@ -1188,7 +980,6 @@ export async function iosDescribePoint(x, y, udid) {
|
|
|
1188
980
|
throw new Error(result.stderr);
|
|
1189
981
|
stdout = result.stdout;
|
|
1190
982
|
}
|
|
1191
|
-
// Parse JSON response - may be single element or array
|
|
1192
983
|
let element;
|
|
1193
984
|
try {
|
|
1194
985
|
const parsed = JSON.parse(stdout);
|
|
@@ -1201,7 +992,6 @@ export async function iosDescribePoint(x, y, udid) {
|
|
|
1201
992
|
elements: [],
|
|
1202
993
|
};
|
|
1203
994
|
}
|
|
1204
|
-
// Format for human-readable output
|
|
1205
995
|
const parts = [];
|
|
1206
996
|
if (element.type)
|
|
1207
997
|
parts.push(`Type: ${element.type}`);
|
|
@@ -1231,9 +1021,6 @@ export async function iosDescribePoint(x, y, udid) {
|
|
|
1231
1021
|
};
|
|
1232
1022
|
}
|
|
1233
1023
|
}
|
|
1234
|
-
/**
|
|
1235
|
-
* Helper to flatten nested accessibility elements
|
|
1236
|
-
*/
|
|
1237
1024
|
function flattenElements(elements) {
|
|
1238
1025
|
const result = [];
|
|
1239
1026
|
for (const el of elements) {
|
|
@@ -1244,10 +1031,6 @@ function flattenElements(elements) {
|
|
|
1244
1031
|
}
|
|
1245
1032
|
return result;
|
|
1246
1033
|
}
|
|
1247
|
-
/**
|
|
1248
|
-
* Tap an element by its accessibility label using IDB
|
|
1249
|
-
* This simplifies the workflow: no need to manually find coordinates
|
|
1250
|
-
*/
|
|
1251
1034
|
export async function iosTapElement(options) {
|
|
1252
1035
|
try {
|
|
1253
1036
|
const { label, labelContains, index = 0, duration, udid } = options;
|
|
@@ -1257,7 +1040,6 @@ export async function iosTapElement(options) {
|
|
|
1257
1040
|
error: "Either 'label' (exact match) or 'labelContains' (partial match) is required",
|
|
1258
1041
|
};
|
|
1259
1042
|
}
|
|
1260
|
-
// Get all accessibility elements
|
|
1261
1043
|
const describeResult = await iosDescribeAll(udid);
|
|
1262
1044
|
if (!describeResult.success || !describeResult.elements) {
|
|
1263
1045
|
return {
|
|
@@ -1265,7 +1047,6 @@ export async function iosTapElement(options) {
|
|
|
1265
1047
|
error: describeResult.error || "Failed to get accessibility elements",
|
|
1266
1048
|
};
|
|
1267
1049
|
}
|
|
1268
|
-
// Flatten the tree and find matching elements
|
|
1269
1050
|
const allElements = flattenElements(describeResult.elements);
|
|
1270
1051
|
const matches = allElements.filter((el) => {
|
|
1271
1052
|
if (!el.AXLabel)
|
|
@@ -1285,7 +1066,6 @@ export async function iosTapElement(options) {
|
|
|
1285
1066
|
error: `No element found with ${searchTerm}`,
|
|
1286
1067
|
};
|
|
1287
1068
|
}
|
|
1288
|
-
// Select element by index (default 0 = first match)
|
|
1289
1069
|
if (index >= matches.length) {
|
|
1290
1070
|
return {
|
|
1291
1071
|
success: false,
|
|
@@ -1293,17 +1073,14 @@ export async function iosTapElement(options) {
|
|
|
1293
1073
|
};
|
|
1294
1074
|
}
|
|
1295
1075
|
const element = matches[index];
|
|
1296
|
-
// Check if element has frame coordinates
|
|
1297
1076
|
if (!element.frame) {
|
|
1298
1077
|
return {
|
|
1299
1078
|
success: false,
|
|
1300
1079
|
error: `Element "${element.AXLabel}" has no frame coordinates`,
|
|
1301
1080
|
};
|
|
1302
1081
|
}
|
|
1303
|
-
// Calculate center
|
|
1304
1082
|
const centerX = Math.round(element.frame.x + element.frame.width / 2);
|
|
1305
1083
|
const centerY = Math.round(element.frame.y + element.frame.height / 2);
|
|
1306
|
-
// Tap at center
|
|
1307
1084
|
const tapResult = await iosTap(centerX, centerY, { duration, udid });
|
|
1308
1085
|
if (tapResult.success) {
|
|
1309
1086
|
return {
|
|
@@ -1320,9 +1097,6 @@ export async function iosTapElement(options) {
|
|
|
1320
1097
|
};
|
|
1321
1098
|
}
|
|
1322
1099
|
}
|
|
1323
|
-
/**
|
|
1324
|
-
* Parse IDB accessibility output into simplified element array
|
|
1325
|
-
*/
|
|
1326
1100
|
function parseIdbAccessibilityForFindElement(output) {
|
|
1327
1101
|
const elements = [];
|
|
1328
1102
|
try {
|
|
@@ -1375,14 +1149,9 @@ function parseIdbAccessibilityForFindElement(output) {
|
|
|
1375
1149
|
}
|
|
1376
1150
|
}
|
|
1377
1151
|
catch {
|
|
1378
|
-
// If JSON parsing fails, return empty array
|
|
1379
1152
|
}
|
|
1380
1153
|
return elements;
|
|
1381
1154
|
}
|
|
1382
|
-
/**
|
|
1383
|
-
* Check if an element's center is within the screen viewport.
|
|
1384
|
-
* Filters out off-screen elements in scroll views, horizontal pagers, etc.
|
|
1385
|
-
*/
|
|
1386
1155
|
function isIOSElementInViewport(element, screenWidth, screenHeight) {
|
|
1387
1156
|
const { center } = element;
|
|
1388
1157
|
return (center.x >= 0 &&
|
|
@@ -1390,10 +1159,6 @@ function isIOSElementInViewport(element, screenWidth, screenHeight) {
|
|
|
1390
1159
|
center.y >= 0 &&
|
|
1391
1160
|
center.y <= screenHeight);
|
|
1392
1161
|
}
|
|
1393
|
-
/**
|
|
1394
|
-
* Extract screen dimensions from the parsed UI tree.
|
|
1395
|
-
* The root Application element's frame represents the screen size.
|
|
1396
|
-
*/
|
|
1397
1162
|
function extractIOSScreenSize(rawOutput) {
|
|
1398
1163
|
try {
|
|
1399
1164
|
const data = JSON.parse(rawOutput);
|
|
@@ -1404,13 +1169,9 @@ function extractIOSScreenSize(rawOutput) {
|
|
|
1404
1169
|
}
|
|
1405
1170
|
}
|
|
1406
1171
|
catch {
|
|
1407
|
-
// Fall through
|
|
1408
1172
|
}
|
|
1409
1173
|
return null;
|
|
1410
1174
|
}
|
|
1411
|
-
/**
|
|
1412
|
-
* Match iOS element against find options
|
|
1413
|
-
*/
|
|
1414
1175
|
function matchesIOSFindElement(element, options) {
|
|
1415
1176
|
if (options.label !== undefined) {
|
|
1416
1177
|
if (element.label !== options.label)
|
|
@@ -1444,9 +1205,6 @@ function matchesIOSFindElement(element, options) {
|
|
|
1444
1205
|
}
|
|
1445
1206
|
return true;
|
|
1446
1207
|
}
|
|
1447
|
-
/**
|
|
1448
|
-
* Get UI accessibility tree from iOS simulator using IDB (for find_element)
|
|
1449
|
-
*/
|
|
1450
1208
|
export async function iosGetUITree(udid) {
|
|
1451
1209
|
try {
|
|
1452
1210
|
const preflight = await ensureUiDriverReady(udid);
|
|
@@ -1476,20 +1234,6 @@ export async function iosGetUITree(udid) {
|
|
|
1476
1234
|
};
|
|
1477
1235
|
}
|
|
1478
1236
|
}
|
|
1479
|
-
/**
|
|
1480
|
-
* Bug 3 (2026-05-16): RN rolls up child Text labels into a parent View's
|
|
1481
|
-
* accessibilityLabel when the parent is marked `accessible`. Result: a screen-root
|
|
1482
|
-
* View whose label is a 200+ char concatenation of every visible text. `labelContains`
|
|
1483
|
-
* matches it, frame is the whole screen, centroid happens to land on whatever button
|
|
1484
|
-
* sits near the middle — and the tap silently lands on the wrong target.
|
|
1485
|
-
*
|
|
1486
|
-
* Heuristic filter: an element is an aggregator wrapper when EITHER
|
|
1487
|
-
* (a) its label is much longer than the query AND contains concatenation hints
|
|
1488
|
-
* (newlines, commas, semicolons — RN rollup typically joins with `, ` or `\n`)
|
|
1489
|
-
* (b) its frame covers >50% of the viewport (a real button never does this)
|
|
1490
|
-
*
|
|
1491
|
-
* Both heuristics together catch the gifted.co LoginScreen wrapper case the doc cites.
|
|
1492
|
-
*/
|
|
1493
1237
|
function isAggregatorWrapper(element, query, screenSize) {
|
|
1494
1238
|
const labelLen = element.label?.length ?? 0;
|
|
1495
1239
|
const queryLen = query.length;
|
|
@@ -1507,15 +1251,6 @@ function isAggregatorWrapper(element, query, screenSize) {
|
|
|
1507
1251
|
}
|
|
1508
1252
|
return isLongConcatenatedLabel || isFullScreenFrame;
|
|
1509
1253
|
}
|
|
1510
|
-
/**
|
|
1511
|
-
* Find element(s) in the iOS UI tree matching the given criteria.
|
|
1512
|
-
*
|
|
1513
|
-
* `tree` lets a caller reuse an accessibility dump it already has. Each fetch
|
|
1514
|
-
* shells out to `axe describe-ui` (~210ms measured 2026-08-01) and the tap
|
|
1515
|
-
* strategy runs two or three passes with different predicates, so re-fetching
|
|
1516
|
-
* per pass was costing 400-600ms per accessibility tap for a tree that cannot
|
|
1517
|
-
* change between passes.
|
|
1518
|
-
*/
|
|
1519
1254
|
export async function iosFindElement(options, udid, tree) {
|
|
1520
1255
|
try {
|
|
1521
1256
|
if (!options.label &&
|
|
@@ -1539,8 +1274,6 @@ export async function iosFindElement(options, udid, tree) {
|
|
|
1539
1274
|
error: treeResult.error,
|
|
1540
1275
|
};
|
|
1541
1276
|
}
|
|
1542
|
-
// Filter to viewport-visible elements first, then match criteria.
|
|
1543
|
-
// This prevents selecting off-screen duplicates in scroll views / pagers.
|
|
1544
1277
|
const screenSize = treeResult.rawOutput
|
|
1545
1278
|
? extractIOSScreenSize(treeResult.rawOutput)
|
|
1546
1279
|
: null;
|
|
@@ -1548,21 +1281,9 @@ export async function iosFindElement(options, udid, tree) {
|
|
|
1548
1281
|
? treeResult.elements.filter((el) => isIOSElementInViewport(el, screenSize.width, screenSize.height))
|
|
1549
1282
|
: treeResult.elements;
|
|
1550
1283
|
let matches = visibleElements.filter((el) => matchesIOSFindElement(el, options));
|
|
1551
|
-
// If no visible matches, fall back to all elements (element may be partially visible
|
|
1552
|
-
// or the screen size extraction failed)
|
|
1553
1284
|
if (matches.length === 0) {
|
|
1554
1285
|
matches = treeResult.elements.filter((el) => matchesIOSFindElement(el, options));
|
|
1555
1286
|
}
|
|
1556
|
-
// Bug 3 (2026-05-16): when matching by partial string (labelContains / identifierContains),
|
|
1557
|
-
// drop aggregator-wrapper elements that match only because their label rolls up every
|
|
1558
|
-
// visible child's text. Without this filter, tapping such a wrapper's centroid
|
|
1559
|
-
// silently lands on the wrong on-screen element.
|
|
1560
|
-
//
|
|
1561
|
-
// When ALL matches are aggregator wrappers (the common case — RN often rolls labels
|
|
1562
|
-
// up to a single accessible wrapper, leaving no individual element with a matching
|
|
1563
|
-
// label), refuse with a clear error pointing the agent at component= or coordinates.
|
|
1564
|
-
// This is safer than silently tapping the wrapper centroid (which lands wherever the
|
|
1565
|
-
// wrapper's geometric center happens to be — usually NOT on the requested button).
|
|
1566
1287
|
if (options.labelContains !== undefined && matches.length > 0) {
|
|
1567
1288
|
const filtered = matches.filter((el) => !isAggregatorWrapper(el, options.labelContains, screenSize));
|
|
1568
1289
|
if (filtered.length > 0) {
|
|
@@ -1628,26 +1349,11 @@ export async function iosFindElement(options, udid, tree) {
|
|
|
1628
1349
|
};
|
|
1629
1350
|
}
|
|
1630
1351
|
}
|
|
1631
|
-
// DPR cache: UDID → device pixel ratio (never changes during session)
|
|
1632
1352
|
const dprCache = new Map();
|
|
1633
|
-
/**
|
|
1634
|
-
* Screen size in device pixels, recorded on the same cold pass that resolves the DPR.
|
|
1635
|
-
* The layout tools need it to predict the delivery downscale, and re-deriving it later
|
|
1636
|
-
* would mean a second screenshot for a number this call already had in hand.
|
|
1637
|
-
*/
|
|
1638
1353
|
const pixelSizeCache = new Map();
|
|
1639
|
-
/**
|
|
1640
|
-
* Pure calculation: DPR = screenshotPixelWidth / screenPointWidth.
|
|
1641
|
-
* Exported for unit testing.
|
|
1642
|
-
*/
|
|
1643
1354
|
export function calculateDPR(screenshotPixelWidth, screenPointWidth) {
|
|
1644
1355
|
return Math.round(screenshotPixelWidth / screenPointWidth);
|
|
1645
1356
|
}
|
|
1646
|
-
/**
|
|
1647
|
-
* Query the actual device pixel ratio for an iOS simulator.
|
|
1648
|
-
* Uses screenshot pixel width vs accessibility root frame width.
|
|
1649
|
-
* Result is cached per UDID for the session.
|
|
1650
|
-
*/
|
|
1651
1357
|
export async function getDevicePixelRatio(udid, pixelDims) {
|
|
1652
1358
|
const cacheKey = udid || "__default__";
|
|
1653
1359
|
const cached = dprCache.get(cacheKey);
|
|
@@ -1660,14 +1366,10 @@ export async function getDevicePixelRatio(udid, pixelDims) {
|
|
|
1660
1366
|
let pixelWidth;
|
|
1661
1367
|
let pixelHeight;
|
|
1662
1368
|
if (pixelDims && pixelDims.width > 0 && pixelDims.height > 0) {
|
|
1663
|
-
// The caller already captured a frame this turn (tap always does) — reuse
|
|
1664
|
-
// its dimensions instead of shelling out for a screenshot of our own, which
|
|
1665
|
-
// measured ~230ms of the ~470ms first-call cost.
|
|
1666
1369
|
pixelWidth = pixelDims.width;
|
|
1667
1370
|
pixelHeight = pixelDims.height;
|
|
1668
1371
|
}
|
|
1669
1372
|
else {
|
|
1670
|
-
// Take screenshot to get pixel dimensions
|
|
1671
1373
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
1672
1374
|
const screenshotPath = path.join(os.tmpdir(), `ios-dpr-screenshot-${timestamp}.png`);
|
|
1673
1375
|
await execAsync(`xcrun simctl io ${resolvedUdid} screenshot "${screenshotPath}"`, {
|
|
@@ -1680,7 +1382,6 @@ export async function getDevicePixelRatio(udid, pixelDims) {
|
|
|
1680
1382
|
if (pixelWidth > 0 && pixelHeight > 0) {
|
|
1681
1383
|
pixelSizeCache.set(cacheKey, { width: pixelWidth, height: pixelHeight });
|
|
1682
1384
|
}
|
|
1683
|
-
// Try accessibility tree for exact DPR, fall back to inference
|
|
1684
1385
|
const driver = getIosDriver();
|
|
1685
1386
|
const driverOk = driver === "axe" ? await isAxeAvailable() : await isIdbAvailable();
|
|
1686
1387
|
if (driverOk) {
|
|
@@ -1703,37 +1404,25 @@ export async function getDevicePixelRatio(udid, pixelDims) {
|
|
|
1703
1404
|
}
|
|
1704
1405
|
}
|
|
1705
1406
|
catch {
|
|
1706
|
-
// driver failed — fall through to inference
|
|
1707
1407
|
}
|
|
1708
1408
|
}
|
|
1709
|
-
// Fallback: infer DPR from screenshot pixel dimensions
|
|
1710
1409
|
const { inferIOSDevicePixelRatio } = await import("./ocr.js");
|
|
1711
1410
|
const dpr = inferIOSDevicePixelRatio(pixelWidth, pixelHeight);
|
|
1712
1411
|
dprCache.set(cacheKey, dpr);
|
|
1713
1412
|
return dpr;
|
|
1714
1413
|
}
|
|
1715
|
-
/**
|
|
1716
|
-
* Screen size in device pixels, or null when no simulator could be measured.
|
|
1717
|
-
* Piggybacks on the DPR probe's cache, so this is free once either has run.
|
|
1718
|
-
*/
|
|
1719
1414
|
export async function getIOSScreenPixelSize(udid, pixelDims) {
|
|
1720
1415
|
const cacheKey = udid || "__default__";
|
|
1721
1416
|
const cached = pixelSizeCache.get(cacheKey);
|
|
1722
1417
|
if (cached)
|
|
1723
1418
|
return cached;
|
|
1724
|
-
// Resolving the DPR is what populates the size cache; its own failure modes
|
|
1725
|
-
// (no simulator, no UI driver) are not this caller's problem — it degrades to null.
|
|
1726
1419
|
await getDevicePixelRatio(udid, pixelDims).catch(() => undefined);
|
|
1727
1420
|
return pixelSizeCache.get(cacheKey) ?? null;
|
|
1728
1421
|
}
|
|
1729
|
-
/** Clear DPR cache (for testing or when devices change) */
|
|
1730
1422
|
export function clearDPRCache() {
|
|
1731
1423
|
dprCache.clear();
|
|
1732
1424
|
pixelSizeCache.clear();
|
|
1733
1425
|
}
|
|
1734
|
-
/**
|
|
1735
|
-
* Wait for element to appear on iOS screen with polling
|
|
1736
|
-
*/
|
|
1737
1426
|
export async function iosWaitForElement(options, udid) {
|
|
1738
1427
|
const timeoutMs = options.timeoutMs ?? 10000;
|
|
1739
1428
|
const pollIntervalMs = options.pollIntervalMs ?? 500;
|