/** * prefetch-hover.ts — Hover-intent prefetch/prerender for key navigation targets. * * Enabled server-side via WEB_PREFETCH=1. Elements opt in with: * data-prefetch="1" — enable prefetch on this element * data-prefetch-url="" — URL to prefetch (falls back to el.href) * data-prerender-ok="1" — allow Chrome Speculation Rules prerender * (only safe GET routes with no side effects) * * Strategy selection (per element): * data-prerender-ok="1" + Chrome Speculation Rules support → prerender * otherwise → rel=prefetch * * Progressive enhancement: degrades gracefully when unsupported. * Respects navigator.connection.saveData and slow (2G) effective connections. */ (function () { 'use strict'; const MAX_CONCURRENT = 2; const MAX_PRERENDERS = 2; const DELAY_MS = 100; let _inflight = 0; const _prefetched: Record = {}; // Speculation Rules API detection (Chrome 108+) const _supportsSpeculation: boolean = (function () { try { return typeof HTMLScriptElement !== 'undefined' && 'supports' in HTMLScriptElement && typeof (HTMLScriptElement as any).supports === 'function' && (HTMLScriptElement as any).supports('speculationrules'); } catch (_) { return false; } })(); let _speculationEl: HTMLScriptElement | null = null; const _prerenderQueued: string[] = []; function _saverMode(): boolean { try { const conn: any = (navigator as any).connection || (navigator as any).mozConnection || (navigator as any).webkitConnection || {}; if (conn.saveData === true) return true; const et: string = conn.effectiveType || ''; return et === '2g' || et === 'slow-2g'; } catch (_) { return false; } } /** Inject/update a single