| 45 | }; |
| 46 | |
| 47 | export const initDesktopCrashReporting = (): void => { |
| 48 | if (typeof window === "undefined") return; |
| 49 | const bridge = (window as Window & { readonly executor?: CrashReportingBridge }).executor; |
| 50 | if (typeof bridge?.getCrashReporting !== "function") return; |
| 51 | const init = async () => { |
| 52 | // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: crash reporting must never take the app down with it |
| 53 | try { |
| 54 | const config = await bridge.getCrashReporting?.(); |
| 55 | if (!config?.dsn) return; |
| 56 | const Sentry = await import("@sentry/browser"); |
| 57 | Sentry.init({ |
| 58 | dsn: config.dsn, |
| 59 | release: config.release, |
| 60 | environment: config.environment, |
| 61 | sendDefaultPii: false, |
| 62 | tracesSampleRate: 0, |
| 63 | initialScope: { |
| 64 | tags: { |
| 65 | process: "renderer", |
| 66 | runId: config.runId, |
| 67 | }, |
| 68 | }, |
| 69 | // Route chunks are content-hashed, so an unresolved frame names |
| 70 | // `atoms-<hash>` and one bug re-groups on every release. Pin a |
| 71 | // fingerprint with the hash normalized out; the event itself keeps its |
| 72 | // hashed filenames so sourcemap resolution is unaffected. |
| 73 | beforeSend: withStableGroupingFingerprint, |
| 74 | }); |
| 75 | initializedReporter = createSentryFrontendErrorReporter((error, applyScope) => { |
| 76 | Sentry.captureException(error, (scope) => { |
| 77 | applyScope(scope); |
| 78 | return scope; |
| 79 | }); |
| 80 | }); |
| 81 | } catch { |
| 82 | // Reporting failures stay silent — there is nowhere left to report them. |
| 83 | } |
| 84 | }; |
| 85 | void init(); |
| 86 | }; |