/**
* Browser analytics (PostHog). Everything here is a no-op unless the server sent a config, which it only does
* when POSTHOG_KEY is set. posthog-js is loaded lazily, so instances without analytics never download it.
*
* Privacy rules enforced here:
* - Honors Do Not Track and Global Privacy Control, and a per-browser opt-out (setAnalyticsOptOut).
* - Runs only on the forge's own origin, never on editor, user-content, sites or pages origins.
* - URLs lose query strings and fragments; repository paths are redacted unless the repo is known public.
* - Autocapture never records element text or attributes; inputs are masked in replays, and replays pause on
* private repositories and on account, settings and auth pages.
*/
import type { PostHog } from "posthog-js";
import { scrubErrorMessage } from "./scrub-error";
import { APP_ROOTS, isRouteTemplate, routeTemplate } from "./route-template";
export type ClientAnalyticsConfig = { key: string; host: string; origin: string; personEmail: boolean };
export type AnalyticsViewer = { id: string; email?: string; isAdmin?: boolean } | null;
const OPT_OUT_KEY = "gitfor:analytics-opt-out";
export const INGEST_PREFIX = "/ingest";
/** PostHog's app host for toolbar links: us.i.posthog.com -> us.posthog.com. */
function uiHost(host: string): string {
return host.replace(/^https:\/\/(us|eu)\.i\.posthog\.com$/, "https://$1.posthog.com");
}
const REDACTED = "[private]";
const NO_RECORDING = [
/^\/settings(\/|$)/,
/^\/admin(\/|$)/,
/^\/(login|signup|reset-password|forgot-password|verify-email)/,
/^\/[^/]+\/[^/]+\/settings(\/|$)/,
/^\/orgs\/[^/]+\/settings/,
];
let client: PostHog | null = null;
let loading: Promise<PostHog | null> | null = null;
const publicRepos = new Set<string>();
function repoKey(owner: string, repo: string): string {