diff --git a/instrumentation-client.ts b/instrumentation-client.ts index 57d4d0c..539beb5 100644 --- a/instrumentation-client.ts +++ b/instrumentation-client.ts @@ -11,7 +11,15 @@ posthog.init(process.env.NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN!, { debug: process.env.NODE_ENV === 'development', }); -const sentryOptions = buildSentryOptions('client'); +// These must be written as literal `process.env.X` expressions: that is the +// only form Next substitutes when building the client bundle. Reading them +// dynamically leaves `undefined` in the browser and silently disables Sentry. +const sentryOptions = buildSentryOptions('client', { + NEXT_PUBLIC_SENTRY_DSN: process.env.NEXT_PUBLIC_SENTRY_DSN, + NODE_ENV: process.env.NODE_ENV, + SENTRY_ENVIRONMENT: process.env.NEXT_PUBLIC_SENTRY_ENVIRONMENT, + SENTRY_RELEASE: process.env.NEXT_PUBLIC_SENTRY_RELEASE, +}); Sentry.init({ enabled: sentryOptions.enabled, diff --git a/tests/observability/client-dsn-inlining.test.ts b/tests/observability/client-dsn-inlining.test.ts new file mode 100644 index 0000000..81f85e8 --- /dev/null +++ b/tests/observability/client-dsn-inlining.test.ts @@ -0,0 +1,28 @@ +import { readFileSync } from 'node:fs'; +import path from 'node:path'; + +import { describe, expect, test } from 'vitest'; + +/** + * Next only substitutes *literal* `process.env.NEXT_PUBLIC_*` references when + * it builds the client bundle. Reading the same name dynamically — say + * `env['NEXT_PUBLIC_SENTRY_DSN']` inside a helper — is left untouched, so the + * value is `undefined` in the browser and Sentry disables itself without any + * error. That shipped once already; these tests keep it from shipping again. + */ +describe('instrumentation-client.ts', () => { + const source = readFileSync( + path.resolve(__dirname, '../../instrumentation-client.ts'), + 'utf8', + ); + + test('references the public DSN literally so the bundler can inline it', () => { + expect(source).toContain('process.env.NEXT_PUBLIC_SENTRY_DSN'); + }); + + test('does not hand the raw process.env object to the options builder', () => { + // buildSentryOptions('client') with no second argument falls back to + // process.env, which is exactly the non-inlinable path. + expect(source).not.toMatch(/buildSentryOptions\(\s*'client'\s*\)/); + }); +});