diff --git a/web/scripts/analytics.test.mjs b/web/scripts/analytics.test.mjs index 09671b7..cccf52f 100644 --- a/web/scripts/analytics.test.mjs +++ b/web/scripts/analytics.test.mjs @@ -143,3 +143,15 @@ test("the built pages do not fetch posthog-js up front", async () => { } } }); + +test("errors leave the browser", () => { + // The other silent break: turn this off and the site keeps working, the + // console keeps logging, and the exception stream just stops. main.ts's + // trap renders the recovery screen but reports nothing off-box; this option + // is the only thing that does. + assert.match( + analytics, + /capture_exceptions:\s*true/, + "exception capture is off - crashes in the field are invisible again", + ); +}); diff --git a/web/src/analytics.ts b/web/src/analytics.ts index 323adb1..dd362d4 100644 --- a/web/src/analytics.ts +++ b/web/src/analytics.ts @@ -121,6 +121,15 @@ export async function mountAnalytics(): Promise { // for every stranger who reads one blog post. Signing in is what creates // one, in identify() below. person_profiles: "identified_only", + + // Escaped errors and unhandled rejections leave the browser as $exception + // events. Until this, a crash in the field was a console.error in a tab + // nobody was looking at - main.ts's trap renders the recovery screen, but + // nothing off-box ever heard about it. PostHog's listeners see the same + // two channels the trap does, on every page the layout mounts, blog + // included. Free to 100k exceptions a month, and a runaway error loop is + // one page's session at a time, not the site multiplying pageviews. + capture_exceptions: true, }); ph = posthog;