domain.ts

1.5 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 * LV-2: true when the URL's hostname contains non-ASCII characters — a32 * potential homograph (Cyrillic "аpple.com" etc.) — OR a punycode (xn--)33 * label, which is the URL parser's normalized form of the same risk.34 * Matching/keying is NOT affected (tldts keeps Unicode labels distinct,35 * preventing cross-matching); this only drives a non-blocking UI warning so a36 * user who saved or imported such a URL can see it isn't the ASCII site.37 * Accepts bare hostnames as well as full URLs.38 */39export function detectHomograph(url: string): boolean {40  try {41    const hostname = new URL(url.includes("://") ? url : `https://${url}`).hostname;42    return (43      // biome-ignore lint/suspicious/noControlCharactersInRegex: intentional — detects non-ASCII hostnames44      /[^\x00-\x7F]/.test(hostname) ||45      hostname.split(".").some((l) => l.startsWith("xn--"))46    );47  } catch {48    return false;49  }50}