Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions src/lib/sentry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,35 @@ export function initSentry(router: SentryRouter) {
replaysOnErrorSampleRate: 1.0,

enableLogs: true,

// Drop noise that's never actionable, so it doesn't bury real errors or
// burn quota: intentional fetch cancellation plus errors injected by the
// user's environment rather than our code.
ignoreErrors: [
// Intentional fetch/query cancellation.
"AbortError",
"The user aborted a request",
"signal is aborted without reason",
// Benign layout-loop notices the browser emits under load.
"ResizeObserver loop limit exceeded",
"ResizeObserver loop completed with undelivered notifications",
// Translating proxies (Yandex/Google Translate) rewrite the DOM and trip the History API.
"Failed to execute 'replaceState' on 'History'",
// Sandboxed-iframe / privacy-mode storage access denied.
"The operation is insecure",
],
denyUrls: [
/^chrome-extension:\/\//i,
/^moz-extension:\/\//i,
/^safari-web-extension:\/\//i,
],
beforeSend(event, hint) {
// Intentional cancellation (a query/fetch aborted mid-flight) is expected
// noise — never report it, however the SDK wrapped it.
const exception = hint.originalException as { name?: string } | undefined
if (exception?.name === "AbortError") return null
return event
},
})

initialized = true
Expand Down
24 changes: 24 additions & 0 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,30 @@ import { FeatureFlagProvider } from "./lib/feature-flags"
import { initSentry } from "./lib/sentry"
import { routeTree } from "./routeTree.gen"

// A `vite:preloadError` fires when a dynamic import for a code-split chunk
// fails — almost always because a newer build was deployed and this tab is now
// stale (it references content-hashed chunk filenames the deploy has replaced).
// Prompt to reload rather than force-reloading: a forced reload discards unsaved
// work (e.g. a half-filled login/registration form), and because the router uses
// `defaultPreload: "intent"` this event also fires for speculative preloads
// (hovering a stale link), where auto-reloading would be jarring.
// `preventDefault()` stops Vite re-throwing the failed import. Shown once so a
// burst of failures can't stack it.
let updateToastShown = false
window.addEventListener("vite:preloadError", (event) => {
event.preventDefault()
if (updateToastShown) return
updateToastShown = true
toast.info("A new version is available", {
description: "Reload to get the latest update.",
duration: Infinity,
action: {
label: "Reload",
onClick: () => window.location.reload(),
},
})
})

const queryClient = new QueryClient({
defaultOptions: {
queries: {
Expand Down
Loading