@expo/cli 56.0.2 → 56.0.3

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015-present 650 Industries, Inc. (aka Expo)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/build/bin/cli CHANGED
@@ -139,7 +139,7 @@ const args = (0, _arg().default)({
139
139
  });
140
140
  if (args['--version']) {
141
141
  // Version is added in the build script.
142
- console.log("56.0.2");
142
+ console.log("56.0.3");
143
143
  process.exit(0);
144
144
  }
145
145
  if (args['--non-interactive']) {
package/build/bin/cli.map CHANGED
File without changes
@@ -58,12 +58,13 @@ function createSessionInfo({ exp, runtime, url }) {
58
58
  }
59
59
  };
60
60
  }
61
- async function updateDevelopmentSessionAsync({ deviceIds, exp, runtime, url }) {
61
+ async function updateDevelopmentSessionAsync({ deviceIds, exp, runtime, url, signal }) {
62
62
  const searchParams = new (_url()).URLSearchParams();
63
63
  deviceIds.forEach((id)=>{
64
64
  searchParams.append('deviceId', id);
65
65
  });
66
66
  const results = await (0, _client.fetchAsync)('development-sessions/notify-alive', {
67
+ signal,
67
68
  searchParams,
68
69
  method: 'POST',
69
70
  body: JSON.stringify({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/api/updateDevelopmentSession.ts"],"sourcesContent":["import type { ExpoConfig } from '@expo/config';\nimport os from 'os';\nimport { URLSearchParams } from 'url';\n\nimport { fetchAsync } from './rest/client';\nimport { CommandError } from '../utils/errors';\n\n/** Create the expected session info. */\nexport function createSessionInfo({\n exp,\n runtime,\n url,\n}: {\n exp: Pick<ExpoConfig, 'name' | 'description' | 'slug' | 'primaryColor'>;\n runtime: 'native' | 'web';\n url: string;\n}) {\n return {\n session: {\n description: `${exp.name} on ${os.hostname()}`,\n hostname: os.hostname(),\n platform: runtime,\n config: {\n // TODO: if icons are specified, upload a url for them too so people can distinguish\n description: exp.description,\n name: exp.name,\n slug: exp.slug,\n primaryColor: exp.primaryColor,\n },\n url,\n source: 'desktop',\n },\n };\n}\n\n/** Send a request to Expo API to keep the 'development session' alive for the provided devices. */\nexport async function updateDevelopmentSessionAsync({\n deviceIds,\n exp,\n runtime,\n url,\n}: {\n deviceIds: string[];\n exp: Pick<ExpoConfig, 'name' | 'description' | 'slug' | 'primaryColor'>;\n runtime: 'native' | 'web';\n url: string;\n}) {\n const searchParams = new URLSearchParams();\n deviceIds.forEach((id) => {\n searchParams.append('deviceId', id);\n });\n\n const results = await fetchAsync('development-sessions/notify-alive', {\n searchParams,\n method: 'POST',\n body: JSON.stringify({\n data: createSessionInfo({ exp, runtime, url }),\n }),\n });\n\n if (!results.ok) {\n throw new CommandError(\n 'API',\n `Unexpected response when updating the development session on Expo servers: ${results.statusText}.`\n );\n }\n}\n\n/** Send a request to Expo API to close the 'development session' for the provided devices. */\nexport async function closeDevelopmentSessionAsync({\n deviceIds,\n url,\n}: {\n deviceIds: string[];\n url: string;\n}) {\n const searchParams = new URLSearchParams();\n deviceIds.forEach((id) => {\n searchParams.append('deviceId', id);\n });\n\n const results = await fetchAsync('development-sessions/notify-close', {\n searchParams,\n method: 'POST',\n body: JSON.stringify({\n session: { url },\n }),\n });\n\n if (!results.ok) {\n throw new CommandError(\n 'API',\n `Unexpected response when closing the development session on Expo servers: ${results.statusText}.`\n );\n }\n}\n"],"names":["closeDevelopmentSessionAsync","createSessionInfo","updateDevelopmentSessionAsync","exp","runtime","url","session","description","name","os","hostname","platform","config","slug","primaryColor","source","deviceIds","searchParams","URLSearchParams","forEach","id","append","results","fetchAsync","method","body","JSON","stringify","data","ok","CommandError","statusText"],"mappings":";;;;;;;;;;;QAqEsBA;eAAAA;;QA7DNC;eAAAA;;QA4BMC;eAAAA;;;;gEAnCP;;;;;;;yBACiB;;;;;;wBAEL;wBACE;;;;;;AAGtB,SAASD,kBAAkB,EAChCE,GAAG,EACHC,OAAO,EACPC,GAAG,EAKJ;IACC,OAAO;QACLC,SAAS;YACPC,aAAa,GAAGJ,IAAIK,IAAI,CAAC,IAAI,EAAEC,aAAE,CAACC,QAAQ,IAAI;YAC9CA,UAAUD,aAAE,CAACC,QAAQ;YACrBC,UAAUP;YACVQ,QAAQ;gBACN,oFAAoF;gBACpFL,aAAaJ,IAAII,WAAW;gBAC5BC,MAAML,IAAIK,IAAI;gBACdK,MAAMV,IAAIU,IAAI;gBACdC,cAAcX,IAAIW,YAAY;YAChC;YACAT;YACAU,QAAQ;QACV;IACF;AACF;AAGO,eAAeb,8BAA8B,EAClDc,SAAS,EACTb,GAAG,EACHC,OAAO,EACPC,GAAG,EAMJ;IACC,MAAMY,eAAe,IAAIC,CAAAA,MAAc,iBAAC;IACxCF,UAAUG,OAAO,CAAC,CAACC;QACjBH,aAAaI,MAAM,CAAC,YAAYD;IAClC;IAEA,MAAME,UAAU,MAAMC,IAAAA,kBAAU,EAAC,qCAAqC;QACpEN;QACAO,QAAQ;QACRC,MAAMC,KAAKC,SAAS,CAAC;YACnBC,MAAM3B,kBAAkB;gBAAEE;gBAAKC;gBAASC;YAAI;QAC9C;IACF;IAEA,IAAI,CAACiB,QAAQO,EAAE,EAAE;QACf,MAAM,IAAIC,oBAAY,CACpB,OACA,CAAC,2EAA2E,EAAER,QAAQS,UAAU,CAAC,CAAC,CAAC;IAEvG;AACF;AAGO,eAAe/B,6BAA6B,EACjDgB,SAAS,EACTX,GAAG,EAIJ;IACC,MAAMY,eAAe,IAAIC,CAAAA,MAAc,iBAAC;IACxCF,UAAUG,OAAO,CAAC,CAACC;QACjBH,aAAaI,MAAM,CAAC,YAAYD;IAClC;IAEA,MAAME,UAAU,MAAMC,IAAAA,kBAAU,EAAC,qCAAqC;QACpEN;QACAO,QAAQ;QACRC,MAAMC,KAAKC,SAAS,CAAC;YACnBrB,SAAS;gBAAED;YAAI;QACjB;IACF;IAEA,IAAI,CAACiB,QAAQO,EAAE,EAAE;QACf,MAAM,IAAIC,oBAAY,CACpB,OACA,CAAC,0EAA0E,EAAER,QAAQS,UAAU,CAAC,CAAC,CAAC;IAEtG;AACF"}
1
+ {"version":3,"sources":["../../../src/api/updateDevelopmentSession.ts"],"sourcesContent":["import type { ExpoConfig } from '@expo/config';\nimport os from 'os';\nimport { URLSearchParams } from 'url';\n\nimport { fetchAsync } from './rest/client';\nimport { CommandError } from '../utils/errors';\n\n/** Create the expected session info. */\nexport function createSessionInfo({\n exp,\n runtime,\n url,\n}: {\n exp: Pick<ExpoConfig, 'name' | 'description' | 'slug' | 'primaryColor'>;\n runtime: 'native' | 'web';\n url: string;\n}) {\n return {\n session: {\n description: `${exp.name} on ${os.hostname()}`,\n hostname: os.hostname(),\n platform: runtime,\n config: {\n // TODO: if icons are specified, upload a url for them too so people can distinguish\n description: exp.description,\n name: exp.name,\n slug: exp.slug,\n primaryColor: exp.primaryColor,\n },\n url,\n source: 'desktop',\n },\n };\n}\n\n/** Send a request to Expo API to keep the 'development session' alive for the provided devices. */\nexport async function updateDevelopmentSessionAsync({\n deviceIds,\n exp,\n runtime,\n url,\n signal,\n}: {\n deviceIds: string[];\n exp: Pick<ExpoConfig, 'name' | 'description' | 'slug' | 'primaryColor'>;\n runtime: 'native' | 'web';\n url: string;\n signal?: AbortSignal;\n}) {\n const searchParams = new URLSearchParams();\n deviceIds.forEach((id) => {\n searchParams.append('deviceId', id);\n });\n\n const results = await fetchAsync('development-sessions/notify-alive', {\n signal,\n searchParams,\n method: 'POST',\n body: JSON.stringify({\n data: createSessionInfo({ exp, runtime, url }),\n }),\n });\n\n if (!results.ok) {\n throw new CommandError(\n 'API',\n `Unexpected response when updating the development session on Expo servers: ${results.statusText}.`\n );\n }\n}\n\n/** Send a request to Expo API to close the 'development session' for the provided devices. */\nexport async function closeDevelopmentSessionAsync({\n deviceIds,\n url,\n}: {\n deviceIds: string[];\n url: string;\n}) {\n const searchParams = new URLSearchParams();\n deviceIds.forEach((id) => {\n searchParams.append('deviceId', id);\n });\n\n const results = await fetchAsync('development-sessions/notify-close', {\n searchParams,\n method: 'POST',\n body: JSON.stringify({\n session: { url },\n }),\n });\n\n if (!results.ok) {\n throw new CommandError(\n 'API',\n `Unexpected response when closing the development session on Expo servers: ${results.statusText}.`\n );\n }\n}\n"],"names":["closeDevelopmentSessionAsync","createSessionInfo","updateDevelopmentSessionAsync","exp","runtime","url","session","description","name","os","hostname","platform","config","slug","primaryColor","source","deviceIds","signal","searchParams","URLSearchParams","forEach","id","append","results","fetchAsync","method","body","JSON","stringify","data","ok","CommandError","statusText"],"mappings":";;;;;;;;;;;QAwEsBA;eAAAA;;QAhENC;eAAAA;;QA4BMC;eAAAA;;;;gEAnCP;;;;;;;yBACiB;;;;;;wBAEL;wBACE;;;;;;AAGtB,SAASD,kBAAkB,EAChCE,GAAG,EACHC,OAAO,EACPC,GAAG,EAKJ;IACC,OAAO;QACLC,SAAS;YACPC,aAAa,GAAGJ,IAAIK,IAAI,CAAC,IAAI,EAAEC,aAAE,CAACC,QAAQ,IAAI;YAC9CA,UAAUD,aAAE,CAACC,QAAQ;YACrBC,UAAUP;YACVQ,QAAQ;gBACN,oFAAoF;gBACpFL,aAAaJ,IAAII,WAAW;gBAC5BC,MAAML,IAAIK,IAAI;gBACdK,MAAMV,IAAIU,IAAI;gBACdC,cAAcX,IAAIW,YAAY;YAChC;YACAT;YACAU,QAAQ;QACV;IACF;AACF;AAGO,eAAeb,8BAA8B,EAClDc,SAAS,EACTb,GAAG,EACHC,OAAO,EACPC,GAAG,EACHY,MAAM,EAOP;IACC,MAAMC,eAAe,IAAIC,CAAAA,MAAc,iBAAC;IACxCH,UAAUI,OAAO,CAAC,CAACC;QACjBH,aAAaI,MAAM,CAAC,YAAYD;IAClC;IAEA,MAAME,UAAU,MAAMC,IAAAA,kBAAU,EAAC,qCAAqC;QACpEP;QACAC;QACAO,QAAQ;QACRC,MAAMC,KAAKC,SAAS,CAAC;YACnBC,MAAM5B,kBAAkB;gBAAEE;gBAAKC;gBAASC;YAAI;QAC9C;IACF;IAEA,IAAI,CAACkB,QAAQO,EAAE,EAAE;QACf,MAAM,IAAIC,oBAAY,CACpB,OACA,CAAC,2EAA2E,EAAER,QAAQS,UAAU,CAAC,CAAC,CAAC;IAEvG;AACF;AAGO,eAAehC,6BAA6B,EACjDgB,SAAS,EACTX,GAAG,EAIJ;IACC,MAAMa,eAAe,IAAIC,CAAAA,MAAc,iBAAC;IACxCH,UAAUI,OAAO,CAAC,CAACC;QACjBH,aAAaI,MAAM,CAAC,YAAYD;IAClC;IAEA,MAAME,UAAU,MAAMC,IAAAA,kBAAU,EAAC,qCAAqC;QACpEN;QACAO,QAAQ;QACRC,MAAMC,KAAKC,SAAS,CAAC;YACnBtB,SAAS;gBAAED;YAAI;QACjB;IACF;IAEA,IAAI,CAACkB,QAAQO,EAAE,EAAE;QACf,MAAM,IAAIC,oBAAY,CACpB,OACA,CAAC,0EAA0E,EAAER,QAAQS,UAAU,CAAC,CAAC,CAAC;IAEtG;AACF"}
@@ -76,7 +76,7 @@ function getInitMetadata() {
76
76
  return {
77
77
  format: 'v0-jsonl',
78
78
  // Version is added in the build script.
79
- version: "56.0.2" ?? 'UNVERSIONED'
79
+ version: "56.0.3" ?? 'UNVERSIONED'
80
80
  };
81
81
  }
82
82
  function getWellKnownTemporaryLogFile(projectRoot, command) {
@@ -97,14 +97,14 @@ async function startInterfaceAsync(devServerManager, options) {
97
97
  devClient: devServerManager.options.devClient,
98
98
  ...options
99
99
  };
100
- // Wait briefly for the dependency check to resolve (it runs in the background since early startup).
101
- // With a warm fetch cache this resolves near-instantly; on cold starts it may not be ready,
102
- // in which case it will appear on the next TUI re-print (e.g. pressing 'c').
100
+ // Print the dependency check if it completed (it runs in the background since early startup).
101
+ // With a warm fetch cache this resolves near-instantly, so we defer by a tick
102
+ // On cold starts it may not be ready, in which case it will appear on the next reprint or restart
103
103
  let dependencyCheckResult;
104
104
  if (options.dependencyCheckPromise) {
105
105
  dependencyCheckResult = await Promise.race([
106
106
  options.dependencyCheckPromise,
107
- new Promise((resolve)=>setTimeout(resolve, 100))
107
+ Promise.resolve(null)
108
108
  ]);
109
109
  if (!dependencyCheckResult) {
110
110
  // Not ready yet — capture once resolved for display on next reprint
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/start/interface/startInterface.ts"],"sourcesContent":["import chalk from 'chalk';\n\nimport { KeyPressHandler } from './KeyPressHandler';\nimport type { StartOptions } from './commandsTable';\nimport { BLT, printHelp, printUsage } from './commandsTable';\nimport { DevServerManagerActions } from './interactiveActions';\nimport * as Log from '../../log';\nimport { openInEditorAsync } from '../../utils/editor';\nimport { AbortCommandError } from '../../utils/errors';\nimport { getAllSpinners, ora } from '../../utils/ora';\nimport { getProgressBar, setProgressBar } from '../../utils/progress';\nimport { addInteractionListener, pauseInteractions } from '../../utils/prompts';\nimport type { DependencyCheckResult } from '../checkDependenciesOnStart';\nimport { printDependencyCheckResult } from '../checkDependenciesOnStart';\nimport { WebSupportProjectPrerequisite } from '../doctor/web/WebSupportProjectPrerequisite';\nimport type { DevServerManager } from '../server/DevServerManager';\n\nconst debug = require('debug')('expo:start:interface:startInterface') as typeof console.log;\n\nconst CTRL_C = '\\u0003';\nconst CTRL_D = '\\u0004';\nconst CTRL_L = '\\u000C';\n\nconst PLATFORM_SETTINGS: Record<\n string,\n { name: string; key: 'android' | 'ios'; launchTarget: 'emulator' | 'simulator' }\n> = {\n android: {\n name: 'Android',\n key: 'android',\n launchTarget: 'emulator',\n },\n ios: {\n name: 'iOS',\n key: 'ios',\n launchTarget: 'simulator',\n },\n};\n\nexport async function startInterfaceAsync(\n devServerManager: DevServerManager,\n options: Pick<StartOptions, 'devClient' | 'platforms' | 'mcpServer' | 'dependencyCheckPromise'>\n) {\n const actions = new DevServerManagerActions(devServerManager, options);\n\n const isWebSocketsEnabled = devServerManager.getDefaultDevServer()?.isTargetingNative();\n\n const usageOptions = {\n isWebSocketsEnabled,\n devClient: devServerManager.options.devClient,\n ...options,\n };\n\n // Wait briefly for the dependency check to resolve (it runs in the background since early startup).\n // With a warm fetch cache this resolves near-instantly; on cold starts it may not be ready,\n // in which case it will appear on the next TUI re-print (e.g. pressing 'c').\n let dependencyCheckResult: DependencyCheckResult | null | undefined;\n if (options.dependencyCheckPromise) {\n dependencyCheckResult = await Promise.race([\n options.dependencyCheckPromise,\n new Promise<undefined>((resolve) => setTimeout(resolve, 100)),\n ]);\n if (!dependencyCheckResult) {\n // Not ready yet — capture once resolved for display on next reprint\n options.dependencyCheckPromise.then((result) => {\n if (result) {\n dependencyCheckResult = result;\n }\n });\n }\n }\n\n const printDependencyCheckIfAvailable = () => {\n if (dependencyCheckResult) {\n printDependencyCheckResult(dependencyCheckResult);\n }\n };\n\n actions.printDevServerInfo(usageOptions);\n printDependencyCheckIfAvailable();\n\n const onPressAsync = async (key: string) => {\n // Auxillary commands all escape.\n switch (key) {\n case CTRL_C:\n case CTRL_D: {\n // Prevent terminal UI from accepting commands while the process is closing.\n // Without this, fast typers will close the server then start typing their\n // next command and have a bunch of unrelated things pop up.\n pauseInteractions();\n\n const spinners = getAllSpinners();\n spinners.forEach((spinner) => {\n spinner.fail();\n });\n\n const currentProgress = getProgressBar();\n if (currentProgress) {\n currentProgress.terminate();\n setProgressBar(null);\n }\n const spinner = ora({ text: 'Stopping server', color: 'white' }).start();\n try {\n await devServerManager.stopAsync();\n if (options.mcpServer) {\n await options.mcpServer.closeAsync();\n }\n spinner.stopAndPersist({ text: 'Stopped server', symbol: `\\u203A` });\n // @ts-ignore: Argument of type '\"SIGINT\"' is not assignable to parameter of type '\"disconnect\"'.\n process.emit('SIGINT');\n\n // TODO: Is this the right place to do this?\n process.exit();\n } catch (error) {\n spinner.fail('Failed to stop server');\n throw error;\n }\n break;\n }\n case CTRL_L:\n return Log.clear();\n case '?':\n return printUsage(usageOptions, { verbose: true });\n }\n\n // Optionally enabled\n\n if (isWebSocketsEnabled) {\n switch (key) {\n case 'm':\n return actions.toggleDevMenu();\n case 'M':\n return actions.openMoreToolsAsync();\n }\n }\n\n const { platforms = ['ios', 'android', 'web'] } = options;\n\n if (['i', 'a'].includes(key.toLowerCase())) {\n const platform = key.toLowerCase() === 'i' ? 'ios' : 'android';\n\n const shouldPrompt = ['I', 'A'].includes(key);\n if (shouldPrompt) {\n Log.clear();\n }\n\n const server = devServerManager.getDefaultDevServer();\n const settings = PLATFORM_SETTINGS[platform]!;\n\n Log.log(`${BLT} Opening on ${settings.name}...`);\n\n if (server.isTargetingNative() && !platforms.includes(settings.key)) {\n Log.warn(\n chalk`${settings.name} is disabled, enable it by adding {bold ${settings.key}} to the platforms array in your app.json or app.config.js`\n );\n } else {\n try {\n await server.openPlatformAsync(settings.launchTarget, { shouldPrompt });\n printHelp();\n } catch (error: any) {\n if (!(error instanceof AbortCommandError)) {\n Log.exception(error);\n }\n }\n }\n // Break out early.\n return;\n }\n\n switch (key) {\n case 's': {\n Log.clear();\n if (await devServerManager.toggleRuntimeMode()) {\n usageOptions.devClient = devServerManager.options.devClient;\n actions.printDevServerInfo(usageOptions);\n printDependencyCheckIfAvailable();\n return;\n }\n break;\n }\n case 'w': {\n try {\n await devServerManager.ensureProjectPrerequisiteAsync(WebSupportProjectPrerequisite);\n if (!platforms.includes('web')) {\n platforms.push('web');\n options.platforms?.push('web');\n }\n } catch (e: any) {\n Log.warn(e.message);\n break;\n }\n\n const isDisabled = !platforms.includes('web');\n if (isDisabled) {\n debug('Web is disabled');\n // Use warnings from the web support setup.\n break;\n }\n\n // Ensure the Webpack dev server is running first\n if (!devServerManager.getWebDevServer()) {\n debug('Starting up webpack dev server');\n await devServerManager.ensureWebDevServerRunningAsync();\n // When this is the first time webpack is started, reprint the connection info.\n actions.printDevServerInfo(usageOptions);\n }\n\n Log.log(`${BLT} Open in the web browser...`);\n try {\n await devServerManager.getWebDevServer()?.openPlatformAsync('desktop');\n printHelp();\n } catch (error: any) {\n if (!(error instanceof AbortCommandError)) {\n Log.exception(error);\n }\n }\n break;\n }\n case 'c':\n Log.clear();\n actions.printDevServerInfo(usageOptions);\n printDependencyCheckIfAvailable();\n return;\n case 'j':\n return actions.openJsInspectorAsync();\n case 'r':\n return actions.reloadApp();\n case 'o':\n Log.log(`${BLT} Opening the editor...`);\n return openInEditorAsync(devServerManager.projectRoot);\n }\n };\n\n const keyPressHandler = new KeyPressHandler(onPressAsync);\n\n const listener = keyPressHandler.createInteractionListener();\n\n addInteractionListener(listener);\n\n // Start observing...\n keyPressHandler.startInterceptingKeyStrokes();\n}\n"],"names":["startInterfaceAsync","debug","require","CTRL_C","CTRL_D","CTRL_L","PLATFORM_SETTINGS","android","name","key","launchTarget","ios","devServerManager","options","actions","DevServerManagerActions","isWebSocketsEnabled","getDefaultDevServer","isTargetingNative","usageOptions","devClient","dependencyCheckResult","dependencyCheckPromise","Promise","race","resolve","setTimeout","then","result","printDependencyCheckIfAvailable","printDependencyCheckResult","printDevServerInfo","onPressAsync","pauseInteractions","spinners","getAllSpinners","forEach","spinner","fail","currentProgress","getProgressBar","terminate","setProgressBar","ora","text","color","start","stopAsync","mcpServer","closeAsync","stopAndPersist","symbol","process","emit","exit","error","Log","clear","printUsage","verbose","toggleDevMenu","openMoreToolsAsync","platforms","includes","toLowerCase","platform","shouldPrompt","server","settings","log","BLT","warn","chalk","openPlatformAsync","printHelp","AbortCommandError","exception","toggleRuntimeMode","ensureProjectPrerequisiteAsync","WebSupportProjectPrerequisite","push","e","message","isDisabled","getWebDevServer","ensureWebDevServerRunningAsync","openJsInspectorAsync","reloadApp","openInEditorAsync","projectRoot","keyPressHandler","KeyPressHandler","listener","createInteractionListener","addInteractionListener","startInterceptingKeyStrokes"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../src/start/interface/startInterface.ts"],"sourcesContent":["import chalk from 'chalk';\n\nimport { KeyPressHandler } from './KeyPressHandler';\nimport type { StartOptions } from './commandsTable';\nimport { BLT, printHelp, printUsage } from './commandsTable';\nimport { DevServerManagerActions } from './interactiveActions';\nimport * as Log from '../../log';\nimport { openInEditorAsync } from '../../utils/editor';\nimport { AbortCommandError } from '../../utils/errors';\nimport { getAllSpinners, ora } from '../../utils/ora';\nimport { getProgressBar, setProgressBar } from '../../utils/progress';\nimport { addInteractionListener, pauseInteractions } from '../../utils/prompts';\nimport type { DependencyCheckResult } from '../checkDependenciesOnStart';\nimport { printDependencyCheckResult } from '../checkDependenciesOnStart';\nimport { WebSupportProjectPrerequisite } from '../doctor/web/WebSupportProjectPrerequisite';\nimport type { DevServerManager } from '../server/DevServerManager';\n\nconst debug = require('debug')('expo:start:interface:startInterface') as typeof console.log;\n\nconst CTRL_C = '\\u0003';\nconst CTRL_D = '\\u0004';\nconst CTRL_L = '\\u000C';\n\nconst PLATFORM_SETTINGS: Record<\n string,\n { name: string; key: 'android' | 'ios'; launchTarget: 'emulator' | 'simulator' }\n> = {\n android: {\n name: 'Android',\n key: 'android',\n launchTarget: 'emulator',\n },\n ios: {\n name: 'iOS',\n key: 'ios',\n launchTarget: 'simulator',\n },\n};\n\nexport async function startInterfaceAsync(\n devServerManager: DevServerManager,\n options: Pick<StartOptions, 'devClient' | 'platforms' | 'mcpServer' | 'dependencyCheckPromise'>\n) {\n const actions = new DevServerManagerActions(devServerManager, options);\n\n const isWebSocketsEnabled = devServerManager.getDefaultDevServer()?.isTargetingNative();\n\n const usageOptions = {\n isWebSocketsEnabled,\n devClient: devServerManager.options.devClient,\n ...options,\n };\n\n // Print the dependency check if it completed (it runs in the background since early startup).\n // With a warm fetch cache this resolves near-instantly, so we defer by a tick\n // On cold starts it may not be ready, in which case it will appear on the next reprint or restart\n let dependencyCheckResult: DependencyCheckResult | null | undefined;\n if (options.dependencyCheckPromise) {\n dependencyCheckResult = await Promise.race([\n options.dependencyCheckPromise,\n Promise.resolve(null),\n ]);\n if (!dependencyCheckResult) {\n // Not ready yet — capture once resolved for display on next reprint\n options.dependencyCheckPromise.then((result) => {\n if (result) {\n dependencyCheckResult = result;\n }\n });\n }\n }\n\n const printDependencyCheckIfAvailable = () => {\n if (dependencyCheckResult) {\n printDependencyCheckResult(dependencyCheckResult);\n }\n };\n\n actions.printDevServerInfo(usageOptions);\n printDependencyCheckIfAvailable();\n\n const onPressAsync = async (key: string) => {\n // Auxillary commands all escape.\n switch (key) {\n case CTRL_C:\n case CTRL_D: {\n // Prevent terminal UI from accepting commands while the process is closing.\n // Without this, fast typers will close the server then start typing their\n // next command and have a bunch of unrelated things pop up.\n pauseInteractions();\n\n const spinners = getAllSpinners();\n spinners.forEach((spinner) => {\n spinner.fail();\n });\n\n const currentProgress = getProgressBar();\n if (currentProgress) {\n currentProgress.terminate();\n setProgressBar(null);\n }\n const spinner = ora({ text: 'Stopping server', color: 'white' }).start();\n try {\n await devServerManager.stopAsync();\n if (options.mcpServer) {\n await options.mcpServer.closeAsync();\n }\n spinner.stopAndPersist({ text: 'Stopped server', symbol: `\\u203A` });\n // @ts-ignore: Argument of type '\"SIGINT\"' is not assignable to parameter of type '\"disconnect\"'.\n process.emit('SIGINT');\n\n // TODO: Is this the right place to do this?\n process.exit();\n } catch (error) {\n spinner.fail('Failed to stop server');\n throw error;\n }\n break;\n }\n case CTRL_L:\n return Log.clear();\n case '?':\n return printUsage(usageOptions, { verbose: true });\n }\n\n // Optionally enabled\n\n if (isWebSocketsEnabled) {\n switch (key) {\n case 'm':\n return actions.toggleDevMenu();\n case 'M':\n return actions.openMoreToolsAsync();\n }\n }\n\n const { platforms = ['ios', 'android', 'web'] } = options;\n\n if (['i', 'a'].includes(key.toLowerCase())) {\n const platform = key.toLowerCase() === 'i' ? 'ios' : 'android';\n\n const shouldPrompt = ['I', 'A'].includes(key);\n if (shouldPrompt) {\n Log.clear();\n }\n\n const server = devServerManager.getDefaultDevServer();\n const settings = PLATFORM_SETTINGS[platform]!;\n\n Log.log(`${BLT} Opening on ${settings.name}...`);\n\n if (server.isTargetingNative() && !platforms.includes(settings.key)) {\n Log.warn(\n chalk`${settings.name} is disabled, enable it by adding {bold ${settings.key}} to the platforms array in your app.json or app.config.js`\n );\n } else {\n try {\n await server.openPlatformAsync(settings.launchTarget, { shouldPrompt });\n printHelp();\n } catch (error: any) {\n if (!(error instanceof AbortCommandError)) {\n Log.exception(error);\n }\n }\n }\n // Break out early.\n return;\n }\n\n switch (key) {\n case 's': {\n Log.clear();\n if (await devServerManager.toggleRuntimeMode()) {\n usageOptions.devClient = devServerManager.options.devClient;\n actions.printDevServerInfo(usageOptions);\n printDependencyCheckIfAvailable();\n return;\n }\n break;\n }\n case 'w': {\n try {\n await devServerManager.ensureProjectPrerequisiteAsync(WebSupportProjectPrerequisite);\n if (!platforms.includes('web')) {\n platforms.push('web');\n options.platforms?.push('web');\n }\n } catch (e: any) {\n Log.warn(e.message);\n break;\n }\n\n const isDisabled = !platforms.includes('web');\n if (isDisabled) {\n debug('Web is disabled');\n // Use warnings from the web support setup.\n break;\n }\n\n // Ensure the Webpack dev server is running first\n if (!devServerManager.getWebDevServer()) {\n debug('Starting up webpack dev server');\n await devServerManager.ensureWebDevServerRunningAsync();\n // When this is the first time webpack is started, reprint the connection info.\n actions.printDevServerInfo(usageOptions);\n }\n\n Log.log(`${BLT} Open in the web browser...`);\n try {\n await devServerManager.getWebDevServer()?.openPlatformAsync('desktop');\n printHelp();\n } catch (error: any) {\n if (!(error instanceof AbortCommandError)) {\n Log.exception(error);\n }\n }\n break;\n }\n case 'c':\n Log.clear();\n actions.printDevServerInfo(usageOptions);\n printDependencyCheckIfAvailable();\n return;\n case 'j':\n return actions.openJsInspectorAsync();\n case 'r':\n return actions.reloadApp();\n case 'o':\n Log.log(`${BLT} Opening the editor...`);\n return openInEditorAsync(devServerManager.projectRoot);\n }\n };\n\n const keyPressHandler = new KeyPressHandler(onPressAsync);\n\n const listener = keyPressHandler.createInteractionListener();\n\n addInteractionListener(listener);\n\n // Start observing...\n keyPressHandler.startInterceptingKeyStrokes();\n}\n"],"names":["startInterfaceAsync","debug","require","CTRL_C","CTRL_D","CTRL_L","PLATFORM_SETTINGS","android","name","key","launchTarget","ios","devServerManager","options","actions","DevServerManagerActions","isWebSocketsEnabled","getDefaultDevServer","isTargetingNative","usageOptions","devClient","dependencyCheckResult","dependencyCheckPromise","Promise","race","resolve","then","result","printDependencyCheckIfAvailable","printDependencyCheckResult","printDevServerInfo","onPressAsync","pauseInteractions","spinners","getAllSpinners","forEach","spinner","fail","currentProgress","getProgressBar","terminate","setProgressBar","ora","text","color","start","stopAsync","mcpServer","closeAsync","stopAndPersist","symbol","process","emit","exit","error","Log","clear","printUsage","verbose","toggleDevMenu","openMoreToolsAsync","platforms","includes","toLowerCase","platform","shouldPrompt","server","settings","log","BLT","warn","chalk","openPlatformAsync","printHelp","AbortCommandError","exception","toggleRuntimeMode","ensureProjectPrerequisiteAsync","WebSupportProjectPrerequisite","push","e","message","isDisabled","getWebDevServer","ensureWebDevServerRunningAsync","openJsInspectorAsync","reloadApp","openInEditorAsync","projectRoot","keyPressHandler","KeyPressHandler","listener","createInteractionListener","addInteractionListener","startInterceptingKeyStrokes"],"mappings":"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"}
@@ -75,36 +75,53 @@ class DevelopmentSession {
75
75
  * @param props.exp Partial Expo config with values that will be used in the Expo Go app.
76
76
  * @param props.runtime which runtime the app should be opened in. `native` for dev clients, `web` for web browsers.
77
77
  */ async startAsync({ exp = (0, _config().getConfig)(this.projectRoot).exp, runtime }) {
78
- try {
79
- if (_env.env.CI || _env.env.EXPO_OFFLINE) {
80
- debug(_env.env.CI ? 'This project will not be suggested in Expo Go or Dev Clients because Expo CLI is running in CI.' : 'This project will not be suggested in Expo Go or Dev Clients because Expo CLI is running in offline-mode.');
81
- return;
82
- }
83
- const deviceIds = await this.getDeviceInstallationIdsAsync();
84
- if (!(0, _UserSettings.hasCredentials)() && !(deviceIds == null ? void 0 : deviceIds.length)) {
85
- debug('Development session will not ping because the user is not authenticated and there are no devices.');
86
- return;
87
- }
88
- if (this.url) {
89
- debug(`Development session ping (runtime: ${runtime}, url: ${this.url})`);
90
- await (0, _updateDevelopmentSession.updateDevelopmentSessionAsync)({
91
- url: this.url,
92
- runtime,
93
- exp,
94
- deviceIds
95
- });
96
- this.hasActiveSession = true;
97
- }
98
- } catch (error) {
99
- debug(`Error updating development session API: ${error}`);
78
+ if (_env.env.CI || _env.env.EXPO_OFFLINE) {
79
+ debug(_env.env.CI ? 'This project will not be suggested in Expo Go or Dev Clients because Expo CLI is running in CI.' : 'This project will not be suggested in Expo Go or Dev Clients because Expo CLI is running in offline-mode.');
80
+ return;
100
81
  }
82
+ const fireAndForget = async ()=>{
83
+ try {
84
+ const deviceIds = await this.getDeviceInstallationIdsAsync();
85
+ if (!(0, _UserSettings.hasCredentials)() && !(deviceIds == null ? void 0 : deviceIds.length)) {
86
+ debug('Development session will not ping because the user is not authenticated and there are no devices.');
87
+ return;
88
+ }
89
+ if (this.url) {
90
+ debug(`Development session ping (runtime: ${runtime}, url: ${this.url})`);
91
+ this.abortController = new AbortController();
92
+ await (0, _updateDevelopmentSession.updateDevelopmentSessionAsync)({
93
+ url: this.url,
94
+ runtime,
95
+ exp,
96
+ deviceIds,
97
+ signal: this.abortController.signal
98
+ });
99
+ this.hasActiveSession = true;
100
+ }
101
+ } catch (error) {
102
+ debug(`Error updating development session API: ${error}`);
103
+ } finally{
104
+ this.abortController = undefined;
105
+ }
106
+ };
107
+ // NOTE(@kitten): We never want to wait for this call, as it's not essential to the CLI startup
108
+ // But we do add a tick delay, for testing
109
+ await Promise.race([
110
+ fireAndForget(),
111
+ Promise.resolve()
112
+ ]);
101
113
  }
102
114
  /** Get all recent devices for the project. */ async getDeviceInstallationIdsAsync() {
103
115
  const { devices } = await _devices.getDevicesInfoAsync(this.projectRoot);
104
116
  return devices.map(({ installationId })=>installationId);
105
117
  }
106
118
  /** Try to close any pending development sessions, but always resolve */ async closeAsync() {
107
- if (_env.env.CI || _env.env.EXPO_OFFLINE || !this.hasActiveSession) {
119
+ if (_env.env.CI || _env.env.EXPO_OFFLINE) {
120
+ return false;
121
+ } else if (this.abortController) {
122
+ this.abortController.abort();
123
+ return false;
124
+ } else if (!this.hasActiveSession) {
108
125
  return false;
109
126
  }
110
127
  // Clear out the development session, even if the call fails.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/start/server/DevelopmentSession.ts"],"sourcesContent":["import type { ExpoConfig } from '@expo/config';\nimport { getConfig } from '@expo/config';\n\nimport {\n closeDevelopmentSessionAsync,\n updateDevelopmentSessionAsync,\n} from '../../api/updateDevelopmentSession';\nimport { hasCredentials } from '../../api/user/UserSettings';\nimport { env } from '../../utils/env';\nimport * as ProjectDevices from '../project/devices';\n\nconst debug = require('debug')('expo:start:server:developmentSession') as typeof console.log;\n\nexport class DevelopmentSession {\n /** If the `startAsync` was successfully called */\n private hasActiveSession = false;\n\n constructor(\n /** Project root directory. */\n private projectRoot: string,\n /** Development Server URL. */\n public url: string | null\n ) {}\n\n /**\n * Notify the Expo servers that a project is running, this enables the Expo Go app\n * and Dev Clients to offer a \"recently in development\" section for quick access.\n *\n * @param projectRoot Project root folder, used for retrieving device installation IDs.\n * @param props.exp Partial Expo config with values that will be used in the Expo Go app.\n * @param props.runtime which runtime the app should be opened in. `native` for dev clients, `web` for web browsers.\n */\n public async startAsync({\n exp = getConfig(this.projectRoot).exp,\n runtime,\n }: {\n exp?: Pick<ExpoConfig, 'name' | 'description' | 'slug' | 'primaryColor'>;\n runtime: 'native' | 'web';\n }): Promise<void> {\n try {\n if (env.CI || env.EXPO_OFFLINE) {\n debug(\n env.CI\n ? 'This project will not be suggested in Expo Go or Dev Clients because Expo CLI is running in CI.'\n : 'This project will not be suggested in Expo Go or Dev Clients because Expo CLI is running in offline-mode.'\n );\n return;\n }\n\n const deviceIds = await this.getDeviceInstallationIdsAsync();\n\n if (!hasCredentials() && !deviceIds?.length) {\n debug(\n 'Development session will not ping because the user is not authenticated and there are no devices.'\n );\n return;\n }\n\n if (this.url) {\n debug(`Development session ping (runtime: ${runtime}, url: ${this.url})`);\n await updateDevelopmentSessionAsync({\n url: this.url,\n runtime,\n exp,\n deviceIds,\n });\n this.hasActiveSession = true;\n }\n } catch (error: any) {\n debug(`Error updating development session API: ${error}`);\n }\n }\n\n /** Get all recent devices for the project. */\n private async getDeviceInstallationIdsAsync(): Promise<string[]> {\n const { devices } = await ProjectDevices.getDevicesInfoAsync(this.projectRoot);\n return devices.map(({ installationId }) => installationId);\n }\n\n /** Try to close any pending development sessions, but always resolve */\n public async closeAsync(): Promise<boolean> {\n if (env.CI || env.EXPO_OFFLINE || !this.hasActiveSession) {\n return false;\n }\n\n // Clear out the development session, even if the call fails.\n // This blocks subsequent calls to `stopAsync`\n this.hasActiveSession = false;\n\n try {\n const deviceIds = await this.getDeviceInstallationIdsAsync();\n\n if (!hasCredentials() && !deviceIds?.length) {\n return false;\n }\n\n if (this.url) {\n await closeDevelopmentSessionAsync({\n url: this.url,\n deviceIds,\n });\n }\n\n return true;\n } catch (error: any) {\n debug(`Error closing development session API: ${error}`);\n return false;\n }\n }\n}\n"],"names":["DevelopmentSession","debug","require","projectRoot","url","hasActiveSession","startAsync","exp","getConfig","runtime","env","CI","EXPO_OFFLINE","deviceIds","getDeviceInstallationIdsAsync","hasCredentials","length","updateDevelopmentSessionAsync","error","devices","ProjectDevices","getDevicesInfoAsync","map","installationId","closeAsync","closeDevelopmentSessionAsync"],"mappings":";;;;+BAaaA;;;eAAAA;;;;yBAZa;;;;;;0CAKnB;8BACwB;qBACX;iEACY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEhC,MAAMC,QAAQC,QAAQ,SAAS;AAExB,MAAMF;IAIX,YACE,4BAA4B,GAC5B,AAAQG,WAAmB,EAC3B,4BAA4B,GAC5B,AAAOC,GAAkB,CACzB;aAHQD,cAAAA;aAEDC,MAAAA;QAPT,gDAAgD,QACxCC,mBAAmB;IAOxB;IAEH;;;;;;;GAOC,GACD,MAAaC,WAAW,EACtBC,MAAMC,IAAAA,mBAAS,EAAC,IAAI,CAACL,WAAW,EAAEI,GAAG,EACrCE,OAAO,EAIR,EAAiB;QAChB,IAAI;YACF,IAAIC,QAAG,CAACC,EAAE,IAAID,QAAG,CAACE,YAAY,EAAE;gBAC9BX,MACES,QAAG,CAACC,EAAE,GACF,oGACA;gBAEN;YACF;YAEA,MAAME,YAAY,MAAM,IAAI,CAACC,6BAA6B;YAE1D,IAAI,CAACC,IAAAA,4BAAc,OAAM,EAACF,6BAAAA,UAAWG,MAAM,GAAE;gBAC3Cf,MACE;gBAEF;YACF;YAEA,IAAI,IAAI,CAACG,GAAG,EAAE;gBACZH,MAAM,CAAC,mCAAmC,EAAEQ,QAAQ,OAAO,EAAE,IAAI,CAACL,GAAG,CAAC,CAAC,CAAC;gBACxE,MAAMa,IAAAA,uDAA6B,EAAC;oBAClCb,KAAK,IAAI,CAACA,GAAG;oBACbK;oBACAF;oBACAM;gBACF;gBACA,IAAI,CAACR,gBAAgB,GAAG;YAC1B;QACF,EAAE,OAAOa,OAAY;YACnBjB,MAAM,CAAC,wCAAwC,EAAEiB,OAAO;QAC1D;IACF;IAEA,4CAA4C,GAC5C,MAAcJ,gCAAmD;QAC/D,MAAM,EAAEK,OAAO,EAAE,GAAG,MAAMC,SAAeC,mBAAmB,CAAC,IAAI,CAAClB,WAAW;QAC7E,OAAOgB,QAAQG,GAAG,CAAC,CAAC,EAAEC,cAAc,EAAE,GAAKA;IAC7C;IAEA,sEAAsE,GACtE,MAAaC,aAA+B;QAC1C,IAAId,QAAG,CAACC,EAAE,IAAID,QAAG,CAACE,YAAY,IAAI,CAAC,IAAI,CAACP,gBAAgB,EAAE;YACxD,OAAO;QACT;QAEA,6DAA6D;QAC7D,8CAA8C;QAC9C,IAAI,CAACA,gBAAgB,GAAG;QAExB,IAAI;YACF,MAAMQ,YAAY,MAAM,IAAI,CAACC,6BAA6B;YAE1D,IAAI,CAACC,IAAAA,4BAAc,OAAM,EAACF,6BAAAA,UAAWG,MAAM,GAAE;gBAC3C,OAAO;YACT;YAEA,IAAI,IAAI,CAACZ,GAAG,EAAE;gBACZ,MAAMqB,IAAAA,sDAA4B,EAAC;oBACjCrB,KAAK,IAAI,CAACA,GAAG;oBACbS;gBACF;YACF;YAEA,OAAO;QACT,EAAE,OAAOK,OAAY;YACnBjB,MAAM,CAAC,uCAAuC,EAAEiB,OAAO;YACvD,OAAO;QACT;IACF;AACF"}
1
+ {"version":3,"sources":["../../../../src/start/server/DevelopmentSession.ts"],"sourcesContent":["import type { ExpoConfig } from '@expo/config';\nimport { getConfig } from '@expo/config';\n\nimport {\n closeDevelopmentSessionAsync,\n updateDevelopmentSessionAsync,\n} from '../../api/updateDevelopmentSession';\nimport { hasCredentials } from '../../api/user/UserSettings';\nimport { env } from '../../utils/env';\nimport * as ProjectDevices from '../project/devices';\n\nconst debug = require('debug')('expo:start:server:developmentSession') as typeof console.log;\n\nexport class DevelopmentSession {\n /** If the `startAsync` was successfully called */\n private hasActiveSession = false;\n\n private abortController: AbortController | undefined;\n\n constructor(\n /** Project root directory. */\n private projectRoot: string,\n /** Development Server URL. */\n public url: string | null\n ) {}\n\n /**\n * Notify the Expo servers that a project is running, this enables the Expo Go app\n * and Dev Clients to offer a \"recently in development\" section for quick access.\n *\n * @param projectRoot Project root folder, used for retrieving device installation IDs.\n * @param props.exp Partial Expo config with values that will be used in the Expo Go app.\n * @param props.runtime which runtime the app should be opened in. `native` for dev clients, `web` for web browsers.\n */\n public async startAsync({\n exp = getConfig(this.projectRoot).exp,\n runtime,\n }: {\n exp?: Pick<ExpoConfig, 'name' | 'description' | 'slug' | 'primaryColor'>;\n runtime: 'native' | 'web';\n }): Promise<void> {\n if (env.CI || env.EXPO_OFFLINE) {\n debug(\n env.CI\n ? 'This project will not be suggested in Expo Go or Dev Clients because Expo CLI is running in CI.'\n : 'This project will not be suggested in Expo Go or Dev Clients because Expo CLI is running in offline-mode.'\n );\n return;\n }\n\n const fireAndForget = async () => {\n try {\n const deviceIds = await this.getDeviceInstallationIdsAsync();\n\n if (!hasCredentials() && !deviceIds?.length) {\n debug(\n 'Development session will not ping because the user is not authenticated and there are no devices.'\n );\n return;\n }\n\n if (this.url) {\n debug(`Development session ping (runtime: ${runtime}, url: ${this.url})`);\n this.abortController = new AbortController();\n await updateDevelopmentSessionAsync({\n url: this.url,\n runtime,\n exp,\n deviceIds,\n signal: this.abortController.signal,\n });\n this.hasActiveSession = true;\n }\n } catch (error: any) {\n debug(`Error updating development session API: ${error}`);\n } finally {\n this.abortController = undefined;\n }\n };\n\n // NOTE(@kitten): We never want to wait for this call, as it's not essential to the CLI startup\n // But we do add a tick delay, for testing\n await Promise.race([fireAndForget(), Promise.resolve()]);\n }\n\n /** Get all recent devices for the project. */\n private async getDeviceInstallationIdsAsync(): Promise<string[]> {\n const { devices } = await ProjectDevices.getDevicesInfoAsync(this.projectRoot);\n return devices.map(({ installationId }) => installationId);\n }\n\n /** Try to close any pending development sessions, but always resolve */\n public async closeAsync(): Promise<boolean> {\n if (env.CI || env.EXPO_OFFLINE) {\n return false;\n } else if (this.abortController) {\n this.abortController.abort();\n return false;\n } else if (!this.hasActiveSession) {\n return false;\n }\n\n // Clear out the development session, even if the call fails.\n // This blocks subsequent calls to `stopAsync`\n this.hasActiveSession = false;\n\n try {\n const deviceIds = await this.getDeviceInstallationIdsAsync();\n\n if (!hasCredentials() && !deviceIds?.length) {\n return false;\n }\n\n if (this.url) {\n await closeDevelopmentSessionAsync({\n url: this.url,\n deviceIds,\n });\n }\n\n return true;\n } catch (error: any) {\n debug(`Error closing development session API: ${error}`);\n return false;\n }\n }\n}\n"],"names":["DevelopmentSession","debug","require","projectRoot","url","hasActiveSession","startAsync","exp","getConfig","runtime","env","CI","EXPO_OFFLINE","fireAndForget","deviceIds","getDeviceInstallationIdsAsync","hasCredentials","length","abortController","AbortController","updateDevelopmentSessionAsync","signal","error","undefined","Promise","race","resolve","devices","ProjectDevices","getDevicesInfoAsync","map","installationId","closeAsync","abort","closeDevelopmentSessionAsync"],"mappings":";;;;+BAaaA;;;eAAAA;;;;yBAZa;;;;;;0CAKnB;8BACwB;qBACX;iEACY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEhC,MAAMC,QAAQC,QAAQ,SAAS;AAExB,MAAMF;IAMX,YACE,4BAA4B,GAC5B,AAAQG,WAAmB,EAC3B,4BAA4B,GAC5B,AAAOC,GAAkB,CACzB;aAHQD,cAAAA;aAEDC,MAAAA;QATT,gDAAgD,QACxCC,mBAAmB;IASxB;IAEH;;;;;;;GAOC,GACD,MAAaC,WAAW,EACtBC,MAAMC,IAAAA,mBAAS,EAAC,IAAI,CAACL,WAAW,EAAEI,GAAG,EACrCE,OAAO,EAIR,EAAiB;QAChB,IAAIC,QAAG,CAACC,EAAE,IAAID,QAAG,CAACE,YAAY,EAAE;YAC9BX,MACES,QAAG,CAACC,EAAE,GACF,oGACA;YAEN;QACF;QAEA,MAAME,gBAAgB;YACpB,IAAI;gBACF,MAAMC,YAAY,MAAM,IAAI,CAACC,6BAA6B;gBAE1D,IAAI,CAACC,IAAAA,4BAAc,OAAM,EAACF,6BAAAA,UAAWG,MAAM,GAAE;oBAC3ChB,MACE;oBAEF;gBACF;gBAEA,IAAI,IAAI,CAACG,GAAG,EAAE;oBACZH,MAAM,CAAC,mCAAmC,EAAEQ,QAAQ,OAAO,EAAE,IAAI,CAACL,GAAG,CAAC,CAAC,CAAC;oBACxE,IAAI,CAACc,eAAe,GAAG,IAAIC;oBAC3B,MAAMC,IAAAA,uDAA6B,EAAC;wBAClChB,KAAK,IAAI,CAACA,GAAG;wBACbK;wBACAF;wBACAO;wBACAO,QAAQ,IAAI,CAACH,eAAe,CAACG,MAAM;oBACrC;oBACA,IAAI,CAAChB,gBAAgB,GAAG;gBAC1B;YACF,EAAE,OAAOiB,OAAY;gBACnBrB,MAAM,CAAC,wCAAwC,EAAEqB,OAAO;YAC1D,SAAU;gBACR,IAAI,CAACJ,eAAe,GAAGK;YACzB;QACF;QAEA,+FAA+F;QAC/F,0CAA0C;QAC1C,MAAMC,QAAQC,IAAI,CAAC;YAACZ;YAAiBW,QAAQE,OAAO;SAAG;IACzD;IAEA,4CAA4C,GAC5C,MAAcX,gCAAmD;QAC/D,MAAM,EAAEY,OAAO,EAAE,GAAG,MAAMC,SAAeC,mBAAmB,CAAC,IAAI,CAAC1B,WAAW;QAC7E,OAAOwB,QAAQG,GAAG,CAAC,CAAC,EAAEC,cAAc,EAAE,GAAKA;IAC7C;IAEA,sEAAsE,GACtE,MAAaC,aAA+B;QAC1C,IAAItB,QAAG,CAACC,EAAE,IAAID,QAAG,CAACE,YAAY,EAAE;YAC9B,OAAO;QACT,OAAO,IAAI,IAAI,CAACM,eAAe,EAAE;YAC/B,IAAI,CAACA,eAAe,CAACe,KAAK;YAC1B,OAAO;QACT,OAAO,IAAI,CAAC,IAAI,CAAC5B,gBAAgB,EAAE;YACjC,OAAO;QACT;QAEA,6DAA6D;QAC7D,8CAA8C;QAC9C,IAAI,CAACA,gBAAgB,GAAG;QAExB,IAAI;YACF,MAAMS,YAAY,MAAM,IAAI,CAACC,6BAA6B;YAE1D,IAAI,CAACC,IAAAA,4BAAc,OAAM,EAACF,6BAAAA,UAAWG,MAAM,GAAE;gBAC3C,OAAO;YACT;YAEA,IAAI,IAAI,CAACb,GAAG,EAAE;gBACZ,MAAM8B,IAAAA,sDAA4B,EAAC;oBACjC9B,KAAK,IAAI,CAACA,GAAG;oBACbU;gBACF;YACF;YAEA,OAAO;QACT,EAAE,OAAOQ,OAAY;YACnBrB,MAAM,CAAC,uCAAuC,EAAEqB,OAAO;YACvD,OAAO;QACT;IACF;AACF"}
@@ -97,7 +97,7 @@ function attachMasterListener() {
97
97
  }
98
98
  };
99
99
  }
100
- function ensureProcessExitsAfterDelay(waitUntilExitMs = 10000, startedAtMs = Date.now()) {
100
+ function ensureProcessExitsAfterDelay(waitUntilExitMs = 4000, startedAtMs = Date.now()) {
101
101
  // Create a list of the expected active resources before exiting.
102
102
  // Note, the order is undeterministic
103
103
  const expectedResources = [
@@ -116,19 +116,16 @@ function ensureProcessExitsAfterDelay(waitUntilExitMs = 10000, startedAtMs = Dat
116
116
  }
117
117
  return true;
118
118
  });
119
- // Check if only Timeouts remain (no blocking resources like ProcessWrap/PipeWrap)
120
- const hasBlockingResources = unexpectedActiveResources.some((resource)=>resource !== 'Timeout' && resource !== 'CloseReq');
121
- const canExitProcess = !unexpectedActiveResources.length || !hasBlockingResources;
122
- if (canExitProcess) {
123
- if (unexpectedActiveResources.length && !hasBlockingResources) {
124
- debug('only non-blocking resources remain (Timeout/CloseReq), process can safely exit');
125
- } else {
126
- debug('no active resources detected, process can safely exit');
127
- }
119
+ // Check if there are any resources that block the process from exiting.
120
+ // CloseReq is always transient and completes on its own.
121
+ // NOTE: Timeout is NOT excluded — getActiveResourcesInfo() only reports ref'd timers,
122
+ // so any Timeout in this list will keep the event loop alive indefinitely.
123
+ const hasBlockingResources = unexpectedActiveResources.some((resource)=>resource !== 'CloseReq');
124
+ if (!hasBlockingResources) {
125
+ debug(unexpectedActiveResources.length ? 'only transient resources remain (CloseReq), process can safely exit' : 'no active resources detected, process can safely exit');
128
126
  return;
129
- } else {
130
- debug(`process is trying to exit, but is stuck on unexpected active resources:`, unexpectedActiveResources);
131
127
  }
128
+ debug(`process is trying to exit, but is stuck on unexpected active resources:`, unexpectedActiveResources);
132
129
  // Check if the process needs to be force-closed
133
130
  const elapsedTime = Date.now() - startedAtMs;
134
131
  if (elapsedTime > waitUntilExitMs) {
@@ -137,10 +134,9 @@ function ensureProcessExitsAfterDelay(waitUntilExitMs = 10000, startedAtMs = Dat
137
134
  return _nodeprocess().default.exit(0);
138
135
  }
139
136
  const timeoutId = setTimeout(()=>{
140
- // Ensure the timeout is cleared before checking the active resources
141
- clearTimeout(timeoutId);
142
- // Check if the process can exit
143
- ensureProcessExitsAfterDelay(waitUntilExitMs, startedAtMs);
137
+ // Delay the next check by one tick so the current timer is fully cleaned up
138
+ // and doesn't appear in the active resources list.
139
+ _nodeprocess().default.nextTick(()=>ensureProcessExitsAfterDelay(waitUntilExitMs, startedAtMs));
144
140
  // setTimeout is using the global definitions from React Native which is missing the unref method in Node.js.
145
141
  }, 100);
146
142
  // Unref the timeout so it doesn't prevent the process from exiting naturally
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/exit.ts"],"sourcesContent":["import { ChildProcess } from 'node:child_process';\nimport process from 'node:process';\n\nimport { guardAsync } from './fn';\nimport { warn } from '../log';\n\nconst debug = require('debug')('expo:utils:exit') as typeof console.log;\n\n// NOTE: This is an internal method, not designed to be exposed. It's also our only way to get this info\ndeclare global {\n namespace NodeJS {\n interface Process {\n _getActiveHandles(): readonly any[];\n }\n }\n}\n\ntype AsyncExitHook = (signal: NodeJS.Signals) => void | Promise<void>;\n\nconst PRE_EXIT_SIGNALS: NodeJS.Signals[] = ['SIGHUP', 'SIGINT', 'SIGTERM', 'SIGBREAK'];\n\n// We create a queue since Node.js throws an error if we try to append too many listeners:\n// (node:4405) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 SIGINT listeners added to [process]. Use emitter.setMaxListeners() to increase limit\nconst queue: AsyncExitHook[] = [];\n\nlet unsubscribe: (() => void) | null = null;\n\n/** Add functions that run before the process exits. Returns a function for removing the listeners. */\nexport function installExitHooks(asyncExitHook: AsyncExitHook): () => void {\n // We need to instantiate the master listener the first time the queue is used.\n if (!queue.length) {\n // Track the master listener so we can remove it later.\n unsubscribe = attachMasterListener();\n }\n\n queue.push(asyncExitHook);\n\n return () => {\n const index = queue.indexOf(asyncExitHook);\n if (index >= 0) {\n queue.splice(index, 1);\n }\n // Clean up the master listener if we don't need it anymore.\n if (!queue.length) {\n unsubscribe?.();\n }\n };\n}\n\n// Create a function that runs before the process exits and guards against running multiple times.\nfunction createExitHook(signal: NodeJS.Signals) {\n return guardAsync(async () => {\n debug(`pre-exit (signal: ${signal}, queue length: ${queue.length})`);\n\n for (const [index, hookAsync] of Object.entries(queue)) {\n try {\n await hookAsync(signal);\n } catch (error: any) {\n debug(`Error in exit hook: %O (queue: ${index})`, error);\n }\n }\n\n debug(`post-exit (code: ${process.exitCode ?? 0})`);\n\n process.exit();\n });\n}\n\nfunction attachMasterListener() {\n const hooks: [NodeJS.Signals, () => any][] = [];\n for (const signal of PRE_EXIT_SIGNALS) {\n const hook = createExitHook(signal);\n hooks.push([signal, hook]);\n process.on(signal, hook);\n }\n return () => {\n for (const [signal, hook] of hooks) {\n process.removeListener(signal, hook);\n }\n };\n}\n\n/**\n * Monitor if the current process is exiting before the delay is reached.\n * If there are active resources, the process will be forced to exit after the delay is reached.\n *\n * @see https://nodejs.org/docs/latest-v18.x/api/process.html#processgetactiveresourcesinfo\n */\nexport function ensureProcessExitsAfterDelay(waitUntilExitMs = 10000, startedAtMs = Date.now()) {\n // Create a list of the expected active resources before exiting.\n // Note, the order is undeterministic\n const expectedResources = [\n process.stdout.isTTY ? 'TTYWrap' : 'PipeWrap',\n process.stderr.isTTY ? 'TTYWrap' : 'PipeWrap',\n process.stdin.isTTY ? 'TTYWrap' : 'PipeWrap',\n ];\n // Check active resources, besides the TTYWrap/PipeWrap (process.stdin, process.stdout, process.stderr)\n const activeResources = process.getActiveResourcesInfo() as string[];\n // Filter the active resource list by subtracting the expected resources, in undeterministic order\n const unexpectedActiveResources = activeResources.filter((activeResource) => {\n const index = expectedResources.indexOf(activeResource);\n if (index >= 0) {\n expectedResources.splice(index, 1);\n return false;\n }\n\n return true;\n });\n\n // Check if only Timeouts remain (no blocking resources like ProcessWrap/PipeWrap)\n const hasBlockingResources = unexpectedActiveResources.some(\n (resource) => resource !== 'Timeout' && resource !== 'CloseReq'\n );\n\n const canExitProcess = !unexpectedActiveResources.length || !hasBlockingResources;\n if (canExitProcess) {\n if (unexpectedActiveResources.length && !hasBlockingResources) {\n debug('only non-blocking resources remain (Timeout/CloseReq), process can safely exit');\n } else {\n debug('no active resources detected, process can safely exit');\n }\n return;\n } else {\n debug(\n `process is trying to exit, but is stuck on unexpected active resources:`,\n unexpectedActiveResources\n );\n }\n\n // Check if the process needs to be force-closed\n const elapsedTime = Date.now() - startedAtMs;\n if (elapsedTime > waitUntilExitMs) {\n debug('active handles detected past the exit delay, forcefully exiting:', activeResources);\n tryWarnActiveProcesses();\n return process.exit(0);\n }\n\n const timeoutId = setTimeout(() => {\n // Ensure the timeout is cleared before checking the active resources\n clearTimeout(timeoutId);\n // Check if the process can exit\n ensureProcessExitsAfterDelay(waitUntilExitMs, startedAtMs);\n\n // setTimeout is using the global definitions from React Native which is missing the unref method in Node.js.\n }, 100) as unknown as NodeJS.Timeout;\n\n // Unref the timeout so it doesn't prevent the process from exiting naturally\n // when this timeout is the only remaining active resource\n timeoutId.unref();\n}\n\n/**\n * Try to warn the user about unexpected active processes running in the background.\n * This uses the internal `process._getActiveHandles` method, within a try-catch block.\n * If active child processes are detected, the commands of these processes are logged.\n *\n * @example ```bash\n * Done writing bundle output\n * Detected 2 processes preventing Expo from exiting, forcefully exiting now.\n * - node /Users/cedric/../node_modules/nativewind/dist/metro/tailwind/v3/child.js\n * - node /Users/cedric/../node_modules/nativewind/dist/metro/tailwind/v3/child.js\n * ```\n */\nfunction tryWarnActiveProcesses() {\n const activeProcesses: string[] = [];\n const handleSummary: Record<string, number> = {};\n const timeoutDetails: string[] = [];\n\n try {\n const handles = process._getActiveHandles();\n\n // Categorize handles by their constructor name\n for (const handle of handles) {\n const name = handle?.constructor?.name ?? 'Unknown';\n handleSummary[name] = (handleSummary[name] ?? 0) + 1;\n\n // Collect ChildProcess command info\n if (handle instanceof ChildProcess) {\n activeProcesses.push(handle.spawnargs.join(' '));\n }\n\n // Try to get more info about Timeout handles\n if (name === 'Timeout') {\n try {\n // Attempt to get callback name or source info\n const callback = handle._onTimeout ?? handle._repeat;\n const callbackName = callback?.name || '<anonymous>';\n const delay = handle._idleTimeout ?? 'unknown';\n timeoutDetails.push(`Timeout(${delay}ms, fn: ${callbackName})`);\n } catch {\n timeoutDetails.push('Timeout(details unavailable)');\n }\n }\n }\n\n // Log detailed handle info when debug is enabled\n debug('active handles by type:', handleSummary);\n if (timeoutDetails.length) {\n debug('timeout details:', timeoutDetails);\n }\n } catch (error) {\n debug('failed to get active process information:', error);\n }\n\n if (!activeProcesses.length) {\n warn('Something prevented Expo from exiting, forcefully exiting now.');\n // Log handle summary when no specific processes are identified\n if (Object.keys(handleSummary).length > 0) {\n debug('handle summary (use DEBUG=expo:utils:exit for details):', handleSummary);\n }\n } else {\n const singularOrPlural =\n activeProcesses.length === 1 ? '1 process' : `${activeProcesses.length} processes`;\n\n warn(`Detected ${singularOrPlural} preventing Expo from exiting, forcefully exiting now.`);\n warn(' - ' + activeProcesses.join('\\n - '));\n }\n}\n"],"names":["ensureProcessExitsAfterDelay","installExitHooks","debug","require","PRE_EXIT_SIGNALS","queue","unsubscribe","asyncExitHook","length","attachMasterListener","push","index","indexOf","splice","createExitHook","signal","guardAsync","hookAsync","Object","entries","error","process","exitCode","exit","hooks","hook","on","removeListener","waitUntilExitMs","startedAtMs","Date","now","expectedResources","stdout","isTTY","stderr","stdin","activeResources","getActiveResourcesInfo","unexpectedActiveResources","filter","activeResource","hasBlockingResources","some","resource","canExitProcess","elapsedTime","tryWarnActiveProcesses","timeoutId","setTimeout","clearTimeout","unref","activeProcesses","handleSummary","timeoutDetails","handles","_getActiveHandles","handle","name","ChildProcess","spawnargs","join","callback","_onTimeout","_repeat","callbackName","delay","_idleTimeout","warn","keys","singularOrPlural"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/utils/exit.ts"],"sourcesContent":["import { ChildProcess } from 'node:child_process';\nimport process from 'node:process';\n\nimport { guardAsync } from './fn';\nimport { warn } from '../log';\n\nconst debug = require('debug')('expo:utils:exit') as typeof console.log;\n\n// NOTE: This is an internal method, not designed to be exposed. It's also our only way to get this info\ndeclare global {\n namespace NodeJS {\n interface Process {\n _getActiveHandles(): readonly any[];\n }\n }\n}\n\ntype AsyncExitHook = (signal: NodeJS.Signals) => void | Promise<void>;\n\nconst PRE_EXIT_SIGNALS: NodeJS.Signals[] = ['SIGHUP', 'SIGINT', 'SIGTERM', 'SIGBREAK'];\n\n// We create a queue since Node.js throws an error if we try to append too many listeners:\n// (node:4405) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 SIGINT listeners added to [process]. Use emitter.setMaxListeners() to increase limit\nconst queue: AsyncExitHook[] = [];\n\nlet unsubscribe: (() => void) | null = null;\n\n/** Add functions that run before the process exits. Returns a function for removing the listeners. */\nexport function installExitHooks(asyncExitHook: AsyncExitHook): () => void {\n // We need to instantiate the master listener the first time the queue is used.\n if (!queue.length) {\n // Track the master listener so we can remove it later.\n unsubscribe = attachMasterListener();\n }\n\n queue.push(asyncExitHook);\n\n return () => {\n const index = queue.indexOf(asyncExitHook);\n if (index >= 0) {\n queue.splice(index, 1);\n }\n // Clean up the master listener if we don't need it anymore.\n if (!queue.length) {\n unsubscribe?.();\n }\n };\n}\n\n// Create a function that runs before the process exits and guards against running multiple times.\nfunction createExitHook(signal: NodeJS.Signals) {\n return guardAsync(async () => {\n debug(`pre-exit (signal: ${signal}, queue length: ${queue.length})`);\n\n for (const [index, hookAsync] of Object.entries(queue)) {\n try {\n await hookAsync(signal);\n } catch (error: any) {\n debug(`Error in exit hook: %O (queue: ${index})`, error);\n }\n }\n\n debug(`post-exit (code: ${process.exitCode ?? 0})`);\n\n process.exit();\n });\n}\n\nfunction attachMasterListener() {\n const hooks: [NodeJS.Signals, () => any][] = [];\n for (const signal of PRE_EXIT_SIGNALS) {\n const hook = createExitHook(signal);\n hooks.push([signal, hook]);\n process.on(signal, hook);\n }\n return () => {\n for (const [signal, hook] of hooks) {\n process.removeListener(signal, hook);\n }\n };\n}\n\n/**\n * Monitor if the current process is exiting before the delay is reached.\n * If there are active resources, the process will be forced to exit after the delay is reached.\n *\n * @see https://nodejs.org/docs/latest-v18.x/api/process.html#processgetactiveresourcesinfo\n */\nexport function ensureProcessExitsAfterDelay(waitUntilExitMs = 4_000, startedAtMs = Date.now()) {\n // Create a list of the expected active resources before exiting.\n // Note, the order is undeterministic\n const expectedResources = [\n process.stdout.isTTY ? 'TTYWrap' : 'PipeWrap',\n process.stderr.isTTY ? 'TTYWrap' : 'PipeWrap',\n process.stdin.isTTY ? 'TTYWrap' : 'PipeWrap',\n ];\n // Check active resources, besides the TTYWrap/PipeWrap (process.stdin, process.stdout, process.stderr)\n const activeResources = process.getActiveResourcesInfo() as string[];\n // Filter the active resource list by subtracting the expected resources, in undeterministic order\n const unexpectedActiveResources = activeResources.filter((activeResource) => {\n const index = expectedResources.indexOf(activeResource);\n if (index >= 0) {\n expectedResources.splice(index, 1);\n return false;\n }\n\n return true;\n });\n\n // Check if there are any resources that block the process from exiting.\n // CloseReq is always transient and completes on its own.\n // NOTE: Timeout is NOT excluded — getActiveResourcesInfo() only reports ref'd timers,\n // so any Timeout in this list will keep the event loop alive indefinitely.\n const hasBlockingResources = unexpectedActiveResources.some(\n (resource) => resource !== 'CloseReq'\n );\n\n if (!hasBlockingResources) {\n debug(\n unexpectedActiveResources.length\n ? 'only transient resources remain (CloseReq), process can safely exit'\n : 'no active resources detected, process can safely exit'\n );\n return;\n }\n\n debug(\n `process is trying to exit, but is stuck on unexpected active resources:`,\n unexpectedActiveResources\n );\n\n // Check if the process needs to be force-closed\n const elapsedTime = Date.now() - startedAtMs;\n if (elapsedTime > waitUntilExitMs) {\n debug('active handles detected past the exit delay, forcefully exiting:', activeResources);\n tryWarnActiveProcesses();\n return process.exit(0);\n }\n\n const timeoutId = setTimeout(() => {\n // Delay the next check by one tick so the current timer is fully cleaned up\n // and doesn't appear in the active resources list.\n process.nextTick(() => ensureProcessExitsAfterDelay(waitUntilExitMs, startedAtMs));\n\n // setTimeout is using the global definitions from React Native which is missing the unref method in Node.js.\n }, 100) as unknown as NodeJS.Timeout;\n\n // Unref the timeout so it doesn't prevent the process from exiting naturally\n // when this timeout is the only remaining active resource\n timeoutId.unref();\n}\n\n/**\n * Try to warn the user about unexpected active processes running in the background.\n * This uses the internal `process._getActiveHandles` method, within a try-catch block.\n * If active child processes are detected, the commands of these processes are logged.\n *\n * @example ```bash\n * Done writing bundle output\n * Detected 2 processes preventing Expo from exiting, forcefully exiting now.\n * - node /Users/cedric/../node_modules/nativewind/dist/metro/tailwind/v3/child.js\n * - node /Users/cedric/../node_modules/nativewind/dist/metro/tailwind/v3/child.js\n * ```\n */\nfunction tryWarnActiveProcesses() {\n const activeProcesses: string[] = [];\n const handleSummary: Record<string, number> = {};\n const timeoutDetails: string[] = [];\n\n try {\n const handles = process._getActiveHandles();\n\n // Categorize handles by their constructor name\n for (const handle of handles) {\n const name = handle?.constructor?.name ?? 'Unknown';\n handleSummary[name] = (handleSummary[name] ?? 0) + 1;\n\n // Collect ChildProcess command info\n if (handle instanceof ChildProcess) {\n activeProcesses.push(handle.spawnargs.join(' '));\n }\n\n // Try to get more info about Timeout handles\n if (name === 'Timeout') {\n try {\n // Attempt to get callback name or source info\n const callback = handle._onTimeout ?? handle._repeat;\n const callbackName = callback?.name || '<anonymous>';\n const delay = handle._idleTimeout ?? 'unknown';\n timeoutDetails.push(`Timeout(${delay}ms, fn: ${callbackName})`);\n } catch {\n timeoutDetails.push('Timeout(details unavailable)');\n }\n }\n }\n\n // Log detailed handle info when debug is enabled\n debug('active handles by type:', handleSummary);\n if (timeoutDetails.length) {\n debug('timeout details:', timeoutDetails);\n }\n } catch (error) {\n debug('failed to get active process information:', error);\n }\n\n if (!activeProcesses.length) {\n warn('Something prevented Expo from exiting, forcefully exiting now.');\n // Log handle summary when no specific processes are identified\n if (Object.keys(handleSummary).length > 0) {\n debug('handle summary (use DEBUG=expo:utils:exit for details):', handleSummary);\n }\n } else {\n const singularOrPlural =\n activeProcesses.length === 1 ? '1 process' : `${activeProcesses.length} processes`;\n\n warn(`Detected ${singularOrPlural} preventing Expo from exiting, forcefully exiting now.`);\n warn(' - ' + activeProcesses.join('\\n - '));\n }\n}\n"],"names":["ensureProcessExitsAfterDelay","installExitHooks","debug","require","PRE_EXIT_SIGNALS","queue","unsubscribe","asyncExitHook","length","attachMasterListener","push","index","indexOf","splice","createExitHook","signal","guardAsync","hookAsync","Object","entries","error","process","exitCode","exit","hooks","hook","on","removeListener","waitUntilExitMs","startedAtMs","Date","now","expectedResources","stdout","isTTY","stderr","stdin","activeResources","getActiveResourcesInfo","unexpectedActiveResources","filter","activeResource","hasBlockingResources","some","resource","elapsedTime","tryWarnActiveProcesses","timeoutId","setTimeout","nextTick","unref","activeProcesses","handleSummary","timeoutDetails","handles","_getActiveHandles","handle","name","ChildProcess","spawnargs","join","callback","_onTimeout","_repeat","callbackName","delay","_idleTimeout","warn","keys","singularOrPlural"],"mappings":"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"}
@@ -26,7 +26,7 @@ class FetchClient {
26
26
  this.headers = {
27
27
  accept: 'application/json',
28
28
  'content-type': 'application/json',
29
- 'user-agent': `expo-cli/${"56.0.2"}`,
29
+ 'user-agent': `expo-cli/${"56.0.3"}`,
30
30
  authorization: 'Basic ' + _nodebuffer().Buffer.from(`${target}:`).toString('base64')
31
31
  };
32
32
  }
@@ -83,7 +83,7 @@ function createContext() {
83
83
  cpu: summarizeCpuInfo(),
84
84
  app: {
85
85
  name: 'expo/cli',
86
- version: "56.0.2"
86
+ version: "56.0.3"
87
87
  },
88
88
  ci: _ciinfo().isCI ? {
89
89
  name: _ciinfo().name,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expo/cli",
3
- "version": "56.0.2",
3
+ "version": "56.0.3",
4
4
  "description": "The Expo CLI",
5
5
  "main": "main.js",
6
6
  "bin": {
@@ -22,17 +22,6 @@
22
22
  "static",
23
23
  "build"
24
24
  ],
25
- "scripts": {
26
- "build": "taskr",
27
- "prepare": "taskr release",
28
- "clean": "expo-module clean",
29
- "lint": "expo-module lint",
30
- "typecheck": "expo-module typecheck",
31
- "test": "expo-module test",
32
- "test:e2e": "jest --config e2e/jest.config.js",
33
- "test:playwright": "playwright test --config e2e/playwright.config.ts",
34
- "expo-module": "expo-module"
35
- },
36
25
  "repository": {
37
26
  "type": "git",
38
27
  "url": "https://github.com/expo/expo.git",
@@ -50,24 +39,24 @@
50
39
  "homepage": "https://github.com/expo/expo/tree/main/packages/@expo/cli",
51
40
  "dependencies": {
52
41
  "@expo/code-signing-certificates": "^0.0.6",
53
- "@expo/config": "56.0.0",
54
- "@expo/config-plugins": "56.0.0",
42
+ "@expo/config": "~56.0.0",
43
+ "@expo/config-plugins": "~56.0.0",
55
44
  "@expo/devcert": "^1.2.1",
56
- "@expo/env": "2.2.0",
57
- "@expo/image-utils": "0.9.0",
45
+ "@expo/env": "~2.2.0",
46
+ "@expo/image-utils": "^0.9.0",
58
47
  "@expo/inline-modules": "0.0.2",
59
- "@expo/json-file": "10.1.0",
48
+ "@expo/json-file": "^10.1.0",
60
49
  "@expo/log-box": "56.0.1",
61
50
  "@expo/metro": "~56.0.0",
62
- "@expo/metro-config": "56.0.1",
51
+ "@expo/metro-config": "~56.0.1",
63
52
  "@expo/metro-file-map": "56.0.0-0",
64
- "@expo/osascript": "2.5.0",
65
- "@expo/package-manager": "1.11.0",
66
- "@expo/plist": "0.6.0",
67
- "@expo/prebuild-config": "56.0.0",
68
- "@expo/require-utils": "56.0.0",
69
- "@expo/router-server": "56.0.1",
70
- "@expo/schema-utils": "56.0.0",
53
+ "@expo/osascript": "^2.5.0",
54
+ "@expo/package-manager": "^1.11.0",
55
+ "@expo/plist": "^0.6.0",
56
+ "@expo/prebuild-config": "^56.0.0",
57
+ "@expo/require-utils": "^56.0.0",
58
+ "@expo/router-server": "^56.0.1",
59
+ "@expo/schema-utils": "^56.0.0",
71
60
  "@expo/spawn-async": "^1.7.2",
72
61
  "@expo/ws-tunnel": "^1.0.1",
73
62
  "@expo/xcpretty": "^4.4.0",
@@ -83,7 +72,7 @@
83
72
  "connect": "^3.7.0",
84
73
  "debug": "^4.3.4",
85
74
  "dnssd-advertise": "^1.1.4",
86
- "expo-server": "56.0.0",
75
+ "expo-server": "^56.0.0",
87
76
  "fetch-nodeshim": "^0.4.10",
88
77
  "getenv": "^2.0.0",
89
78
  "glob": "^13.0.0",
@@ -115,8 +104,8 @@
115
104
  ]
116
105
  },
117
106
  "peerDependencies": {
118
- "expo": "56.0.0-preview.2",
119
- "expo-router": "56.0.1",
107
+ "expo": "*",
108
+ "expo-router": "*",
120
109
  "react-native": "*"
121
110
  },
122
111
  "peerDependenciesMeta": {
@@ -128,7 +117,6 @@
128
117
  }
129
118
  },
130
119
  "devDependencies": {
131
- "@expo/fingerprint": "0.17.1",
132
120
  "@expo/mcp-tunnel": "~0.2.3",
133
121
  "@expo/ngrok": "^4.1.3",
134
122
  "@jest/globals": "^29.7.0",
@@ -162,8 +150,6 @@
162
150
  "devtools-protocol": "^0.0.1113120",
163
151
  "expect": "^29.7.0",
164
152
  "expo-atlas": "^0.4.1",
165
- "expo-module-scripts": "56.0.0",
166
- "expo-modules-autolinking": "56.0.0",
167
153
  "find-process": "^1.4.7",
168
154
  "jest-matcher-utils": "^29.7.0",
169
155
  "klaw-sync": "^6.0.0",
@@ -172,7 +158,22 @@
172
158
  "node-html-parser": "^6.1.5",
173
159
  "playwright": "^1.59.0",
174
160
  "taskr": "^1.1.0",
175
- "tree-kill": "^1.2.2"
161
+ "tree-kill": "^1.2.2",
162
+ "expo": "56.0.0-preview.3",
163
+ "expo-modules-autolinking": "56.0.1",
164
+ "expo-module-scripts": "56.0.1",
165
+ "expo-router": "56.0.2",
166
+ "@expo/fingerprint": "0.17.2"
176
167
  },
177
- "gitHead": "222b3b12610d69784bab6c5a188a46ea388f866a"
178
- }
168
+ "gitHead": "71ec800b2fa6133d22d69b70945132e22f1b7ae8",
169
+ "scripts": {
170
+ "build": "taskr",
171
+ "clean": "expo-module clean",
172
+ "lint": "expo-module lint",
173
+ "typecheck": "expo-module typecheck",
174
+ "test": "expo-module test",
175
+ "test:e2e": "jest --config e2e/jest.config.js",
176
+ "test:playwright": "playwright test --config e2e/playwright.config.ts",
177
+ "expo-module": "expo-module"
178
+ }
179
+ }