'use client'; import { useEffect } from 'react'; /** * Client-side Sentry init (Session 10). * * Manual init rather than the @sentry/nextjs `withSentryConfig` * wrapper because that plugin conflicts with standalone output mode * (the Coolify production build). Manual init keeps the bundle * simple: nothing imported when DSN is unset, lazy import via * dynamic import() when it is. * * Mount once at the root layout — repeated mounts get the * Sentry-internal idempotency guard, but we keep the layout single- * mount to avoid unnecessary work. */ export default function SentryInit() { useEffect(() => { const dsn = process.env.NEXT_PUBLIC_SENTRY_DSN; if (!dsn) return; let cancelled = false; (async () => { try { const Sentry = await import('@sentry/nextjs'); if (cancelled) return; Sentry.init({ dsn, tracesSampleRate: 0.1, // PII posture: don't sweep up IPs / cookies automatically. sendDefaultPii: false, // Trim heavy integrations we don't need for free-tier volume. integrations: (defaults) => defaults.filter((i) => i.name !== 'Replay'), beforeSend(event) { if (event.user) { delete event.user.ip_address; delete event.user.email; } return event; }, }); } catch { // Sentry init failure is never user-facing — degrade silently. } })(); return () => { cancelled = true; }; }, []); return null; }