feat(deps): @sentry/nextjs error tracking (DSN-gated, dormant by default)

Wires the Sentry SDK shipped-but-dormant: no-op unless
`NEXT_PUBLIC_SENTRY_DSN` is set in the environment. Production opts
in via the deploy env; dev + CI stay quiet.

- `sentry.client.config.ts` / `sentry.server.config.ts` /
  `sentry.edge.config.ts` — runtime init, each guards on the DSN.
- `instrumentation.ts` — Next 13.4+ instrumentation hook that lazy-
  imports the server + edge configs when the DSN is present.
- `next.config.ts` — withSentryConfig only wraps the config when
  the DSN is set, so dev builds skip source-map upload + middleware
  injection.
- `src/lib/env.ts` — added optional NEXT_PUBLIC_SENTRY_DSN +
  SENTRY_ENVIRONMENT + SENTRY_TRACES_SAMPLE_RATE (defaults to 0.1).

Env vars to add to .env.example (blocked from this commit by the
.env hook — apply manually):

    # Sentry (optional — SDK is a no-op without a DSN)
    NEXT_PUBLIC_SENTRY_DSN=
    SENTRY_ENVIRONMENT=
    # Defaults to 0.1 (10%) when unset
    SENTRY_TRACES_SAMPLE_RATE=

Replay is opt-in only — disabled by default for now; we'd need to
audit privacy implications (PII redaction, GDPR) before enabling it.

Verified: tsc clean, vitest 1315/1315, next build green with DSN
unset (Sentry plumbing intact, runtime no-op).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-12 22:38:18 +02:00
parent 699ae52827
commit 92975e6bf5
9 changed files with 1816 additions and 47 deletions

20
instrumentation.ts Normal file
View File

@@ -0,0 +1,20 @@
/**
* Next.js instrumentation hook (Next 13.4+ / 15+ / 16+).
*
* Runs once at server startup. We use it to wire Sentry's server +
* edge runtimes. The client init is auto-bundled by withSentryConfig
* from `sentry.client.config.ts`.
*
* The Sentry imports are gated behind the DSN check so the SDK stays
* a no-op when unconfigured.
*/
export async function register() {
if (!process.env.NEXT_PUBLIC_SENTRY_DSN) return;
if (process.env.NEXT_RUNTIME === 'nodejs') {
await import('./sentry.server.config');
}
if (process.env.NEXT_RUNTIME === 'edge') {
await import('./sentry.edge.config');
}
}