← back to Homesonspec
apps/web/src/lib/analytics.ts
42 lines
// Fire an analytics event down every available channel, best-effort and non-throwing —
// analytics must never break the page.
// • gtag → GA4 (no-op until a GA4 tag is added; lights up the instant it is)
// • dataLayer → Google Tag Manager
// • /api/pwa-event beacon → first-party sink so the funnel is measurable TODAY,
// with no external analytics account required.
type Params = Record<string, string | number | boolean | undefined>;
export function track(event: string, params: Params = {}): void {
if (typeof window === "undefined") return;
const payload = { event, ...params };
// gtag + dataLayer (dormant-safe)
try {
const w = window as unknown as {
gtag?: (...a: unknown[]) => void;
dataLayer?: unknown[];
};
if (typeof w.gtag === "function") w.gtag("event", event, params);
(w.dataLayer = w.dataLayer || []).push(payload);
} catch {
/* ignore */
}
// First-party sink — anonymous funnel counters, no PII.
try {
const body = JSON.stringify({ ...payload, t: Date.now(), path: location.pathname });
if (navigator.sendBeacon) {
navigator.sendBeacon("/api/pwa-event", new Blob([body], { type: "application/json" }));
} else {
void fetch("/api/pwa-event", {
method: "POST",
body,
keepalive: true,
headers: { "content-type": "application/json" },
}).catch(() => {});
}
} catch {
/* ignore */
}
}