Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
c27f8ee
Add Sentry error monitoring using individual SDKs
RisingOrange Apr 2, 2026
73c1f9e
Remove @sentry/cli dep, use npx @sentry/cli instead
RisingOrange Apr 2, 2026
6d77299
Fix ESLint warnings in Sentry hooks
RisingOrange Apr 2, 2026
c8e72ae
Clean up import order in vite.config.ts
RisingOrange Apr 2, 2026
06de1f4
Enable Sentry logs on server
RisingOrange Apr 2, 2026
22a8470
Use waitUntil for Sentry flush instead of blocking response
RisingOrange Apr 2, 2026
332a315
Remove unnecessary type cast for platform context
RisingOrange Apr 2, 2026
b1d3ed6
Inject Debug IDs into edge source maps, skip Deno SDK in Node prerender
RisingOrange Apr 2, 2026
88fb77d
Add Sentry CLI as dev dependency
Wituareard Apr 2, 2026
7c116d6
Ignore @sentry/cli as unused dependency
Wituareard Apr 2, 2026
bafb3b9
Truncate sentry CLI output
Wituareard Apr 2, 2026
168ddfc
Revert "Truncate sentry CLI output"
Wituareard Apr 2, 2026
12c71aa
Silence Sentry Vite plugin
Wituareard Apr 2, 2026
55a6740
Add test page
Wituareard Apr 2, 2026
0af2e3d
Restore error logging on the server?
Wituareard Apr 2, 2026
1488d90
Fix client Sentry: use $env/dynamic/public for DSN
RisingOrange Apr 3, 2026
60f7a82
Add logs to Sentry init
Wituareard Apr 3, 2026
c579fe0
Fix client Sentry: read DSN inside init() to avoid race condition
RisingOrange Apr 3, 2026
4566b30
Merge main branch and resolve lockfile conflicts
Wituareard Apr 3, 2026
0eeb03a
Move edge sourcemap upload to Netlify build plugin
RisingOrange Apr 3, 2026
e9e44aa
Add manifest.yml for Netlify build plugin
RisingOrange Apr 3, 2026
e43adf6
Add debug logging for edge functions dist contents
RisingOrange Apr 3, 2026
bb77f04
Fix CI lint: include plugins/ in tsconfig.eslint.json
RisingOrange Apr 3, 2026
69f1a15
Remove edge sourcemap upload plugin
RisingOrange Apr 3, 2026
6c1f055
Remove @sentry/cli
Wituareard Apr 3, 2026
45328d4
Remove Sentry test routes
Wituareard Apr 3, 2026
e72f3eb
Remove console.log statements
Wituareard Apr 3, 2026
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@ deno.lock
/static/open-letter/portraits/original
CLAUDE.md
.claude/

# Sentry Config File
.env.sentry-build-plugin
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@
"@pagefind/default-ui": "^1.4.0",
"@prgm/sveltekit-progress-bar": "^3.0.2",
"@remix-run/headers": "^0.12.0",
"@sentry/deno": "^10.47.0",
"@sentry/svelte": "^10.47.0",
"@sentry/vite-plugin": "^5.1.1",
"@sveltejs/enhanced-img": "^0.10.4",
"@turf/distance": "^7.3.4",
"airtable": "^0.12.2",
Expand Down
671 changes: 613 additions & 58 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

36 changes: 36 additions & 0 deletions src/hooks.client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import * as Sentry from '@sentry/svelte'
import type { HandleClientError } from '@sveltejs/kit'
import { env } from '$env/dynamic/public'

let dsn: string | undefined

export const init = () => {
dsn = env.PUBLIC_SENTRY_DSN
const release = import.meta.env.SENTRY_RELEASE as string | undefined

if (dsn) {
try {
Sentry.init({
dsn,
release,
tracesSampleRate: 0
})
} catch (e) {
console.error('[Sentry Client] Failed to initialize:', e)
}
}
}

export const handleError: HandleClientError = ({ error, event, status, message }) => {
if (dsn) {
Sentry.captureException(error, {
extra: {
status,
message,
url: event.url?.href,
route: event.route?.id
}
})
}
return { message: 'An unexpected error occurred.' }
}
41 changes: 40 additions & 1 deletion src/hooks.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,32 @@ if (
delete (globalThis as { window?: Window }).window
}

