browser.ts
1/**2 * browser.ts — feature-detected browser identifier shared by background,3 * popup, and content scripts.4 *5 * The discriminator is feature presence + extension URL scheme, not user6 * agent string. `chrome.webAuthenticationProxy` is Chrome-only; the7 * `moz-extension://` URL scheme tells us we are running under Firefox.8 */910export type Browser = "chrome" | "firefox" | "unknown";1112export function detectBrowser(): Browser {13 if (typeof chrome === "undefined") return "unknown";14 // chrome.webAuthenticationProxy exists on Chrome 122+ regardless of15 // whether it has been attached. Treat its mere presence as the discriminator.16 if ((chrome as unknown as { webAuthenticationProxy?: unknown }).webAuthenticationProxy) {17 return "chrome";18 }19 try {20 const url = chrome.runtime?.getURL?.("") ?? "";21 if (url.startsWith("moz-extension://")) return "firefox";22 } catch { /* extension context unavailable */ }23 return "unknown";24}2526/**27 * Best-effort Firefox version detection. Used to gate the Firefox passkey28 * path on Firefox 150+. Returns NaN for non-Firefox or when we can't parse.29 */30export function firefoxVersion(): number {31 if (detectBrowser() !== "firefox") return NaN;32 const match = navigator.userAgent.match(/Firefox\/(\d+)/);33 return match ? Number(match[1]) : NaN;34}