mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
## Problem The website initializes Sentry only on the server and edge runtimes, leaving browser crashes unreported. Its crash-reporting setup also needs the same consent and third-party filtering policy that docs and Studio otherwise maintain separately. ## Fix Add www browser initialization and tagged crash capture for both Next.js routers, with accessible fallback focus. Move the shared consent/platform and third-party filtering into common/sentry, reuse it from all three apps, and remove the duplicated docs/www helpers and tests. Preserve each app's initialization and Studio's additional noise filtering, sampling, and sanitization. Include the source-map upload token in www's build cache inputs, and trigger the shared/www and Studio test workflows when the shared policy changes. ## How to test - Run `pnpm --filter www test ../../packages/common/sentry.test.ts lib/sentry-capture.test.tsx`: all 22 shared-policy and real-SDK capture tests passed locally. - Run `pnpm --filter studio exec vitest run lib/sentry-client-options.test.ts`: all 42 Studio options and policy-parity tests passed locally. - The www capture tests exercise the actual initializer and both router handlers with an in-memory transport, verify crash tags and fallback focus, and enforce consent. Removing initialization, capture calls, boundary tags, or consent gating was verified to fail these tests. - On a www preview with its DSN configured, accept telemetry consent and trigger temporary render errors in both routers. Verify they reach the www Sentry project with the boundary tag and readable stack traces. Formatting passes. Full local app typechecks encounter existing dependency/generated-file drift, with no diagnostics in changed files. Three unchanged TanStack mock call-count tests fail locally and reproduce against the pre-refactor implementation. Live Sentry ingestion and source-map uploads remain deployment checks. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Accessibility** - Error pages now automatically move focus to a clearly labeled error message, helping screen-reader and keyboard users understand when a page fails. - **Reliability** - Browser error reporting now captures application crashes more consistently across supported page types and navigation transitions. - Reporting respects consent and platform availability while filtering unrelated third-party failures. - **Testing** - Expanded automated coverage for error capture, reporting rules, consent handling, and accessible error-page behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
286 lines
8.3 KiB
JavaScript
286 lines
8.3 KiB
JavaScript
import { remarkCodeHike } from '@code-hike/mdx'
|
|
import bundleAnalyzer from '@next/bundle-analyzer'
|
|
import nextMdx from '@next/mdx'
|
|
import { withSentryConfig } from '@sentry/nextjs'
|
|
import codeHikeTheme from 'config/code-hike.theme.json' with { type: 'json' }
|
|
import rehypeSlug from 'rehype-slug'
|
|
import remarkGfm from 'remark-gfm'
|
|
|
|
import redirects from './lib/redirects.js'
|
|
import remotePatterns from './lib/remotePatterns.js'
|
|
import rewrites from './lib/rewrites.js'
|
|
|
|
const withMDX = nextMdx({
|
|
extension: /\.mdx?$/,
|
|
options: {
|
|
remarkPlugins: [
|
|
[
|
|
remarkCodeHike,
|
|
{
|
|
theme: codeHikeTheme,
|
|
lineNumbers: true,
|
|
showCopyButton: true,
|
|
},
|
|
],
|
|
remarkGfm,
|
|
],
|
|
rehypePlugins: [rehypeSlug],
|
|
// This is required for `MDXProvider` component
|
|
providerImportSource: '@mdx-js/react',
|
|
},
|
|
})
|
|
|
|
const withBundleAnalyzer = bundleAnalyzer({
|
|
enabled: process.env.ANALYZE === 'true',
|
|
})
|
|
|
|
/**
|
|
* @type {import('next').NextConfig}
|
|
*/
|
|
const nextConfig = {
|
|
basePath: '',
|
|
assetPrefix: getAssetPrefix(),
|
|
pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx'],
|
|
trailingSlash: false,
|
|
transpilePackages: [
|
|
'ui',
|
|
'ui-patterns',
|
|
'common',
|
|
'shared-data',
|
|
'icons',
|
|
'api-types',
|
|
'marketing',
|
|
// needed to make the octokit packages work in /changelog
|
|
'@octokit/plugin-paginate-graphql',
|
|
],
|
|
experimental: {
|
|
// needed to make the octokit packages work in /changelog
|
|
esmExternals: 'loose',
|
|
},
|
|
/**
|
|
* Exclude huge directories from being traced into serverless functions
|
|
* to avoid the max size limit for Serverless Functions on Vercel:
|
|
* https://vercel.com/guides/troubleshooting-function-250mb-limit
|
|
*/
|
|
outputFileTracingExcludes: {
|
|
'*': [
|
|
// Next.js build artifacts
|
|
'.next/cache/**/*',
|
|
'.next/static/**/*',
|
|
// Static assets
|
|
'public/**/*',
|
|
],
|
|
},
|
|
/**
|
|
* /llms.txt enumerates top-level guide directories at runtime via fs.readdir
|
|
* on apps/docs/content/guides and reads each <dir>.mdx for its frontmatter
|
|
* title. /llms-full.txt recursively reads the generated .md files under
|
|
* apps/docs/public/markdown/. Both directories live in a sibling app, so
|
|
* they have to be explicitly traced into this lambda's bundle.
|
|
*/
|
|
outputFileTracingIncludes: {
|
|
'/llms.txt': ['../docs/content/guides/*'],
|
|
'/llms-full.txt': ['../docs/public/markdown/guides/**/*.md'],
|
|
},
|
|
reactStrictMode: true,
|
|
images: {
|
|
dangerouslyAllowSVG: false,
|
|
remotePatterns,
|
|
},
|
|
async headers() {
|
|
return [
|
|
{
|
|
source: '/changelog-rss.xml',
|
|
headers: [
|
|
{ key: 'Content-Type', value: 'application/rss+xml; charset=utf-8' },
|
|
{ key: 'X-Content-Type-Options', value: 'nosniff' },
|
|
{ key: 'Cache-Control', value: 'public, s-maxage=900, stale-while-revalidate=900' },
|
|
],
|
|
},
|
|
{
|
|
source: '/changelog-rss/:slug.xml',
|
|
headers: [
|
|
{ key: 'Content-Type', value: 'application/rss+xml; charset=utf-8' },
|
|
{ key: 'X-Content-Type-Options', value: 'nosniff' },
|
|
{ key: 'Cache-Control', value: 'public, s-maxage=900, stale-while-revalidate=900' },
|
|
],
|
|
},
|
|
{
|
|
source: '/changelog/:slug.md',
|
|
headers: [
|
|
{ key: 'Content-Type', value: 'text/markdown; charset=utf-8' },
|
|
{ key: 'X-Content-Type-Options', value: 'nosniff' },
|
|
{ key: 'Cache-Control', value: 'public, s-maxage=900, stale-while-revalidate=900' },
|
|
{ key: 'Vary', value: 'Accept' },
|
|
],
|
|
},
|
|
{
|
|
source: '/changelog.md',
|
|
headers: [
|
|
{ key: 'Content-Type', value: 'text/markdown; charset=utf-8' },
|
|
{ key: 'X-Content-Type-Options', value: 'nosniff' },
|
|
{ key: 'Cache-Control', value: 'public, s-maxage=900, stale-while-revalidate=900' },
|
|
{ key: 'Vary', value: 'Accept' },
|
|
],
|
|
},
|
|
{
|
|
source: '/',
|
|
headers: [
|
|
{
|
|
key: 'Link',
|
|
value: '</.well-known/api-catalog>; rel="api-catalog"',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
source: '/.well-known/api-catalog',
|
|
headers: [
|
|
{ key: 'content-type', value: 'application/linkset+json' },
|
|
{ key: 'access-control-allow-origin', value: '*' },
|
|
],
|
|
},
|
|
{
|
|
source: '/.well-known/vercel/flags',
|
|
headers: [
|
|
{
|
|
key: 'content-type',
|
|
value: 'application/json',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
source: '/favicon/:slug*',
|
|
headers: [{ key: 'cache-control', value: 'public, max-age=86400' }],
|
|
},
|
|
{
|
|
source: '/(.*)',
|
|
headers: [
|
|
{
|
|
key: 'Strict-Transport-Security',
|
|
value:
|
|
process.env.NEXT_PUBLIC_IS_PLATFORM === 'true' && process.env.VERCEL === '1'
|
|
? 'max-age=31536000; includeSubDomains; preload'
|
|
: '',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
source: '/(docs|blog)/:path*',
|
|
headers: [
|
|
{
|
|
key: 'X-Robots-Tag',
|
|
value: 'all',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
source: '/dashboard/:path*',
|
|
headers: [
|
|
{
|
|
key: 'X-Robots-Tag',
|
|
value: 'noindex',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
source: '/enterprise-terms',
|
|
headers: [
|
|
{
|
|
key: 'X-Robots-Tag',
|
|
value: 'noindex, nofollow',
|
|
},
|
|
],
|
|
},
|
|
]
|
|
},
|
|
async rewrites() {
|
|
return {
|
|
afterFiles: rewrites,
|
|
fallback: [
|
|
{
|
|
source: '/:path(.*\\.md)',
|
|
destination: '/api-v2/md-404/:path',
|
|
},
|
|
{
|
|
source: '/:path*',
|
|
has: [{ type: 'header', key: 'accept', value: '.*text/(markdown|\\*).*' }],
|
|
destination: '/api-v2/md-404/:path*',
|
|
},
|
|
],
|
|
}
|
|
},
|
|
async redirects() {
|
|
// For /docs/guides/ redirects, auto-generate .md variants so renamed/deleted pages
|
|
// redirect correctly when fetched as markdown
|
|
const docsMdRedirects = redirects
|
|
.filter(
|
|
(r) =>
|
|
r.source.startsWith('/docs/guides/') &&
|
|
typeof r.destination === 'string' &&
|
|
r.destination.startsWith('/')
|
|
)
|
|
.map((r) => ({ ...r, source: `${r.source}.md`, destination: `${r.destination}.md` }))
|
|
|
|
return [...docsMdRedirects, ...redirects]
|
|
},
|
|
typescript: {
|
|
// On previews, typechecking is run via GitHub Action only for efficiency
|
|
// On production, we turn it on to prevent errors from conflicting PRs getting into
|
|
// prod
|
|
ignoreBuildErrors: process.env.NEXT_PUBLIC_VERCEL_ENV === 'production' ? false : true,
|
|
},
|
|
eslint: {
|
|
// We are already running linting via GH action, this will skip linting during production build on Vercel.
|
|
ignoreDuringBuilds: true,
|
|
},
|
|
}
|
|
|
|
// next.config.js.
|
|
const configExport = () => {
|
|
const plugins = [withMDX, withBundleAnalyzer]
|
|
return plugins.reduce((acc, next) => next(acc), nextConfig)
|
|
}
|
|
|
|
export default withSentryConfig(configExport, {
|
|
// For all available options, see:
|
|
// https://www.npmjs.com/package/@sentry/webpack-plugin#options
|
|
|
|
org: 'supabase',
|
|
project: 'www',
|
|
unstable_sentryWebpackPluginOptions: {
|
|
applicationKey: 'supabase-www',
|
|
},
|
|
|
|
// Only print logs for uploading source maps in CI
|
|
silent: !process.env.CI,
|
|
|
|
// For all available options, see:
|
|
// https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
|
|
|
|
// Upload a larger set of source maps for prettier stack traces (increases build time)
|
|
widenClientFileUpload: true,
|
|
|
|
// Uncomment to route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers.
|
|
// This can increase your server load as well as your hosting bill.
|
|
// Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client-
|
|
// side errors will fail.
|
|
// tunnelRoute: "/monitoring",
|
|
|
|
// Automatically tree-shake Sentry logger statements to reduce bundle size
|
|
disableLogger: true,
|
|
})
|
|
|
|
function getAssetPrefix() {
|
|
// If not force enabled, but not production env, disable CDN
|
|
if (process.env.FORCE_ASSET_CDN !== '1' && process.env.VERCEL_ENV !== 'production') {
|
|
return undefined
|
|
}
|
|
|
|
// Force disable CDN
|
|
if (process.env.FORCE_ASSET_CDN === '-1') {
|
|
return undefined
|
|
}
|
|
|
|
return `https://frontend-assets.supabase.com/${process.env.SITE_NAME}/${process.env.VERCEL_GIT_COMMIT_SHA.substring(0, 12)}`
|
|
}
|