Skip to content
62 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Site analytics with HeyCatch on g1t.sh, with names removed inside the app; the privacy policy says so1/**
2 * Product analytics (HeyCatch), on g1t.sh only: pageviews, clicks and
Analytics asks first in the EU, EEA, UK and Switzerland, and loads only when the page is idle3 * route changes. Every event passes through lib/analytics-scrub.ts first,
4 * so inside the app names, text and titles never leave the browser, and
5 * recordings are never sent. A signed-in person is known by their account
6 * id alone (`identify` below).
Site analytics with HeyCatch on g1t.sh, with names removed inside the app; the privacy policy says so7 *
Analytics asks first in the EU, EEA, UK and Switzerland, and loads only when the page is idle8 * The SDK is fetched once the page has loaded and the browser is idle, so
9 * it never slows a page down; where a visitor is asked first
10 * (lib/analytics-consent.ts) it is not fetched at all until they agree.
11 * Imported by entry.client.tsx. A self-hosted g1t sends nothing.
Site analytics with HeyCatch on g1t.sh, with names removed inside the app; the privacy policy says so12 */
Analytics asks first in the EU, EEA, UK and Switzerland, and loads only when the page is idle13import type { analytics as Analytics } from "@heycatch/sdk";
Site analytics with HeyCatch on g1t.sh, with names removed inside the app; the privacy policy says so14
Analytics asks first in the EU, EEA, UK and Switzerland, and loads only when the page is idle15import { allowed, analyticsHere, choice, consentRequired, onChoice } from "./analytics-consent";
Site analytics with HeyCatch on g1t.sh, with names removed inside the app; the privacy policy says so16import { scrubEvent } from "./analytics-scrub";
17
Analytics asks first in the EU, EEA, UK and Switzerland, and loads only when the page is idle18let sdk: Promise<typeof Analytics> | null = null;
19/** Who is signed in, kept for when the SDK arrives. */
20let signedIn: string | null = null;
21
22function load(): Promise<typeof Analytics> {
23 sdk ??= import("@heycatch/sdk").then(({ analytics }) => {
24 analytics.init({
25 projectKey: "hck_pk_EsF6nrWNZ0tOM4cmDKA6tKBzdTr-GEW3",
26 install: { framework: "react", frameworkVersion: "19", agent: "claude-code" },
27 respectDnt: true,
28 beforeSend: (event) => scrubEvent(event, window.location.pathname, window.location.origin),
29 });
30 if (signedIn) analytics.setIdentity(signedIn);
31 return analytics;
32 });
33 return sdk;
34}
Site analytics with HeyCatch on g1t.sh, with names removed inside the app; the privacy policy says so35
Analytics asks first in the EU, EEA, UK and Switzerland, and loads only when the page is idle36/** After the page has loaded, when the browser has a moment. */
37function whenIdle(run: () => void) {
38 // Safari has no requestIdleCallback.
39 const idle = () => (typeof window.requestIdleCallback === "function" ? window.requestIdleCallback(run, { timeout: 4000 }) : setTimeout(run, 1500));
40 if (document.readyState === "complete") idle();
41 else window.addEventListener("load", idle, { once: true });
42}
Site analytics with HeyCatch on g1t.sh, with names removed inside the app; the privacy policy says so43
Analytics asks first in the EU, EEA, UK and Switzerland, and loads only when the page is idle44if (analyticsHere()) {
45 if (allowed()) whenIdle(() => void load());
46 // A choice made on the banner, or changed from the footer.
47 onChoice(() => {
48 if (!consentRequired()) return;
49 if (choice() === "yes") void load().then((analytics) => analytics.optInCapturing());
50 else if (sdk) void sdk.then((analytics) => analytics.optOutCapturing());
Site analytics with HeyCatch on g1t.sh, with names removed inside the app; the privacy policy says so51 });
52}
53
54/** Who is signed in, by account id only; null after signing out. */
55export function identify(userId: string | null, previous: string | null) {
Analytics asks first in the EU, EEA, UK and Switzerland, and loads only when the page is idle56 signedIn = userId;
57 if (!sdk) return;
58 void sdk.then((analytics) => {
59 if (userId) analytics.setIdentity(userId);
60 else if (previous) analytics.resetIdentity();
61 });
Site analytics with HeyCatch on g1t.sh, with names removed inside the app; the privacy policy says so62}