@expo/cli 55.0.31 → 55.0.32
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/build/bin/cli +1 -1
- package/build/src/api/rest/client.js +27 -12
- package/build/src/api/rest/client.js.map +1 -1
- package/build/src/api/user/UserSettings.js +4 -2
- package/build/src/api/user/UserSettings.js.map +1 -1
- package/build/src/events/index.js +1 -1
- package/build/src/export/embed/exportEmbedAsync.js +2 -2
- package/build/src/export/embed/exportEmbedAsync.js.map +1 -1
- package/build/src/prebuild/resolveTemplate.js +10 -5
- package/build/src/prebuild/resolveTemplate.js.map +1 -1
- package/build/src/start/platforms/android/adb.js +16 -15
- package/build/src/start/platforms/android/adb.js.map +1 -1
- package/build/src/start/server/getStaticRenderFunctions.js +2 -1
- package/build/src/start/server/getStaticRenderFunctions.js.map +1 -1
- package/build/src/start/server/metro/debugging/createDebugMiddleware.js +6 -5
- package/build/src/start/server/metro/debugging/createDebugMiddleware.js.map +1 -1
- package/build/src/start/server/metro/debugging/messageHandlers/NetworkResponse.js +17 -1
- package/build/src/start/server/metro/debugging/messageHandlers/NetworkResponse.js.map +1 -1
- package/build/src/start/server/metro/dev-server/createMetroMiddleware.js +7 -1
- package/build/src/start/server/metro/dev-server/createMetroMiddleware.js.map +1 -1
- package/build/src/start/server/metro/instantiateMetro.js +3 -1
- package/build/src/start/server/metro/instantiateMetro.js.map +1 -1
- package/build/src/start/server/metro/metroErrorInterface.js +5 -2
- package/build/src/start/server/metro/metroErrorInterface.js.map +1 -1
- package/build/src/start/server/middleware/InterstitialPageMiddleware.js +7 -4
- package/build/src/start/server/middleware/InterstitialPageMiddleware.js.map +1 -1
- package/build/src/start/server/middleware/inspector/createJsInspectorMiddleware.js +14 -24
- package/build/src/start/server/middleware/inspector/createJsInspectorMiddleware.js.map +1 -1
- package/build/src/utils/codesigning.js +6 -0
- package/build/src/utils/codesigning.js.map +1 -1
- package/build/src/utils/net.js +13 -0
- package/build/src/utils/net.js.map +1 -1
- package/build/src/utils/telemetry/clients/FetchClient.js +1 -1
- package/build/src/utils/telemetry/utils/context.js +1 -1
- package/build/src/utils/url.js +0 -12
- package/build/src/utils/url.js.map +1 -1
- package/package.json +9 -9
- package/static/loading-page/index.html +10 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/start/platforms/android/adb.ts"],"sourcesContent":["import chalk from 'chalk';\nimport os from 'os';\n\nimport { ADBServer } from './ADBServer';\nimport * as Log from '../../../log';\nimport { env } from '../../../utils/env';\nimport { CommandError } from '../../../utils/errors';\nimport { learnMore } from '../../../utils/link';\n\nconst debug = require('debug')('expo:start:platforms:android:adb') as typeof console.log;\n\nexport enum DeviceABI {\n // The arch specific android target platforms are soft-deprecated.\n // Instead of using TargetPlatform as a combination arch + platform\n // the code will be updated to carry arch information in [DarwinArch]\n // and [AndroidArch].\n arm = 'arm',\n arm64 = 'arm64',\n x64 = 'x64',\n x86 = 'x86',\n x8664 = 'x86_64',\n arm64v8a = 'arm64-v8a',\n armeabiV7a = 'armeabi-v7a',\n armeabi = 'armeabi',\n universal = 'universal',\n}\n\n/** Represents a connected Android device. */\nexport type Device = {\n /** Process ID. */\n pid?: string;\n /** Name of the device, also used as the ID for opening devices. */\n name: string;\n /** Is emulator or connected device. */\n type: 'emulator' | 'device';\n /** Is the device booted (emulator). */\n isBooted: boolean;\n /** Is device authorized for developing. https://expo.fyi/authorize-android-device */\n isAuthorized: boolean;\n /** The connection type to ADB, only available when `type: device` */\n connectionType?: 'USB' | 'Network';\n};\n\ntype DeviceContext = Pick<Device, 'pid'>;\n\ntype DeviceProperties = Record<string, string>;\n\nconst CANT_START_ACTIVITY_ERROR = 'Activity not started, unable to resolve Intent';\n// http://developer.android.com/ndk/guides/abis.html\nconst PROP_CPU_NAME = 'ro.product.cpu.abi';\n\nconst PROP_CPU_ABI_LIST_NAME = 'ro.product.cpu.abilist';\n\n// Can sometimes be null\n// http://developer.android.com/ndk/guides/abis.html\nconst PROP_BOOT_ANIMATION_STATE = 'init.svc.bootanim';\n\nlet _server: ADBServer | null;\n\n/** Return the lazily loaded ADB server instance. */\nexport function getServer() {\n _server ??= new ADBServer();\n return _server;\n}\n\n/** Logs an FYI message about authorizing your device. */\nexport function logUnauthorized(device: Device) {\n Log.warn(\n `\\nThis computer is not authorized for developing on ${chalk.bold(device.name)}. ${chalk.dim(\n learnMore('https://expo.fyi/authorize-android-device')\n )}`\n );\n}\n\n/** Returns true if the provided package name is installed on the provided Android device. */\nexport async function isPackageInstalledAsync(\n device: DeviceContext,\n androidPackage: string\n): Promise<boolean> {\n const packages = await getServer().runAsync(\n adbArgs(\n device.pid,\n 'shell',\n 'pm',\n 'list',\n 'packages',\n '--user',\n env.EXPO_ADB_USER,\n androidPackage\n )\n );\n\n const lines = packages.split(/\\r?\\n/);\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n if (line === `package:${androidPackage}`) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * @param device.pid Process ID of the Android device to launch.\n * @param props.launchActivity Activity to launch `[application identifier]/.[main activity name]`, ex: `com.bacon.app/.MainActivity`\n * @param props.url Optional (dev client) URL to launch\n */\nexport async function launchActivityAsync(\n device: DeviceContext,\n {\n launchActivity,\n url,\n }: {\n launchActivity: string;\n url?: string;\n }\n) {\n const args: string[] = [\n 'shell',\n 'am',\n 'start',\n // FLAG_ACTIVITY_SINGLE_TOP -- If set, the activity will not be launched if it is already running at the top of the history stack.\n '-f',\n '0x20000000',\n // Activity to open first: com.bacon.app/.MainActivity\n '-n',\n launchActivity,\n ];\n\n if (url) {\n args.push('-d', url);\n }\n\n return openAsync(adbArgs(device.pid, ...args));\n}\n\n/**\n * @param device.pid Process ID of the Android device to launch.\n * @param props.url URL to launch.\n */\nexport async function openUrlAsync(\n device: DeviceContext,\n {\n url,\n }: {\n url: string;\n }\n) {\n return openAsync(\n adbArgs(\n device.pid,\n 'shell',\n 'am',\n 'start',\n '-a',\n 'android.intent.action.VIEW',\n '-d',\n // ADB requires ampersands to be escaped.\n url.replace(/&/g, String.raw`\\&`)\n )\n );\n}\n\n/** Runs a generic command watches for common errors in order to throw with an expected code. */\nasync function openAsync(args: string[]): Promise<string> {\n const results = await getServer().runAsync(args);\n if (\n results.includes(CANT_START_ACTIVITY_ERROR) ||\n results.match(/Error: Activity class .* does not exist\\./g)\n ) {\n throw new CommandError('APP_NOT_INSTALLED', results.substring(results.indexOf('Error: ')));\n }\n return results;\n}\n\n/** Uninstall an app given its Android package name. */\nexport async function uninstallAsync(\n device: DeviceContext,\n { appId }: { appId: string }\n): Promise<string> {\n return await getServer().runAsync(\n adbArgs(device.pid, 'uninstall', '--user', env.EXPO_ADB_USER, appId)\n );\n}\n\n/** Get package info from an app based on its Android package name. */\nexport async function getPackageInfoAsync(\n device: DeviceContext,\n { appId }: { appId: string }\n): Promise<string> {\n return await getServer().runAsync(adbArgs(device.pid, 'shell', 'dumpsys', 'package', appId));\n}\n\n/** Install an app on a connected device. */\nexport async function installAsync(device: DeviceContext, { filePath }: { filePath: string }) {\n // TODO: Handle the `INSTALL_FAILED_INSUFFICIENT_STORAGE` error.\n return await getServer().runAsync(\n adbArgs(device.pid, 'install', '-r', '-d', '--user', env.EXPO_ADB_USER, filePath)\n );\n}\n\n/** Format ADB args with process ID. */\nexport function adbArgs(pid: Device['pid'], ...options: string[]): string[] {\n const args = [];\n if (pid) {\n args.push('-s', pid);\n }\n\n return args.concat(options);\n}\n\n// TODO: This is very expensive for some operations.\nexport async function getAttachedDevicesAsync(): Promise<Device[]> {\n const output = await getServer().runAsync(['devices', '-l']);\n\n const splitItems = output\n .trim()\n .replace(/\\n$/, '')\n .split(os.EOL)\n // Filter ADB trace logs from the output, e.g.\n // adb D 03-06 15:25:53 63677 4018815 adb_client.cpp:393] adb_query: host:devices-l\n // 03-04 12:29:44.557 16415 16415 D adb : commandline.cpp:1646 Using server socket: tcp:172.27.192.1:5037\n // 03-04 12:29:44.557 16415 16415 D adb : adb_client.cpp:160 _adb_connect: host:version\n .filter((line) => !line.match(/\\.cpp:[0-9]+/));\n\n // First line is `\"List of devices attached\"`, remove it\n // @ts-ignore: todo\n const attachedDevices: {\n props: string[];\n type: Device['type'];\n isAuthorized: Device['isAuthorized'];\n isBooted: Device['isBooted'];\n connectionType?: Device['connectionType'];\n }[] = splitItems\n .slice(1, splitItems.length)\n .map((line) => {\n // unauthorized: ['FA8251A00719', 'unauthorized', 'usb:338690048X', 'transport_id:5']\n // authorized: ['FA8251A00719', 'device', 'usb:336592896X', 'product:walleye', 'model:Pixel_2', 'device:walleye', 'transport_id:4']\n // emulator: ['emulator-5554', 'offline', 'transport_id:1']\n const props = line.split(' ').filter(Boolean);\n const type = line.includes('emulator') ? 'emulator' : 'device';\n\n let connectionType;\n if (type === 'device' && line.includes('usb:')) {\n connectionType = 'USB';\n } else if (type === 'device' && line.includes('_adb-tls-connect.')) {\n connectionType = 'Network';\n }\n\n const isBooted = type === 'emulator' || props[1] !== 'offline';\n const isAuthorized =\n connectionType === 'Network'\n ? line.includes('model:') // Network connected devices show `model:<name>` when authorized\n : props[1] !== 'unauthorized';\n\n return { props, type, isAuthorized, isBooted, connectionType };\n })\n .filter(({ props: [pid] }) => !!pid);\n\n const devicePromises = attachedDevices.map<Promise<Device>>(async (props) => {\n const {\n type,\n props: [pid, ...deviceInfo],\n isAuthorized,\n isBooted,\n } = props;\n\n let name: string | null = null;\n\n if (type === 'device') {\n if (isAuthorized) {\n // Possibly formatted like `model:Pixel_2`\n // Transform to `Pixel_2`\n const modelItem = deviceInfo.find((info) => info.includes('model:'));\n if (modelItem) {\n name = modelItem.replace('model:', '');\n }\n }\n // unauthorized devices don't have a name available to read\n if (!name) {\n // Device FA8251A00719\n name = `Device ${pid}`;\n }\n } else {\n // Given an emulator pid, get the emulator name which can be used to start the emulator later.\n name = (await getAdbNameForDeviceIdAsync({ pid })) ?? '';\n }\n\n return props.connectionType\n ? { pid, name, type, isAuthorized, isBooted, connectionType: props.connectionType }\n : { pid, name, type, isAuthorized, isBooted };\n });\n\n return Promise.all(devicePromises);\n}\n\n/**\n * Return the Emulator name for an emulator ID, this can be used to determine if an emulator is booted.\n *\n * @param device.pid a value like `emulator-5554` from `abd devices`\n */\nexport async function getAdbNameForDeviceIdAsync(device: DeviceContext): Promise<string | null> {\n const results = await getServer().runAsync(adbArgs(device.pid, 'emu', 'avd', 'name'));\n\n if (results.match(/could not connect to TCP port .*: Connection refused/)) {\n // Can also occur when the emulator does not exist.\n throw new CommandError('EMULATOR_NOT_FOUND', results);\n }\n\n return sanitizeAdbDeviceName(results) ?? null;\n}\n\nexport async function isDeviceBootedAsync({\n name,\n}: { name?: string } = {}): Promise<Device | null> {\n const devices = await getAttachedDevicesAsync();\n\n if (!name) {\n return devices[0] ?? null;\n }\n\n return devices.find((device) => device.name === name) ?? null;\n}\n\n/**\n * Returns true when a device's splash screen animation has stopped.\n * This can be used to detect when a device is fully booted and ready to use.\n *\n * @param pid\n */\nexport async function isBootAnimationCompleteAsync(pid?: string): Promise<boolean> {\n try {\n const props = await getPropertyDataForDeviceAsync({ pid }, PROP_BOOT_ANIMATION_STATE);\n return !!props[PROP_BOOT_ANIMATION_STATE].match(/stopped/);\n } catch {\n return false;\n }\n}\n\n/** Get a list of ABIs for the provided device. */\nexport async function getDeviceABIsAsync(\n device: Pick<Device, 'name' | 'pid'>\n): Promise<DeviceABI[]> {\n const cpuAbiList = (await getPropertyDataForDeviceAsync(device, PROP_CPU_ABI_LIST_NAME))[\n PROP_CPU_ABI_LIST_NAME\n ];\n\n if (cpuAbiList) {\n return cpuAbiList.trim().split(',') as DeviceABI[];\n }\n\n const abi = (await getPropertyDataForDeviceAsync(device, PROP_CPU_NAME))[\n PROP_CPU_NAME\n ] as DeviceABI;\n return [abi];\n}\n\nexport async function getPropertyDataForDeviceAsync(\n device: DeviceContext,\n prop?: string\n): Promise<DeviceProperties> {\n // @ts-ignore\n const propCommand = adbArgs(...[device.pid, 'shell', 'getprop', prop].filter(Boolean));\n try {\n // Prevent reading as UTF8.\n const results = await getServer().getFileOutputAsync(propCommand);\n // Like:\n // [wifi.direct.interface]: [p2p-dev-wlan0]\n // [wifi.interface]: [wlan0]\n\n if (prop) {\n debug(`Property data: (device pid: ${device.pid}, prop: ${prop}, data: ${results})`);\n return {\n [prop]: results,\n };\n }\n const props = parseAdbDeviceProperties(results);\n\n debug(`Parsed data:`, props);\n\n return props;\n } catch (error: any) {\n // TODO: Ensure error has message and not stderr\n throw new CommandError(`Failed to get properties for device (${device.pid}): ${error.message}`);\n }\n}\n\nfunction parseAdbDeviceProperties(devicePropertiesString: string) {\n const properties: DeviceProperties = {};\n const propertyExp = /\\[(.*?)\\]: \\[(.*?)\\]/gm;\n for (const match of devicePropertiesString.matchAll(propertyExp)) {\n properties[match[1]] = match[2];\n }\n return properties;\n}\n\n/**\n * Sanitize the ADB device name to only get the actual device name.\n * On Windows, we need to do \\r, \\n, and \\r\\n filtering to get the name.\n */\nexport function sanitizeAdbDeviceName(deviceName: string) {\n return deviceName\n .trim()\n .split(/[\\r\\n]+/)\n .shift();\n}\n"],"names":["DeviceABI","adbArgs","getAdbNameForDeviceIdAsync","getAttachedDevicesAsync","getDeviceABIsAsync","getPackageInfoAsync","getPropertyDataForDeviceAsync","getServer","installAsync","isBootAnimationCompleteAsync","isDeviceBootedAsync","isPackageInstalledAsync","launchActivityAsync","logUnauthorized","openUrlAsync","sanitizeAdbDeviceName","uninstallAsync","debug","require","CANT_START_ACTIVITY_ERROR","PROP_CPU_NAME","PROP_CPU_ABI_LIST_NAME","PROP_BOOT_ANIMATION_STATE","_server","ADBServer","device","Log","warn","chalk","bold","name","dim","learnMore","androidPackage","packages","runAsync","pid","env","EXPO_ADB_USER","lines","split","i","length","line","trim","launchActivity","url","args","push","openAsync","replace","String","raw","results","includes","match","CommandError","substring","indexOf","appId","filePath","options","concat","output","splitItems","os","EOL","filter","attachedDevices","slice","map","props","Boolean","type","connectionType","isBooted","isAuthorized","devicePromises","deviceInfo","modelItem","find","info","Promise","all","devices","cpuAbiList","abi","prop","propCommand","getFileOutputAsync","parseAdbDeviceProperties","error","message","devicePropertiesString","properties","propertyExp","matchAll","deviceName","shift"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../src/start/platforms/android/adb.ts"],"sourcesContent":["import chalk from 'chalk';\nimport os from 'os';\n\nimport { ADBServer } from './ADBServer';\nimport * as Log from '../../../log';\nimport { env } from '../../../utils/env';\nimport { CommandError } from '../../../utils/errors';\nimport { learnMore } from '../../../utils/link';\n\nconst debug = require('debug')('expo:start:platforms:android:adb') as typeof console.log;\n\nexport enum DeviceABI {\n // The arch specific android target platforms are soft-deprecated.\n // Instead of using TargetPlatform as a combination arch + platform\n // the code will be updated to carry arch information in [DarwinArch]\n // and [AndroidArch].\n arm = 'arm',\n arm64 = 'arm64',\n x64 = 'x64',\n x86 = 'x86',\n x8664 = 'x86_64',\n arm64v8a = 'arm64-v8a',\n armeabiV7a = 'armeabi-v7a',\n armeabi = 'armeabi',\n universal = 'universal',\n}\n\n/** Represents a connected Android device. */\nexport type Device = {\n /** Process ID. */\n pid?: string;\n /** Name of the device, also used as the ID for opening devices. */\n name: string;\n /** Is emulator or connected device. */\n type: 'emulator' | 'device';\n /** Is the device booted (emulator). */\n isBooted: boolean;\n /** Is device authorized for developing. https://expo.fyi/authorize-android-device */\n isAuthorized: boolean;\n /** The connection type to ADB, only available when `type: device` */\n connectionType?: 'USB' | 'Network';\n};\n\ntype DeviceContext = Pick<Device, 'pid'>;\n\ntype DeviceProperties = Record<string, string>;\n\nconst CANT_START_ACTIVITY_ERROR = 'Activity not started, unable to resolve Intent';\n// http://developer.android.com/ndk/guides/abis.html\nconst PROP_CPU_NAME = 'ro.product.cpu.abi';\n\nconst PROP_CPU_ABI_LIST_NAME = 'ro.product.cpu.abilist';\n\n// Can sometimes be null\n// http://developer.android.com/ndk/guides/abis.html\nconst PROP_BOOT_ANIMATION_STATE = 'init.svc.bootanim';\n\nlet _server: ADBServer | null;\n\n/** Return the lazily loaded ADB server instance. */\nexport function getServer() {\n _server ??= new ADBServer();\n return _server;\n}\n\n/** Logs an FYI message about authorizing your device. */\nexport function logUnauthorized(device: Device) {\n Log.warn(\n `\\nThis computer is not authorized for developing on ${chalk.bold(device.name)}. ${chalk.dim(\n learnMore('https://expo.fyi/authorize-android-device')\n )}`\n );\n}\n\n/** Returns true if the provided package name is installed on the provided Android device. */\nexport async function isPackageInstalledAsync(\n device: DeviceContext,\n androidPackage: string\n): Promise<boolean> {\n const packages = await getServer().runAsync(\n adbShellArgs(device.pid, 'pm', 'list', 'packages', '--user', env.EXPO_ADB_USER, androidPackage)\n );\n\n const lines = packages.split(/\\r?\\n/);\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n if (line === `package:${androidPackage}`) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * @param device.pid Process ID of the Android device to launch.\n * @param props.launchActivity Activity to launch `[application identifier]/.[main activity name]`, ex: `com.bacon.app/.MainActivity`\n * @param props.url Optional (dev client) URL to launch\n */\nexport async function launchActivityAsync(\n device: DeviceContext,\n {\n launchActivity,\n url,\n }: {\n launchActivity: string;\n url?: string;\n }\n) {\n const command: string[] = [\n 'am',\n 'start',\n // FLAG_ACTIVITY_SINGLE_TOP -- If set, the activity will not be launched if it is already running at the top of the history stack.\n '-f',\n '0x20000000',\n // Activity to open first: com.bacon.app/.MainActivity\n '-n',\n launchActivity,\n ];\n\n if (url) {\n command.push('-d', url);\n }\n\n return openAsync(adbShellArgs(device.pid, ...command));\n}\n\n/**\n * @param device.pid Process ID of the Android device to launch.\n * @param props.url URL to launch.\n */\nexport async function openUrlAsync(\n device: DeviceContext,\n {\n url,\n }: {\n url: string;\n }\n) {\n return openAsync(\n adbShellArgs(device.pid, 'am', 'start', '-a', 'android.intent.action.VIEW', '-d', url)\n );\n}\n\n/** Runs a generic command watches for common errors in order to throw with an expected code. */\nasync function openAsync(args: string[]): Promise<string> {\n const results = await getServer().runAsync(args);\n if (\n results.includes(CANT_START_ACTIVITY_ERROR) ||\n results.match(/Error: Activity class .* does not exist\\./g)\n ) {\n throw new CommandError('APP_NOT_INSTALLED', results.substring(results.indexOf('Error: ')));\n }\n return results;\n}\n\n/** Uninstall an app given its Android package name. */\nexport async function uninstallAsync(\n device: DeviceContext,\n { appId }: { appId: string }\n): Promise<string> {\n return await getServer().runAsync(\n adbArgs(device.pid, 'uninstall', '--user', env.EXPO_ADB_USER, appId)\n );\n}\n\n/** Get package info from an app based on its Android package name. */\nexport async function getPackageInfoAsync(\n device: DeviceContext,\n { appId }: { appId: string }\n): Promise<string> {\n return await getServer().runAsync(adbShellArgs(device.pid, 'dumpsys', 'package', appId));\n}\n\n/** Install an app on a connected device. */\nexport async function installAsync(device: DeviceContext, { filePath }: { filePath: string }) {\n // TODO: Handle the `INSTALL_FAILED_INSUFFICIENT_STORAGE` error.\n return await getServer().runAsync(\n adbArgs(device.pid, 'install', '-r', '-d', '--user', env.EXPO_ADB_USER, filePath)\n );\n}\n\n/** Format ADB args with process ID. */\nexport function adbArgs(pid: Device['pid'], ...options: string[]): string[] {\n const args = [];\n if (pid) {\n args.push('-s', pid);\n }\n\n return args.concat(options);\n}\n\n/**\n * `adb shell` concatenates trailing args and runs the result through `sh -c` on\n * the device, so unquoted metacharacters in tainted tokens execute on-device.\n */\nexport function adbShellArgs(pid: Device['pid'], ...command: string[]): string[] {\n return adbArgs(pid, 'shell', ...command.map(shellQuote));\n}\n\nfunction shellQuote(value: string): string {\n return `'${value.replace(/'/g, `'\\\\''`)}'`;\n}\n\n// TODO: This is very expensive for some operations.\nexport async function getAttachedDevicesAsync(): Promise<Device[]> {\n const output = await getServer().runAsync(['devices', '-l']);\n\n const splitItems = output\n .trim()\n .replace(/\\n$/, '')\n .split(os.EOL)\n // Filter ADB trace logs from the output, e.g.\n // adb D 03-06 15:25:53 63677 4018815 adb_client.cpp:393] adb_query: host:devices-l\n // 03-04 12:29:44.557 16415 16415 D adb : commandline.cpp:1646 Using server socket: tcp:172.27.192.1:5037\n // 03-04 12:29:44.557 16415 16415 D adb : adb_client.cpp:160 _adb_connect: host:version\n .filter((line) => !line.match(/\\.cpp:[0-9]+/));\n\n // First line is `\"List of devices attached\"`, remove it\n // @ts-ignore: todo\n const attachedDevices: {\n props: string[];\n type: Device['type'];\n isAuthorized: Device['isAuthorized'];\n isBooted: Device['isBooted'];\n connectionType?: Device['connectionType'];\n }[] = splitItems\n .slice(1, splitItems.length)\n .map((line) => {\n // unauthorized: ['FA8251A00719', 'unauthorized', 'usb:338690048X', 'transport_id:5']\n // authorized: ['FA8251A00719', 'device', 'usb:336592896X', 'product:walleye', 'model:Pixel_2', 'device:walleye', 'transport_id:4']\n // emulator: ['emulator-5554', 'offline', 'transport_id:1']\n const props = line.split(' ').filter(Boolean);\n const type = line.includes('emulator') ? 'emulator' : 'device';\n\n let connectionType;\n if (type === 'device' && line.includes('usb:')) {\n connectionType = 'USB';\n } else if (type === 'device' && line.includes('_adb-tls-connect.')) {\n connectionType = 'Network';\n }\n\n const isBooted = type === 'emulator' || props[1] !== 'offline';\n const isAuthorized =\n connectionType === 'Network'\n ? line.includes('model:') // Network connected devices show `model:<name>` when authorized\n : props[1] !== 'unauthorized';\n\n return { props, type, isAuthorized, isBooted, connectionType };\n })\n .filter(({ props: [pid] }) => !!pid);\n\n const devicePromises = attachedDevices.map<Promise<Device>>(async (props) => {\n const {\n type,\n props: [pid, ...deviceInfo],\n isAuthorized,\n isBooted,\n } = props;\n\n let name: string | null = null;\n\n if (type === 'device') {\n if (isAuthorized) {\n // Possibly formatted like `model:Pixel_2`\n // Transform to `Pixel_2`\n const modelItem = deviceInfo.find((info) => info.includes('model:'));\n if (modelItem) {\n name = modelItem.replace('model:', '');\n }\n }\n // unauthorized devices don't have a name available to read\n if (!name) {\n // Device FA8251A00719\n name = `Device ${pid}`;\n }\n } else {\n // Given an emulator pid, get the emulator name which can be used to start the emulator later.\n name = (await getAdbNameForDeviceIdAsync({ pid })) ?? '';\n }\n\n return props.connectionType\n ? { pid, name, type, isAuthorized, isBooted, connectionType: props.connectionType }\n : { pid, name, type, isAuthorized, isBooted };\n });\n\n return Promise.all(devicePromises);\n}\n\n/**\n * Return the Emulator name for an emulator ID, this can be used to determine if an emulator is booted.\n *\n * @param device.pid a value like `emulator-5554` from `abd devices`\n */\nexport async function getAdbNameForDeviceIdAsync(device: DeviceContext): Promise<string | null> {\n const results = await getServer().runAsync(adbArgs(device.pid, 'emu', 'avd', 'name'));\n\n if (results.match(/could not connect to TCP port .*: Connection refused/)) {\n // Can also occur when the emulator does not exist.\n throw new CommandError('EMULATOR_NOT_FOUND', results);\n }\n\n return sanitizeAdbDeviceName(results) ?? null;\n}\n\nexport async function isDeviceBootedAsync({\n name,\n}: { name?: string } = {}): Promise<Device | null> {\n const devices = await getAttachedDevicesAsync();\n\n if (!name) {\n return devices[0] ?? null;\n }\n\n return devices.find((device) => device.name === name) ?? null;\n}\n\n/**\n * Returns true when a device's splash screen animation has stopped.\n * This can be used to detect when a device is fully booted and ready to use.\n *\n * @param pid\n */\nexport async function isBootAnimationCompleteAsync(pid?: string): Promise<boolean> {\n try {\n const props = await getPropertyDataForDeviceAsync({ pid }, PROP_BOOT_ANIMATION_STATE);\n return !!props[PROP_BOOT_ANIMATION_STATE].match(/stopped/);\n } catch {\n return false;\n }\n}\n\n/** Get a list of ABIs for the provided device. */\nexport async function getDeviceABIsAsync(\n device: Pick<Device, 'name' | 'pid'>\n): Promise<DeviceABI[]> {\n const cpuAbiList = (await getPropertyDataForDeviceAsync(device, PROP_CPU_ABI_LIST_NAME))[\n PROP_CPU_ABI_LIST_NAME\n ];\n\n if (cpuAbiList) {\n return cpuAbiList.trim().split(',') as DeviceABI[];\n }\n\n const abi = (await getPropertyDataForDeviceAsync(device, PROP_CPU_NAME))[\n PROP_CPU_NAME\n ] as DeviceABI;\n return [abi];\n}\n\nexport async function getPropertyDataForDeviceAsync(\n device: DeviceContext,\n prop?: string\n): Promise<DeviceProperties> {\n const propCommand = prop\n ? adbShellArgs(device.pid, 'getprop', prop)\n : adbShellArgs(device.pid, 'getprop');\n try {\n // Prevent reading as UTF8.\n const results = await getServer().getFileOutputAsync(propCommand);\n // Like:\n // [wifi.direct.interface]: [p2p-dev-wlan0]\n // [wifi.interface]: [wlan0]\n\n if (prop) {\n debug(`Property data: (device pid: ${device.pid}, prop: ${prop}, data: ${results})`);\n return {\n [prop]: results,\n };\n }\n const props = parseAdbDeviceProperties(results);\n\n debug(`Parsed data:`, props);\n\n return props;\n } catch (error: any) {\n // TODO: Ensure error has message and not stderr\n throw new CommandError(`Failed to get properties for device (${device.pid}): ${error.message}`);\n }\n}\n\nfunction parseAdbDeviceProperties(devicePropertiesString: string) {\n const properties: DeviceProperties = {};\n const propertyExp = /\\[(.*?)\\]: \\[(.*?)\\]/gm;\n for (const match of devicePropertiesString.matchAll(propertyExp)) {\n properties[match[1]] = match[2];\n }\n return properties;\n}\n\n/**\n * Sanitize the ADB device name to only get the actual device name.\n * On Windows, we need to do \\r, \\n, and \\r\\n filtering to get the name.\n */\nexport function sanitizeAdbDeviceName(deviceName: string) {\n return deviceName\n .trim()\n .split(/[\\r\\n]+/)\n .shift();\n}\n"],"names":["DeviceABI","adbArgs","adbShellArgs","getAdbNameForDeviceIdAsync","getAttachedDevicesAsync","getDeviceABIsAsync","getPackageInfoAsync","getPropertyDataForDeviceAsync","getServer","installAsync","isBootAnimationCompleteAsync","isDeviceBootedAsync","isPackageInstalledAsync","launchActivityAsync","logUnauthorized","openUrlAsync","sanitizeAdbDeviceName","uninstallAsync","debug","require","CANT_START_ACTIVITY_ERROR","PROP_CPU_NAME","PROP_CPU_ABI_LIST_NAME","PROP_BOOT_ANIMATION_STATE","_server","ADBServer","device","Log","warn","chalk","bold","name","dim","learnMore","androidPackage","packages","runAsync","pid","env","EXPO_ADB_USER","lines","split","i","length","line","trim","launchActivity","url","command","push","openAsync","args","results","includes","match","CommandError","substring","indexOf","appId","filePath","options","concat","map","shellQuote","value","replace","output","splitItems","os","EOL","filter","attachedDevices","slice","props","Boolean","type","connectionType","isBooted","isAuthorized","devicePromises","deviceInfo","modelItem","find","info","Promise","all","devices","cpuAbiList","abi","prop","propCommand","getFileOutputAsync","parseAdbDeviceProperties","error","message","devicePropertiesString","properties","propertyExp","matchAll","deviceName","shift"],"mappings":"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"}
|
|
@@ -59,6 +59,7 @@ const _metroErrorInterface = require("./metro/metroErrorInterface");
|
|
|
59
59
|
const _metroOptions = require("./middleware/metroOptions");
|
|
60
60
|
const _serverLogLikeMetro = require("./serverLogLikeMetro");
|
|
61
61
|
const _delay = require("../../utils/delay");
|
|
62
|
+
const _dir = require("../../utils/dir");
|
|
62
63
|
const _errors = require("../../utils/errors");
|
|
63
64
|
const _filePath = require("../../utils/filePath");
|
|
64
65
|
const _profile = require("../../utils/profile");
|
|
@@ -83,7 +84,7 @@ if (!process.isBun) {
|
|
|
83
84
|
async function ensureFileInRootDirectory(projectRoot, otherFile) {
|
|
84
85
|
// Cannot be accessed using Metro's server API, we need to move the file
|
|
85
86
|
// into the project root and try again.
|
|
86
|
-
if (!
|
|
87
|
+
if (!(0, _dir.isPathInside)(otherFile, projectRoot)) {
|
|
87
88
|
return otherFile;
|
|
88
89
|
}
|
|
89
90
|
// Copy the file into the project to ensure it works in monorepos.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/start/server/getStaticRenderFunctions.ts"],"sourcesContent":["/**\n * Copyright © 2022 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 */\nimport { getMetroServerRoot } from '@expo/config/paths';\nimport { evalModule } from '@expo/require-utils';\nimport fs from 'fs';\nimport path from 'path';\n\nimport { IS_METRO_BUNDLE_ERROR_SYMBOL, logMetroError } from './metro/metroErrorInterface';\nimport { createBundleUrlPath, ExpoMetroOptions } from './middleware/metroOptions';\nimport { augmentLogs } from './serverLogLikeMetro';\nimport { delayAsync } from '../../utils/delay';\nimport { SilentError } from '../../utils/errors';\nimport { toPosixPath } from '../../utils/filePath';\nimport { profile } from '../../utils/profile';\n\nconst debug = require('debug')('expo:start:server:getStaticRenderFunctions') as typeof console.log;\n\n/** The list of input keys will become optional, everything else will remain the same. */\nexport type PickPartial<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;\n\nexport const cachedSourceMaps: Map<string, { url: string; map: string }> = new Map();\n\n// Support unhandled rejections\n\ndeclare global {\n namespace NodeJS {\n interface Process {\n isBun?: boolean;\n }\n }\n}\n\n// Detect if running in Bun\nif (!process.isBun) {\n require('source-map-support').install({\n retrieveSourceMap(source: string) {\n if (cachedSourceMaps.has(source)) {\n return cachedSourceMaps.get(source);\n }\n return null;\n },\n });\n}\n\nasync function ensureFileInRootDirectory(projectRoot: string, otherFile: string) {\n // Cannot be accessed using Metro's server API, we need to move the file\n // into the project root and try again.\n if (!
|
|
1
|
+
{"version":3,"sources":["../../../../src/start/server/getStaticRenderFunctions.ts"],"sourcesContent":["/**\n * Copyright © 2022 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 */\nimport { getMetroServerRoot } from '@expo/config/paths';\nimport { evalModule } from '@expo/require-utils';\nimport fs from 'fs';\nimport path from 'path';\n\nimport { IS_METRO_BUNDLE_ERROR_SYMBOL, logMetroError } from './metro/metroErrorInterface';\nimport { createBundleUrlPath, ExpoMetroOptions } from './middleware/metroOptions';\nimport { augmentLogs } from './serverLogLikeMetro';\nimport { delayAsync } from '../../utils/delay';\nimport { isPathInside } from '../../utils/dir';\nimport { SilentError } from '../../utils/errors';\nimport { toPosixPath } from '../../utils/filePath';\nimport { profile } from '../../utils/profile';\n\nconst debug = require('debug')('expo:start:server:getStaticRenderFunctions') as typeof console.log;\n\n/** The list of input keys will become optional, everything else will remain the same. */\nexport type PickPartial<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;\n\nexport const cachedSourceMaps: Map<string, { url: string; map: string }> = new Map();\n\n// Support unhandled rejections\n\ndeclare global {\n namespace NodeJS {\n interface Process {\n isBun?: boolean;\n }\n }\n}\n\n// Detect if running in Bun\nif (!process.isBun) {\n require('source-map-support').install({\n retrieveSourceMap(source: string) {\n if (cachedSourceMaps.has(source)) {\n return cachedSourceMaps.get(source);\n }\n return null;\n },\n });\n}\n\nasync function ensureFileInRootDirectory(projectRoot: string, otherFile: string) {\n // Cannot be accessed using Metro's server API, we need to move the file\n // into the project root and try again.\n if (!isPathInside(otherFile, projectRoot)) {\n return otherFile;\n }\n\n // Copy the file into the project to ensure it works in monorepos.\n // This means the file cannot have any relative imports.\n const tempDir = path.join(projectRoot, '.expo/static-tmp');\n await fs.promises.mkdir(tempDir, { recursive: true });\n const moduleId = path.join(tempDir, path.basename(otherFile));\n await fs.promises.writeFile(moduleId, await fs.promises.readFile(otherFile, 'utf8'));\n // Sleep to give watchman time to register the file.\n await delayAsync(50);\n return moduleId;\n}\n\nexport async function createMetroEndpointAsync(\n projectRoot: string,\n devServerUrl: string,\n absoluteFilePath: string,\n props: PickPartial<ExpoMetroOptions, 'mainModuleName' | 'bytecode'>\n): Promise<string> {\n const root = getMetroServerRoot(projectRoot);\n const safeOtherFile = await ensureFileInRootDirectory(projectRoot, absoluteFilePath);\n const serverPath = path.relative(root, safeOtherFile).replace(/\\.[jt]sx?$/, '');\n\n const urlFragment = createBundleUrlPath({\n mainModuleName: serverPath,\n lazy: false,\n asyncRoutes: false,\n inlineSourceMap: false,\n engine: 'hermes',\n minify: false,\n bytecode: false,\n ...props,\n });\n\n let url: string;\n if (devServerUrl) {\n url = new URL(urlFragment.replace(/^\\//, ''), devServerUrl).toString();\n } else {\n url = '/' + urlFragment.replace(/^\\/+/, '');\n }\n return url;\n}\n\nexport function evalMetroAndWrapFunctions<T = Record<string, any>>(\n projectRoot: string,\n script: string,\n filename: string,\n isExporting: boolean\n): T {\n // TODO: Add back stack trace logic that hides traces from metro-runtime and other internal modules.\n const contents = evalMetroNoHandling(projectRoot, script, filename);\n\n if (!contents) {\n // This can happen if ErrorUtils isn't working correctly on web and failing to throw an error when a module throws.\n // This is unexpected behavior and should not be pretty formatted, therefore we're avoiding CommandError.\n throw new Error(\n '[Expo SSR] Module returned undefined, this could be due to a misconfiguration in Metro error handling'\n );\n }\n // wrap each function with a try/catch that uses Metro's error formatter\n return Object.keys(contents).reduce((acc, key) => {\n const fn = contents[key];\n if (typeof fn !== 'function') {\n return { ...acc, [key]: fn };\n }\n\n acc[key] = async function (...props: any[]) {\n try {\n return await fn.apply(this, props);\n } catch (error: any) {\n await logMetroError(projectRoot, { error });\n\n if (isExporting || error[IS_METRO_BUNDLE_ERROR_SYMBOL]) {\n throw error;\n } else {\n // TODO: When does this happen?\n throw new SilentError(error);\n }\n }\n };\n return acc;\n }, {} as any);\n}\n\nexport function evalMetroNoHandling(projectRoot: string, src: string, filename: string) {\n augmentLogs(projectRoot);\n\n // NOTE(@kitten): `require-from-string` derives a base path from the filename we pass it,\n // but doesn't validate that the filename exists. These debug messages should help identify\n // these problems, if they occur in user projects without reproductions\n if (!fs.existsSync(path.dirname(filename))) {\n debug(`evalMetroNoHandling received filename in a directory that does not exist: ${filename}`);\n } else if (!toPosixPath(path.dirname(filename)).startsWith(toPosixPath(projectRoot))) {\n debug(`evalMetroNoHandling received filename outside of the project root: ${filename}`);\n }\n\n return profile(evalModule, 'eval-metro-bundle')(src, filename);\n}\n"],"names":["cachedSourceMaps","createMetroEndpointAsync","evalMetroAndWrapFunctions","evalMetroNoHandling","debug","require","Map","process","isBun","install","retrieveSourceMap","source","has","get","ensureFileInRootDirectory","projectRoot","otherFile","isPathInside","tempDir","path","join","fs","promises","mkdir","recursive","moduleId","basename","writeFile","readFile","delayAsync","devServerUrl","absoluteFilePath","props","root","getMetroServerRoot","safeOtherFile","serverPath","relative","replace","urlFragment","createBundleUrlPath","mainModuleName","lazy","asyncRoutes","inlineSourceMap","engine","minify","bytecode","url","URL","toString","script","filename","isExporting","contents","Error","Object","keys","reduce","acc","key","fn","apply","error","logMetroError","IS_METRO_BUNDLE_ERROR_SYMBOL","SilentError","src","augmentLogs","existsSync","dirname","toPosixPath","startsWith","profile","evalModule"],"mappings":"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"}
|
|
@@ -16,9 +16,9 @@ function _ws() {
|
|
|
16
16
|
return data;
|
|
17
17
|
}
|
|
18
18
|
const _createHandlersFactory = require("./createHandlersFactory");
|
|
19
|
+
const _NetworkResponse = require("./messageHandlers/NetworkResponse");
|
|
19
20
|
const _env = require("../../../../utils/env");
|
|
20
21
|
const _net = require("../../../../utils/net");
|
|
21
|
-
const _NetworkResponse = require("./messageHandlers/NetworkResponse");
|
|
22
22
|
const debug = require('debug')('expo:metro:debugging:middleware');
|
|
23
23
|
function createDebugMiddleware({ serverBaseUrl, reporter }) {
|
|
24
24
|
// Load the React Native debugging tools from project
|
|
@@ -95,9 +95,10 @@ function makeLogger(reporter, level) {
|
|
|
95
95
|
const wss = new (_ws()).WebSocketServer({
|
|
96
96
|
noServer: true,
|
|
97
97
|
perMessageDeflate: true,
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
|
|
98
|
+
// Bounded so an unauthenticated client cannot exhaust dev-server memory
|
|
99
|
+
// with one oversized frame. 16 MiB covers realistic response bodies
|
|
100
|
+
// (image previews, large JSON dumps, base64-inflated assets).
|
|
101
|
+
maxPayload: 16 * 1024 * 1024
|
|
101
102
|
});
|
|
102
103
|
wss.on('connection', (networkSocket)=>{
|
|
103
104
|
networkSocket.on('message', (data)=>{
|
|
@@ -107,7 +108,7 @@ function makeLogger(reporter, level) {
|
|
|
107
108
|
if (message.method === 'Expo(Network.receivedResponseBody)' && message.params) {
|
|
108
109
|
// If its a response body, write it to the global storage
|
|
109
110
|
const { requestId, ...requestInfo } = message.params;
|
|
110
|
-
_NetworkResponse.
|
|
111
|
+
(0, _NetworkResponse.recordNetworkResponse)(requestId, requestInfo);
|
|
111
112
|
} else if (message.method.startsWith('Network.')) {
|
|
112
113
|
// Otherwise, directly re-broadcast the Network events to all connected debuggers
|
|
113
114
|
debuggerWebsocket.clients.forEach((debuggerSocket)=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../src/start/server/metro/debugging/createDebugMiddleware.ts"],"sourcesContent":["import type { NextHandleFunction } from 'connect';\nimport { WebSocketServer } from 'ws';\n\nimport { createHandlersFactory } from './createHandlersFactory';\nimport { env, envIsHeadless } from '../../../../utils/env';\nimport { isLocalSocket, isMatchingOrigin } from '../../../../utils/net';\nimport { TerminalReporter } from '../TerminalReporter';\
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/start/server/metro/debugging/createDebugMiddleware.ts"],"sourcesContent":["import type { NextHandleFunction } from 'connect';\nimport { WebSocketServer } from 'ws';\n\nimport { createHandlersFactory } from './createHandlersFactory';\nimport { recordNetworkResponse } from './messageHandlers/NetworkResponse';\nimport { env, envIsHeadless } from '../../../../utils/env';\nimport { isLocalSocket, isMatchingOrigin } from '../../../../utils/net';\nimport { TerminalReporter } from '../TerminalReporter';\n\nconst debug = require('debug')('expo:metro:debugging:middleware') as typeof console.log;\n\ninterface DebugMiddleware {\n debugMiddleware: NextHandleFunction;\n debugWebsocketEndpoints: Record<string, WebSocketServer>;\n}\n\ninterface DebugMiddlewareParams {\n serverBaseUrl: string;\n reporter: TerminalReporter;\n}\n\nexport function createDebugMiddleware({\n serverBaseUrl,\n reporter,\n}: DebugMiddlewareParams): DebugMiddleware {\n // Load the React Native debugging tools from project\n // TODO: check if this works with isolated modules\n const { createDevMiddleware } =\n require('@react-native/dev-middleware') as typeof import('@react-native/dev-middleware');\n\n const { middleware, websocketEndpoints } = createDevMiddleware({\n // TODO: Check with cedric why this can be removed\n // https://github.com/facebook/react-native/pull/53921\n // projectRoot: metroBundler.projectRoot,\n serverBaseUrl,\n logger: createLogger(reporter),\n unstable_customInspectorMessageHandler: createHandlersFactory(),\n // TODO: Forward all events to the shared Metro log reporter. Do this when we have opinions on how all logs should be presented.\n // unstable_eventReporter: {\n // logEvent(event) {\n // reporter.update(event);\n // },\n // },\n unstable_experiments: {\n // Enable the Network tab in React Native DevTools\n enableNetworkInspector: true,\n // Only enable opening the browser version of React Native DevTools when debugging.\n // This is useful when debugging the React Native DevTools by going to `/open-debugger` in the browser.\n enableOpenDebuggerRedirect: env.EXPO_DEBUG,\n // The standalone fusebox shell (@react-native/debugger-shell) starts installing almost\n // immediately in the background when the dev middleware is created, which we don't\n // always want to happen\n enableStandaloneFuseboxShell: !envIsHeadless(),\n },\n });\n\n const debuggerWebsocketEndpoint = websocketEndpoints['/inspector/debug'] as WebSocketServer;\n\n // NOTE(cedric): add a temporary websocket to handle Network-related CDP events\n websocketEndpoints['/inspector/network'] = createNetworkWebsocket(debuggerWebsocketEndpoint);\n\n // Explicitly limit debugger websocket to loopback requests\n debuggerWebsocketEndpoint.on('connection', (socket, request) => {\n if (!isLocalSocket(request.socket) || !isMatchingOrigin(request, serverBaseUrl)) {\n // NOTE: `socket.close` nicely closes the websocket, which will still allow incoming messages\n // `socket.terminate` instead forcefully closes down the socket\n socket.terminate();\n }\n });\n\n return {\n debugMiddleware(req, res, next) {\n // The debugger middleware is skipped entirely if the connection isn't a loopback request\n if (isLocalSocket(req.socket)) {\n return middleware(req, res, next);\n } else {\n return next();\n }\n },\n debugWebsocketEndpoints: websocketEndpoints,\n };\n}\n\nfunction createLogger(\n reporter: TerminalReporter\n): Parameters<typeof import('@react-native/dev-middleware').createDevMiddleware>[0]['logger'] {\n return {\n info: makeLogger(reporter, 'info'),\n warn: makeLogger(reporter, 'warn'),\n error: makeLogger(reporter, 'error'),\n };\n}\n\nfunction makeLogger(reporter: TerminalReporter, level: 'info' | 'warn' | 'error') {\n return (...data: any[]) =>\n reporter.update({\n type: 'unstable_server_log',\n level,\n data,\n });\n}\n\n/**\n * This adds a dedicated websocket connection that handles Network-related CDP events.\n * It's a temporary solution until Fusebox either implements the Network CDP domain,\n * or allows external domain agents that can send messages over the CDP socket to the debugger.\n * The Network websocket rebroadcasts events on the debugger CDP connections.\n */\nfunction createNetworkWebsocket(debuggerWebsocket: WebSocketServer) {\n const wss = new WebSocketServer({\n noServer: true,\n perMessageDeflate: true,\n // Bounded so an unauthenticated client cannot exhaust dev-server memory\n // with one oversized frame. 16 MiB covers realistic response bodies\n // (image previews, large JSON dumps, base64-inflated assets).\n maxPayload: 16 * 1024 * 1024,\n });\n\n wss.on('connection', (networkSocket) => {\n networkSocket.on('message', (data) => {\n try {\n // Parse the network message, to determine how the message should be handled\n const message = JSON.parse(data.toString());\n\n if (message.method === 'Expo(Network.receivedResponseBody)' && message.params) {\n // If its a response body, write it to the global storage\n const { requestId, ...requestInfo } = message.params;\n recordNetworkResponse(requestId, requestInfo);\n } else if (message.method.startsWith('Network.')) {\n // Otherwise, directly re-broadcast the Network events to all connected debuggers\n debuggerWebsocket.clients.forEach((debuggerSocket) => {\n if (debuggerSocket.readyState === debuggerSocket.OPEN) {\n debuggerSocket.send(data.toString());\n }\n });\n }\n } catch (error) {\n debug('Failed to handle Network CDP event', error);\n }\n });\n });\n\n return wss;\n}\n"],"names":["createDebugMiddleware","debug","require","serverBaseUrl","reporter","createDevMiddleware","middleware","websocketEndpoints","logger","createLogger","unstable_customInspectorMessageHandler","createHandlersFactory","unstable_experiments","enableNetworkInspector","enableOpenDebuggerRedirect","env","EXPO_DEBUG","enableStandaloneFuseboxShell","envIsHeadless","debuggerWebsocketEndpoint","createNetworkWebsocket","on","socket","request","isLocalSocket","isMatchingOrigin","terminate","debugMiddleware","req","res","next","debugWebsocketEndpoints","info","makeLogger","warn","error","level","data","update","type","debuggerWebsocket","wss","WebSocketServer","noServer","perMessageDeflate","maxPayload","networkSocket","message","JSON","parse","toString","method","params","requestId","requestInfo","recordNetworkResponse","startsWith","clients","forEach","debuggerSocket","readyState","OPEN","send"],"mappings":"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"}
|
|
@@ -14,15 +14,31 @@ _export(exports, {
|
|
|
14
14
|
},
|
|
15
15
|
NetworkResponseHandler: function() {
|
|
16
16
|
return NetworkResponseHandler;
|
|
17
|
+
},
|
|
18
|
+
recordNetworkResponse: function() {
|
|
19
|
+
return recordNetworkResponse;
|
|
17
20
|
}
|
|
18
21
|
});
|
|
19
22
|
const _MessageHandler = require("../MessageHandler");
|
|
20
23
|
const NETWORK_RESPONSE_STORAGE = new Map();
|
|
24
|
+
// Bounded so an unauthenticated client on `/inspector/network` cannot grow this
|
|
25
|
+
// map without limit. `Map` preserves insertion order, so the first key is the
|
|
26
|
+
// oldest entry — drop it on overflow (FIFO).
|
|
27
|
+
const MAX_NETWORK_RESPONSES = 512;
|
|
28
|
+
function recordNetworkResponse(requestId, info) {
|
|
29
|
+
NETWORK_RESPONSE_STORAGE.set(requestId, info);
|
|
30
|
+
if (NETWORK_RESPONSE_STORAGE.size > MAX_NETWORK_RESPONSES) {
|
|
31
|
+
const oldestKey = NETWORK_RESPONSE_STORAGE.keys().next().value;
|
|
32
|
+
if (oldestKey !== undefined) {
|
|
33
|
+
NETWORK_RESPONSE_STORAGE.delete(oldestKey);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
21
37
|
class NetworkResponseHandler extends _MessageHandler.MessageHandler {
|
|
22
38
|
handleDeviceMessage(message) {
|
|
23
39
|
if (message.method === 'Expo(Network.receivedResponseBody)') {
|
|
24
40
|
const { requestId, ...requestInfo } = message.params;
|
|
25
|
-
|
|
41
|
+
recordNetworkResponse(requestId, requestInfo);
|
|
26
42
|
return true;
|
|
27
43
|
}
|
|
28
44
|
return false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../src/start/server/metro/debugging/messageHandlers/NetworkResponse.ts"],"sourcesContent":["import type { Protocol } from 'devtools-protocol';\n\nimport { MessageHandler } from '../MessageHandler';\nimport type {\n CdpMessage,\n DeviceRequest,\n DebuggerRequest,\n DebuggerResponse,\n DeviceResponse,\n} from '../types';\n\n/**\n * The global network response storage, as a workaround for the network inspector.\n * @see createDebugMiddleware#createNetworkWebsocket\n */\nexport const NETWORK_RESPONSE_STORAGE = new Map<\n string,\n DebuggerResponse<NetworkGetResponseBody>['result']\n>();\n\nexport class NetworkResponseHandler extends MessageHandler {\n /** All known responses, mapped by request id */\n storage = NETWORK_RESPONSE_STORAGE;\n\n handleDeviceMessage(message: DeviceRequest<NetworkReceivedResponseBody>) {\n if (message.method === 'Expo(Network.receivedResponseBody)') {\n const { requestId, ...requestInfo } = message.params;\n
|
|
1
|
+
{"version":3,"sources":["../../../../../../../src/start/server/metro/debugging/messageHandlers/NetworkResponse.ts"],"sourcesContent":["import type { Protocol } from 'devtools-protocol';\n\nimport { MessageHandler } from '../MessageHandler';\nimport type {\n CdpMessage,\n DeviceRequest,\n DebuggerRequest,\n DebuggerResponse,\n DeviceResponse,\n} from '../types';\n\n/**\n * The global network response storage, as a workaround for the network inspector.\n * @see createDebugMiddleware#createNetworkWebsocket\n */\nexport const NETWORK_RESPONSE_STORAGE = new Map<\n string,\n DebuggerResponse<NetworkGetResponseBody>['result']\n>();\n\n// Bounded so an unauthenticated client on `/inspector/network` cannot grow this\n// map without limit. `Map` preserves insertion order, so the first key is the\n// oldest entry — drop it on overflow (FIFO).\nconst MAX_NETWORK_RESPONSES = 512;\n\nexport function recordNetworkResponse(\n requestId: string,\n info: DebuggerResponse<NetworkGetResponseBody>['result']\n) {\n NETWORK_RESPONSE_STORAGE.set(requestId, info);\n if (NETWORK_RESPONSE_STORAGE.size > MAX_NETWORK_RESPONSES) {\n const oldestKey = NETWORK_RESPONSE_STORAGE.keys().next().value;\n if (oldestKey !== undefined) {\n NETWORK_RESPONSE_STORAGE.delete(oldestKey);\n }\n }\n}\n\nexport class NetworkResponseHandler extends MessageHandler {\n /** All known responses, mapped by request id */\n storage = NETWORK_RESPONSE_STORAGE;\n\n handleDeviceMessage(message: DeviceRequest<NetworkReceivedResponseBody>) {\n if (message.method === 'Expo(Network.receivedResponseBody)') {\n const { requestId, ...requestInfo } = message.params;\n recordNetworkResponse(requestId, requestInfo);\n return true;\n }\n\n return false;\n }\n\n handleDebuggerMessage(message: DebuggerRequest<NetworkGetResponseBody>) {\n if (\n message.method === 'Network.getResponseBody' &&\n this.storage.has(message.params.requestId)\n ) {\n return this.sendToDebugger<DeviceResponse<NetworkGetResponseBody>>({\n id: message.id,\n result: this.storage.get(message.params.requestId)!,\n });\n }\n\n return false;\n }\n}\n\n/** Custom message to transfer the response body data to the proxy */\nexport type NetworkReceivedResponseBody = CdpMessage<\n 'Expo(Network.receivedResponseBody)',\n Protocol.Network.GetResponseBodyRequest & Protocol.Network.GetResponseBodyResponse,\n never\n>;\n\n/** @see https://chromedevtools.github.io/devtools-protocol/1-2/Network/#method-getResponseBody */\nexport type NetworkGetResponseBody = CdpMessage<\n 'Network.getResponseBody',\n Protocol.Network.GetResponseBodyRequest,\n Protocol.Network.GetResponseBodyResponse\n>;\n"],"names":["NETWORK_RESPONSE_STORAGE","NetworkResponseHandler","recordNetworkResponse","Map","MAX_NETWORK_RESPONSES","requestId","info","set","size","oldestKey","keys","next","value","undefined","delete","MessageHandler","handleDeviceMessage","message","method","requestInfo","params","handleDebuggerMessage","storage","has","sendToDebugger","id","result","get"],"mappings":";;;;;;;;;;;IAeaA,wBAAwB;eAAxBA;;IAuBAC,sBAAsB;eAAtBA;;IAbGC,qBAAqB;eAArBA;;;gCAvBe;AAaxB,MAAMF,2BAA2B,IAAIG;AAK5C,gFAAgF;AAChF,8EAA8E;AAC9E,6CAA6C;AAC7C,MAAMC,wBAAwB;AAEvB,SAASF,sBACdG,SAAiB,EACjBC,IAAwD;IAExDN,yBAAyBO,GAAG,CAACF,WAAWC;IACxC,IAAIN,yBAAyBQ,IAAI,GAAGJ,uBAAuB;QACzD,MAAMK,YAAYT,yBAAyBU,IAAI,GAAGC,IAAI,GAAGC,KAAK;QAC9D,IAAIH,cAAcI,WAAW;YAC3Bb,yBAAyBc,MAAM,CAACL;QAClC;IACF;AACF;AAEO,MAAMR,+BAA+Bc,8BAAc;IAIxDC,oBAAoBC,OAAmD,EAAE;QACvE,IAAIA,QAAQC,MAAM,KAAK,sCAAsC;YAC3D,MAAM,EAAEb,SAAS,EAAE,GAAGc,aAAa,GAAGF,QAAQG,MAAM;YACpDlB,sBAAsBG,WAAWc;YACjC,OAAO;QACT;QAEA,OAAO;IACT;IAEAE,sBAAsBJ,OAAgD,EAAE;QACtE,IACEA,QAAQC,MAAM,KAAK,6BACnB,IAAI,CAACI,OAAO,CAACC,GAAG,CAACN,QAAQG,MAAM,CAACf,SAAS,GACzC;YACA,OAAO,IAAI,CAACmB,cAAc,CAAyC;gBACjEC,IAAIR,QAAQQ,EAAE;gBACdC,QAAQ,IAAI,CAACJ,OAAO,CAACK,GAAG,CAACV,QAAQG,MAAM,CAACf,SAAS;YACnD;QACF;QAEA,OAAO;IACT;;QA1BK,gBACL,8CAA8C,QAC9CiB,UAAUtB;;AAyBZ"}
|
|
@@ -47,7 +47,9 @@ const _compression = require("./compression");
|
|
|
47
47
|
const _createEventSocket = require("./createEventSocket");
|
|
48
48
|
const _createMessageSocket = require("./createMessageSocket");
|
|
49
49
|
const _log = require("../../../../log");
|
|
50
|
+
const _dir = require("../../../../utils/dir");
|
|
50
51
|
const _editor = require("../../../../utils/editor");
|
|
52
|
+
const _net = require("../../../../utils/net");
|
|
51
53
|
function _interop_require_default(obj) {
|
|
52
54
|
return obj && obj.__esModule ? obj : {
|
|
53
55
|
default: obj
|
|
@@ -112,6 +114,10 @@ function createMetroOpenStackFrameMiddleware(metroConfig) {
|
|
|
112
114
|
res.statusCode = 400;
|
|
113
115
|
return res.end('Open stack frame requires target file to be in server root');
|
|
114
116
|
}
|
|
117
|
+
if ((0, _net.shouldThrottleRemoteDevCall)()) {
|
|
118
|
+
res.statusCode = 429;
|
|
119
|
+
return res.end();
|
|
120
|
+
}
|
|
115
121
|
try {
|
|
116
122
|
await (0, _editor.openInEditorAsync)(file, frame.lineNumber);
|
|
117
123
|
return res.end('OK');
|
|
@@ -127,7 +133,7 @@ const ensureFileInRootDirectory = async (root, file)=>{
|
|
|
127
133
|
file = await _nodefs().default.promises.realpath(file);
|
|
128
134
|
// Cannot be accessed using Metro's server API, we need to move the file
|
|
129
135
|
// into the project root and try again.
|
|
130
|
-
if (
|
|
136
|
+
if ((0, _dir.isPathInside)(file, root)) {
|
|
131
137
|
return file;
|
|
132
138
|
} else {
|
|
133
139
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../src/start/server/metro/dev-server/createMetroMiddleware.ts"],"sourcesContent":["import { getMetroServerRoot } from '@expo/config/paths';\nimport type { MetroConfig } from '@expo/metro/metro';\nimport type MetroBundler from '@expo/metro/metro/Bundler';\nimport connect from 'connect';\nimport { Body } from 'fetch-nodeshim';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport { compression } from './compression';\nimport { createEventsSocket } from './createEventSocket';\nimport { createMessagesSocket } from './createMessageSocket';\nimport { Log } from '../../../../log';\nimport { openInEditorAsync } from '../../../../utils/editor';\n\ninterface MetroMiddlewareOptions {\n getMetroBundler(): MetroBundler;\n serverBaseUrl: string;\n}\n\ninterface StackFrame {\n file: string;\n lineNumber?: number | undefined;\n}\n\nexport function createMetroMiddleware(\n metroConfig: Pick<MetroConfig, 'projectRoot'>,\n options: MetroMiddlewareOptions\n) {\n const messages = createMessagesSocket({ logger: Log, serverBaseUrl: options.serverBaseUrl });\n const events = createEventsSocket(messages);\n\n const middleware = connect()\n .use(noCacheMiddleware)\n .use(compression)\n // Support opening stack frames from clients directly in the editor\n .use('/open-stack-frame', createMetroOpenStackFrameMiddleware(metroConfig))\n // Support status check to detect if the packager needs to be started from the native side\n .use('/status', createMetroStatusMiddleware(metroConfig, options));\n\n return {\n middleware,\n messagesSocket: messages,\n eventsSocket: events,\n websocketEndpoints: {\n [messages.endpoint]: messages.server,\n [events.endpoint]: events.server,\n },\n };\n}\n\nconst noCacheMiddleware: connect.NextHandleFunction = (_req, res, next) => {\n res.setHeader('Surrogate-Control', 'no-store');\n res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');\n res.setHeader('Pragma', 'no-cache');\n res.setHeader('Expires', '0');\n next();\n};\n\nfunction createMetroStatusMiddleware(\n metroConfig: Pick<MetroConfig, 'projectRoot'>,\n options: MetroMiddlewareOptions\n): connect.NextHandleFunction {\n return async (_req, res) => {\n res.setHeader('X-React-Native-Project-Root', encodeURI(metroConfig.projectRoot!));\n res.flushHeaders();\n await options.getMetroBundler().ready();\n res.end('packager-status:running');\n };\n}\n\nfunction createMetroOpenStackFrameMiddleware(\n metroConfig: Pick<MetroConfig, 'projectRoot'>\n): connect.NextHandleFunction {\n return async (req, res, next) => {\n if (req.method !== 'POST') {\n return next();\n }\n\n let frame: StackFrame | undefined;\n try {\n const json = await new Body(req).json();\n if (typeof json === 'object' && json != null && typeof json.file === 'string') {\n frame = {\n file: json.file,\n lineNumber:\n typeof json.lineNumber === 'number' && Number.isSafeInteger(json.lineNumber)\n ? json.lineNumber\n : undefined,\n };\n }\n } catch {}\n if (!frame) {\n res.statusCode = 400;\n return res.end('Open stack frame requires the JSON stack frame as request body');\n }\n\n const root = getMetroServerRoot(metroConfig.projectRoot!);\n const file = await ensureFileInRootDirectory(root, frame.file);\n if (!file) {\n res.statusCode = 400;\n return res.end('Open stack frame requires target file to be in server root');\n }\n\n try {\n await openInEditorAsync(file, frame.lineNumber);\n return res.end('OK');\n } catch {\n res.statusCode = 5006;\n return res.end('Open stack frame failed to open local editor');\n }\n };\n}\n\nconst ensureFileInRootDirectory = async (root: string, file: string): Promise<string | null> => {\n try {\n file = path.resolve(root, file);\n file = await fs.promises.realpath(file);\n // Cannot be accessed using Metro's server API, we need to move the file\n // into the project root and try again.\n if (
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/start/server/metro/dev-server/createMetroMiddleware.ts"],"sourcesContent":["import { getMetroServerRoot } from '@expo/config/paths';\nimport type { MetroConfig } from '@expo/metro/metro';\nimport type MetroBundler from '@expo/metro/metro/Bundler';\nimport connect from 'connect';\nimport { Body } from 'fetch-nodeshim';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport { compression } from './compression';\nimport { createEventsSocket } from './createEventSocket';\nimport { createMessagesSocket } from './createMessageSocket';\nimport { Log } from '../../../../log';\nimport { isPathInside } from '../../../../utils/dir';\nimport { openInEditorAsync } from '../../../../utils/editor';\nimport { shouldThrottleRemoteDevCall } from '../../../../utils/net';\n\ninterface MetroMiddlewareOptions {\n getMetroBundler(): MetroBundler;\n serverBaseUrl: string;\n}\n\ninterface StackFrame {\n file: string;\n lineNumber?: number | undefined;\n}\n\nexport function createMetroMiddleware(\n metroConfig: Pick<MetroConfig, 'projectRoot'>,\n options: MetroMiddlewareOptions\n) {\n const messages = createMessagesSocket({ logger: Log, serverBaseUrl: options.serverBaseUrl });\n const events = createEventsSocket(messages);\n\n const middleware = connect()\n .use(noCacheMiddleware)\n .use(compression)\n // Support opening stack frames from clients directly in the editor\n .use('/open-stack-frame', createMetroOpenStackFrameMiddleware(metroConfig))\n // Support status check to detect if the packager needs to be started from the native side\n .use('/status', createMetroStatusMiddleware(metroConfig, options));\n\n return {\n middleware,\n messagesSocket: messages,\n eventsSocket: events,\n websocketEndpoints: {\n [messages.endpoint]: messages.server,\n [events.endpoint]: events.server,\n },\n };\n}\n\nconst noCacheMiddleware: connect.NextHandleFunction = (_req, res, next) => {\n res.setHeader('Surrogate-Control', 'no-store');\n res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');\n res.setHeader('Pragma', 'no-cache');\n res.setHeader('Expires', '0');\n next();\n};\n\nfunction createMetroStatusMiddleware(\n metroConfig: Pick<MetroConfig, 'projectRoot'>,\n options: MetroMiddlewareOptions\n): connect.NextHandleFunction {\n return async (_req, res) => {\n res.setHeader('X-React-Native-Project-Root', encodeURI(metroConfig.projectRoot!));\n res.flushHeaders();\n await options.getMetroBundler().ready();\n res.end('packager-status:running');\n };\n}\n\nfunction createMetroOpenStackFrameMiddleware(\n metroConfig: Pick<MetroConfig, 'projectRoot'>\n): connect.NextHandleFunction {\n return async (req, res, next) => {\n if (req.method !== 'POST') {\n return next();\n }\n\n let frame: StackFrame | undefined;\n try {\n const json = await new Body(req).json();\n if (typeof json === 'object' && json != null && typeof json.file === 'string') {\n frame = {\n file: json.file,\n lineNumber:\n typeof json.lineNumber === 'number' && Number.isSafeInteger(json.lineNumber)\n ? json.lineNumber\n : undefined,\n };\n }\n } catch {}\n if (!frame) {\n res.statusCode = 400;\n return res.end('Open stack frame requires the JSON stack frame as request body');\n }\n\n const root = getMetroServerRoot(metroConfig.projectRoot!);\n const file = await ensureFileInRootDirectory(root, frame.file);\n if (!file) {\n res.statusCode = 400;\n return res.end('Open stack frame requires target file to be in server root');\n }\n\n if (shouldThrottleRemoteDevCall()) {\n res.statusCode = 429;\n return res.end();\n }\n\n try {\n await openInEditorAsync(file, frame.lineNumber);\n return res.end('OK');\n } catch {\n res.statusCode = 5006;\n return res.end('Open stack frame failed to open local editor');\n }\n };\n}\n\nconst ensureFileInRootDirectory = async (root: string, file: string): Promise<string | null> => {\n try {\n file = path.resolve(root, file);\n file = await fs.promises.realpath(file);\n // Cannot be accessed using Metro's server API, we need to move the file\n // into the project root and try again.\n if (isPathInside(file, root)) {\n return file;\n } else {\n return null;\n }\n } catch {\n return null;\n }\n};\n"],"names":["createMetroMiddleware","metroConfig","options","messages","createMessagesSocket","logger","Log","serverBaseUrl","events","createEventsSocket","middleware","connect","use","noCacheMiddleware","compression","createMetroOpenStackFrameMiddleware","createMetroStatusMiddleware","messagesSocket","eventsSocket","websocketEndpoints","endpoint","server","_req","res","next","setHeader","encodeURI","projectRoot","flushHeaders","getMetroBundler","ready","end","req","method","frame","json","Body","file","lineNumber","Number","isSafeInteger","undefined","statusCode","root","getMetroServerRoot","ensureFileInRootDirectory","shouldThrottleRemoteDevCall","openInEditorAsync","path","resolve","fs","promises","realpath","isPathInside"],"mappings":"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"}
|
|
@@ -290,7 +290,9 @@ async function instantiateMetroAsync(metroBundler, options, { isExporting, exp =
|
|
|
290
290
|
});
|
|
291
291
|
Object.assign(websocketEndpoints, debugWebsocketEndpoints);
|
|
292
292
|
middleware.use(debugMiddleware);
|
|
293
|
-
middleware.use('/_expo/debugger', (0, _createJsInspectorMiddleware.createJsInspectorMiddleware)(
|
|
293
|
+
middleware.use('/_expo/debugger', (0, _createJsInspectorMiddleware.createJsInspectorMiddleware)({
|
|
294
|
+
serverBaseUrl
|
|
295
|
+
}));
|
|
294
296
|
// TODO(cedric): `enhanceMiddleware` is deprecated, but is currently used to unify the middleware stacks
|
|
295
297
|
// See: https://github.com/facebook/metro/commit/22e85fde85ec454792a1b70eba4253747a2587a9
|
|
296
298
|
// See: https://github.com/facebook/metro/commit/d0d554381f119bb80ab09dbd6a1d310b54737e52
|