@expo/cli 55.0.33 → 55.0.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/build/bin/cli +1 -1
  2. package/build/src/api/graphql/client.js +2 -5
  3. package/build/src/api/graphql/client.js.map +1 -1
  4. package/build/src/events/index.js +1 -1
  5. package/build/src/export/createMetadataJson.js.map +1 -1
  6. package/build/src/export/embed/exportEmbedAsync.js +6 -3
  7. package/build/src/export/embed/exportEmbedAsync.js.map +1 -1
  8. package/build/src/export/embed/resolveOptions.js +2 -1
  9. package/build/src/export/embed/resolveOptions.js.map +1 -1
  10. package/build/src/export/exportApp.js +1 -1
  11. package/build/src/export/exportApp.js.map +1 -1
  12. package/build/src/export/exportHermes.js +10 -1
  13. package/build/src/export/exportHermes.js.map +1 -1
  14. package/build/src/export/resolveOptions.js +9 -10
  15. package/build/src/export/resolveOptions.js.map +1 -1
  16. package/build/src/export/saveAssets.js.map +1 -1
  17. package/build/src/export/static.js +6 -0
  18. package/build/src/export/static.js.map +1 -0
  19. package/build/src/run/android/resolveInstallApkName.js +48 -1
  20. package/build/src/run/android/resolveInstallApkName.js.map +1 -1
  21. package/build/src/run/ios/XcodeBuild.js +23 -0
  22. package/build/src/run/ios/XcodeBuild.js.map +1 -1
  23. package/build/src/run/ios/appleDevice/AppleDevice.js +4 -1
  24. package/build/src/run/ios/appleDevice/AppleDevice.js.map +1 -1
  25. package/build/src/serve/serveAsync.js +23 -11
  26. package/build/src/serve/serveAsync.js.map +1 -1
  27. package/build/src/serve/static.js +69 -0
  28. package/build/src/serve/static.js.map +1 -0
  29. package/build/src/start/platforms/ios/devicectl.js +6 -2
  30. package/build/src/start/platforms/ios/devicectl.js.map +1 -1
  31. package/build/src/start/server/metro/MetroBundlerDevServer.js +2 -1
  32. package/build/src/start/server/metro/MetroBundlerDevServer.js.map +1 -1
  33. package/build/src/start/server/metro/createExpoAutolinkingResolver.js +21 -8
  34. package/build/src/start/server/metro/createExpoAutolinkingResolver.js.map +1 -1
  35. package/build/src/start/server/metro/instantiateMetro.js +5 -2
  36. package/build/src/start/server/metro/instantiateMetro.js.map +1 -1
  37. package/build/src/start/server/metro/withMetroMultiPlatform.js +89 -28
  38. package/build/src/start/server/metro/withMetroMultiPlatform.js.map +1 -1
  39. package/build/src/start/server/middleware/ExpoGoManifestHandlerMiddleware.js +3 -1
  40. package/build/src/start/server/middleware/ExpoGoManifestHandlerMiddleware.js.map +1 -1
  41. package/build/src/start/server/middleware/InterstitialPageMiddleware.js +3 -1
  42. package/build/src/start/server/middleware/InterstitialPageMiddleware.js.map +1 -1
  43. package/build/src/start/server/middleware/ManifestMiddleware.js +4 -23
  44. package/build/src/start/server/middleware/ManifestMiddleware.js.map +1 -1
  45. package/build/src/start/server/middleware/resolvePlatform.js +4 -2
  46. package/build/src/start/server/middleware/resolvePlatform.js.map +1 -1
  47. package/build/src/start/server/platformBundlers.js +3 -1
  48. package/build/src/start/server/platformBundlers.js.map +1 -1
  49. package/build/src/utils/codesigning.js +3 -0
  50. package/build/src/utils/codesigning.js.map +1 -1
  51. package/build/src/utils/npm.js +16 -2
  52. package/build/src/utils/npm.js.map +1 -1
  53. package/build/src/utils/telemetry/Telemetry.js +5 -0
  54. package/build/src/utils/telemetry/Telemetry.js.map +1 -1
  55. package/build/src/utils/telemetry/clients/FetchClient.js +1 -1
  56. package/build/src/utils/telemetry/utils/agent.js +42 -0
  57. package/build/src/utils/telemetry/utils/agent.js.map +1 -0
  58. package/build/src/utils/telemetry/utils/context.js +1 -1
  59. package/package.json +16 -15
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/run/ios/appleDevice/AppleDevice.ts"],"sourcesContent":["import Debug from 'debug';\nimport fs from 'fs';\nimport path from 'path';\n\nimport { ClientManager } from './ClientManager';\nimport { IPLookupResult, OnInstallProgressCallback } from './client/InstallationProxyClient';\nimport { LockdowndClient } from './client/LockdowndClient';\nimport { UsbmuxdClient } from './client/UsbmuxdClient';\nimport { AFC_STATUS, AFCError } from './protocol/AFCProtocol';\nimport { Log } from '../../../log';\nimport { XcodeDeveloperDiskImagePrerequisite } from '../../../start/doctor/apple/XcodeDeveloperDiskImagePrerequisite';\nimport * as devicectl from '../../../start/platforms/ios/devicectl';\nimport { launchAppWithDeviceCtl } from '../../../start/platforms/ios/devicectl';\nimport { OSType } from '../../../start/platforms/ios/simctl';\nimport { uniqBy } from '../../../utils/array';\nimport { delayAsync } from '../../../utils/delay';\nimport { CommandError } from '../../../utils/errors';\nimport { installExitHooks } from '../../../utils/exit';\nimport { profile } from '../../../utils/profile';\n\nconst debug = Debug('expo:apple-device');\n\n// NOTE(EvanBacon): I have a feeling this shape will change with new iOS versions (tested against iOS 15).\nexport interface ConnectedDevice {\n /** @example `00008101-001964A22629003A` */\n udid: string;\n /** @example `Evan's phone` */\n name: string;\n /** @example `iPhone13,4` */\n model: string;\n /** @example `device` */\n deviceType: 'device' | 'catalyst';\n /** @example `USB` */\n connectionType: 'USB' | 'Network';\n /** @example `15.4.1` */\n osVersion: string;\n\n osType: OSType;\n}\n\nasync function getConnectedDevicesUsingNativeToolsAsync(): Promise<ConnectedDevice[]> {\n return (\n (await devicectl.getConnectedAppleDevicesAsync())\n // Filter out unpaired and unavailable devices.\n // TODO: We could improve this logic in the future to attempt pairing if specified.\n .filter(\n (device) =>\n device.connectionProperties.pairingState === 'paired' &&\n device.connectionProperties.tunnelState !== 'unavailable'\n )\n .map((device) => {\n return {\n name: device.deviceProperties.name,\n model: device.hardwareProperties.productType,\n osVersion: device.deviceProperties.osVersionNumber,\n udid: device.hardwareProperties.udid,\n deviceType: 'device',\n connectionType:\n device.connectionProperties.transportType === 'localNetwork' ? 'Network' : 'USB',\n osType: coercePlatformToOsType(device.hardwareProperties.platform),\n };\n })\n );\n}\n\nfunction coercePlatformToOsType(platform: string): OSType {\n // The only two devices I have to test against...\n switch (platform) {\n case 'iOS':\n return 'iOS';\n case 'xrOS':\n return 'xrOS';\n default:\n debug('Unknown devicectl platform (needs to be added to Expo CLI):', platform);\n return platform as OSType;\n }\n}\nfunction coerceUsbmuxdPlatformToOsType(platform: string): OSType {\n // The only connectable device I have to test against...\n switch (platform) {\n case 'iPhone':\n case 'iPad':\n case 'iPhone OS':\n return 'iOS';\n default:\n debug('Unknown usbmuxd platform (needs to be added to Expo CLI):', platform);\n return platform as OSType;\n }\n}\n\n/** @returns a list of connected Apple devices. */\nexport async function getConnectedDevicesAsync(): Promise<ConnectedDevice[]> {\n const devices = await Promise.all([\n // Prioritize native tools since they can provide more accurate information.\n // NOTE: xcrun is substantially slower than custom tooling. +1.5s vs 9ms.\n profile(getConnectedDevicesUsingNativeToolsAsync)(),\n profile(getConnectedDevicesUsingCustomToolingAsync)(),\n ]);\n\n return uniqBy(devices.flat(), (device) => device.udid);\n}\n\n/**\n * This supports devices that are running OS versions older than iOS 17.\n *\n * @returns a list of connected Apple devices.\n */\nasync function getConnectedDevicesUsingCustomToolingAsync(): Promise<ConnectedDevice[]> {\n const client = new UsbmuxdClient(UsbmuxdClient.connectUsbmuxdSocket());\n const devices = await client.getDevices();\n client.socket.end();\n\n return Promise.all(\n devices.map(async (device): Promise<ConnectedDevice> => {\n const socket = await new UsbmuxdClient(UsbmuxdClient.connectUsbmuxdSocket()).connect(\n device,\n 62078\n );\n const deviceValues = await new LockdowndClient(socket).getAllValues();\n socket.end();\n // TODO(EvanBacon): Add support for osType (ipad, watchos, etc)\n return {\n // TODO(EvanBacon): Better name\n name: deviceValues.DeviceName ?? deviceValues.ProductType ?? 'unknown iOS device',\n model: deviceValues.ProductType,\n osVersion: deviceValues.ProductVersion,\n deviceType: 'device',\n connectionType: device.Properties.ConnectionType,\n udid: device.Properties.SerialNumber,\n osType: coerceUsbmuxdPlatformToOsType(deviceValues.DeviceClass),\n };\n })\n );\n}\n\n/** Install and run an Apple app binary on a connected Apple device. */\nexport async function runOnDevice({\n udid,\n appPath,\n bundleId,\n waitForApp,\n deltaPath,\n onProgress,\n}: {\n /** Apple device UDID */\n udid: string;\n /** File path to the app binary (ipa) */\n appPath: string;\n /** Bundle identifier for the app at `appPath` */\n bundleId: string;\n /** Wait for the app to launch before returning */\n waitForApp: boolean;\n /** File path to the app deltas folder to use for faster subsequent installs */\n deltaPath: string;\n /** Callback to be called with progress updates */\n onProgress: OnInstallProgressCallback;\n}) {\n debug('Running on device:', { udid, appPath, bundleId, waitForApp, deltaPath });\n\n const clientManager = await ClientManager.create(udid);\n\n try {\n await mountDeveloperDiskImage(clientManager);\n\n const packageName = path.basename(appPath);\n const destPackagePath = path.join('PublicStaging', packageName);\n\n await uploadApp(clientManager, { appBinaryPath: appPath, destinationPath: destPackagePath });\n\n const installer = await clientManager.getInstallationProxyClient();\n await installer.installApp(\n destPackagePath,\n bundleId,\n {\n // https://github.com/ios-control/ios-deploy/blob/0f2ffb1e564aa67a2dfca7cdf13de47ce489d835/src/ios-deploy/ios-deploy.m#L2491-L2508\n ApplicationsType: 'Any',\n\n CFBundleIdentifier: bundleId,\n CloseOnInvalidate: '1',\n InvalidateOnDetach: '1',\n IsUserInitiated: '1',\n // Disable checking for wifi devices, this is nominally faster.\n PreferWifi: '0',\n // Only info I could find on these:\n // https://github.com/wwxxyx/Quectel_BG96/blob/310876f90fc1093a59e45e381160eddcc31697d0/Apple_Homekit/homekit_certification_tools/ATS%206/ATS%206/ATS.app/Contents/Frameworks/CaptureKit.framework/Versions/A/Resources/MobileDevice/MobileInstallation.h#L112-L121\n PackageType: 'Developer',\n ShadowParentKey: deltaPath,\n // SkipUninstall: '1'\n },\n onProgress\n );\n\n const {\n // TODO(EvanBacon): This can be undefined when querying App Clips.\n [bundleId]: appInfo,\n } = await installer.lookupApp([bundleId]);\n\n if (appInfo) {\n // launch fails with EBusy or ENotFound if you try to launch immediately after install\n await delayAsync(200);\n const debugServerClient = await launchApp(clientManager, {\n bundleId,\n appInfo,\n detach: !waitForApp,\n });\n\n if (waitForApp && debugServerClient) {\n installExitHooks(async () => {\n // causes continue() to return\n debugServerClient.halt();\n // give continue() time to return response\n await delayAsync(64);\n });\n\n debug(`Waiting for app to close...\\n`);\n const result = await debugServerClient.continue();\n // TODO: I have no idea what this packet means yet (successful close?)\n // if not a close (ie, most likely due to halt from onBeforeExit), then kill the app\n if (result !== 'W00') {\n await debugServerClient.kill();\n }\n }\n } else {\n Log.warn(`App \"${bundleId}\" installed but couldn't be launched. Open on device manually.`);\n }\n } finally {\n clientManager.end();\n }\n}\n\n/** Mount the developer disk image for Xcode. */\nasync function mountDeveloperDiskImage(clientManager: ClientManager) {\n const imageMounter = await clientManager.getMobileImageMounterClient();\n // Check if already mounted. If not, mount.\n if (!(await imageMounter.lookupImage()).ImageSignature) {\n // verify DeveloperDiskImage exists (TODO: how does this work on Windows/Linux?)\n // TODO: if windows/linux, download?\n const version = await (await clientManager.getLockdowndClient()).getValue('ProductVersion');\n const developerDiskImagePath = await XcodeDeveloperDiskImagePrerequisite.instance.assertAsync({\n version,\n });\n const developerDiskImageSig = fs.readFileSync(`${developerDiskImagePath}.signature`);\n await imageMounter.uploadImage(developerDiskImagePath, developerDiskImageSig);\n await imageMounter.mountImage(developerDiskImagePath, developerDiskImageSig);\n }\n}\n\nasync function uploadApp(\n clientManager: ClientManager,\n { appBinaryPath, destinationPath }: { appBinaryPath: string; destinationPath: string }\n) {\n const afcClient = await clientManager.getAFCClient();\n try {\n await afcClient.getFileInfo('PublicStaging');\n } catch (err: any) {\n if (err instanceof AFCError && err.status === AFC_STATUS.OBJECT_NOT_FOUND) {\n await afcClient.makeDirectory('PublicStaging');\n } else {\n throw err;\n }\n }\n await afcClient.uploadDirectory(appBinaryPath, destinationPath);\n}\n\nasync function launchAppWithUsbmux(\n clientManager: ClientManager,\n { appInfo, detach }: { appInfo: IPLookupResult[string]; detach?: boolean }\n) {\n let tries = 0;\n while (tries < 3) {\n const debugServerClient = await clientManager.getDebugserverClient();\n await debugServerClient.setMaxPacketSize(1024);\n await debugServerClient.setWorkingDir(appInfo.Container);\n await debugServerClient.launchApp(appInfo.Path, appInfo.CFBundleExecutable);\n\n const result = await debugServerClient.checkLaunchSuccess();\n if (result === 'OK') {\n if (detach) {\n // https://github.com/libimobiledevice/libimobiledevice/blob/25059d4c7d75e03aab516af2929d7c6e6d4c17de/tools/idevicedebug.c#L455-L464\n const res = await debugServerClient.sendCommand('D', []);\n debug('Disconnect from debug server request:', res);\n if (res !== 'OK') {\n console.warn(\n 'Something went wrong while attempting to disconnect from iOS debug server, you may need to reopen the app manually.'\n );\n }\n }\n\n return debugServerClient;\n } else if (result === 'EBusy' || result === 'ENotFound') {\n debug('Device busy or app not found, trying to launch again in .5s...');\n tries++;\n debugServerClient.socket.end();\n await delayAsync(500);\n } else {\n throw new CommandError(`There was an error launching app: ${result}`);\n }\n }\n throw new CommandError('Unable to launch app, number of tries exceeded');\n}\n\n/**\n * iOS 17 introduces a new protocol called RemoteXPC.\n * This is not yet implemented, so we fallback to devicectl.\n *\n * @see https://github.com/doronz88/pymobiledevice3/blob/master/misc/RemoteXPC.md#process-remoted\n */\nasync function launchApp(\n clientManager: ClientManager,\n {\n bundleId,\n appInfo,\n detach,\n }: { bundleId: string; appInfo: IPLookupResult[string]; detach?: boolean }\n) {\n try {\n return await launchAppWithUsbmux(clientManager, { appInfo, detach });\n } catch (error) {\n debug('Failed to launch app with Usbmuxd, falling back to xcrun...', error);\n\n // Get the device UDID and close the connection, to allow `xcrun devicectl` to connect\n const deviceId = clientManager.device.Properties.SerialNumber;\n clientManager.end();\n\n // Fallback to devicectl for iOS 17 support\n return await launchAppWithDeviceCtl(deviceId, bundleId);\n }\n}\n"],"names":["getConnectedDevicesAsync","runOnDevice","debug","Debug","getConnectedDevicesUsingNativeToolsAsync","devicectl","getConnectedAppleDevicesAsync","filter","device","connectionProperties","pairingState","tunnelState","map","name","deviceProperties","model","hardwareProperties","productType","osVersion","osVersionNumber","udid","deviceType","connectionType","transportType","osType","coercePlatformToOsType","platform","coerceUsbmuxdPlatformToOsType","devices","Promise","all","profile","getConnectedDevicesUsingCustomToolingAsync","uniqBy","flat","client","UsbmuxdClient","connectUsbmuxdSocket","getDevices","socket","end","connect","deviceValues","LockdowndClient","getAllValues","DeviceName","ProductType","ProductVersion","Properties","ConnectionType","SerialNumber","DeviceClass","appPath","bundleId","waitForApp","deltaPath","onProgress","clientManager","ClientManager","create","mountDeveloperDiskImage","packageName","path","basename","destPackagePath","join","uploadApp","appBinaryPath","destinationPath","installer","getInstallationProxyClient","installApp","ApplicationsType","CFBundleIdentifier","CloseOnInvalidate","InvalidateOnDetach","IsUserInitiated","PreferWifi","PackageType","ShadowParentKey","appInfo","lookupApp","delayAsync","debugServerClient","launchApp","detach","installExitHooks","halt","result","continue","kill","Log","warn","imageMounter","getMobileImageMounterClient","lookupImage","ImageSignature","version","getLockdowndClient","getValue","developerDiskImagePath","XcodeDeveloperDiskImagePrerequisite","instance","assertAsync","developerDiskImageSig","fs","readFileSync","uploadImage","mountImage","afcClient","getAFCClient","getFileInfo","err","AFCError","status","AFC_STATUS","OBJECT_NOT_FOUND","makeDirectory","uploadDirectory","launchAppWithUsbmux","tries","getDebugserverClient","setMaxPacketSize","setWorkingDir","Container","Path","CFBundleExecutable","checkLaunchSuccess","res","sendCommand","console","CommandError","error","deviceId","launchAppWithDeviceCtl"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../src/run/ios/appleDevice/AppleDevice.ts"],"sourcesContent":["import Debug from 'debug';\nimport fs from 'fs';\nimport path from 'path';\n\nimport { ClientManager } from './ClientManager';\nimport { IPLookupResult, OnInstallProgressCallback } from './client/InstallationProxyClient';\nimport { LockdowndClient } from './client/LockdowndClient';\nimport { UsbmuxdClient } from './client/UsbmuxdClient';\nimport { AFC_STATUS, AFCError } from './protocol/AFCProtocol';\nimport { Log } from '../../../log';\nimport { XcodeDeveloperDiskImagePrerequisite } from '../../../start/doctor/apple/XcodeDeveloperDiskImagePrerequisite';\nimport * as devicectl from '../../../start/platforms/ios/devicectl';\nimport { launchAppWithDeviceCtl } from '../../../start/platforms/ios/devicectl';\nimport { OSType } from '../../../start/platforms/ios/simctl';\nimport { uniqBy } from '../../../utils/array';\nimport { delayAsync } from '../../../utils/delay';\nimport { CommandError } from '../../../utils/errors';\nimport { installExitHooks } from '../../../utils/exit';\nimport { profile } from '../../../utils/profile';\n\nconst debug = Debug('expo:apple-device');\n\n// NOTE(EvanBacon): I have a feeling this shape will change with new iOS versions (tested against iOS 15).\nexport interface ConnectedDevice {\n /** @example `00008101-001964A22629003A` */\n udid: string;\n /** @example `Evan's phone` */\n name: string;\n /** @example `iPhone13,4` */\n model: string;\n /** @example `device` */\n deviceType: 'device' | 'catalyst';\n /** @example `USB` */\n connectionType: 'USB' | 'Network';\n /** @example `15.4.1` */\n osVersion: string;\n\n osType: OSType;\n}\n\nasync function getConnectedDevicesUsingNativeToolsAsync(): Promise<ConnectedDevice[]> {\n return (\n (await devicectl.getConnectedAppleDevicesAsync())\n // Filter out unpaired and unavailable devices.\n // TODO: We could improve this logic in the future to attempt pairing if specified.\n .filter(\n (device) =>\n device.connectionProperties.pairingState === 'paired' &&\n device.connectionProperties.tunnelState !== 'unavailable'\n )\n .map((device) => {\n return {\n name: device.deviceProperties.name,\n model: device.hardwareProperties.productType,\n osVersion: device.deviceProperties.osVersionNumber,\n udid: device.hardwareProperties.udid,\n deviceType: 'device',\n connectionType:\n device.connectionProperties.transportType === 'localNetwork' ? 'Network' : 'USB',\n osType: coercePlatformToOsType(device.hardwareProperties.platform),\n };\n })\n );\n}\n\nfunction coercePlatformToOsType(platform: string): OSType {\n // The only two devices I have to test against...\n switch (platform) {\n case 'iOS':\n return 'iOS';\n case 'xrOS':\n return 'xrOS';\n default:\n debug('Unknown devicectl platform (needs to be added to Expo CLI):', platform);\n return platform as OSType;\n }\n}\nfunction coerceUsbmuxdPlatformToOsType(platform: string): OSType {\n // The only connectable device I have to test against...\n switch (platform) {\n case 'iPhone':\n case 'iPad':\n case 'iPhone OS':\n return 'iOS';\n default:\n debug('Unknown usbmuxd platform (needs to be added to Expo CLI):', platform);\n return platform as OSType;\n }\n}\n\n/** @returns a list of connected Apple devices. */\nexport async function getConnectedDevicesAsync(): Promise<ConnectedDevice[]> {\n const devices = await Promise.all([\n // Prioritize native tools since they can provide more accurate information.\n // NOTE: xcrun is substantially slower than custom tooling. +1.5s vs 9ms.\n profile(getConnectedDevicesUsingNativeToolsAsync)(),\n profile(getConnectedDevicesUsingCustomToolingAsync)(),\n ]);\n\n return uniqBy(devices.flat(), (device) => device.udid);\n}\n\n/**\n * This supports devices that are running OS versions older than iOS 17.\n *\n * @returns a list of connected Apple devices.\n */\nasync function getConnectedDevicesUsingCustomToolingAsync(): Promise<ConnectedDevice[]> {\n const client = new UsbmuxdClient(UsbmuxdClient.connectUsbmuxdSocket());\n const devices = await client.getDevices();\n client.socket.end();\n\n return Promise.all(\n devices.map(async (device): Promise<ConnectedDevice> => {\n const socket = await new UsbmuxdClient(UsbmuxdClient.connectUsbmuxdSocket()).connect(\n device,\n 62078\n );\n const deviceValues = await new LockdowndClient(socket).getAllValues();\n socket.end();\n // TODO(EvanBacon): Add support for osType (ipad, watchos, etc)\n return {\n // TODO(EvanBacon): Better name\n name: deviceValues.DeviceName ?? deviceValues.ProductType ?? 'unknown iOS device',\n model: deviceValues.ProductType,\n osVersion: deviceValues.ProductVersion,\n deviceType: 'device',\n connectionType: device.Properties.ConnectionType,\n udid: device.Properties.SerialNumber,\n // Lockdownd returns a reduced property set (without `DeviceClass`) for devices\n // connected over the network (\"Connect via network\" on iOS <= 16), so fall back\n // to `ProductName` (\"iPhone OS\") to avoid dropping wireless devices from the list.\n osType: coerceUsbmuxdPlatformToOsType(deviceValues.DeviceClass ?? deviceValues.ProductName),\n };\n })\n );\n}\n\n/** Install and run an Apple app binary on a connected Apple device. */\nexport async function runOnDevice({\n udid,\n appPath,\n bundleId,\n waitForApp,\n deltaPath,\n onProgress,\n}: {\n /** Apple device UDID */\n udid: string;\n /** File path to the app binary (ipa) */\n appPath: string;\n /** Bundle identifier for the app at `appPath` */\n bundleId: string;\n /** Wait for the app to launch before returning */\n waitForApp: boolean;\n /** File path to the app deltas folder to use for faster subsequent installs */\n deltaPath: string;\n /** Callback to be called with progress updates */\n onProgress: OnInstallProgressCallback;\n}) {\n debug('Running on device:', { udid, appPath, bundleId, waitForApp, deltaPath });\n\n const clientManager = await ClientManager.create(udid);\n\n try {\n await mountDeveloperDiskImage(clientManager);\n\n const packageName = path.basename(appPath);\n const destPackagePath = path.join('PublicStaging', packageName);\n\n await uploadApp(clientManager, { appBinaryPath: appPath, destinationPath: destPackagePath });\n\n const installer = await clientManager.getInstallationProxyClient();\n await installer.installApp(\n destPackagePath,\n bundleId,\n {\n // https://github.com/ios-control/ios-deploy/blob/0f2ffb1e564aa67a2dfca7cdf13de47ce489d835/src/ios-deploy/ios-deploy.m#L2491-L2508\n ApplicationsType: 'Any',\n\n CFBundleIdentifier: bundleId,\n CloseOnInvalidate: '1',\n InvalidateOnDetach: '1',\n IsUserInitiated: '1',\n // Disable checking for wifi devices, this is nominally faster.\n PreferWifi: '0',\n // Only info I could find on these:\n // https://github.com/wwxxyx/Quectel_BG96/blob/310876f90fc1093a59e45e381160eddcc31697d0/Apple_Homekit/homekit_certification_tools/ATS%206/ATS%206/ATS.app/Contents/Frameworks/CaptureKit.framework/Versions/A/Resources/MobileDevice/MobileInstallation.h#L112-L121\n PackageType: 'Developer',\n ShadowParentKey: deltaPath,\n // SkipUninstall: '1'\n },\n onProgress\n );\n\n const {\n // TODO(EvanBacon): This can be undefined when querying App Clips.\n [bundleId]: appInfo,\n } = await installer.lookupApp([bundleId]);\n\n if (appInfo) {\n // launch fails with EBusy or ENotFound if you try to launch immediately after install\n await delayAsync(200);\n const debugServerClient = await launchApp(clientManager, {\n bundleId,\n appInfo,\n detach: !waitForApp,\n });\n\n if (waitForApp && debugServerClient) {\n installExitHooks(async () => {\n // causes continue() to return\n debugServerClient.halt();\n // give continue() time to return response\n await delayAsync(64);\n });\n\n debug(`Waiting for app to close...\\n`);\n const result = await debugServerClient.continue();\n // TODO: I have no idea what this packet means yet (successful close?)\n // if not a close (ie, most likely due to halt from onBeforeExit), then kill the app\n if (result !== 'W00') {\n await debugServerClient.kill();\n }\n }\n } else {\n Log.warn(`App \"${bundleId}\" installed but couldn't be launched. Open on device manually.`);\n }\n } finally {\n clientManager.end();\n }\n}\n\n/** Mount the developer disk image for Xcode. */\nasync function mountDeveloperDiskImage(clientManager: ClientManager) {\n const imageMounter = await clientManager.getMobileImageMounterClient();\n // Check if already mounted. If not, mount.\n if (!(await imageMounter.lookupImage()).ImageSignature) {\n // verify DeveloperDiskImage exists (TODO: how does this work on Windows/Linux?)\n // TODO: if windows/linux, download?\n const version = await (await clientManager.getLockdowndClient()).getValue('ProductVersion');\n const developerDiskImagePath = await XcodeDeveloperDiskImagePrerequisite.instance.assertAsync({\n version,\n });\n const developerDiskImageSig = fs.readFileSync(`${developerDiskImagePath}.signature`);\n await imageMounter.uploadImage(developerDiskImagePath, developerDiskImageSig);\n await imageMounter.mountImage(developerDiskImagePath, developerDiskImageSig);\n }\n}\n\nasync function uploadApp(\n clientManager: ClientManager,\n { appBinaryPath, destinationPath }: { appBinaryPath: string; destinationPath: string }\n) {\n const afcClient = await clientManager.getAFCClient();\n try {\n await afcClient.getFileInfo('PublicStaging');\n } catch (err: any) {\n if (err instanceof AFCError && err.status === AFC_STATUS.OBJECT_NOT_FOUND) {\n await afcClient.makeDirectory('PublicStaging');\n } else {\n throw err;\n }\n }\n await afcClient.uploadDirectory(appBinaryPath, destinationPath);\n}\n\nasync function launchAppWithUsbmux(\n clientManager: ClientManager,\n { appInfo, detach }: { appInfo: IPLookupResult[string]; detach?: boolean }\n) {\n let tries = 0;\n while (tries < 3) {\n const debugServerClient = await clientManager.getDebugserverClient();\n await debugServerClient.setMaxPacketSize(1024);\n await debugServerClient.setWorkingDir(appInfo.Container);\n await debugServerClient.launchApp(appInfo.Path, appInfo.CFBundleExecutable);\n\n const result = await debugServerClient.checkLaunchSuccess();\n if (result === 'OK') {\n if (detach) {\n // https://github.com/libimobiledevice/libimobiledevice/blob/25059d4c7d75e03aab516af2929d7c6e6d4c17de/tools/idevicedebug.c#L455-L464\n const res = await debugServerClient.sendCommand('D', []);\n debug('Disconnect from debug server request:', res);\n if (res !== 'OK') {\n console.warn(\n 'Something went wrong while attempting to disconnect from iOS debug server, you may need to reopen the app manually.'\n );\n }\n }\n\n return debugServerClient;\n } else if (result === 'EBusy' || result === 'ENotFound') {\n debug('Device busy or app not found, trying to launch again in .5s...');\n tries++;\n debugServerClient.socket.end();\n await delayAsync(500);\n } else {\n throw new CommandError(`There was an error launching app: ${result}`);\n }\n }\n throw new CommandError('Unable to launch app, number of tries exceeded');\n}\n\n/**\n * iOS 17 introduces a new protocol called RemoteXPC.\n * This is not yet implemented, so we fallback to devicectl.\n *\n * @see https://github.com/doronz88/pymobiledevice3/blob/master/misc/RemoteXPC.md#process-remoted\n */\nasync function launchApp(\n clientManager: ClientManager,\n {\n bundleId,\n appInfo,\n detach,\n }: { bundleId: string; appInfo: IPLookupResult[string]; detach?: boolean }\n) {\n try {\n return await launchAppWithUsbmux(clientManager, { appInfo, detach });\n } catch (error) {\n debug('Failed to launch app with Usbmuxd, falling back to xcrun...', error);\n\n // Get the device UDID and close the connection, to allow `xcrun devicectl` to connect\n const deviceId = clientManager.device.Properties.SerialNumber;\n clientManager.end();\n\n // Fallback to devicectl for iOS 17 support\n return await launchAppWithDeviceCtl(deviceId, bundleId);\n }\n}\n"],"names":["getConnectedDevicesAsync","runOnDevice","debug","Debug","getConnectedDevicesUsingNativeToolsAsync","devicectl","getConnectedAppleDevicesAsync","filter","device","connectionProperties","pairingState","tunnelState","map","name","deviceProperties","model","hardwareProperties","productType","osVersion","osVersionNumber","udid","deviceType","connectionType","transportType","osType","coercePlatformToOsType","platform","coerceUsbmuxdPlatformToOsType","devices","Promise","all","profile","getConnectedDevicesUsingCustomToolingAsync","uniqBy","flat","client","UsbmuxdClient","connectUsbmuxdSocket","getDevices","socket","end","connect","deviceValues","LockdowndClient","getAllValues","DeviceName","ProductType","ProductVersion","Properties","ConnectionType","SerialNumber","DeviceClass","ProductName","appPath","bundleId","waitForApp","deltaPath","onProgress","clientManager","ClientManager","create","mountDeveloperDiskImage","packageName","path","basename","destPackagePath","join","uploadApp","appBinaryPath","destinationPath","installer","getInstallationProxyClient","installApp","ApplicationsType","CFBundleIdentifier","CloseOnInvalidate","InvalidateOnDetach","IsUserInitiated","PreferWifi","PackageType","ShadowParentKey","appInfo","lookupApp","delayAsync","debugServerClient","launchApp","detach","installExitHooks","halt","result","continue","kill","Log","warn","imageMounter","getMobileImageMounterClient","lookupImage","ImageSignature","version","getLockdowndClient","getValue","developerDiskImagePath","XcodeDeveloperDiskImagePrerequisite","instance","assertAsync","developerDiskImageSig","fs","readFileSync","uploadImage","mountImage","afcClient","getAFCClient","getFileInfo","err","AFCError","status","AFC_STATUS","OBJECT_NOT_FOUND","makeDirectory","uploadDirectory","launchAppWithUsbmux","tries","getDebugserverClient","setMaxPacketSize","setWorkingDir","Container","Path","CFBundleExecutable","checkLaunchSuccess","res","sendCommand","console","CommandError","error","deviceId","launchAppWithDeviceCtl"],"mappings":"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"}
@@ -29,16 +29,16 @@ function _http() {
29
29
  };