import { type Handle } from '@sveltejs/kit'
import { type Handle, type HandleServerError } from '@sveltejs/kit'
import { env } from '$env/dynamic/public'
import { paraglideMiddleware } from '$lib/paraglide/server.js'

let Sentry: typeof import('@sentry/deno') | undefined

export const init = async () => {
const dsn = env.PUBLIC_SENTRY_DSN
const release = import.meta.env.SENTRY_RELEASE as string | undefined
const isDeno = typeof Deno !== 'undefined'

if (dsn && isDeno) {
try {
Sentry = await import('@sentry/deno')
Sentry.init({
dsn,
release,
tracesSampleRate: 0,
enableLogs: true
})
} catch (e) {
console.error('[Sentry Server] Failed to initialize:', e)
}
}
}

const handle: Handle = ({ event, resolve }) =>
paraglideMiddleware(event.request, ({ request: localizedRequest, locale }) => {
event.request = localizedRequest
Expand All @@ -27,3 +50,19 @@ const handle: Handle = ({ event, resolve }) =>
})

export { handle }

export const handleError: HandleServerError = ({ error, event, status, message }) => {
console.error(error)
if (Sentry) {
Sentry.captureException(error, {
extra: {
status,
message,
url: event.url?.href,
method: event.request?.method
}
})
event.platform?.context.waitUntil(Sentry.flush(2000))
}
return { message: 'An unexpected error occurred.' }
}
3 changes: 3 additions & 0 deletions template.env
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ PARAGLIDE_LOCALES=en
L10N_OPENROUTER_API_KEY=""
# Add your OpenRouter API key above to enable l10n generation

# Sentry DSN for error monitoring (PUBLIC_ prefix makes it available in client code)
PUBLIC_SENTRY_DSN=""

# Optionally override which branch of the l10n cache to use
# (defaults to same name as current website branch, or 'main' if on main)
# L10N_BRANCH="my-feature-branch"
35 changes: 33 additions & 2 deletions vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
import { enhancedImages } from '@sveltejs/enhanced-img'
import { sveltekit } from '@sveltejs/kit/vite'
import { execSync } from 'child_process'
import dotenv from 'dotenv'
import fs from 'fs'
import path from 'path'
import { defineConfig } from 'vite'
import lucidePreprocess from 'vite-plugin-lucide-preprocess'
import { sentryVitePlugin } from '@sentry/vite-plugin'
import { isDev } from './src/lib/env'
import { MARKDOWN_L10NS } from './src/lib/l10n'
import { locales as compiledLocales } from './src/lib/paraglide/runtime.js'

function getSentryRelease(): string | undefined {
try {
return process.env.COMMIT_REF || execSync('git rev-parse HEAD').toString().trim()
} catch {
return undefined
}
}

function getLocaleExcludePatterns(): RegExp[] {
const md = path.resolve(MARKDOWN_L10NS)
const reposLocales = fs.existsSync(md)
Expand All @@ -33,7 +43,8 @@ export default defineConfig(() => {
return {
define: {
// Make PARAGLIDE_LOCALES accessible to browser code via import.meta.env
'import.meta.env.PARAGLIDE_LOCALES': JSON.stringify(process.env.PARAGLIDE_LOCALES)
'import.meta.env.PARAGLIDE_LOCALES': JSON.stringify(process.env.PARAGLIDE_LOCALES),
'import.meta.env.SENTRY_RELEASE': JSON.stringify(getSentryRelease())
},

server: {
Expand Down Expand Up @@ -61,6 +72,26 @@ export default defineConfig(() => {
external: getLocaleExcludePatterns()
}
} as const,
plugins: [lucidePreprocess(), enhancedImages(), sveltekit()]
plugins: [
lucidePreprocess(),
enhancedImages(),
sveltekit(),
!isDev() &&
process.env.SENTRY_AUTH_TOKEN &&
process.env.SENTRY_ORG &&
process.env.SENTRY_PROJECT
? sentryVitePlugin({
org: process.env.SENTRY_ORG,
project: process.env.SENTRY_PROJECT,
authToken: process.env.SENTRY_AUTH_TOKEN,
release: { name: getSentryRelease() },
sourcemaps: {
filesToDeleteAfterUpload: ['./build/**/*.map']
},
telemetry: false,
silent: true
})
: null
].filter(Boolean)
}
})
Loading