1d42dd5d4SCedric van Puttenimport { ExpoConfig } from '@expo/config'; 2d42dd5d4SCedric van Puttenimport { Middleware } from 'metro-config'; 3d42dd5d4SCedric van Putten 4d42dd5d4SCedric van Puttenimport { DebugTool, getMetroDebugProperties } from './getMetroDebugProperties'; 5d42dd5d4SCedric van Puttenimport { logEventAsync } from './rudderstackClient'; 6*8a424bebSJames Ideimport { env } from '../env'; 7d42dd5d4SCedric van Putten 80381251eSTomasz Sapetatype Request = Parameters<Middleware>[0]; 90381251eSTomasz Sapetatype Response = Parameters<Middleware>[1]; 100381251eSTomasz Sapetatype Next = Parameters<Middleware>[2]; 110381251eSTomasz Sapeta 12d42dd5d4SCedric van Putten/** 13d42dd5d4SCedric van Putten * Create a Metro middleware that reports when a debugger request was found. 14d42dd5d4SCedric van Putten * This will only be reported once, if the app uses Hermes and telemetry is not enabled. 15d42dd5d4SCedric van Putten */ 16d42dd5d4SCedric van Puttenexport function createDebuggerTelemetryMiddleware( 17d42dd5d4SCedric van Putten projectRoot: string, 18d42dd5d4SCedric van Putten exp: ExpoConfig 19d42dd5d4SCedric van Putten): Middleware { 20d42dd5d4SCedric van Putten let hasReported = false; 21d42dd5d4SCedric van Putten 22d42dd5d4SCedric van Putten // This only works for Hermes apps, disable when telemetry is turned off 23d42dd5d4SCedric van Putten if (env.EXPO_NO_TELEMETRY || exp.jsEngine !== 'hermes') { 240381251eSTomasz Sapeta return (req: Request, res: Response, next: Next) => { 250381251eSTomasz Sapeta if (typeof next === 'function') { 260381251eSTomasz Sapeta next(undefined); 270381251eSTomasz Sapeta } 280381251eSTomasz Sapeta }; 29d42dd5d4SCedric van Putten } 30d42dd5d4SCedric van Putten 310381251eSTomasz Sapeta return (req: Request, res: Response, next: Next) => { 32d42dd5d4SCedric van Putten // Only report once 330381251eSTomasz Sapeta if (hasReported && typeof next === 'function') { 34d42dd5d4SCedric van Putten return next(undefined); 35d42dd5d4SCedric van Putten } 36d42dd5d4SCedric van Putten 37d42dd5d4SCedric van Putten const debugTool = findDebugTool(req); 38d42dd5d4SCedric van Putten if (debugTool) { 39d42dd5d4SCedric van Putten hasReported = true; 40d42dd5d4SCedric van Putten logEventAsync('metro debug', getMetroDebugProperties(projectRoot, exp, debugTool)); 41d42dd5d4SCedric van Putten } 42d42dd5d4SCedric van Putten 430381251eSTomasz Sapeta if (typeof next === 'function') { 44d42dd5d4SCedric van Putten return next(undefined); 450381251eSTomasz Sapeta } 46d42dd5d4SCedric van Putten }; 47d42dd5d4SCedric van Putten} 48d42dd5d4SCedric van Putten 49d42dd5d4SCedric van Putten/** Exposed for testing */ 50d42dd5d4SCedric van Puttenexport function findDebugTool( 51d42dd5d4SCedric van Putten req: Pick<Parameters<Middleware>[0], 'headers' | 'url'> 52d42dd5d4SCedric van Putten): DebugTool | null { 53d42dd5d4SCedric van Putten if (req.headers['origin']?.includes('chrome-devtools')) { 54d42dd5d4SCedric van Putten return { name: 'chrome' }; 55d42dd5d4SCedric van Putten } 56d42dd5d4SCedric van Putten 57d42dd5d4SCedric van Putten if (req.url?.startsWith('/json')) { 58d42dd5d4SCedric van Putten const flipperUserAgent = req.headers['user-agent']?.match(/(Flipper)\/([^\s]+)/); 59d42dd5d4SCedric van Putten if (flipperUserAgent) { 60d42dd5d4SCedric van Putten return { 61d42dd5d4SCedric van Putten name: flipperUserAgent[1].toLowerCase(), 62d42dd5d4SCedric van Putten version: flipperUserAgent[2], 63d42dd5d4SCedric van Putten }; 64d42dd5d4SCedric van Putten } 65d42dd5d4SCedric van Putten } 66d42dd5d4SCedric van Putten 67d42dd5d4SCedric van Putten return null; 68d42dd5d4SCedric van Putten} 69