declare global { interface Window { umami?: { track: (event: string, data?: Record) => void; }; } } type Pending = { event: string; data?: Record; onSent?: () => void }; // Events fired during page setup (onMounted) often run before the deferred // Umami script has loaded; they used to be dropped silently. Queue them and // flush once the tracker exists. If it never appears (blocked, offline), // they are discarded after ~10 s without throwing. const queue: Pending[] = []; let timer: ReturnType | null = null; let attempts = 0; const MAX_ATTEMPTS = 40; const INTERVAL_MS = 250; function send(item: Pending) { try { window.umami!.track(item.event, item.data); item.onSent?.(); } catch { // tracking must never break the shop } } function flush() { if (window.umami) { while (queue.length) send(queue.shift()!); } if (window.umami || ++attempts >= MAX_ATTEMPTS) { queue.length = 0; if (timer) clearInterval(timer); timer = null; attempts = 0; } } export const trackEvent = (event: string, data?: Record, onSent?: () => void) => { if (typeof window === "undefined") return; const item = { event, data, onSent }; if (window.umami) return send(item); queue.push(item); if (!timer) timer = setInterval(flush, INTERVAL_MS); }; // Test hook: reset module state between cases. export const __resetTrackQueue = () => { queue.length = 0; if (timer) clearInterval(timer); timer = null; attempts = 0; };