import { env, waitUntil } from "cloudflare:workers";
/**
* Product analytics (PostHog), optional like Stripe: off unless POSTHOG_KEY (or the VITE_POSTHOG_PROJECT_TOKEN
* alias) is set. Server events carry no PII: distinct ids are opaque account ids, and properties are enums
* and counts only, never emails, names, private repository names or paths.
*/
export type AnalyticsConfig = { key: string; host: string };
const DEFAULT_HOST = "https://us.i.posthog.com";
const CAPTURE_TIMEOUT_MS = 3000;
export function analyticsConfig(): AnalyticsConfig | null {
const key = (env.POSTHOG_KEY || env.VITE_POSTHOG_PROJECT_TOKEN || "").trim();
if (!/^phc_[A-Za-z0-9]+$/.test(key)) return null;
const raw = (env.POSTHOG_HOST || env.VITE_POSTHOG_HOST || DEFAULT_HOST).trim();
try {
const url = new URL(raw);
if (url.protocol !== "https:" || url.username || url.password) return null;
return { key, host: url.origin };
} catch {
return null;
}
}
/** What the browser needs to start PostHog; null when analytics is off. */
export function clientAnalyticsConfig(): { key: string; host: string; origin: string; personEmail: boolean } | null {
const config = analyticsConfig();
if (!config) return null;
try {
return { ...config, origin: new URL(env.PUBLIC_URL).origin, personEmail: env.POSTHOG_PERSON_EMAIL === "true" };
} catch {
return null;
}
}
type Value = string | number | boolean | null;
/**
* Fire-and-forget server event. Runs under waitUntil with a short timeout and swallows every error, so it can