ref(server-utils): Move ServerRuntimeClient, node stack parser and server-only utils out of core - #23833
Draft
mydea wants to merge 11 commits into
Draft
ref(server-utils): Move ServerRuntimeClient, node stack parser and server-only utils out of core#23833mydea wants to merge 11 commits into
mydea wants to merge 11 commits into
Conversation
mydea
force-pushed
the
fn/move-server-only-apis-to-server-utils
branch
from
September 2, 2026 11:39
172fba6 to
d52ac53
Compare
mydea
force-pushed
the
fn/move-server-runtime-client-to-server-utils
branch
from
September 2, 2026 11:39
b8c658c to
0f1963a
Compare
mydea
force-pushed
the
fn/move-server-runtime-client-to-server-utils
branch
3 times, most recently
from
September 2, 2026 12:01
6a3662a to
d85e36e
Compare
Contributor
size-limit report 📦
|
mydea
changed the base branch from
fn/move-server-only-apis-to-server-utils
to
develop
September 2, 2026 12:06
mydea
force-pushed
the
fn/move-server-runtime-client-to-server-utils
branch
from
September 2, 2026 12:17
d85e36e to
194f1bd
Compare
mydea
force-pushed
the
fn/move-server-runtime-client-to-server-utils
branch
2 times, most recently
from
September 3, 2026 11:04
6396adc to
a6bdaf3
Compare
mydea
force-pushed
the
fn/move-server-runtime-client-to-server-utils
branch
from
September 7, 2026 12:39
a6bdaf3 to
a3c0438
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit a3c0438. Configure here.
…rver-only utils out of core Continues slimming `@sentry/core` down to its isomorphic surface by moving the remaining server-only APIs into `@sentry/server-utils`. Moved out of core: - `flushIfServerless` + `vercelWaitUntil` (used by the meta-framework SDKs) - `trpcMiddleware` - `callFrameToStackFrame` / `watchdogTimer` (the anr worker helpers) - `loadModule` (split out of `utils/node`; `isNodeEnv` stays, since core depends on it via `isBrowser`) - the base `ServerRuntimeClient` (with `ServerRuntimeClientOptions` / `ServerRuntimeOptions`) and the node stack-trace parser (`nodeStackLineParser`, `node`, `filenameIsInApp`) `@sentry/server-utils` is added as a dependency to `@sentry/node-native` and `@sentry/bundler-plugins`, the only two consumers that didn't already have it. Every other SDK already depended on server-utils and keeps re-exporting these under the same names, so there is no user-facing change. `mcp-server` and the `integrations/http/*` subtree stay in core. `ServerRuntimeClient` and the stack parser reach for a few core building blocks that aren't public. Rather than route them through the semi-internal `@sentry/core/server` entry, the three that are genuinely needed are exported from the public `@sentry/core` entry (`getTraceInfoFromScope`, `addUserAgentToTransportHeaders`, `normalizeStackTracePath`); the other two are dropped — the transport buffer size becomes a local constant, and the span-streaming integration name is read off the integration instance. No metric or transport internals are exposed. `ServerRuntimeOptions` was extracted from the shared `types/options.ts` (which stays in core) into its own server-utils file. Keeping the moved code out of edge/client bundles: relocating this into `@sentry/server-utils` surfaced Next.js bundling regressions (every `next build` e2e app failed with `UnhandledSchemeError` on `node:async_hooks` / `node:net`), because server-only server-utils code was reaching the edge and browser bundles, which can't resolve `node:` builtins. This was latent before the move (the same paths pulled these helpers from browser-safe `@sentry/core`). - Make the `@sentry/server-utils` barrel tree-shakeable: re-export the deprecated `attachHapiErrorHandler` normally with the deprecation moved onto the source function (matching `attachKoaErrorHandler`) instead of a non-shakeable `const` re-export that pinned the whole barrel graph. - Point `@sentry/vercel-edge` at `@sentry/server-utils/no-diagnostic-channels` for `ServerRuntimeClient` / `nodeStackLineParser` / `trpcMiddleware`, so the heavy barrel (Node integrations, `node:net`) never reaches the Next.js edge bundle. - Split the server-only App-Router wrappers and `captureRequestError` out of the client-reachable `common` barrel into `common/serverOnlyExports`, re-exported only from the server and edge entrypoints. Make `responseEnd` client-safe by inlining `vercelWaitUntil`, so the dual-bundled pages-router `_error` path no longer pulls `node:async_hooks` into the browser bundle. `vercelWaitUntil` stays in server-utils for `flushIfServerless`. Relocates the node-stack-parsing tests to `@sentry/server-utils`; keeps the core-internal unit tests (`metadata`, `debug-ids`, `third-party-errors-filter`) in core, fed by a local node stack parser fixture. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ASNdTRtxNEjNMEBGCxENT7
After `ServerRuntimeClient` and `nodeStackLineParser` moved from the edge-safe `@sentry/core/server` entry into `@sentry/server-utils`, `client.ts` and `vendor/stacktrace.ts` pulled the full `@sentry/server-utils` barrel (which subscribes to `node:diagnostics_channel`) into the `wrapRequestHandler` graph, breaking runtimes without `nodejs_compat` (e.g. Shopify Oxygen / the remix-hydrogen app). Route those two imports through `@sentry/server-utils/no-diagnostic-channels` — the edge-safe subset that carries `ServerRuntimeClient` and the node stack parser without the channel subscription — matching how `@sentry/vercel-edge` already consumes them. Update the `requestModuleGraph` guard to allow that one edge-safe entry while still forbidding the heavy barrel and `@sentry/node`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D7JDQBD9J2okCe1hkWCanU
…move - node `httpServerIntegration` test imported `ServerRuntimeClient` from `@sentry/core/server`, which no longer exports it — import it from `@sentry/server-utils`. - tanstackstart-react `wrapFetchWithSentry` test mocked `@sentry/server-utils` without spreading the original module, so `nodeStackLineParser` (now pulled from server-utils by the `@sentry/node` init path) was undefined and the mock factory threw. Spread `importOriginal()` like the sibling mocks. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D7JDQBD9J2okCe1hkWCanU
`nodeStackLineParser` moved out of `@sentry/core/server` into `@sentry/server-utils`. Update the deno unit tests (`mod`, `deno-runtime-metrics`) and the deno integration `direct-client-acs` scenario to import it from `@sentry/server-utils`; the deno SDK src already does. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D7JDQBD9J2okCe1hkWCanU
`@sentry/bundler-plugins` now externalizes `@sentry/server-utils` (it `require`s it at build time for `ServerRuntimeClient` / `nodeStackLineParser`). The fixtures pinned `@sentry/core` and `@sentry/bundler-plugins` to local tarballs but not server-utils, so pnpm pulled a published `@sentry/server-utils` from the registry that mismatched the local `@sentry/core` build (`SPAN_KIND` export error), failing every bundler run. Pack `@sentry/server-utils` in `setup.mjs` and add a `pnpm.overrides` entry for it to every fixture, pointing at the local tarball like core and bundler-plugins. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D7JDQBD9J2okCe1hkWCanU
The nuxt-3 app imported `flushIfServerless` from `@sentry/core/server`, which no longer exports it after the move to `@sentry/server-utils`. Use the public `Sentry.flush()` (already imported as `SentryNode`) instead of reaching into an internal entry point. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D7JDQBD9J2okCe1hkWCanU
The Hydrogen (Oxygen) e2e app `remix-hydrogen` failed to build: its client bundle
pulled `@sentry/server-utils/async-context` (`import { AsyncLocalStorage } from
'node:async_hooks'`), which Vite externalizes into an empty stub, throwing
`"AsyncLocalStorage" is not exported by "__vite-browser-external"`.
`async-context` is the only module in the shared `exports.ts` surface that
statically imports a `node:` builtin, yet it was re-exported from both the `index`
and `no-diagnostic-channels` barrels — so any browser/edge bundle importing *any*
helper from those barrels dragged `node:async_hooks` into the graph.
- Move `setAsyncLocalStorageAsyncContextStrategy` out of the shared `exports.ts`
into a dedicated `@sentry/server-utils/async-context` entry, so `index` /
`no-diagnostic-channels` are free of `node:` builtins. Update its consumers
(Node/Deno/Cloudflare SDKs + tests), all of which run where `node:async_hooks`
resolves.
- Point the two `@sentry/remix/cloudflare`-reachable imports (`instrumentServer`'s
`loadModule`, `cloudflare/index`'s `trpcMiddleware`) at the lean
`no-diagnostic-channels` barrel instead of the full `@sentry/server-utils`
barrel, whose Node-only integrations (`tedious` → `node:events`, …) otherwise
reach the bundled Hydrogen client build.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D7JDQBD9J2okCe1hkWCanU
mydea
force-pushed
the
fn/move-server-runtime-client-to-server-utils
branch
from
September 8, 2026 11:20
5972456 to
b9c6beb
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Continues slimming
@sentry/coredown to its isomorphic surface by moving the remaining server-only APIs into@sentry/server-utils.Moved out of core:
flushIfServerless+vercelWaitUntil(used by the meta-framework SDKs)trpcMiddlewarecallFrameToStackFrame/watchdogTimer(the anr worker helpers)loadModule(split out ofutils/node;isNodeEnvstays, since core depends on it viaisBrowser)ServerRuntimeClient(withServerRuntimeClientOptions/ServerRuntimeOptions) and the node stack-trace parser (nodeStackLineParser,node,filenameIsInApp)@sentry/server-utilsis added as a dependency to@sentry/node-nativeand@sentry/bundler-plugins, the only two consumers that didn't already have it. Every other SDK already depended on server-utils and keeps re-exporting these under the same names, so there is no user-facing change.mcp-serverand theintegrations/http/*subtree stay in core.ServerRuntimeClientand the stack parser reach for a few core building blocks that aren't public. Rather than route them through the semi-internal@sentry/core/serverentry, the three that are genuinely needed are exported from the public@sentry/coreentry —getTraceInfoFromScope,addUserAgentToTransportHeaders,normalizeStackTracePath— and the other two dependencies are dropped: the transport buffer size becomes a local constant, and the span-streaming integration name is read off the integration instance. No metric or transport internals are exposed.ServerRuntimeOptionswas extracted from the sharedtypes/options.ts(which stays in core) into its own server-utils file.Keeping the moved code out of edge/client bundles
Relocating this into
@sentry/server-utilssurfaced Next.js bundling regressions — everynext builde2e app failed withUnhandledSchemeErroronnode:async_hooks/node:net.Root cause: server-only
@sentry/server-utilscode (which statically importsnode:async_hooksvia the async-context strategy, andnode:netvia the firebase integration) was reaching the Next.js edge and browser bundles, which can't resolvenode:builtins. This was latent before the move — the same code paths pulled these helpers from browser-safe@sentry/core. Fixed at each layer:@sentry/server-utilsbarrel re-exportedattachHapiErrorHandlervia aconstbinding (export const x = _x) — a module-level statement that pinned the whole barrel graph against tree-shaking, so importing any one symbol dragged in every Node integration. It's now a plain re-export with the@deprecatedmarker moved onto the source function (matchingattachKoaErrorHandler).@sentry/vercel-edgenow importsServerRuntimeClient/nodeStackLineParser/trpcMiddlewarefrom the lean@sentry/server-utils/no-diagnostic-channelsentry (as it already did for its AI/OTLP integrations), so the heavy barrel never reaches the Next.js edge bundle.common, which pulled the server-only App-Router wrappers (wrapServerComponent,wrapRouteHandler,wrapMiddleware,wrapGenerationFunction,withServerActionInstrumentation) andcaptureRequestErrorinto the browser bundle. These moved tocommon/serverOnlyExports, re-exported only from the server and edge entrypoints. And because the pages-router_errorinstrumentation (captureUnderscoreErrorException) is legitimately dual-bundled and usesresponseEnd'swaitUntil,responseEndnow inlines a client-safevercelWaitUntilinstead of importing it from server-utils (vercelWaitUntilstays in server-utils forflushIfServerless).Tests
The node-stack-parsing tests move to
@sentry/server-utilsalongside the parser. Core unit tests that only used the parser as a realistic fixture (metadata,debug-ids,third-party-errors-filter) stay in core with a local node-stack-parser fixture, so core keeps that coverage without depending on server-utils.🤖 Generated with Claude Code
https://claude.ai/code/session_01ASNdTRtxNEjNMEBGCxENT7