getsentry / getsentry/sentry-javascript

Support Remix 3 (v3 rewrite) in @sentry/remix

Đang mở
#23,901 1 bình luận 0 reaction 1 người được giao Được @chargome nhận Xem trên GitHub
Remix Task
Ngôn ngữ chính
TypeScript
Star
8.7k
Fork
1.8k
Merge trung bình
1 ngày 17 giờ
Pull request đã merge (30 ngày)
523

Mô tả

## Summary

Remix 3 published its first [release candidate](https://remix.run/blog/remix-3-release-candidate) on 2026-08-31. It is a **ground-up rewrite, not a version bump**: it drops React entirely (its UI runtime is a fork of Preact with its own component model — no virtual DOM, explicit updates instead of hooks), drops React Router, and rebuilds around the Fetch API (`Request`/`Response`) so it runs on any Fetch runtime (Node, Deno, Bun). It ships as a new set of composable packages, **none of which are the `@remix-run/*` packages our current SDK targets**.

This issue tracks adding Remix 3 support to `@sentry/remix`.

## Why the current SDK is fully incompatible

Everything in `@sentry/remix` today assumes the Remix 2 internals:

- **Server (orchestrion):** our tracing channels target `@remix-run/server-runtime` internals — `requestHandler`, `matchServerRoutes`, `callRouteLoader`, `callRouteAction` — version-gated `>=2.0.0 <3` (`packages/server-utils/src/orchestrion/config/remix.ts`). None of these exist in v3; there is no `@remix-run/server-runtime`.
- **Server (monkey-patch fallback):** `instrumentServer.ts` fills `createRequestHandler` from `@remix-run/server-runtime` — same non-existent module.
- **Client:** we are a wrapper around `@sentry/react` (`withSentry` React error boundaries, React-Router browser tracing / route parameterization). With React gone, the entire client is inapplicable.
- **Deps:** peer deps are `@remix-run/{node,react,server-runtime} 2.x` + `react 18.x`; all irrelevant to v3.

## Relevant Remix 3 packages

- `@remix-run/fetch-router` — composable Fetch-API router with `router.mount()` and a **middleware system**
- `@remix-run/node-fetch-server` — Node adapter for Fetch-API servers
- `@remix-run/ui` — the new reconciler + component model (the Preact fork)
- a suite of middleware packages (`logger-middleware`, `session-middleware`, `cors-middleware`, …)

No `diagnostics_channel` usage was found in the framework, so there is no native channel to subscribe to.

## Proposed approach

### Packaging — keep it in `@sentry/remix`

One package covers both majors. The two runtimes share nothing but never collide: orchestrion configs are gated by the module + version they patch, so the v2 config (`@remix-run/server-runtime` `>=2 <3`) and a new v3 config (`@remix-run/fetch-router` / `@remix-run/node-fetch-server`, `>=3` / `0.x` during RC) each no-op on the other major. End-user experience stays: install `@sentry/remix`, done. Package exports stay `index.server` / `index.client`; we branch internally on which framework is present. Peer deps widen so both the v2 and v3 `@remix-run/*` sets are optional.

### Server — orchestrion auto-injects a Sentry middleware (zero config)

Reuse the existing module-injected-registration machinery (`registrationOnly` in `packages/server-utils/src/orchestrion/config/registration-only.ts`, `bundler/moduleInjectedTransform.ts`): orchestrion transforms `@remix-run/fetch-router`'s router construction at load time and **prepends our Sentry middleware** to every router — no manual wiring by the user. Orchestrion's role is auto-registration; the span logic lives in normal SDK code (the middleware).

The middleware — `(context, next) => Response`, with `context.request`/`url`/typed `params` and the ability to run before and after `await next()` — handles:
- open the `http.server` span, run `next()` inside it, set status from the returned `Response`, close it;
- name the transaction from the matched route pattern (low-cardinality / span-streaming logic we already have);
- `continueTrace` from incoming headers + inject `sentry-trace` / `baggage`.

**Error capture:** try/catch around `next()` → `captureException` → rethrow. Open question (spike): whether every handler throw surfaces at the middleware boundary or gets converted to an error `Response` upstream first. If middleware misses some, add a second orchestrion patch at the `node-fetch-server` request-handler choke point (the v3 analog of wrapping `createRequestHandler` today).

**Manual fallback:** also export the middleware (e.g. `Sentry.remixMiddleware()`) for bundlers/runtimes where orchestrion injection doesn't fire — matches our usual "auto, with a manual escape hatch" pattern.

**Sub-request spans: out of scope for v1.** No loader/action split exists in v3 (routes are Fetch handlers returning `Response`), so the middleware request span is the whole server story initially.

### Client — build on `@sentry/browser`

No React → `@sentry/react` is out. `@remix-run/ui` gives us:
- **Wrappable entry `run(options)`** (returns `app` with `app.ready()`) — wrap to init Sentry and catch init/hydration errors.
- **Navigation via the standard Navigation API** — the runtime intercepts same-origin links/forms through `window.navigation`, so pageload + navigation spans subscribe to `navigate` events instead of patching History. Route parameterization from the router's matched pattern. Strongest reuse point on the client.
- **Missing: no documented `onError` / component error boundary** in `run()`/`app`. Client error capture for v1 leans on `@sentry/browser` global `onerror`/`unhandledrejection` handlers + the `run()` wrap. Whether the `ui` reconciler exposes a render-error hook is a spike item; if not, global handlers are the v1 answer and we document the gap.

## Spike checklist (against the RC, before committing)

1. Does a `fetch-router` middleware `try/catch` around `next()` see all handler throws, or do some become error `Response`s upstream? → decides whether we also need the `node-fetch-server` wrap for errors.
2. Where to inject in `fetch-router` so every router (incl. `router.mount()` sub-routers) gets the middleware.
3. Confirm `window.navigation` `navigate` events give clean start/end + destination URL + matched route for span naming; check pageload vs. SPA (`remix/spa` `render()`) paths.
4. Any render-error hook in the `ui` reconciler for component-level capture; otherwise global-handlers-only for v1.
5. Confirm v3 package/version identifiers for the orchestrion `versionRange` and optional peer deps (still `0.x` RC).

## v1 scope

**In:** server `http.server` span + trace propagation + route naming (auto-injected middleware); server error capture; client init + pageload/navigation tracing (Navigation API); client global error capture.

**Out:** sub-request spans; component-level client error boundaries (pending a hook); anything depending on APIs still moving in the RC.

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.