mirror of
https://github.com/supabase/supabase.git
synced 2026-09-06 09:59:03 +08:00
Stacked on #47666 (base `alaister/tanstack-sentry-init`; retarget to `master` when that merges). **Supersedes #47721** (the manual `@sentry/node` wrapper). Client stays on #47666's `@sentry/react` setup. Adopts the official `@sentry/tanstackstart-react` SDK **on the server only**, after a spike (#47723) evaluating the full unified client+server SDK. The spike found the SDK's **browser** `tanstackRouterBrowserTracingIntegration` is a broken no-op stub at 10.59.0/10.64.0 — so the client stays on `@sentry/react` (whose equivalent integration is a real, working implementation, already shipped in #47666). The **server** exports, however, are a clear upgrade and slot in cleanly. ### What this adds (server-side, TanStack build only) - **`instrument.server.mjs`** — `Sentry.init` from `@sentry/tanstackstart-react`, mirroring `sentry.server.config.ts` + `release: VERCEL_GIT_COMMIT_SHA`. - **`start.ts`** — `sentryGlobalRequestMiddleware` + `sentryGlobalFunctionMiddleware` at the front of the existing `createStart(...)` middleware. **This is the win**: it captures request- and server-function errors *including the ones swallowed into 500s* — the exact class the manual wrapper (and the Next server SDK) miss. - **`api/server.js` / `scripts/serve.js`** — gated (`STUDIO_FRAMEWORK==='tanstack'`) instrument init + `wrapFetchWithSentry` on the handler. - **`vite.config.ts`** — `sentryTanstackStart({ …, autoInstrumentMiddleware: false })` as the last plugin: source-map upload + release injection (skips gracefully without an auth token). Middleware is wired explicitly rather than via the plugin's string-rewrite. ### Guarantees - **Client untouched** — the `@sentry/nextjs`→`@sentry/react` alias and #47666's client init are unchanged. - **Next untouched** — `instrumentation.ts` / `sentry.server.config.ts` etc. stay as-is; all new code is TanStack-gated. - **No server SDK in the client bundle** — verified after build: no `@sentry/node` / server middleware / `wrapFetchWithSentry` in `dist/client/assets` (`start.ts`'s server import is tree-shaken out). ### Verified TanStack build exit 0 (past `assertNoChunkCycles`), post-build server boot served `/api/get-utc-time → 200`, `tsc --noEmit` clean, prettier/eslint clean. Node smoke: no-DSN init is a clean no-op; wrapped handler returns 200. ### To test (deploy with a server DSN) Throw a server error from an `/api/*` route (or a `/_serverFn/*`) — including one that gets turned into a 500 without rethrowing — and confirm a server event in Sentry with `release` = the deploy SHA. Compared to #47721, the swallowed-500 case should now be captured via the middleware. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added Sentry integration for the Studio app’s TanStack Start runtime, including request and server-function instrumentation. * Wrapped server request handling to capture errors reliably, with tracing enabled. * Updated build tooling to conditionally upload source maps when credentials are present. * **Bug Fixes** * Improved resilience by safely falling back to a no-op Sentry setup if instrumentation cannot be loaded. * Ensured existing request protection remains enabled while adding observability middleware. * **Chores / Config** * Added `SKIP_ASSET_UPLOAD` to the build environment list to control cache/build behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Alaister Young <[email protected]> Co-authored-by: Joshen Lim <[email protected]>
51 lines
2.5 KiB
JavaScript
51 lines
2.5 KiB
JavaScript
// STUDIO_FRAMEWORK gates whether this function actually serves the TanStack
|
|
// SSR handler. Vercel auto-detects every file under /api as a Function
|
|
// regardless of the framework preset (vercel.com/docs/functions), so we
|
|
// can't keep this file from being deployed in the Next.js prod build — we
|
|
// just make it inert when the env var is unset.
|
|
const isTanstack = process.env.STUDIO_FRAMEWORK === 'tanstack'
|
|
|
|
// Computed path keeps `dist/server/server.js` out of Vercel's function
|
|
// bundler's static analysis. In the Next.js prod deploy the `dist/` tree
|
|
// doesn't exist, but Vercel still bundles this file because it lives under
|
|
// `api/`. With the .join() the bundler treats the import as runtime-only
|
|
// and the missing dist/ isn't a build error. In TanStack mode the SSR
|
|
// bundle is shipped into the function via the `functions['api/server.js']
|
|
// .includeFiles` config in vercel.ts.
|
|
const tanstackEntry = ['..', 'dist', 'server', 'server.js'].join('/')
|
|
|
|
// Initialize server-side Sentry BEFORE the handler module is imported, so its
|
|
// instrumentation is in place when route modules evaluate. Gated to TanStack
|
|
// (the Next deploy uses instrumentation.ts / sentry.server.config.ts instead).
|
|
// Vercel functions can't use a `--import` startup flag, so we import the
|
|
// instrument module here at boot. Vercel provides env vars via process.env.
|
|
// A Sentry boot failure must never take the API down — mirror scripts/serve.js
|
|
// and fall back to the identity wrapper if init or the SDK import throws.
|
|
let wrapFetchWithSentry = (fetchHandler) => fetchHandler
|
|
if (isTanstack) {
|
|
try {
|
|
await import('../instrument.server.mjs')
|
|
} catch (err) {
|
|
console.warn('[api/server] Sentry server init skipped:', err?.message ?? err)
|
|
}
|
|
;({ wrapFetchWithSentry } = await import('@sentry/tanstackstart-react').catch(() => ({
|
|
wrapFetchWithSentry: (fetchHandler) => fetchHandler,
|
|
})))
|
|
}
|
|
|
|
const rawHandler = isTanstack
|
|
? (await import(tanstackEntry)).default
|
|
: { fetch: () => new Response('Not Found', { status: 404 }) }
|
|
|
|
// Wrap the fetch handler so request-scoped errors (including those swallowed
|
|
// into a 500 downstream) are captured with request context.
|
|
const handler = isTanstack
|
|
? { ...rawHandler, fetch: wrapFetchWithSentry(rawHandler.fetch.bind(rawHandler)) }
|
|
: rawHandler
|
|
|
|
// Vercel's Web API handler convention: export an object with `fetch(request)`.
|
|
// TanStack's server build is already shaped that way — default-export it
|
|
// verbatim and Vercel hands us a real Web Request.
|
|
// eslint-disable-next-line no-restricted-exports
|
|
export default handler
|