|
| 1 | +// Links to other sites that are confirmed dead (404 on re-check 2026-10-17, from |
| 2 | +// the ThreatCrush Link Watch report). Post bodies arrive by webhook or as user |
| 3 | +// content, so rather than hand-edit rows the dead anchors are unwrapped at |
| 4 | +// render time: the link goes, the text stays. |
| 5 | + |
| 6 | +const DEAD_URLS = new Set<string>([ |
| 7 | + "https://analytics.ahrefs.com", |
| 8 | + "https://www.googletagmanager.com", |
| 9 | + "https://bxiie.artsfol.io/artwork/in-paint-bxiie-br-34", |
| 10 | + "https://c0upons.com/blog/doordash-promo-codes-for-existing-users-2026-workflow", |
| 11 | + "https://c0upons.com/blog/encrypted-messaging-coupon-codes-private-deal-sharing", |
| 12 | + "https://c0upons.com/blog/shutterfly-promo-codes-2026-checkout-workflow", |
| 13 | + "https://c0upons.com/blog/walgreens-coupon-code-workflow", |
| 14 | + "https://crates.io/crates/graphforge-rel", |
| 15 | + "https://crates.io/crates/piu_node_resolver", |
| 16 | + "https://crates.io/crates/rightkit-credentials", |
| 17 | + "https://crates.io/crates/willbooster-tree-sitter-rust", |
| 18 | + "https://crates.io/crates/wsi-dicom-annotations", |
| 19 | + "https://devdrafts.netlify.app", |
| 20 | + "https://feedbackminer.co/cookies", |
| 21 | + "https://feedbackminer.co/privacy", |
| 22 | + "https://feedbackminer.co/sub-processors", |
| 23 | + "https://feedbackminer.co/terms", |
| 24 | + "https://seniorcontact.app/en/about-us", |
| 25 | +]); |
| 26 | + |
| 27 | +/** Placeholder hosts (RFC 2606): never a real page, so never worth a link. */ |
| 28 | +const PLACEHOLDER_HOST = /^(?:www\.)?example\.(?:com|org|net)$/i; |
| 29 | + |
| 30 | +function normalize(href: string): string { |
| 31 | + return href.replace(/#.*$/, "").replace(/\/+$/, "").toLowerCase(); |
| 32 | +} |
| 33 | + |
| 34 | +export function isDeadExternalUrl(href: string): boolean { |
| 35 | + let url: URL; |
| 36 | + try { |
| 37 | + url = new URL(href); |
| 38 | + } catch { |
| 39 | + return false; |
| 40 | + } |
| 41 | + if (url.protocol !== "http:" && url.protocol !== "https:") return false; |
| 42 | + if (PLACEHOLDER_HOST.test(url.hostname)) return true; |
| 43 | + if (url.pathname.startsWith("/cdn-cgi/l/email-protection")) return true; |
| 44 | + return DEAD_URLS.has(normalize(href)); |
| 45 | +} |
| 46 | + |
| 47 | +const ANCHOR = /<a\s[^>]*?href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/gi; |
| 48 | + |
| 49 | +/** Unwrap every anchor that points at a known-dead URL, keeping its text. */ |
| 50 | +export function stripDeadLinks(html: string): string { |
| 51 | + return html.replace(ANCHOR, (whole, href: string, inner: string) => |
| 52 | + isDeadExternalUrl(href.replace(/&/g, "&")) ? inner : whole, |
| 53 | + ); |
| 54 | +} |
0 commit comments