/** * Imperative shell for page view analytics: sends one page view to the * Umami collection API, identifying the visitor via request headers. * Fire-and-forget by design — analytics must never delay a response. */ import type { PageviewFacts } from "@/src/domain/pageview-decision"; import { buildUmamiPageviewBody } from "@/src/domain/umami-payload"; const SEND_TIMEOUT_MS = 3_000; const websiteId = process.env.UMAMI_WEBSITE_ID ?? ""; const apiEndpoint = (process.env.UMAMI_API_ENDPOINT ?? "").replace(/\/+$/, ""); export function recordPageviewToUmami(facts: PageviewFacts): void { if (!websiteId || !apiEndpoint) return; const body = buildUmamiPageviewBody(facts, websiteId); void fetch(`${apiEndpoint}/api/send`, { method: "POST", headers: { "Content-Type": "application/json", "User-Agent": facts.userAgent, "X-Forwarded-For": facts.clientIp, "Accept-Language": facts.language, }, body: JSON.stringify(body), signal: AbortSignal.timeout(SEND_TIMEOUT_MS), }).catch((error: unknown) => { console.error("umami pageview send failed", { url: facts.url, error: error instanceof Error ? error.message : String(error), }); }); }