import { ExpoConfig , getConfig } from '@expo/config' ; import { IOSConfig } from '@expo/config-plugins' ; import * as osascript from '@expo/osascript' ; import plist from '@expo/plist' ; import spawnAsync from '@expo/spawn-async' ; import chalk from 'chalk' ; import { execSync } from 'child_process' ; import fs from 'fs-extra' ; import path from 'path' ; import prompts from 'prompts' ; import semver from 'semver' ; import { ensureSimulatorAppRunningAsync } from './apple/utils/ensureSimulatorAppRunningAsync' ; import { TimeoutError } from './apple/utils/waitForActionAsync' ; import { Analytics , BundleIdentifier , CoreSimulator , delayAsync , downloadAppAsync , isDevClientPackageInstalled , learnMore , LoadingEvent , Logger , Prompts , SimControl , SimControlLogs , UrlUtils , UserSettings , Versions , Webpack , Xcode , } from './internal' ; import { profileMethod } from './utils/profileMethod' ; let _lastUrl = null ; const EXPO_GO_BUNDLE_IDENTIFIER = 'host.exp.Exponent' ; const SUGGESTED_XCODE_VERSION = `${ Xcode . minimumVersion }.0` ; const INSTALL_WARNING_TIMEOUT = 60 * 1000 ; export function isPlatformSupported ( ) { return process . platform === 'darwin' ; } return export async function ensureXcodeInstalledAsync ( ) { const promptToOpenAppStoreAsync = async ( message ) => { const confirm = await Prompts . confirmAsync ( { initial : true , message } ) ; if ( confirm ) { Logger . global . info ( `Going to the App Store, re-run Expo when Xcode is finished installing.` ) ; Xcode . openAppStore ( Xcode . appStoreId ) ; } } ; const version = profileMethod ( Xcode . getXcodeVersion ) ( ) ; if ( ! version ) { await promptToOpenAppStoreAsync ( `Xcode needs to be installed (don't worry, you won't have to use it), would you like to continue to the App Store?` ) ; return false ; } if ( ! semver . valid ( version ) ) { Logger . global . error ( `Xcode version is in an unknown format: ${ version }` ) ; return false ; } if ( semver . lt ( version , SUGGESTED_XCODE_VERSION ) ) { await promptToOpenAppStoreAsync ( `Xcode (${ version }) needs to be updated to at least version ${ Xcode . minimumVersion }, would you like to continue to the App Store?` ) ; return false ; } return true ; } let _isXcodeCLIInstalled = null ; export async function ensureXcodeCommandLineToolsInstalledAsync ( ) { if ( _isXcodeCLIInstalled != null ) { return _isXcodeCLIInstalled ; } const _ensureXcodeCommandLineToolsInstalledAsync = async ( ) => { if ( ! ( await ensureXcodeInstalledAsync ( ) ) ) { return false ; } else if ( await SimControl . isXcrunInstalledAsync ( ) ) { return true ; } async function pendingAsync ( ) { if ( await SimControl . isXcrunInstalledAsync ( ) ) { return true ; } else { await delayAsync ( 100 ) ; return await pendingAsync ( ) ; } } const confirm = await Prompts . confirmAsync ( { initial : true , message : `Xcode ${ chalk . bold `Command Line Tools` } needs to be installed (requires ${ chalk . bold `sudo` }), continue?` } ) ; if ( ! confirm ) { return false ; } try { await spawnAsync ( 'sudo' , [ 'xcode-select' , '--install' , ] ) ; await pendingAsync ( ) ; return true ; } catch ( error ) { } return false ; } ; _isXcodeCLIInstalled = await _ensureXcodeCommandLineToolsInstalledAsync ( ) ; return _isXcodeCLIInstalled ; } async function getSimulatorAppIdAsync ( ) { let result ; try { result = ( await osascript . execAsync ( 'id of app "Simulator"' ) ) . trim ( ) ; } catch { return null ; } return result ; } let _isSimulatorInstalled = null ; export async function isSimulatorInstalledAsync ( ) { if ( _isSimulatorInstalled != null ) { return _isSimulatorInstalled ; } const _isSimulatorInstalledAsync = async ( ) => { if ( ! ( await ensureXcodeCommandLineToolsInstalledAsync ( ) ) ) { return false ; } const result = await getSimulatorAppIdAsync ( ) ; if ( ! result ) { Logger . global . error ( "Can't determine id of Simulator app; the Simulator is most likely not installed on this machine. Run `sudo xcode-select -s /Applications/Xcode.app`" ) ; return false ; } if ( result !== 'com.apple.iphonesimulator' && result !== 'com.apple.CoreSimulator.SimulatorTrampoline' ) { Logger . global . warn ( "Simulator is installed but is identified as '" + result + "'; don't know what that is." ) ; return false ; } try { await SimControl . simctlAsync ( [ 'help' ] ) ; } catch ( e ) { if ( e . isXDLError ) { Logger . global . error ( e . toString ( ) ) ; } else { Logger . global . warn ( `Unable to run simctl: ${ e . toString ( ) }` ) ; Logger . global . error ( 'xcrun may not be configured correctly. Try running `sudo xcode-select --reset` and running this again.' ) ; } return false ; } return true ; } ; _isSimulatorInstalled = await _isSimulatorInstalledAsync ( ) ; return _isSimulatorInstalled ; } /**
 * Ensure a simulator is booted and the Simulator app is opened.
 * This is where any timeout related error handling should live.
 */ export async function ensureSimulatorOpenAsync ( { udid , osType } = { } , tryAgain = true ) { if ( ! udid ) { const simulatorOpenedByApp = await getBestBootedSimulatorAsync ( { osType } ) ; if ( simulatorOpenedByApp ) { return simulatorOpenedByApp ; } udid = await getBestUnbootedSimulatorAsync ( { osType } ) ; } const bootedDevice = await profileMethod ( SimControl . waitForDeviceToBootAsync ) ( { udid } ) ; if ( ! bootedDevice ) { if ( tryAgain ) { return await ensureSimulatorOpenAsync ( { udid , osType } , false ) ; } throw new TimeoutError ( `Simulator didn't boot fast enough. Try opening Simulator first, then running your app.` ) ; } return bootedDevice ; } async function getBestBootedSimulatorAsync ( { osType } ) { let simulatorOpenedByApp ; if ( CoreSimulator . isEnabled ( ) ) { simulatorOpenedByApp = await CoreSimulator . getDeviceInfoAsync ( ) . catch ( ( ) => null ) ; } else { const simulatorDeviceInfo = await SimControl . listAsync ( 'devices' ) ; const devices = Object . values ( simulatorDeviceInfo . devices ) . reduce ( ( prev , runtime ) => { return prev . concat ( runtime . filter ( device => device . state === 'Booted' ) ) ; } , [ ] ) ; simulatorOpenedByApp = devices [ 0 ] ; } if ( simulatorOpenedByApp ?. udid && ( ! osType || ( osType && simulatorOpenedByApp . osType === osType ) ) ) { return simulatorOpenedByApp ; } return null ; } async function getBestUnbootedSimulatorAsync ( { osType } ) { const defaultUdid = _getDefaultSimulatorDeviceUDID ( ) ; if ( defaultUdid && ! osType ) { return defaultUdid ; } const simulators = await getSelectableSimulatorsAsync ( { osType } ) ; if ( ! simulators . length ) { throw new Error ( `No ${ osType || 'iOS' } devices available in Simulator.app` ) ; } if ( defaultUdid ) { const defaultSimulator = simulators . find ( device => device . udid === defaultUdid ) ; if ( defaultSimulator ?. osType === osType ) { return defaultUdid ; } } return simulators [ 0 ] . udid ; } async function getBestSimulatorAsync ( { osType } ) { const simulatorOpenedByApp = await getBestBootedSimulatorAsync ( { osType } ) ; if ( simulatorOpenedByApp ) { return simulatorOpenedByApp . udid ; } return await getBestUnbootedSimulatorAsync ( { osType } ) ; } /**
 * Get all simulators supported by Expo (iOS only).
 */ async function getSelectableSimulatorsAsync ( { osType = 'iOS' } = { } ) { const simulators = await SimControl . listSimulatorDevicesAsync ( ) ; return simulators . filter ( device => device . isAvailable && device . osType === osType ) ; } async function getBootedSimulatorsAsync ( ) { const simulators = await SimControl . listSimulatorDevicesAsync ( ) ; return simulators . filter ( device => device . state === 'Booted' ) ; } export async function isSimulatorBootedAsync ( { udid , } = { } ) { const devices = await getBootedSimulatorsAsync ( ) ; if ( udid ) { return devices . find ( bootedDevice => bootedDevice . udid === udid ) ?? null ; } else { return devices [ 0 ] ?? null ; } } function _getDefaultSimulatorDeviceUDID ( ) { try { const defaultDeviceUDID = execSync ( `defaults read com.apple.iphonesimulator CurrentDeviceUDID` , { stdio : 'pipe' } ) . toString ( ) ; return defaultDeviceUDID . trim ( ) ; } catch ( e ) { return null ; } } export async function activateSimulatorWindowAsync ( ) { return await osascript . execAsync ( `tell application "Simulator" to activate` ) ; } export async function closeSimulatorAppAsync ( ) { return await osascript . execAsync ( 'tell application "Simulator" to quit' ) ; } export async function isExpoClientInstalledOnSimulatorAsync ( { udid , } ) { return ! ! ( await SimControl . getContainerPathAsync ( { udid , bundleIdentifier : EXPO_GO_BUNDLE_IDENTIFIER } ) ) ; } export async function waitForExpoClientInstalledOnSimulatorAsync ( { udid , } ) { if ( await isExpoClientInstalledOnSimulatorAsync ( { udid } ) ) { return true ; } else { await delayAsync ( 100 ) ; return await waitForExpoClientInstalledOnSimulatorAsync ( { udid } ) ; } } export async function waitForExpoClientUninstalledOnSimulatorAsync ( { udid , } ) { if ( ! ( await isExpoClientInstalledOnSimulatorAsync ( { udid } ) ) ) { return true ; } else { await delayAsync ( 100 ) ; return await waitForExpoClientInstalledOnSimulatorAsync ( { udid } ) ; } } export async function expoVersionOnSimulatorAsync ( { udid , } ) { const localPath = await SimControl . getContainerPathAsync ( { udid , bundleIdentifier : EXPO_GO_BUNDLE_IDENTIFIER } ) ; if ( ! localPath ) { return null ; } const regex = /Exponent-([0-9.]+).*\.app$/ ; const regexMatch = regex . exec ( localPath ) ; if ( ! regexMatch ) { return null ; } let matched = regexMatch [ 1 ] ; if ( matched . endsWith ( '.' ) ) { matched = matched . substr ( 0 , matched . length - 1 ) ; } return matched ; } export async function doesExpoClientNeedUpdatedAsync ( simulator , sdkVersion ? ) { const versions = await profileMethod ( Versions . versionsAsync ) ( ) ; const clientForSdk = await profileMethod ( getClientForSDK ) ( sdkVersion ) ; const latestVersionForSdk = clientForSdk ?. version ?? versions . iosVersion ; const installedVersion = await expoVersionOnSimulatorAsync ( simulator ) ; if ( installedVersion && semver . lt ( installedVersion , latestVersionForSdk ) ) { return true ; } return false ; } export async function _downloadSimulatorAppAsync ( url ? , downloadProgressCallback ? ) { if ( ! url ) { const versions = await Versions . versionsAsync ( ) ; url = versions . iosUrl ; } const filename = path . parse ( url ) . name ; const dir = path . join ( simulatorCacheDirectory ( ) , `${ filename }.app` ) ; if ( await fs . pathExists ( dir ) ) { const filesInDir = await fs . readdir ( dir ) ; if ( filesInDir . length > 0 ) { return dir ; } else { fs . removeSync ( dir ) ; } } fs . mkdirpSync ( dir ) ; try { await downloadAppAsync ( url , dir , { extract : true } , downloadProgressCallback ) ; } catch ( e ) { fs . removeSync ( dir ) ; throw e ; } return dir ; } export async function installExpoOnSimulatorAsync ( { url , simulator , version , } ) { let warningTimer ; const setWarningTimer = ( ) => { if ( warningTimer ) { clearTimeout ( warningTimer ) ; } return setTimeout ( ( ) => { Logger . global . info ( '' ) ; Logger . global . info ( 'This download is taking longer than expected. You can also try downloading the clients from the website at https://expo.dev/tools' ) ; } , INSTALL_WARNING_TIMEOUT ) ; } ; Logger . notifications . info ( { code : LoadingEvent . START_PROGRESS_BAR } , 'Downloading the Expo Go app [:bar] :percent :etas' ) ; warningTimer = setWarningTimer ( ) ; const dir = await _downloadSimulatorAppAsync ( url , progress => { Logger . notifications . info ( { code : LoadingEvent . TICK_PROGRESS_BAR } , progress ) ; } ) ; Logger . notifications . info ( { code : LoadingEvent . STOP_PROGRESS_BAR } ) ; const message = version ? `Installing Expo Go ${ version } on ${ simulator . name }` : `Installing Expo Go on ${ simulator . name }` ; Logger . notifications . info ( { code : LoadingEvent . START_LOADING } , message ) ; warningTimer = setWarningTimer ( ) ; const result = await SimControl . installAsync ( { udid : simulator . udid , dir } ) ; Logger . notifications . info ( { code : LoadingEvent . STOP_LOADING } ) ; clearTimeout ( warningTimer ) ; return result ; } export async function uninstallExpoAppFromSimulatorAsync ( { udid } = { } ) { try { Logger . global . info ( 'Uninstalling Expo Go from iOS simulator.' ) ; await SimControl . uninstallAsync ( { udid , bundleIdentifier : EXPO_GO_BUNDLE_IDENTIFIER } ) ; } catch ( e ) { if ( ! e . message ?. includes ( 'No devices are booted.' ) ) { Logger . global . error ( e ) ; throw e ; } } } function simulatorCacheDirectory ( ) { const dotExpoHomeDirectory = UserSettings . dotExpoHomeDirectory ( ) ; const dir = path . join ( dotExpoHomeDirectory , 'ios-simulator-app-cache' ) ; fs . mkdirpSync ( dir ) ; return dir ; } export async function upgradeExpoAsync ( options = { } ) { if ( ! ( await isSimulatorInstalledAsync ( ) ) ) { return false ; } const simulator = await ensureSimulatorOpenAsync ( options ) ; await uninstallExpoAppFromSimulatorAsync ( simulator ) ; const installResult = await installExpoOnSimulatorAsync ( { url : options . url , version : options . version , simulator } ) ; if ( installResult . status !== 0 ) { return false ; } if ( _lastUrl ) { Logger . global . info ( `\u203A Opening ${ chalk . underline ( _lastUrl ) } in Expo Go` ) ; await Promise . all ( [ ensureSimulatorAppRunningAsync ( simulator ) , SimControl . openURLAsync ( { udid : simulator . udid , url : _lastUrl } ) , ] ) ; _lastUrl = null ; } return true ; } async function openUrlInSimulatorSafeAsync ( { url , udid , isDetached = false , sdkVersion , devClient = false , projectRoot , exp , skipNativeLogs = false , } ) { if ( ! exp ) { exp = profileMethod ( getConfig ) ( projectRoot , { skipSDKVersionRequirement : true } ) . exp ; } let simulator = null ; try { simulator = await profileMethod ( ensureSimulatorOpenAsync ) ( { udid } ) ; } catch ( error ) { return { success : false , msg : error . message } ; } Logger . global . info ( `\u203A Opening ${ chalk . underline ( url ) } on ${ chalk . bold ( simulator . name ) }` ) ; let bundleIdentifier = EXPO_GO_BUNDLE_IDENTIFIER ; try { if ( devClient ) { bundleIdentifier = await profileMethod ( BundleIdentifier . configureBundleIdentifierAsync ) ( projectRoot , exp ) ; await profileMethod ( assertDevClientInstalledAsync ) ( simulator , bundleIdentifier ) ; if ( ! skipNativeLogs ) { await streamLogsAsync ( { udid : simulator . udid , bundleIdentifier } ) ; } } else if ( ! isDetached ) { await profileMethod ( ensureExpoClientInstalledAsync ) ( simulator , sdkVersion ) ; _lastUrl = url ; } else if ( ! devClient && isDevClientPackageInstalled ( projectRoot ) ) { bundleIdentifier = '' ; } await Promise . all ( [ profileMethod ( async ( ) => { await ensureSimulatorAppRunningAsync ( { udid : simulator ?. udid } ) ; activateSimulatorWindowAsync ( ) ; } , 'parallel: ensureSimulatorAppRunningAsync' ) ( ) , profileMethod ( SimControl . openURLAsync , 'parallel: openURLAsync' ) ( { udid : simulator . udid , url } ) , ] ) ; } catch ( e ) { if ( e . status === 194 ) { return { success : false , msg : `Device ${ simulator . name } (${ simulator . udid }) has no app to handle the URI: ${ url }` } ; } if ( e . isXDLError ) { return { success : false , msg : `${ e . toString ( ) }` } ; } return { success : false , msg : `${ e . toString ( ) }` } ; } Analytics . logEvent ( 'Open Url on Device' , { platform : 'ios' } ) ; return { success : true , device : simulator , bundleIdentifier } ; } async function assertDevClientInstalledAsync ( simulator , bundleIdentifier ) { if ( ! ( await SimControl . getContainerPathAsync ( { udid : simulator . udid , bundleIdentifier } ) ) ) { throw new Error ( `The development client (${ bundleIdentifier }) for this project is not installed. ` + `Please build and install the client on the simulator first.\n${ learnMore ( 'https://docs.expo.dev/clients/distribution-for-ios/#building-for-ios' ) }` ) ; } } const hasPromptedToUpgrade = { } ; async function ensureExpoClientInstalledAsync ( simulator , sdkVersion ? ) { let isInstalled = await isExpoClientInstalledOnSimulatorAsync ( simulator ) ; if ( isInstalled && ! hasPromptedToUpgrade [ simulator . udid ] ) { hasPromptedToUpgrade [ simulator . udid ] = true ; if ( await profileMethod ( doesExpoClientNeedUpdatedAsync ) ( simulator , sdkVersion ) ) { const confirm = await Prompts . confirmAsync ( { initial : true , message : `Expo Go on ${ simulator . name } is outdated, would you like to upgrade?` } ) ; if ( confirm ) { isInstalled = false ; } } } if ( ! isInstalled ) { const iosClient = await getClientForSDK ( sdkVersion ) ; await installExpoOnSimulatorAsync ( { simulator , ... iosClient } ) ; await waitForExpoClientInstalledOnSimulatorAsync ( simulator ) ; } } async function getClientForSDK ( sdkVersionString ? ) { if ( ! sdkVersionString ) { return null ; } const sdkVersion = ( await Versions . sdkVersionsAsync ( ) ) [ sdkVersionString ] ; if ( ! sdkVersion ) { return null ; } return { url : sdkVersion . iosClientUrl , version : sdkVersion . iosClientVersion } ; } export async function resolveApplicationIdAsync ( projectRoot ) { try { const bundleId = await IOSConfig . BundleIdentifier . getBundleIdentifierFromPbxproj ( projectRoot ) ; if ( bundleId ) { return bundleId ; } } catch { } try { const infoPlistPath = IOSConfig . Paths . getInfoPlistPath ( projectRoot ) ; const data = await plist . parse ( fs . readFileSync ( infoPlistPath , 'utf8' ) ) ; if ( data . CFBundleIdentifier && ! data . CFBundleIdentifier . startsWith ( '$(' ) ) { return data . CFBundleIdentifier ; } } catch { } const { exp } = getConfig ( projectRoot , { skipSDKVersionRequirement : true } ) ; return exp . ios ?. bundleIdentifier ; } async function constructDeepLinkAsync ( projectRoot , scheme ? , devClient ? ) { if ( process . env [ 'EXPO_ENABLE_INTERSTITIAL_PAGE' ] && ! devClient && isDevClientPackageInstalled ( projectRoot ) ) { return UrlUtils . constructLoadingUrlAsync ( projectRoot , 'ios' , 'localhost' ) ; } else { try { return await UrlUtils . constructDeepLinkAsync ( projectRoot , { scheme } ) ; } catch ( e ) { if ( devClient ) { return null ; } throw e ; } } } export async function openProjectAsync ( { projectRoot , shouldPrompt , devClient , udid , scheme , skipNativeLogs , applicationId , } ) { if ( ! ( await profileMethod ( isSimulatorInstalledAsync ) ( ) ) ) { return { success : false , error : 'Unable to verify Xcode and Simulator installation.' } ; } const projectUrl = await constructDeepLinkAsync ( projectRoot , scheme , devClient ) ; Logger . global . debug ( `iOS project url: ${ projectUrl }` ) ; const { exp } = getConfig ( projectRoot , { skipSDKVersionRequirement : true } ) ; let device = null ; if ( ! udid && shouldPrompt ) { const devices = await getSelectableSimulatorsAsync ( ) ; device = await promptForSimulatorAsync ( devices ) ; if ( ! device ) { return { success : false , error : 'escaped' } ; } } else { device = await ensureSimulatorOpenAsync ( { udid } ) ; } if ( ! projectUrl ) { applicationId = applicationId ?? ( await resolveApplicationIdAsync ( projectRoot ) ) ; Logger . global . debug ( `Open iOS project from app id: ${ applicationId }` ) ; if ( ! applicationId ) { return { success : false , error : 'Cannot resolve bundle identifier or URI scheme to open the native iOS app.\nBuild the native app with `expo run:ios` or `eas build -p ios`' } ; } Logger . global . info ( `\u203A Opening ${ chalk . underline ( applicationId ) } on ${ chalk . bold ( device . name ) }` ) ; const result = await SimControl . openBundleIdAsync ( { udid : device . udid , bundleIdentifier : applicationId } ) . catch ( error => { if ( 'status' in error ) { return error ; } throw error ; } ) ; if ( result . status === 0 ) { await ensureSimulatorAppRunningAsync ( { udid : device ?. udid } ) ; activateSimulatorWindowAsync ( ) ; } else { let errorMessage = `Couldn't open iOS app with ID "${ applicationId }" on device "${ device . name }".` ; if ( result . status === 4 ) { errorMessage += `\nThe app might not be installed, try installing it with: ${ chalk . bold ( `expo run:ios -d ${ device . udid }` ) }` ; } errorMessage += chalk . gray ( `\n${ result . stderr }` ) ; return { success : false , error : errorMessage } ; } return { success : true , udid : device . udid , bundleIdentifier : applicationId , url : '' } ; } const result = await profileMethod ( openUrlInSimulatorSafeAsync ) ( { udid : device ?. udid , url : projectUrl , sdkVersion : exp . sdkVersion , isDetached : ! ! exp . isDetached , devClient , exp , projectRoot , skipNativeLogs } ) ; if ( result . success ) { return { success : true , url : projectUrl , udid : result . device . udid , bundleIdentifier : result . bundleIdentifier } ; } return { success : result . success , error : result . msg } ; } export async function streamLogsAsync ( { bundleIdentifier , udid , } ) { if ( SimControlLogs . isStreamingLogs ( udid ) ) { return ; } const imageName = await SimControlLogs . getImageNameFromBundleIdentifierAsync ( udid , bundleIdentifier ) ; if ( imageName ) { SimControlLogs . streamLogs ( { pid : imageName , udid } ) ; } } export async function openWebProjectAsync ( { projectRoot , shouldPrompt , } ) { if ( ! ( await isSimulatorInstalledAsync ( ) ) ) { return { success : false , error : 'Unable to verify Xcode and Simulator installation.' } ; } const projectUrl = await Webpack . getUrlAsync ( projectRoot ) ; if ( projectUrl === null ) { return { success : false , error : `The web project has not been started yet` } ; } let device = null ; if ( shouldPrompt ) { const devices = await getSelectableSimulatorsAsync ( ) ; device = await promptForSimulatorAsync ( devices ) ; if ( ! device ) { return { success : false , error : 'escaped' } ; } } const result = await openUrlInSimulatorSafeAsync ( { url : projectUrl , udid : device ?. udid , isDetached : true , projectRoot } ) ; if ( result . success ) { activateSimulatorWindowAsync ( ) ; return { success : true , url : projectUrl } ; } return { success : result . success , error : result . msg } ; } param devices export async function sortDefaultDeviceToBeginningAsync ( devices , osType ? ) { const defaultUdid = await getBestSimulatorAsync ( { osType } ) ; if ( defaultUdid ) { let iterations = 0 ; while ( devices [ 0 ] . udid !== defaultUdid && iterations < devices . length ) { devices . push ( devices . shift ( ) ! ) ; iterations ++ ; } } return devices ; } export async function promptForSimulatorAsync ( devices , osType ? ) { devices = await sortDefaultDeviceToBeginningAsync ( devices , osType ) ; const results = await promptForDeviceAsync ( devices ) ; return results ? devices . find ( ( { udid } ) => results === udid ) ! : null ; } async function promptForDeviceAsync ( devices ) { Prompts . pauseInteractions ( ) ; const { value } = await prompts ( { type : 'autocomplete' , name : 'value' , limit : 11 , message : 'Select a simulator' , choices : devices . map ( item => { const isActive = item . state === 'Booted' ; const format = isActive ? chalk . bold : ( text ) => text ; return { title : `${ format ( item . name ) } ${ chalk . dim ( `(${ item . osVersion })` ) }` , value : item . udid } ; } ) , suggest : ( input , choices ) => { const regex = new RegExp ( input , 'i' ) ; return choices . filter ( ( choice ) => regex . test ( choice . title ) ) ; } } ) ; Prompts . resumeInteractions ( ) ; return value ; } export { ensureSimulatorAppRunningAsync } ;