30
30
  return data;
31
31
  }
32
- function _http1() {
33
- const data = /*#__PURE__*/ _interop_require_default(require("http"));
34
- _http1 = function() {
32
+ function _nodehttp() {
33
+ const data = /*#__PURE__*/ _interop_require_default(require("node:http"));
34
+ _nodehttp = function() {
35
35
  return data;
36
36
  };
37
37
  return data;
38
38
  }
39
- function _path() {
40
- const data = /*#__PURE__*/ _interop_require_default(require("path"));
41
- _path = function() {
39
+ function _nodepath() {
40
+ const data = /*#__PURE__*/ _interop_require_default(require("node:path"));
41
+ _nodepath = function() {
42
42
  return data;
43
43
  };
44
44
  return data;
@@ -51,6 +51,7 @@ function _send() {
51
51
  return data;
52
52
  }
53
53
  const _log = /*#__PURE__*/ _interop_require_wildcard(require("../log"));
54
+ const _static = require("./static");
54
55
  const _dir = require("../utils/dir");
55
56
  const _errors = require("../utils/errors");
56
57
  const _findUp = require("../utils/findUp");
@@ -115,7 +116,7 @@ async function serveAsync(inputDir, options) {
115
116
  throw new _errors.CommandError('Could not start server. Port is not available.');
116
117
  }
117
118
  options.port = port;
118
- const serverDist = options.isDefaultDirectory ? _path().default.join(inputDir, 'dist') : inputDir;
119
+ const serverDist = options.isDefaultDirectory ? _nodepath().default.join(inputDir, 'dist') : inputDir;
119
120
  // TODO: `.expo/server/ios`, `.expo/server/android`, etc.
120
121
  if (!await (0, _dir.directoryExistsAsync)(serverDist)) {
121
122
  throw new _errors.CommandError(`The server directory ${serverDist} does not exist. Run \`npx expo export\` first.`);
@@ -131,7 +132,8 @@ async function serveAsync(inputDir, options) {
131
132
  // Detect the type of server we need to setup:
132
133
  }
133
134
  async function startStaticServerAsync(dist, options) {
134
- const server = _http1().default.createServer((req, res)=>{
135
+ const staticManifest = await (0, _static.loadStaticManifestAsync)(dist);
136
+ const server = _nodehttp().default.createServer((req, res)=>{
135
137
  var _req_url;
136
138
  // Remove query strings and decode URI
137
139
  const filePath = decodeURI(((_req_url = req.url) == null ? void 0 : _req_url.split('?')[0]) ?? '');
@@ -141,6 +143,16 @@ async function startStaticServerAsync(dist, options) {
141
143
  extensions: [
142
144
  'html'
143
145
  ]
146
+ }).on('headers', (res, resolvedPath)=>{
147
+ // If `headers` doesn't exist in the manifest, do nothing
148
+ if (!(staticManifest == null ? void 0 : staticManifest.headers)) {
149
+ return;
150
+ }
151
+ // Headers only apply to page responses and loader data files, never to other static assets.
152
+ if (!resolvedPath.endsWith('.html') && !filePath.startsWith('/_expo/loaders/')) {
153
+ return;
154
+ }
155
+ (0, _static.applyStaticHeaders)(res, staticManifest.headers);
144
156
  }).on('error', (err)=>{
145
157
  if (err.status === 404) {
146
158
  res.statusCode = 404;
@@ -155,8 +167,8 @@ async function startStaticServerAsync(dist, options) {
155
167
  }
156
168
  async function startDynamicServerAsync(dist, options) {
157
169
  const middleware = (0, _connect().default)();
158
- const staticDirectory = _path().default.join(dist, 'client');
159
- const serverDirectory = _path().default.join(dist, 'server');
170
+ const staticDirectory = _nodepath().default.join(dist, 'client');
171
+ const serverDirectory = _nodepath().default.join(dist, 'server');
160
172
  const serverHandler = (0, _http().createRequestHandler)({
161
173
  build: serverDirectory
162
174
  });
@@ -220,7 +232,7 @@ function canParseURL(url) {
220
232
  }
221
233
  }
222
234
  async function isStaticExportAsync(dist) {
223
- const routesFile = _path().default.join(dist, `server/_expo/routes.json`);
235
+ const routesFile = _nodepath().default.join(dist, `server/_expo/routes.json`);
224
236
  return !await (0, _dir.fileExistsAsync)(routesFile);
225
237
  }
226
238
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/serve/serveAsync.ts"],"sourcesContent":["import chalk from 'chalk';\nimport connect from 'connect';\nimport { createRequestHandler } from 'expo-server/adapter/http';\nimport http from 'http';\nimport path from 'path';\nimport send from 'send';\n\nimport * as Log from '../log';\nimport { directoryExistsAsync, fileExistsAsync } from '../utils/dir';\nimport { CommandError } from '../utils/errors';\nimport { findUpProjectRootOrAssert } from '../utils/findUp';\nimport { setNodeEnv, loadEnvFiles } from '../utils/nodeEnv';\nimport { resolvePortAsync } from '../utils/port';\n\ntype Options = {\n port?: number;\n isDefaultDirectory: boolean;\n};\n\nconst debug = require('debug')('expo:serve') as typeof console.log;\n\n// Start a basic http server\nexport async function serveAsync(inputDir: string, options: Options) {\n const projectRoot = findUpProjectRootOrAssert(inputDir);\n\n setNodeEnv('production');\n loadEnvFiles(projectRoot);\n\n const port = await resolvePortAsync(projectRoot, {\n defaultPort: options.port,\n fallbackPort: 8081,\n });\n\n if (port == null) {\n throw new CommandError('Could not start server. Port is not available.');\n }\n options.port = port;\n\n const serverDist = options.isDefaultDirectory ? path.join(inputDir, 'dist') : inputDir;\n // TODO: `.expo/server/ios`, `.expo/server/android`, etc.\n\n if (!(await directoryExistsAsync(serverDist))) {\n throw new CommandError(\n `The server directory ${serverDist} does not exist. Run \\`npx expo export\\` first.`\n );\n }\n\n const isStatic = await isStaticExportAsync(serverDist);\n\n Log.log(chalk.dim(`Starting ${isStatic ? 'static ' : ''}server in ${serverDist}`));\n\n if (isStatic) {\n await startStaticServerAsync(serverDist, options);\n } else {\n await startDynamicServerAsync(serverDist, options);\n }\n Log.log(`Server running at http://localhost:${options.port}`);\n // Detect the type of server we need to setup:\n}\n\nasync function startStaticServerAsync(dist: string, options: Options) {\n const server = http.createServer((req, res) => {\n // Remove query strings and decode URI\n const filePath = decodeURI(req.url?.split('?')[0] ?? '');\n\n send(req, filePath, {\n root: dist,\n index: 'index.html',\n extensions: ['html'],\n })\n .on('error', (err: any) => {\n if (err.status === 404) {\n res.statusCode = 404;\n res.end('Not Found');\n return;\n }\n res.statusCode = err.status || 500;\n res.end('Internal Server Error');\n })\n .pipe(res);\n });\n\n server.listen(options.port!);\n}\n\nasync function startDynamicServerAsync(dist: string, options: Options) {\n const middleware = connect();\n\n const staticDirectory = path.join(dist, 'client');\n const serverDirectory = path.join(dist, 'server');\n\n const serverHandler = createRequestHandler({ build: serverDirectory });\n\n // DOM component CORS support\n middleware.use((req, res, next) => {\n // TODO: Only when origin is `file://` (iOS), and Android equivalent.\n\n // Required for DOM components security in release builds.\n\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader(\n 'Access-Control-Allow-Headers',\n 'Origin, X-Requested-With, Content-Type, Accept, expo-platform'\n );\n\n // Handle OPTIONS preflight requests\n if (req.method === 'OPTIONS') {\n res.statusCode = 200;\n res.end();\n return;\n }\n next();\n });\n\n middleware.use((req, res, next) => {\n if (!req?.url || (req.method !== 'GET' && req.method !== 'HEAD')) {\n return next();\n }\n\n const pathname = canParseURL(req.url) ? new URL(req.url).pathname : req.url;\n if (!pathname) {\n return next();\n }\n\n debug(`Maybe serve static:`, pathname);\n\n const stream = send(req, pathname, {\n root: staticDirectory,\n extensions: ['html'],\n });\n\n // add file listener for fallthrough\n let forwardError = false;\n stream.on('file', function onFile() {\n // once file is determined, always forward error\n forwardError = true;\n });\n\n // forward errors\n stream.on('error', function error(err: any) {\n if (forwardError || !(err.statusCode < 500)) {\n next(err);\n return;\n }\n\n next();\n });\n\n // pipe\n stream.pipe(res);\n });\n\n middleware.use(serverHandler);\n\n middleware.listen(options.port!);\n}\n\nfunction canParseURL(url: string): boolean {\n try {\n // eslint-disable-next-line no-new\n new URL(url);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function isStaticExportAsync(dist: string): Promise<boolean> {\n const routesFile = path.join(dist, `server/_expo/routes.json`);\n return !(await fileExistsAsync(routesFile));\n}\n"],"names":["serveAsync","debug","require","inputDir","options","projectRoot","findUpProjectRootOrAssert","setNodeEnv","loadEnvFiles","port","resolvePortAsync","defaultPort","fallbackPort","CommandError","serverDist","isDefaultDirectory","path","join","directoryExistsAsync","isStatic","isStaticExportAsync","Log","log","chalk","dim","startStaticServerAsync","startDynamicServerAsync","dist","server","http","createServer","req","res","filePath","decodeURI","url","split","send","root","index","extensions","on","err","status","statusCode","end","pipe","listen","middleware","connect","staticDirectory","serverDirectory","serverHandler","createRequestHandler","build","use","next","setHeader","method","pathname","canParseURL","URL","stream","forwardError","onFile","error","routesFile","fileExistsAsync"],"mappings":";;;;+BAsBsBA;;;eAAAA;;;;gEAtBJ;;;;;;;gEACE;;;;;;;yBACiB;;;;;;;gEACpB;;;;;;;gEACA;;;;;;;gEACA;;;;;;6DAEI;qBACiC;wBACzB;wBACa;yBACD;sBACR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOjC,MAAMC,QAAQC,QAAQ,SAAS;AAGxB,eAAeF,WAAWG,QAAgB,EAAEC,OAAgB;IACjE,MAAMC,cAAcC,IAAAA,iCAAyB,EAACH;IAE9CI,IAAAA,mBAAU,EAAC;IACXC,IAAAA,qBAAY,EAACH;IAEb,MAAMI,OAAO,MAAMC,IAAAA,sBAAgB,EAACL,aAAa;QAC/CM,aAAaP,QAAQK,IAAI;QACzBG,cAAc;IAChB;IAEA,IAAIH,QAAQ,MAAM;QAChB,MAAM,IAAII,oBAAY,CAAC;IACzB;IACAT,QAAQK,IAAI,GAAGA;IAEf,MAAMK,aAAaV,QAAQW,kBAAkB,GAAGC,eAAI,CAACC,IAAI,CAACd,UAAU,UAAUA;IAC9E,0DAA0D;IAE1D,IAAI,CAAE,MAAMe,IAAAA,yBAAoB,EAACJ,aAAc;QAC7C,MAAM,IAAID,oBAAY,CACpB,CAAC,qBAAqB,EAAEC,WAAW,+CAA+C,CAAC;IAEvF;IAEA,MAAMK,WAAW,MAAMC,oBAAoBN;IAE3CO,KAAIC,GAAG,CAACC,gBAAK,CAACC,GAAG,CAAC,CAAC,SAAS,EAAEL,WAAW,YAAY,GAAG,UAAU,EAAEL,YAAY;IAEhF,IAAIK,UAAU;QACZ,MAAMM,uBAAuBX,YAAYV;IAC3C,OAAO;QACL,MAAMsB,wBAAwBZ,YAAYV;IAC5C;IACAiB,KAAIC,GAAG,CAAC,CAAC,mCAAmC,EAAElB,QAAQK,IAAI,EAAE;AAC5D,8CAA8C;AAChD;AAEA,eAAegB,uBAAuBE,IAAY,EAAEvB,OAAgB;IAClE,MAAMwB,SAASC,gBAAI,CAACC,YAAY,CAAC,CAACC,KAAKC;YAEVD;QAD3B,sCAAsC;QACtC,MAAME,WAAWC,UAAUH,EAAAA,WAAAA,IAAII,GAAG,qBAAPJ,SAASK,KAAK,CAAC,IAAI,CAAC,EAAE,KAAI;QAErDC,IAAAA,eAAI,EAACN,KAAKE,UAAU;YAClBK,MAAMX;YACNY,OAAO;YACPC,YAAY;gBAAC;aAAO;QACtB,GACGC,EAAE,CAAC,SAAS,CAACC;YACZ,IAAIA,IAAIC,MAAM,KAAK,KAAK;gBACtBX,IAAIY,UAAU,GAAG;gBACjBZ,IAAIa,GAAG,CAAC;gBACR;YACF;YACAb,IAAIY,UAAU,GAAGF,IAAIC,MAAM,IAAI;YAC/BX,IAAIa,GAAG,CAAC;QACV,GACCC,IAAI,CAACd;IACV;IAEAJ,OAAOmB,MAAM,CAAC3C,QAAQK,IAAI;AAC5B;AAEA,eAAeiB,wBAAwBC,IAAY,EAAEvB,OAAgB;IACnE,MAAM4C,aAAaC,IAAAA,kBAAO;IAE1B,MAAMC,kBAAkBlC,eAAI,CAACC,IAAI,CAACU,MAAM;IACxC,MAAMwB,kBAAkBnC,eAAI,CAACC,IAAI,CAACU,MAAM;IAExC,MAAMyB,gBAAgBC,IAAAA,4BAAoB,EAAC;QAAEC,OAAOH;IAAgB;IAEpE,6BAA6B;IAC7BH,WAAWO,GAAG,CAAC,CAACxB,KAAKC,KAAKwB;QACxB,qEAAqE;QAErE,0DAA0D;QAE1DxB,IAAIyB,SAAS,CAAC,+BAA+B;QAC7CzB,IAAIyB,SAAS,CAAC,gCAAgC;QAC9CzB,IAAIyB,SAAS,CACX,gCACA;QAGF,oCAAoC;QACpC,IAAI1B,IAAI2B,MAAM,KAAK,WAAW;YAC5B1B,IAAIY,UAAU,GAAG;YACjBZ,IAAIa,GAAG;YACP;QACF;QACAW;IACF;IAEAR,WAAWO,GAAG,CAAC,CAACxB,KAAKC,KAAKwB;QACxB,IAAI,EAACzB,uBAAAA,IAAKI,GAAG,KAAKJ,IAAI2B,MAAM,KAAK,SAAS3B,IAAI2B,MAAM,KAAK,QAAS;YAChE,OAAOF;QACT;QAEA,MAAMG,WAAWC,YAAY7B,IAAII,GAAG,IAAI,IAAI0B,IAAI9B,IAAII,GAAG,EAAEwB,QAAQ,GAAG5B,IAAII,GAAG;QAC3E,IAAI,CAACwB,UAAU;YACb,OAAOH;QACT;QAEAvD,MAAM,CAAC,mBAAmB,CAAC,EAAE0D;QAE7B,MAAMG,SAASzB,IAAAA,eAAI,EAACN,KAAK4B,UAAU;YACjCrB,MAAMY;YACNV,YAAY;gBAAC;aAAO;QACtB;QAEA,oCAAoC;QACpC,IAAIuB,eAAe;QACnBD,OAAOrB,EAAE,CAAC,QAAQ,SAASuB;YACzB,gDAAgD;YAChDD,eAAe;QACjB;QAEA,iBAAiB;QACjBD,OAAOrB,EAAE,CAAC,SAAS,SAASwB,MAAMvB,GAAQ;YACxC,IAAIqB,gBAAgB,CAAErB,CAAAA,IAAIE,UAAU,GAAG,GAAE,GAAI;gBAC3CY,KAAKd;gBACL;YACF;YAEAc;QACF;QAEA,OAAO;QACPM,OAAOhB,IAAI,CAACd;IACd;IAEAgB,WAAWO,GAAG,CAACH;IAEfJ,WAAWD,MAAM,CAAC3C,QAAQK,IAAI;AAChC;AAEA,SAASmD,YAAYzB,GAAW;IAC9B,IAAI;QACF,kCAAkC;QAClC,IAAI0B,IAAI1B;QACR,OAAO;IACT,EAAE,OAAM;QACN,OAAO;IACT;AACF;AAEA,eAAef,oBAAoBO,IAAY;IAC7C,MAAMuC,aAAalD,eAAI,CAACC,IAAI,CAACU,MAAM,CAAC,wBAAwB,CAAC;IAC7D,OAAO,CAAE,MAAMwC,IAAAA,oBAAe,EAACD;AACjC"}
1
+ {"version":3,"sources":["../../../src/serve/serveAsync.ts"],"sourcesContent":["import chalk from 'chalk';\nimport connect from 'connect';\nimport { createRequestHandler } from 'expo-server/adapter/http';\nimport http from 'node:http';\nimport path from 'node:path';\nimport send from 'send';\n\nimport * as Log from '../log';\nimport { applyStaticHeaders, loadStaticManifestAsync } from './static';\nimport { directoryExistsAsync, fileExistsAsync } from '../utils/dir';\nimport { CommandError } from '../utils/errors';\nimport { findUpProjectRootOrAssert } from '../utils/findUp';\nimport { setNodeEnv, loadEnvFiles } from '../utils/nodeEnv';\nimport { resolvePortAsync } from '../utils/port';\n\ntype Options = {\n port?: number;\n isDefaultDirectory: boolean;\n};\n\nconst debug = require('debug')('expo:serve') as typeof console.log;\n\n// Start a basic http server\nexport async function serveAsync(inputDir: string, options: Options) {\n const projectRoot = findUpProjectRootOrAssert(inputDir);\n\n setNodeEnv('production');\n loadEnvFiles(projectRoot);\n\n const port = await resolvePortAsync(projectRoot, {\n defaultPort: options.port,\n fallbackPort: 8081,\n });\n\n if (port == null) {\n throw new CommandError('Could not start server. Port is not available.');\n }\n options.port = port;\n\n const serverDist = options.isDefaultDirectory ? path.join(inputDir, 'dist') : inputDir;\n // TODO: `.expo/server/ios`, `.expo/server/android`, etc.\n\n if (!(await directoryExistsAsync(serverDist))) {\n throw new CommandError(\n `The server directory ${serverDist} does not exist. Run \\`npx expo export\\` first.`\n );\n }\n\n const isStatic = await isStaticExportAsync(serverDist);\n\n Log.log(chalk.dim(`Starting ${isStatic ? 'static ' : ''}server in ${serverDist}`));\n\n if (isStatic) {\n await startStaticServerAsync(serverDist, options);\n } else {\n await startDynamicServerAsync(serverDist, options);\n }\n Log.log(`Server running at http://localhost:${options.port}`);\n // Detect the type of server we need to setup:\n}\n\nasync function startStaticServerAsync(dist: string, options: Options) {\n const staticManifest = await loadStaticManifestAsync(dist);\n\n const server = http.createServer((req, res) => {\n // Remove query strings and decode URI\n const filePath = decodeURI(req.url?.split('?')[0] ?? '');\n\n send(req, filePath, {\n root: dist,\n index: 'index.html',\n extensions: ['html'],\n })\n .on('headers', (res: http.ServerResponse, resolvedPath: string) => {\n // If `headers` doesn't exist in the manifest, do nothing\n if (!staticManifest?.headers) {\n return;\n }\n // Headers only apply to page responses and loader data files, never to other static assets.\n if (!resolvedPath.endsWith('.html') && !filePath.startsWith('/_expo/loaders/')) {\n return;\n }\n\n applyStaticHeaders(res, staticManifest.headers);\n })\n .on('error', (err: any) => {\n if (err.status === 404) {\n res.statusCode = 404;\n res.end('Not Found');\n return;\n }\n res.statusCode = err.status || 500;\n res.end('Internal Server Error');\n })\n .pipe(res);\n });\n\n server.listen(options.port!);\n}\n\nasync function startDynamicServerAsync(dist: string, options: Options) {\n const middleware = connect();\n\n const staticDirectory = path.join(dist, 'client');\n const serverDirectory = path.join(dist, 'server');\n\n const serverHandler = createRequestHandler({ build: serverDirectory });\n\n // DOM component CORS support\n middleware.use((req, res, next) => {\n // TODO: Only when origin is `file://` (iOS), and Android equivalent.\n\n // Required for DOM components security in release builds.\n\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader(\n 'Access-Control-Allow-Headers',\n 'Origin, X-Requested-With, Content-Type, Accept, expo-platform'\n );\n\n // Handle OPTIONS preflight requests\n if (req.method === 'OPTIONS') {\n res.statusCode = 200;\n res.end();\n return;\n }\n next();\n });\n\n middleware.use((req, res, next) => {\n if (!req?.url || (req.method !== 'GET' && req.method !== 'HEAD')) {\n return next();\n }\n\n const pathname = canParseURL(req.url) ? new URL(req.url).pathname : req.url;\n if (!pathname) {\n return next();\n }\n\n debug(`Maybe serve static:`, pathname);\n\n const stream = send(req, pathname, {\n root: staticDirectory,\n extensions: ['html'],\n });\n\n // add file listener for fallthrough\n let forwardError = false;\n stream.on('file', function onFile() {\n // once file is determined, always forward error\n forwardError = true;\n });\n\n // forward errors\n stream.on('error', function error(err: any) {\n if (forwardError || !(err.statusCode < 500)) {\n next(err);\n return;\n }\n\n next();\n });\n\n // pipe\n stream.pipe(res);\n });\n\n middleware.use(serverHandler);\n\n middleware.listen(options.port!);\n}\n\nfunction canParseURL(url: string): boolean {\n try {\n // eslint-disable-next-line no-new\n new URL(url);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function isStaticExportAsync(dist: string): Promise<boolean> {\n const routesFile = path.join(dist, `server/_expo/routes.json`);\n return !(await fileExistsAsync(routesFile));\n}\n"],"names":["serveAsync","debug","require","inputDir","options","projectRoot","findUpProjectRootOrAssert","setNodeEnv","loadEnvFiles","port","resolvePortAsync","defaultPort","fallbackPort","CommandError","serverDist","isDefaultDirectory","path","join","directoryExistsAsync","isStatic","isStaticExportAsync","Log","log","chalk","dim","startStaticServerAsync","startDynamicServerAsync","dist","staticManifest","loadStaticManifestAsync","server","http","createServer","req","res","filePath","decodeURI","url","split","send","root","index","extensions","on","resolvedPath","headers","endsWith","startsWith","applyStaticHeaders","err","status","statusCode","end","pipe","listen","middleware","connect","staticDirectory","serverDirectory","serverHandler","createRequestHandler","build","use","next","setHeader","method","pathname","canParseURL","URL","stream","forwardError","onFile","error","routesFile","fileExistsAsync"],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: all[name]
9
+ });
10
+ }
11
+ _export(exports, {
12
+ applyStaticHeaders: function() {
13
+ return applyStaticHeaders;
14
+ },
15
+ loadStaticManifestAsync: function() {
16
+ return loadStaticManifestAsync;
17
+ }
18
+ });
19
+ function _promises() {
20
+ const data = /*#__PURE__*/ _interop_require_default(require("node:fs/promises"));
21
+ _promises = function() {
22
+ return data;
23
+ };
24
+ return data;
25
+ }
26
+ function _nodepath() {
27
+ const data = /*#__PURE__*/ _interop_require_default(require("node:path"));
28
+ _nodepath = function() {
29
+ return data;
30
+ };
31
+ return data;
32
+ }
33
+ function _interop_require_default(obj) {
34
+ return obj && obj.__esModule ? obj : {
35
+ default: obj
36
+ };
37
+ }
38
+ async function loadStaticManifestAsync(dist) {
39
+ var _json_redirects;
40
+ let json;
41
+ try {
42
+ json = JSON.parse(await _promises().default.readFile(_nodepath().default.join(dist, '_expo/.routes.json'), 'utf8'));
43
+ } catch {
44
+ return null;
45
+ }
46
+ return {
47
+ ...json,
48
+ redirects: (_json_redirects = json.redirects) == null ? void 0 : _json_redirects.map((rule)=>({
49
+ ...rule,
50
+ namedRegex: new RegExp(rule.namedRegex)
51
+ }))
52
+ };
53
+ }
54
+ function applyStaticHeaders(res, headers) {
55
+ for (const [name, value] of Object.entries(headers)){
56
+ if (name.toLowerCase() === 'content-type') {
57
+ continue;
58
+ }
59
+ if (Array.isArray(value)) {
60
+ for (const headerValue of value){
61
+ res.appendHeader(name, headerValue);
62
+ }
63
+ } else if (!res.hasHeader(name)) {
64
+ res.setHeader(name, value);
65
+ }
66
+ }
67
+ }
68
+
69
+ //# sourceMappingURL=static.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/serve/static.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport http from 'node:http';\nimport path from 'node:path';\n\nimport type { StaticManifest } from '../export/static';\n\nexport async function loadStaticManifestAsync(\n dist: string\n): Promise<StaticManifest<RegExp> | null> {\n let json: StaticManifest<string>;\n try {\n json = JSON.parse(await fs.readFile(path.join(dist, '_expo/.routes.json'), 'utf8'));\n } catch {\n return null;\n }\n\n return {\n ...json,\n redirects: json.redirects?.map((rule) => ({\n ...rule,\n namedRegex: new RegExp(rule.namedRegex),\n })),\n };\n}\n\n/**\n * Applies headers from a static export manifest to a response, except\n * `Content-Type` which must come from the served file.\n */\nexport function applyStaticHeaders(\n res: http.ServerResponse,\n headers: Record<string, string | string[]>\n): void {\n for (const [name, value] of Object.entries(headers)) {\n if (name.toLowerCase() === 'content-type') {\n continue;\n }\n if (Array.isArray(value)) {\n for (const headerValue of value) {\n res.appendHeader(name, headerValue);\n }\n } else if (!res.hasHeader(name)) {\n res.setHeader(name, value);\n }\n }\n}\n"],"names":["applyStaticHeaders","loadStaticManifestAsync","dist","json","JSON","parse","fs","readFile","path","join","redirects","map","rule","namedRegex","RegExp","res","headers","name","value","Object","entries","toLowerCase","Array","isArray","headerValue","appendHeader","hasHeader","setHeader"],"mappings":";;;;;;;;;;;IA6BgBA,kBAAkB;eAAlBA;;IAvBMC,uBAAuB;eAAvBA;;;;gEANP;;;;;;;gEAEE;;;;;;;;;;;AAIV,eAAeA,wBACpBC,IAAY;QAWCC;IATb,IAAIA;IACJ,IAAI;QACFA,OAAOC,KAAKC,KAAK,CAAC,MAAMC,mBAAE,CAACC,QAAQ,CAACC,mBAAI,CAACC,IAAI,CAACP,MAAM,uBAAuB;IAC7E,EAAE,OAAM;QACN,OAAO;IACT;IAEA,OAAO;QACL,GAAGC,IAAI;QACPO,SAAS,GAAEP,kBAAAA,KAAKO,SAAS,qBAAdP,gBAAgBQ,GAAG,CAAC,CAACC,OAAU,CAAA;gBACxC,GAAGA,IAAI;gBACPC,YAAY,IAAIC,OAAOF,KAAKC,UAAU;YACxC,CAAA;IACF;AACF;AAMO,SAASb,mBACde,GAAwB,EACxBC,OAA0C;IAE1C,KAAK,MAAM,CAACC,MAAMC,MAAM,IAAIC,OAAOC,OAAO,CAACJ,SAAU;QACnD,IAAIC,KAAKI,WAAW,OAAO,gBAAgB;YACzC;QACF;QACA,IAAIC,MAAMC,OAAO,CAACL,QAAQ;YACxB,KAAK,MAAMM,eAAeN,MAAO;gBAC/BH,IAAIU,YAAY,CAACR,MAAMO;YACzB;QACF,OAAO,IAAI,CAACT,IAAIW,SAAS,CAACT,OAAO;YAC/BF,IAAIY,SAAS,CAACV,MAAMC;QACtB;IACF;AACF"}
@@ -185,12 +185,16 @@ async function getConnectedAppleDevicesAsync() {
185
185
  const devicesJson = await _jsonfile().default.readAsync(tmpPath);
186
186
  if (![
187
187
  2,
188
- 3
188
+ 3,
189
+ 5
189
190
  ].includes(devicesJson == null ? void 0 : (_devicesJson_info = devicesJson.info) == null ? void 0 : _devicesJson_info.jsonVersion)) {
190
191
  _log.warn('Unexpected devicectl JSON version output from devicectl. Connecting to physical Apple devices may not work as expected.');
191
192
  }
192
193
  assertDevicesJson(devicesJson);
193
- return devicesJson.result.devices;
194
+ return devicesJson.result.devices.filter((device)=>{
195
+ var _device_hardwareProperties;
196
+ return (device == null ? void 0 : (_device_hardwareProperties = device.hardwareProperties) == null ? void 0 : _device_hardwareProperties.reality) !== 'simulated';
197
+ });
194
198
  }
195
199
  function assertDevicesJson(results) {
196
200
  var _results_result;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/start/platforms/ios/devicectl.ts"],"sourcesContent":["/**\n * Copyright © 2024 650 Industries.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport JsonFile from '@expo/json-file';\nimport spawnAsync, { SpawnOptions, SpawnResult } from '@expo/spawn-async';\nimport chalk from 'chalk';\nimport { spawn, execSync } from 'child_process';\nimport fs from 'fs';\nimport assert from 'node:assert';\nimport { Ora } from 'ora';\nimport { EOL } from 'os';\nimport path from 'path';\n\nimport { xcrunAsync } from './xcrun';\nimport { getExpoHomeDirectory } from '../../../api/user/UserSettings';\nimport * as Log from '../../../log';\nimport { createTempFilePath } from '../../../utils/createTempPath';\nimport { CommandError } from '../../../utils/errors';\nimport { installExitHooks } from '../../../utils/exit';\nimport { isInteractive } from '../../../utils/interactive';\nimport { ora } from '../../../utils/ora';\nimport { confirmAsync } from '../../../utils/prompts';\n\nconst DEVICE_CTL_EXISTS_PATH = path.join(getExpoHomeDirectory(), 'devicectl-exists');\n\nconst debug = require('debug')('expo:devicectl') as typeof console.log;\n\ntype AnyEnum<T extends string = string> = T | (string & object);\n\ntype DeviceCtlDevice = {\n capabilities: DeviceCtlDeviceCapability[];\n connectionProperties: DeviceCtlConnectionProperties;\n deviceProperties: DeviceCtlDeviceProperties;\n hardwareProperties: DeviceCtlHardwareProperties;\n /** \"A1A1AAA1-0011-1AA1-11A1-10A1111AA11A\" */\n identifier: string;\n visibilityClass: AnyEnum<'default'>;\n};\n\ntype DeviceCtlHardwareProperties = {\n cpuType: DeviceCtlCpuType;\n deviceType: AnyEnum<'iPhone'>;\n /** 1114404411111111 */\n ecid: number;\n /** \"D74AP\" */\n hardwareModel: string;\n /** 512000000000 */\n internalStorageCapacity: number;\n /** true */\n isProductionFused: boolean;\n /** \"iPhone 14 Pro Max\" */\n marketingName: string;\n /** \"iOS\" */\n platform: AnyEnum<'iOS' | 'xrOS'>;\n /** \"iPhone15,3\" */\n productType: AnyEnum<'iPhone13,4' | 'iPhone15,3'>;\n reality: AnyEnum<'physical'>;\n /** \"X2X1CC1XXX\" */\n serialNumber: string;\n supportedCPUTypes: DeviceCtlCpuType[];\n /** [1] */\n supportedDeviceFamilies: number[];\n thinningProductType: AnyEnum<'iPhone15,3'>;\n /** \"00001110-001111110110101A\" */\n udid: string;\n};\n\ntype DeviceCtlDeviceProperties = {\n /** true */\n bootedFromSnapshot: boolean;\n /** \"com.apple.os.update-AD0CF111ACFF11A11111A76A3D1262AE42A3F56F305AF5AE1135393A7A14A7D1\" */\n bootedSnapshotName: string;\n /** false */\n ddiServicesAvailable: boolean;\n\n developerModeStatus: AnyEnum<'enabled'>;\n /** false */\n hasInternalOSBuild: boolean;\n /** \"Evan's phone\" */\n name: string;\n /** \"21E236\" */\n osBuildUpdate: string;\n /** \"17.4.1\" */\n osVersionNumber: string;\n /** false */\n rootFileSystemIsWritable: boolean;\n};\n\ntype DeviceCtlDeviceCapability =\n | {\n name: AnyEnum;\n featureIdentifier: AnyEnum;\n }\n | {\n featureIdentifier: 'com.apple.coredevice.feature.connectdevice';\n name: 'Connect to Device';\n }\n | {\n featureIdentifier: 'com.apple.coredevice.feature.unpairdevice';\n name: 'Unpair Device';\n }\n | {\n featureIdentifier: 'com.apple.coredevice.feature.acquireusageassertion';\n name: 'Acquire Usage Assertion';\n };\n\ntype DeviceCtlConnectionProperties = {\n authenticationType: AnyEnum<'manualPairing'>;\n isMobileDeviceOnly: boolean;\n /** \"2024-04-20T22:50:04.244Z\" */\n lastConnectionDate: string;\n pairingState: AnyEnum<'paired'>;\n /** [\"00001111-001111110110101A.coredevice.local\", \"A1A1AAA1-0011-1AA1-11A1-10A1111AA11A.coredevice.local\"] */\n potentialHostnames: string[];\n transportType: AnyEnum<'localNetwork' | 'wired'>;\n tunnelState: AnyEnum<'disconnected' | 'unavailable'>;\n tunnelTransportProtocol: AnyEnum<'tcp'>;\n};\n\ntype DeviceCtlCpuType = {\n name: AnyEnum<'arm64e' | 'arm64' | 'arm64_32'>;\n subType: number;\n /** 16777228 */\n type: number;\n};\n\n/** Run a `devicectl` command. */\nexport async function devicectlAsync(\n args: (string | undefined)[],\n options?: SpawnOptions\n): Promise<SpawnResult> {\n try {\n return await xcrunAsync(['devicectl', ...args], options);\n } catch (error: any) {\n if (error instanceof CommandError) {\n throw error;\n }\n if ('stderr' in error) {\n const errorCodes = getDeviceCtlErrorCodes(error.stderr);\n if (errorCodes.includes('Locked')) {\n throw new CommandError('APPLE_DEVICE_LOCKED', 'Device is locked, unlock and try again.');\n }\n }\n throw error;\n }\n}\n\nexport async function getConnectedAppleDevicesAsync() {\n if (!hasDevicectlEverBeenInstalled()) {\n debug('devicectl not found, skipping remote Apple devices.');\n return [];\n }\n\n const tmpPath = createTempFilePath();\n const devices = await devicectlAsync([\n 'list',\n 'devices',\n '--json-output',\n tmpPath,\n // Give two seconds before timing out: between 5 and 9223372036854775807\n '--timeout',\n '5',\n ]);\n debug(devices.stdout);\n const devicesJson = await JsonFile.readAsync(tmpPath);\n\n if (![2, 3].includes((devicesJson as any)?.info?.jsonVersion)) {\n Log.warn(\n 'Unexpected devicectl JSON version output from devicectl. Connecting to physical Apple devices may not work as expected.'\n );\n }\n\n assertDevicesJson(devicesJson);\n\n return devicesJson.result.devices as DeviceCtlDevice[];\n}\n\nfunction assertDevicesJson(\n results: any\n): asserts results is { result: { devices: DeviceCtlDevice[] } } {\n assert(\n results != null && 'result' in results && Array.isArray(results?.result?.devices),\n 'Malformed JSON output from devicectl: ' + JSON.stringify(results, null, 2)\n );\n}\n\nexport async function launchBinaryOnMacAsync(\n bundleId: string,\n appBinaryPath: string\n): Promise<void> {\n const args = ['-b', bundleId, appBinaryPath];\n try {\n await spawnAsync('open', args);\n } catch (error: any) {\n if ('code' in error) {\n if (error.code === 1) {\n throw new CommandError(\n 'MACOS_LAUNCH',\n 'Failed to launch the compatible binary on macOS: open ' +\n args.join(' ') +\n '\\n\\n' +\n error.message\n );\n }\n }\n throw error;\n }\n}\n\nasync function installAppWithDeviceCtlAsync(\n uuid: string,\n bundleIdOrAppPath: string,\n onProgress: (event: { status: string; isComplete: boolean; progress: number }) => void\n): Promise<void> {\n const maxAttempts = 3;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n await installAppWithDeviceCtlInternalAsync(uuid, bundleIdOrAppPath, onProgress, attempt);\n return;\n } catch (error: any) {\n if (error.code === 'detached') {\n throw error;\n }\n const isTransientDisconnect =\n error.message &&\n (error.message.includes('CoreDeviceError') || error.message.includes('0xFA0'));\n\n if (!isTransientDisconnect || attempt === maxAttempts) {\n throw error;\n }\n const backoffDelay = 500 + Math.pow(2, attempt - 1) * 500;\n await new Promise((resolve) => setTimeout(resolve, backoffDelay));\n }\n }\n}\n\nasync function installAppWithDeviceCtlInternalAsync(\n uuid: string,\n bundleIdOrAppPath: string,\n onProgress: (event: { status: string; isComplete: boolean; progress: number }) => void,\n attempt: number\n): Promise<void> {\n // 𝝠 xcrun devicectl device install app --device 00001110-001111110110101A /Users/evanbacon/Library/Developer/Xcode/DerivedData/Router-hgbqaxzhrhkiftfweydvhgttadvn/Build/Products/Debug-iphoneos/Router.app --verbose\n return new Promise((resolve, reject) => {\n const args: string[] = [\n 'devicectl',\n 'device',\n 'install',\n 'app',\n '--device',\n uuid,\n bundleIdOrAppPath,\n ];\n const childProcess = spawn('xcrun', args);\n debug('xcrun ' + args.join(' '));\n\n let currentProgress = 0;\n let hasStarted = false;\n\n function updateProgress(progress: number) {\n hasStarted = true;\n if (progress <= currentProgress) {\n return;\n }\n currentProgress = progress;\n const statusPrefix = attempt > 1 ? `Installing (attempt ${attempt})` : 'Installing';\n onProgress({\n progress,\n isComplete: progress === 100,\n status: statusPrefix,\n });\n }\n\n childProcess.stdout.on('data', (data: Buffer) => {\n // Sometimes more than one chunk comes at a time, here we split by system newline,\n // then trim and filter.\n const strings = data\n .toString()\n .split(EOL)\n .map((value) => value.trim());\n\n strings.forEach((str) => {\n // Match the progress percentage:\n // - '34%... 35%...' -> 34\n // - '31%...' -> 31\n // - 'Complete!' -> 100\n\n const match = str.match(/(\\d+)%\\.\\.\\./);\n if (match) {\n updateProgress(parseInt(match[1], 10));\n } else if (hasStarted) {\n updateProgress(100);\n }\n });\n\n debug('[stdout]:', strings);\n });\n\n let stderrBuffer = '';\n childProcess.stderr.on('data', (data: Buffer) => {\n stderrBuffer += data.toString();\n });\n\n childProcess.on('close', (code) => {\n debug('[close]: ' + code);\n if (code === 0) {\n resolve();\n } else {\n const err = new Error(stderrBuffer || `Command failed with exit code ${code}`);\n (err as any).code = code;\n detach(err);\n }\n });\n\n const detach = async (err?: Error) => {\n off?.();\n if (childProcess) {\n return new Promise<void>((resolve) => {\n childProcess?.on('close', () => resolve());\n childProcess?.kill();\n reject(err ?? new CommandError('detached'));\n });\n }\n };\n\n const off = installExitHooks(() => detach());\n });\n}\n\nexport async function launchAppWithDeviceCtl(deviceId: string, bundleId: string) {\n await devicectlAsync([\n 'device',\n 'process',\n 'launch',\n '--terminate-existing',\n '--device',\n deviceId,\n bundleId,\n ]);\n}\n\n/** Find all error codes from the output log */\nfunction getDeviceCtlErrorCodes(log: string): string[] {\n return [...log.matchAll(/BSErrorCodeDescription\\s+=\\s+(.*)$/gim)].map(([_line, code]) => code);\n}\n\nlet hasEverBeenInstalled: boolean | undefined;\n\nexport function hasDevicectlEverBeenInstalled() {\n if (hasEverBeenInstalled) return hasEverBeenInstalled;\n // It doesn't appear possible for devicectl to ever be uninstalled. We can just check once and store this result forever\n // to prevent cold boots of devicectl from slowing down all invocations of `expo run ios`\n if (fs.existsSync(DEVICE_CTL_EXISTS_PATH)) {\n hasEverBeenInstalled = true;\n return true;\n }\n\n const isInstalled = isDevicectlInstalled();\n\n if (isInstalled) {\n fs.writeFileSync(DEVICE_CTL_EXISTS_PATH, '1');\n }\n hasEverBeenInstalled = isInstalled;\n return isInstalled;\n}\n\nfunction isDevicectlInstalled() {\n try {\n execSync('xcrun devicectl --version', { stdio: 'ignore' });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Wraps the apple device method for installing and running an app,\n * adds indicator and retry loop for when the device is locked.\n */\nexport async function installAndLaunchAppAsync(props: {\n bundle: string;\n bundleIdentifier: string;\n udid: string;\n deviceName: string;\n}): Promise<void> {\n debug('Running on device:', props);\n const { bundle, bundleIdentifier, udid, deviceName } = props;\n let indicator: Ora | undefined;\n\n try {\n if (!indicator) {\n indicator = ora(`Connecting to: ${props.deviceName}`).start();\n }\n\n await installAppWithDeviceCtlAsync(\n udid,\n bundle,\n ({\n status,\n isComplete,\n progress,\n }: {\n status: string;\n isComplete: boolean;\n progress: number;\n }) => {\n if (!indicator) {\n indicator = ora(status).start();\n }\n indicator.text = `${chalk.bold(status)} ${progress}%`;\n if (isComplete) {\n indicator.succeed();\n }\n }\n );\n } catch (error: any) {\n if (indicator) {\n indicator.fail();\n }\n throw error;\n }\n\n async function launchAppOptionally() {\n try {\n await launchAppWithDeviceCtl(udid, bundleIdentifier);\n } catch (error: any) {\n if (indicator) {\n indicator.fail();\n }\n if (error.code === 'APPLE_DEVICE_LOCKED') {\n // Get the app name from the binary path.\n const appName = path.basename(bundle).split('.')[0] ?? 'app';\n if (\n isInteractive() &&\n (await confirmAsync({\n message: `Cannot launch ${appName} because the device is locked. Unlock ${deviceName} to continue...`,\n initial: true,\n }))\n ) {\n return launchAppOptionally();\n }\n throw new CommandError(\n `Cannot launch ${appName} on ${deviceName} because the device is locked.`\n );\n }\n throw error;\n }\n }\n\n await launchAppOptionally();\n}\n"],"names":["devicectlAsync","getConnectedAppleDevicesAsync","hasDevicectlEverBeenInstalled","installAndLaunchAppAsync","launchAppWithDeviceCtl","launchBinaryOnMacAsync","DEVICE_CTL_EXISTS_PATH","path","join","getExpoHomeDirectory","debug","require","args","options","xcrunAsync","error","CommandError","errorCodes","getDeviceCtlErrorCodes","stderr","includes","tmpPath","createTempFilePath","devices","stdout","devicesJson","JsonFile","readAsync","info","jsonVersion","Log","warn","assertDevicesJson","result","results","assert","Array","isArray","JSON","stringify","bundleId","appBinaryPath","spawnAsync","code","message","installAppWithDeviceCtlAsync","uuid","bundleIdOrAppPath","onProgress","maxAttempts","attempt","installAppWithDeviceCtlInternalAsync","isTransientDisconnect","backoffDelay","Math","pow","Promise","resolve","setTimeout","reject","childProcess","spawn","currentProgress","hasStarted","updateProgress","progress","statusPrefix","isComplete","status","on","data","strings","toString","split","EOL","map","value","trim","forEach","str","match","parseInt","stderrBuffer","err","Error","detach","off","kill","installExitHooks","deviceId","log","matchAll","_line","hasEverBeenInstalled","fs","existsSync","isInstalled","isDevicectlInstalled","writeFileSync","execSync","stdio","props","bundle","bundleIdentifier","udid","deviceName","indicator","ora","start","text","chalk","bold","succeed","fail","launchAppOptionally","appName","basename","isInteractive","confirmAsync","initial"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../src/start/platforms/ios/devicectl.ts"],"sourcesContent":["/**\n * Copyright © 2024 650 Industries.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport JsonFile from '@expo/json-file';\nimport spawnAsync, { SpawnOptions, SpawnResult } from '@expo/spawn-async';\nimport chalk from 'chalk';\nimport { spawn, execSync } from 'child_process';\nimport fs from 'fs';\nimport assert from 'node:assert';\nimport { Ora } from 'ora';\nimport { EOL } from 'os';\nimport path from 'path';\n\nimport { xcrunAsync } from './xcrun';\nimport { getExpoHomeDirectory } from '../../../api/user/UserSettings';\nimport * as Log from '../../../log';\nimport { createTempFilePath } from '../../../utils/createTempPath';\nimport { CommandError } from '../../../utils/errors';\nimport { installExitHooks } from '../../../utils/exit';\nimport { isInteractive } from '../../../utils/interactive';\nimport { ora } from '../../../utils/ora';\nimport { confirmAsync } from '../../../utils/prompts';\n\nconst DEVICE_CTL_EXISTS_PATH = path.join(getExpoHomeDirectory(), 'devicectl-exists');\n\nconst debug = require('debug')('expo:devicectl') as typeof console.log;\n\ntype AnyEnum<T extends string = string> = T | (string & object);\n\ntype DeviceCtlDevice = {\n capabilities: DeviceCtlDeviceCapability[];\n connectionProperties: DeviceCtlConnectionProperties;\n deviceProperties: DeviceCtlDeviceProperties;\n hardwareProperties: DeviceCtlHardwareProperties;\n /** \"A1A1AAA1-0011-1AA1-11A1-10A1111AA11A\" */\n identifier: string;\n visibilityClass: AnyEnum<'default'>;\n};\n\ntype DeviceCtlHardwareProperties = {\n cpuType: DeviceCtlCpuType;\n deviceType: AnyEnum<'iPhone'>;\n /** 1114404411111111 */\n ecid: number;\n /** \"D74AP\" */\n hardwareModel: string;\n /** 512000000000 */\n internalStorageCapacity: number;\n /** true */\n isProductionFused: boolean;\n /** \"iPhone 14 Pro Max\" */\n marketingName: string;\n /** \"iOS\" */\n platform: AnyEnum<'iOS' | 'xrOS'>;\n /** \"iPhone15,3\" */\n productType: AnyEnum<'iPhone13,4' | 'iPhone15,3'>;\n reality: AnyEnum<'physical' | 'simulated'>;\n /** \"X2X1CC1XXX\" */\n serialNumber: string;\n supportedCPUTypes: DeviceCtlCpuType[];\n /** [1] */\n supportedDeviceFamilies: number[];\n thinningProductType: AnyEnum<'iPhone15,3'>;\n /** \"00001110-001111110110101A\" */\n udid: string;\n};\n\ntype DeviceCtlDeviceProperties = {\n /** true */\n bootedFromSnapshot: boolean;\n /** \"com.apple.os.update-AD0CF111ACFF11A11111A76A3D1262AE42A3F56F305AF5AE1135393A7A14A7D1\" */\n bootedSnapshotName: string;\n /** false */\n ddiServicesAvailable: boolean;\n\n developerModeStatus: AnyEnum<'enabled'>;\n /** false */\n hasInternalOSBuild: boolean;\n /** \"Evan's phone\" */\n name: string;\n /** \"21E236\" */\n osBuildUpdate: string;\n /** \"17.4.1\" */\n osVersionNumber: string;\n /** false */\n rootFileSystemIsWritable: boolean;\n};\n\ntype DeviceCtlDeviceCapability =\n | {\n name: AnyEnum;\n featureIdentifier: AnyEnum;\n }\n | {\n featureIdentifier: 'com.apple.coredevice.feature.connectdevice';\n name: 'Connect to Device';\n }\n | {\n featureIdentifier: 'com.apple.coredevice.feature.unpairdevice';\n name: 'Unpair Device';\n }\n | {\n featureIdentifier: 'com.apple.coredevice.feature.acquireusageassertion';\n name: 'Acquire Usage Assertion';\n };\n\ntype DeviceCtlConnectionProperties = {\n authenticationType: AnyEnum<'manualPairing'>;\n isMobileDeviceOnly: boolean;\n /** \"2024-04-20T22:50:04.244Z\" */\n lastConnectionDate: string;\n pairingState: AnyEnum<'paired'>;\n /** [\"00001111-001111110110101A.coredevice.local\", \"A1A1AAA1-0011-1AA1-11A1-10A1111AA11A.coredevice.local\"] */\n potentialHostnames: string[];\n transportType: AnyEnum<'localNetwork' | 'wired'>;\n tunnelState: AnyEnum<'disconnected' | 'unavailable'>;\n tunnelTransportProtocol: AnyEnum<'tcp'>;\n};\n\ntype DeviceCtlCpuType = {\n name: AnyEnum<'arm64e' | 'arm64' | 'arm64_32'>;\n subType: number;\n /** 16777228 */\n type: number;\n};\n\n/** Run a `devicectl` command. */\nexport async function devicectlAsync(\n args: (string | undefined)[],\n options?: SpawnOptions\n): Promise<SpawnResult> {\n try {\n return await xcrunAsync(['devicectl', ...args], options);\n } catch (error: any) {\n if (error instanceof CommandError) {\n throw error;\n }\n if ('stderr' in error) {\n const errorCodes = getDeviceCtlErrorCodes(error.stderr);\n if (errorCodes.includes('Locked')) {\n throw new CommandError('APPLE_DEVICE_LOCKED', 'Device is locked, unlock and try again.');\n }\n }\n throw error;\n }\n}\n\nexport async function getConnectedAppleDevicesAsync() {\n if (!hasDevicectlEverBeenInstalled()) {\n debug('devicectl not found, skipping remote Apple devices.');\n return [];\n }\n\n const tmpPath = createTempFilePath();\n const devices = await devicectlAsync([\n 'list',\n 'devices',\n '--json-output',\n tmpPath,\n // Give two seconds before timing out: between 5 and 9223372036854775807\n '--timeout',\n '5',\n ]);\n debug(devices.stdout);\n const devicesJson = await JsonFile.readAsync(tmpPath);\n\n if (![2, 3, 5].includes((devicesJson as any)?.info?.jsonVersion)) {\n Log.warn(\n 'Unexpected devicectl JSON version output from devicectl. Connecting to physical Apple devices may not work as expected.'\n );\n }\n\n assertDevicesJson(devicesJson);\n\n return (devicesJson.result.devices as DeviceCtlDevice[]).filter(\n (device) => device?.hardwareProperties?.reality !== 'simulated'\n );\n}\n\nfunction assertDevicesJson(\n results: any\n): asserts results is { result: { devices: DeviceCtlDevice[] } } {\n assert(\n results != null && 'result' in results && Array.isArray(results?.result?.devices),\n 'Malformed JSON output from devicectl: ' + JSON.stringify(results, null, 2)\n );\n}\n\nexport async function launchBinaryOnMacAsync(\n bundleId: string,\n appBinaryPath: string\n): Promise<void> {\n const args = ['-b', bundleId, appBinaryPath];\n try {\n await spawnAsync('open', args);\n } catch (error: any) {\n if ('code' in error) {\n if (error.code === 1) {\n throw new CommandError(\n 'MACOS_LAUNCH',\n 'Failed to launch the compatible binary on macOS: open ' +\n args.join(' ') +\n '\\n\\n' +\n error.message\n );\n }\n }\n throw error;\n }\n}\n\nasync function installAppWithDeviceCtlAsync(\n uuid: string,\n bundleIdOrAppPath: string,\n onProgress: (event: { status: string; isComplete: boolean; progress: number }) => void\n): Promise<void> {\n const maxAttempts = 3;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n await installAppWithDeviceCtlInternalAsync(uuid, bundleIdOrAppPath, onProgress, attempt);\n return;\n } catch (error: any) {\n if (error.code === 'detached') {\n throw error;\n }\n const isTransientDisconnect =\n error.message &&\n (error.message.includes('CoreDeviceError') || error.message.includes('0xFA0'));\n\n if (!isTransientDisconnect || attempt === maxAttempts) {\n throw error;\n }\n const backoffDelay = 500 + Math.pow(2, attempt - 1) * 500;\n await new Promise((resolve) => setTimeout(resolve, backoffDelay));\n }\n }\n}\n\nasync function installAppWithDeviceCtlInternalAsync(\n uuid: string,\n bundleIdOrAppPath: string,\n onProgress: (event: { status: string; isComplete: boolean; progress: number }) => void,\n attempt: number\n): Promise<void> {\n // 𝝠 xcrun devicectl device install app --device 00001110-001111110110101A /Users/evanbacon/Library/Developer/Xcode/DerivedData/Router-hgbqaxzhrhkiftfweydvhgttadvn/Build/Products/Debug-iphoneos/Router.app --verbose\n return new Promise((resolve, reject) => {\n const args: string[] = [\n 'devicectl',\n 'device',\n 'install',\n 'app',\n '--device',\n uuid,\n bundleIdOrAppPath,\n ];\n const childProcess = spawn('xcrun', args);\n debug('xcrun ' + args.join(' '));\n\n let currentProgress = 0;\n let hasStarted = false;\n\n function updateProgress(progress: number) {\n hasStarted = true;\n if (progress <= currentProgress) {\n return;\n }\n currentProgress = progress;\n const statusPrefix = attempt > 1 ? `Installing (attempt ${attempt})` : 'Installing';\n onProgress({\n progress,\n isComplete: progress === 100,\n status: statusPrefix,\n });\n }\n\n childProcess.stdout.on('data', (data: Buffer) => {\n // Sometimes more than one chunk comes at a time, here we split by system newline,\n // then trim and filter.\n const strings = data\n .toString()\n .split(EOL)\n .map((value) => value.trim());\n\n strings.forEach((str) => {\n // Match the progress percentage:\n // - '34%... 35%...' -> 34\n // - '31%...' -> 31\n // - 'Complete!' -> 100\n\n const match = str.match(/(\\d+)%\\.\\.\\./);\n if (match) {\n updateProgress(parseInt(match[1], 10));\n } else if (hasStarted) {\n updateProgress(100);\n }\n });\n\n debug('[stdout]:', strings);\n });\n\n let stderrBuffer = '';\n childProcess.stderr.on('data', (data: Buffer) => {\n stderrBuffer += data.toString();\n });\n\n childProcess.on('close', (code) => {\n debug('[close]: ' + code);\n if (code === 0) {\n resolve();\n } else {\n const err = new Error(stderrBuffer || `Command failed with exit code ${code}`);\n (err as any).code = code;\n detach(err);\n }\n });\n\n const detach = async (err?: Error) => {\n off?.();\n if (childProcess) {\n return new Promise<void>((resolve) => {\n childProcess?.on('close', () => resolve());\n childProcess?.kill();\n reject(err ?? new CommandError('detached'));\n });\n }\n };\n\n const off = installExitHooks(() => detach());\n });\n}\n\nexport async function launchAppWithDeviceCtl(deviceId: string, bundleId: string) {\n await devicectlAsync([\n 'device',\n 'process',\n 'launch',\n '--terminate-existing',\n '--device',\n deviceId,\n bundleId,\n ]);\n}\n\n/** Find all error codes from the output log */\nfunction getDeviceCtlErrorCodes(log: string): string[] {\n return [...log.matchAll(/BSErrorCodeDescription\\s+=\\s+(.*)$/gim)].map(([_line, code]) => code);\n}\n\nlet hasEverBeenInstalled: boolean | undefined;\n\nexport function hasDevicectlEverBeenInstalled() {\n if (hasEverBeenInstalled) return hasEverBeenInstalled;\n // It doesn't appear possible for devicectl to ever be uninstalled. We can just check once and store this result forever\n // to prevent cold boots of devicectl from slowing down all invocations of `expo run ios`\n if (fs.existsSync(DEVICE_CTL_EXISTS_PATH)) {\n hasEverBeenInstalled = true;\n return true;\n }\n\n const isInstalled = isDevicectlInstalled();\n\n if (isInstalled) {\n fs.writeFileSync(DEVICE_CTL_EXISTS_PATH, '1');\n }\n hasEverBeenInstalled = isInstalled;\n return isInstalled;\n}\n\nfunction isDevicectlInstalled() {\n try {\n execSync('xcrun devicectl --version', { stdio: 'ignore' });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Wraps the apple device method for installing and running an app,\n * adds indicator and retry loop for when the device is locked.\n */\nexport async function installAndLaunchAppAsync(props: {\n bundle: string;\n bundleIdentifier: string;\n udid: string;\n deviceName: string;\n}): Promise<void> {\n debug('Running on device:', props);\n const { bundle, bundleIdentifier, udid, deviceName } = props;\n let indicator: Ora | undefined;\n\n try {\n if (!indicator) {\n indicator = ora(`Connecting to: ${props.deviceName}`).start();\n }\n\n await installAppWithDeviceCtlAsync(\n udid,\n bundle,\n ({\n status,\n isComplete,\n progress,\n }: {\n status: string;\n isComplete: boolean;\n progress: number;\n }) => {\n if (!indicator) {\n indicator = ora(status).start();\n }\n indicator.text = `${chalk.bold(status)} ${progress}%`;\n if (isComplete) {\n indicator.succeed();\n }\n }\n );\n } catch (error: any) {\n if (indicator) {\n indicator.fail();\n }\n throw error;\n }\n\n async function launchAppOptionally() {\n try {\n await launchAppWithDeviceCtl(udid, bundleIdentifier);\n } catch (error: any) {\n if (indicator) {\n indicator.fail();\n }\n if (error.code === 'APPLE_DEVICE_LOCKED') {\n // Get the app name from the binary path.\n const appName = path.basename(bundle).split('.')[0] ?? 'app';\n if (\n isInteractive() &&\n (await confirmAsync({\n message: `Cannot launch ${appName} because the device is locked. Unlock ${deviceName} to continue...`,\n initial: true,\n }))\n ) {\n return launchAppOptionally();\n }\n throw new CommandError(\n `Cannot launch ${appName} on ${deviceName} because the device is locked.`\n );\n }\n throw error;\n }\n }\n\n await launchAppOptionally();\n}\n"],"names":["devicectlAsync","getConnectedAppleDevicesAsync","hasDevicectlEverBeenInstalled","installAndLaunchAppAsync","launchAppWithDeviceCtl","launchBinaryOnMacAsync","DEVICE_CTL_EXISTS_PATH","path","join","getExpoHomeDirectory","debug","require","args","options","xcrunAsync","error","CommandError","errorCodes","getDeviceCtlErrorCodes","stderr","includes","tmpPath","createTempFilePath","devices","stdout","devicesJson","JsonFile","readAsync","info","jsonVersion","Log","warn","assertDevicesJson","result","filter","device","hardwareProperties","reality","results","assert","Array","isArray","JSON","stringify","bundleId","appBinaryPath","spawnAsync","code","message","installAppWithDeviceCtlAsync","uuid","bundleIdOrAppPath","onProgress","maxAttempts","attempt","installAppWithDeviceCtlInternalAsync","isTransientDisconnect","backoffDelay","Math","pow","Promise","resolve","setTimeout","reject","childProcess","spawn","currentProgress","hasStarted","updateProgress","progress","statusPrefix","isComplete","status","on","data","strings","toString","split","EOL","map","value","trim","forEach","str","match","parseInt","stderrBuffer","err","Error","detach","off","kill","installExitHooks","deviceId","log","matchAll","_line","hasEverBeenInstalled","fs","existsSync","isInstalled","isDevicectlInstalled","writeFileSync","execSync","stdio","props","bundle","bundleIdentifier","udid","deviceName","indicator","ora","start","text","chalk","bold","succeed","fail","launchAppOptionally","appName","basename","isInteractive","confirmAsync","initial"],"mappings":"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"}
@@ -199,7 +199,8 @@ class MetroBundlerDevServer extends _BundlerDevServer.BundlerDevServer {
199
199
  // https://github.com/expo/expo/blob/0dffdb15/packages/%40expo/metro-config/src/serializer/serializeChunks.ts#L422-L439
200
200
  // Alternatively, check whether `sourcesRoot` helps here
201
201
  const artifactBasename = encodeURIComponent(_path().default.basename(artifactFilename) + '.map');
202
- src = src.replace(/\/\/# sourceMappingURL=.*/g, `//# sourceMappingURL=${artifactBasename}`);
202
+ // Match only the trailing sourcemap directive
203
+ src = src.replace(/(?<=^|\n)\/\/# sourceMappingURL=[^\n]*(?=\s*$)/, `//# sourceMappingURL=${artifactBasename}`);
203
204
  const parsedMap = typeof contents.map === 'string' ? JSON.parse(contents.map) : contents.map;
204
205
  const mapData = {
205
206
  ...descriptor,