1import { ExpoConfig, getConfig } from '@expo/config'; 2 3import { APISettings } from '../../api/settings'; 4import { updateDevelopmentSessionAsync } from '../../api/updateDevelopmentSession'; 5import { getUserAsync } from '../../api/user/user'; 6import * as ProjectDevices from '../project/devices'; 7 8const UPDATE_FREQUENCY = 20 * 1000; // 20 seconds 9 10async function isAuthenticatedAsync(): Promise<boolean> { 11 return !!(await getUserAsync().catch(() => null)); 12} 13 14export class DevelopmentSession { 15 private timeout: NodeJS.Timeout | null = null; 16 17 constructor( 18 /** Project root directory. */ 19 private projectRoot: string, 20 /** Development Server URL. */ 21 public url: string 22 ) {} 23 24 /** 25 * Notify the Expo servers that a project is running, this enables the Expo Go app 26 * and Dev Clients to offer a "recently in development" section for quick access. 27 * 28 * This method starts an interval that will continue to ping the servers until we stop it. 29 * 30 * @param projectRoot Project root folder, used for retrieving device installation IDs. 31 * @param props.exp Partial Expo config with values that will be used in the Expo Go app. 32 * @param props.runtime which runtime the app should be opened in. `native` for dev clients, `web` for web browsers. 33 * @returns 34 */ 35 public async startAsync({ 36 exp = getConfig(this.projectRoot).exp, 37 runtime, 38 }: { 39 exp?: Pick<ExpoConfig, 'name' | 'description' | 'slug' | 'primaryColor'>; 40 runtime: 'native' | 'web'; 41 }): Promise<void> { 42 if (APISettings.isOffline) { 43 this.stop(); 44 return; 45 } 46 47 const deviceIds = await this.getDeviceInstallationIdsAsync(); 48 49 if (!(await isAuthenticatedAsync()) && !deviceIds?.length) { 50 this.stop(); 51 return; 52 } 53 54 await updateDevelopmentSessionAsync({ 55 url: this.url, 56 runtime, 57 exp, 58 deviceIds, 59 }); 60 61 this.stop(); 62 63 this.timeout = setTimeout(() => this.startAsync({ exp, runtime }), UPDATE_FREQUENCY); 64 } 65 66 /** Get all recent devices for the project. */ 67 private async getDeviceInstallationIdsAsync(): Promise<string[]> { 68 const { devices } = await ProjectDevices.getDevicesInfoAsync(this.projectRoot); 69 return devices.map(({ installationId }) => installationId); 70 } 71 72 /** Stop notifying the Expo servers that the development session is running. */ 73 public stop() { 74 clearTimeout(this.timeout); 75 this.timeout = null; 76 } 77} 78