export interface UserAgentInfo { browser: string | null operatingSystem: string | null device: string | null } const BROWSER_PATTERNS: Array<[RegExp, string]> = [ [/Edg\//i, 'Microsoft Edge'], [/OPR\//i, 'Opera'], [/Chrome\//i, 'Chrome'], [/Safari\//i, 'Safari'], [/Firefox\//i, 'Firefox'], [/MSIE |Trident\//i, 'Internet Explorer'] ] const OS_PATTERNS: Array<[RegExp, string]> = [ [/Windows NT 10\.0/i, 'Windows 10/11'], [/Windows NT 6\.3/i, 'Windows 8.1'], [/Windows NT 6\.2/i, 'Windows 8'], [/Windows NT 6\.1/i, 'Windows 7'], [/Windows/i, 'Windows'], [/Mac OS X ([\d_.]+)/i, 'macOS'], [/iPhone OS ([\d_]+)/i, 'iOS'], [/iPad.*CPU OS ([\d_]+)/i, 'iPadOS'], [/Android ([\d.]+)/i, 'Android'], [/Linux/i, 'Linux'] ] const DEVICE_PATTERNS: Array<[RegExp, string]> = [ [/iPhone/i, 'iPhone'], [/iPad/i, 'iPad'], [/Android.*Mobile/i, 'Android Phone'], [/Android/i, 'Android Tablet'], [/Windows Phone/i, 'Windows Phone'], [/Macintosh/i, 'Mac'], [/Windows/i, 'Windows PC'], [/Linux/i, 'Linux PC'] ] export function parseUserAgent(userAgent: string | null | undefined): UserAgentInfo { const ua = (userAgent ?? '').trim() if (!ua) return { browser: null, operatingSystem: null, device: null } let browser: string | null = null for (const [regex, name] of BROWSER_PATTERNS) { if (regex.test(ua)) { browser = name break } } let operatingSystem: string | null = null for (const [regex, name] of OS_PATTERNS) { if (regex.test(ua)) { operatingSystem = name break } } let device: string | null = null for (const [regex, name] of DEVICE_PATTERNS) { if (regex.test(ua)) { device = name break } } return { browser, operatingSystem, device } } export type LoginMethod = 'password' | 'oauth' | 'webauthn' | 'totp_2fa' | 'email_2fa'