diff --git a/apps/sim/app/_shell/providers/posthog-provider.tsx b/apps/sim/app/_shell/providers/posthog-provider.tsx index 17e3f8c040b..d7c0152476f 100644 --- a/apps/sim/app/_shell/providers/posthog-provider.tsx +++ b/apps/sim/app/_shell/providers/posthog-provider.tsx @@ -35,6 +35,23 @@ export function PostHogProvider({ children }: { children: React.ReactNode }) { capture_performance: false, capture_dead_clicks: false, enable_heatmaps: false, + /** + * PostHog's own error tracking, wired to `window.onerror` and + * `unhandledrejection`. This is the app-wide net: React error + * boundaries only see errors thrown inside the tree they wrap, and + * a failed chunk load, a rejected promise, or anything thrown from + * an event handler or socket callback reaches none of them. + * + * `capture_console_errors` stays off. It is not error reporting — + * it captures every `console.error`, which here means React's + * hydration and dev warnings (the ones `HydrationErrorHandler` + * already filters out as noise) drowning the real exceptions. + */ + capture_exceptions: { + capture_unhandled_errors: true, + capture_unhandled_rejections: true, + capture_console_errors: false, + }, disable_session_recording: true, session_recording: { maskAllInputs: false, diff --git a/apps/sim/app/_shell/public-env-script.test.tsx b/apps/sim/app/_shell/public-env-script.test.tsx index 7eb6aadf3ec..efe90cb73d1 100644 --- a/apps/sim/app/_shell/public-env-script.test.tsx +++ b/apps/sim/app/_shell/public-env-script.test.tsx @@ -2,8 +2,11 @@ * @vitest-environment node */ import { renderToStaticMarkup } from 'react-dom/server' -import { describe, expect, it } from 'vitest' -import { PublicEnvScript } from '@/app/_shell/public-env-script' +import { describe, expect, it, vi } from 'vitest' +import { PUBLIC_ENV_ATTRIBUTE } from '@/lib/core/config/env' +import { PublicEnvScript, publicEnvHtmlAttributes } from '@/app/_shell/public-env-script' + +vi.unmock('@/lib/core/config/env') /** * Guards the one property that matters: the emitted tag assigns `window.__ENV` @@ -32,3 +35,35 @@ describe('PublicEnvScript', () => { expect(keys.every((key) => /^NEXT_PUBLIC_/i.test(key))).toBe(true) }) }) + +/** + * The script above is rendered from the component tree, so it lands at the end + * of `
` - after the bootstrap chunks that can already be executing. These + * attributes go on ``, the document's first tag, which is what makes the + * same values readable by code that runs in that gap. + */ +describe('publicEnvHtmlAttributes', () => { + it('carries the public env under the attribute getEnv reads', () => { + const attributes = publicEnvHtmlAttributes() + + expect(Object.keys(attributes)).toEqual([PUBLIC_ENV_ATTRIBUTE]) + expect(() => JSON.parse(attributes[PUBLIC_ENV_ATTRIBUTE])).not.toThrow() + }) + + it('exposes only NEXT_PUBLIC_ variables', () => { + const values = JSON.parse(publicEnvHtmlAttributes()[PUBLIC_ENV_ATTRIBUTE]) + + expect(Object.keys(values).every((key) => /^NEXT_PUBLIC_/i.test(key))).toBe(true) + }) + + /** + * Two transports for one snapshot only stays safe while they agree; a reader + * that resolved different values depending on which one it happened to hit + * would be worse than the race this replaces. + */ + it('carries exactly what the script assigns', () => { + const values = JSON.parse(publicEnvHtmlAttributes()[PUBLIC_ENV_ATTRIBUTE]) + + expect(values).toEqual(PublicEnvScript().props.env) + }) +}) diff --git a/apps/sim/app/_shell/public-env-script.tsx b/apps/sim/app/_shell/public-env-script.tsx index b2ff7b0f58e..a1a97765c7a 100644 --- a/apps/sim/app/_shell/public-env-script.tsx +++ b/apps/sim/app/_shell/public-env-script.tsx @@ -1,19 +1,48 @@ import { EnvScript } from 'next-runtime-env' +import { PUBLIC_ENV_ATTRIBUTE } from '@/lib/core/config/env' + +/** + * Every `NEXT_PUBLIC_*` value currently in `process.env`. Filter matches + * `next-runtime-env`'s own `getPublicEnv()` exactly. + */ +function readPublicEnv(): Record