domain.ts

1.8 KB · raw

1import { getDomain } from "tldts";23/**4 * Extract eTLD+1 domain from a URL string.5 * Returns null for invalid URLs.6 */7export function extractDomain(url: string): string | null {8  try {9    const normalized = url.startsWith("http") ? url : "https://" + url;10    const parsed = getDomain(normalized);11    return parsed ?? null;12  } catch {13    return null;14  }15}1617/**18 * Get displayable domain from a URL string.19 */20export function getDisplayDomain(url: string): string {21  try {22    const normalized = url.startsWith("http") ? url : "https://" + url;23    const { hostname } = new URL(normalized);24    return hostname;25  } catch {26    return url;27  }28}2930/**31 * Preserve the browser security boundary users need to inspect before a32 * credential or trust decision: scheme, complete hostname, and port.33 */34export function getSecurityOriginDisplay(url: string): string {35  try {36    return new URL(url).origin;37  } catch {38    return url;39  }40}4142/**43 * LV-2: true when the URL's hostname contains non-ASCII characters — a44 * potential homograph (Cyrillic "аpple.com" etc.) — OR a punycode (xn--)45 * label, which is the URL parser's normalized form of the same risk.46 * Matching/keying is NOT affected (tldts keeps Unicode labels distinct,47 * preventing cross-matching); this only drives a non-blocking UI warning so a48 * user who saved or imported such a URL can see it isn't the ASCII site.49 * Accepts bare hostnames as well as full URLs.50 */51export function detectHomograph(url: string): boolean {52  try {53    const hostname = new URL(url.includes("://") ? url : `https://${url}`).hostname;54    return (55      // biome-ignore lint/suspicious/noControlCharactersInRegex: intentional — detects non-ASCII hostnames56      /[^\x00-\x7F]/.test(hostname) ||57      hostname.split(".").some((l) => l.startsWith("xn--"))58    );59  } catch {60    return false;61  }62}