// ───────────────────────────────────────────────────────────── // src/ua.js — minimal User-Agent parser. // // Coarse-grained classification only: device_type / os / browser. // We deliberately do NOT pull in ua-parser-js — the project keeps a // small dependency footprint and we only need three buckets. The raw // UA is also stored alongside parsed fields (see src/events.js) so a // regex miss can be re-classified later. // // Also owns MOBILE_UA_RE — the existing /timeline view-dispatch // regex used by server.js. Single source of truth. // ───────────────────────────────────────────────────────────── // UA substrings that mean "phone-class small screen". Tablets (iPad, // Android tablets) deliberately do NOT match — they get the desktop // view, which matches existing behaviour in server.js. export const MOBILE_UA_RE = /\b(iPhone|iPod|Android.*Mobile|Mobile.*Firefox|IEMobile|BlackBerry|Opera Mini)\b/i; // Tablet-class devices. Order matters in parseUA(): tablet check runs // before mobile so "iPad" doesn't accidentally fall through to desktop. const TABLET_UA_RE = /\b(iPad|Android(?!.*Mobile))\b/i; export function parseUA(ua) { if (!ua || typeof ua !== 'string') { return { device_type: null, os: null, browser: null }; } // device_type — fallback is 'desktop' to mirror server.js // wantsMobileView() (unrecognised UA → desktop page). Analytics // stay consistent with what the user actually saw. let device_type = 'desktop'; if (TABLET_UA_RE.test(ua)) device_type = 'tablet'; else if (MOBILE_UA_RE.test(ua)) device_type = 'mobile'; // os let os = 'other'; if (/\b(iPhone|iPad|iPod)\b/.test(ua)) os = 'iOS'; else if (/\bAndroid\b/.test(ua)) os = 'Android'; else if (/\bWindows\b/.test(ua)) os = 'Windows'; else if (/Mac OS X|Macintosh/.test(ua)) os = 'macOS'; else if (/\bLinux\b/.test(ua)) os = 'Linux'; // browser — order matters. All Chromium UAs include "Safari/", all // Edge UAs include "Chrome/", so the most specific token must win. let browser = 'other'; if (/\bEdg\//.test(ua)) browser = 'Edge'; else if (/\bFirefox\//.test(ua)) browser = 'Firefox'; else if (/\bChrome\//.test(ua)) browser = 'Chrome'; else if (/\bSafari\//.test(ua)) browser = 'Safari'; return { device_type, os, browser }; }