From 5e3890de61e548ee2f6f03d6a65f438a926143f7 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Wed, 9 Sep 2026 16:23:02 +0200 Subject: [PATCH 1/3] feat(node): Add `getInstrumentedModuleNames()` and use it in the node-eve orchestrion test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Exposes `getInstrumentedModuleNames()` — the package names Sentry instruments through the orchestrion module transform — so an app on a framework with no Sentry bundler plugin can keep those packages external and let the transform hook them, instead of hardcoding the list. Lives in `@sentry/server-utils` (where the orchestrion config lives) and is re-exported from `@sentry/node`, so `export *` consumers (nitro — eve's base —, astro, …) surface it too. It returns the plain `module.name` set, deliberately without the bundler-only additions in `INSTRUMENTED_MODULE_NAMES` (those force a helper package to be bundled, the opposite of keeping it external). The node-eve app's orchestrion variant now passes `getInstrumentedModuleNames()` to `build.externalDependencies` instead of a hardcoded `['dataloader']`. The app only uses `dataloader` of that set, so the rest are no-ops; `ai` v7 stays fine externalized (native diagnostics channel + registration-only under the transform). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../test-applications/node-eve/agent/agent.ts | 14 +++++----- packages/node/src/index.ts | 2 +- packages/server-utils/src/index.ts | 1 + .../src/orchestrion/config/index.ts | 15 +++++++++++ .../test/orchestrion/config.test.ts | 27 +++++++++++++++++++ 5 files changed, 52 insertions(+), 7 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/node-eve/agent/agent.ts b/dev-packages/e2e-tests/test-applications/node-eve/agent/agent.ts index f4ec76970a0d..ae8c69e9b6d0 100644 --- a/dev-packages/e2e-tests/test-applications/node-eve/agent/agent.ts +++ b/dev-packages/e2e-tests/test-applications/node-eve/agent/agent.ts @@ -1,4 +1,5 @@ import { createOpenRouter } from '@openrouter/ai-sdk-provider'; +import { getInstrumentedModuleNames } from '@sentry/node'; import { defineAgent } from 'eve'; // We call OpenRouter directly (rather than the default Vercel AI Gateway) so the @@ -19,17 +20,18 @@ export default defineAgent({ // Only configure externals for orchestrion mode, to ensure everything else works without it ...(useOrchestrion ? { - // `dataloader` is instrumented by Sentry via orchestrion (a module - // transform). Keep it external so it stays a real module the transform can - // hook; if eve inlined it into the server bundle it could never be - // instrumented. (The Vercel AI SDK needs none of this — it uses a native - // diagnostics channel.) + // Keep every package Sentry instruments via orchestrion (a module transform) external, so + // it stays a real module the transform can hook rather than being inlined into eve's server + // bundle (an inlined module never reaches the transform's `onLoad`). Rather than hardcode + // the set, ask the SDK for it — this app exercises `dataloader`, and the rest are no-ops + // when the app doesn't use them. (The Vercel AI SDK needs none of this; it uses a native + // diagnostics channel and `ai` v7 is registration-only under the transform.) // // Do NOT add `@sentry/server-runtime-injection` here: the `--import` // loader instruments regardless (so the "bundled ... uninstrumented" // warning is a false positive), and externalizing it makes eve's dev // host fail to resolve its `/register` subpath (`eve dev` only). - externalDependencies: ['dataloader'], + externalDependencies: getInstrumentedModuleNames(), } : {}), }, diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 5ac0d207e455..5fa4d57e2466 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -216,7 +216,7 @@ export { makeNodeTransport } from './transports'; export { createGetModuleFromFilename } from './utils/module'; export { SENTRY_SEGMENT_NAME_SOURCE } from '@sentry/conventions/attributes'; -export { eveConversationHook } from '@sentry/server-utils'; +export { eveConversationHook, getInstrumentedModuleNames } from '@sentry/server-utils'; export { httpServerIntegration } from './integrations/http/httpServerIntegration'; export { httpServerSpansIntegration } from './integrations/http/httpServerSpansIntegration'; export { processSessionIntegration } from './integrations/processSession'; diff --git a/packages/server-utils/src/index.ts b/packages/server-utils/src/index.ts index 9a1520c560c8..74171e9f047b 100644 --- a/packages/server-utils/src/index.ts +++ b/packages/server-utils/src/index.ts @@ -14,6 +14,7 @@ export type { InstrumentationConfig } from './orchestrion/apmTypes'; // helper with no orchestrion build-time dependency. export { orchestrionModuleInjected } from './utils/moduleInjected'; export { eveConversationHook } from './eve'; +export { getInstrumentedModuleNames } from './orchestrion/config'; export { fastifyIntegration, // oxlint-disable-next-line typescript/no-deprecated diff --git a/packages/server-utils/src/orchestrion/config/index.ts b/packages/server-utils/src/orchestrion/config/index.ts index 1fec4fb2c5ad..0d0156bfd0c4 100644 --- a/packages/server-utils/src/orchestrion/config/index.ts +++ b/packages/server-utils/src/orchestrion/config/index.ts @@ -104,6 +104,21 @@ export function instrumentedModuleNames(instrumentations: InstrumentationConfig[ /** The instrumented module names from the default Sentry config, with no custom additions. */ export const INSTRUMENTED_MODULE_NAMES: string[] = instrumentedModuleNames(); +/** + * The package names the SDK instruments through the orchestrion module transform (its + * diagnostics-channel injection). Pass these to a server bundler's "keep external" option so the + * packages load through Node's module loader — the only path the transform can hook — instead of + * being inlined into the server bundle. A framework that has no Sentry bundler plugin (e.g. eve, via + * `build.externalDependencies`) is the main caller; a listed package the app doesn't use is simply + * ignored by the bundler. + * + * Unlike {@link INSTRUMENTED_MODULE_NAMES}, this is the plain instrumented set with no bundler-only + * additions — those force a helper package to be *bundled*, the opposite of keeping it external. + */ +export function getInstrumentedModuleNames(): string[] { + return uniq(SENTRY_INSTRUMENTATIONS.map(instrumentation => instrumentation.module.name)); +} + /** * Returns `external` with any instrumented packages removed, so a bundler that * uses an "external" denylist (esbuild, Bun, Rollup) still bundles — and thus diff --git a/packages/server-utils/test/orchestrion/config.test.ts b/packages/server-utils/test/orchestrion/config.test.ts index ae5899cafdb4..23819f68ec40 100644 --- a/packages/server-utils/test/orchestrion/config.test.ts +++ b/packages/server-utils/test/orchestrion/config.test.ts @@ -1,6 +1,7 @@ import type { InstrumentationConfig } from '@apm-js-collab/code-transformer-bundler-plugins/core'; import { describe, expect, it } from 'vitest'; import { + getInstrumentedModuleNames, INSTRUMENTED_MODULE_NAMES, instrumentedModuleNames, SENTRY_INSTRUMENTATIONS, @@ -21,6 +22,32 @@ describe('orchestrion config — scoped @hapi/hapi module', () => { }); }); +describe('getInstrumentedModuleNames', () => { + it('returns the instrumented package names', () => { + const names = getInstrumentedModuleNames(); + + for (const name of ['dataloader', 'ai', 'express', 'pg', 'redis']) { + expect(names).toContain(name); + } + }); + + it('has no duplicates', () => { + const names = getInstrumentedModuleNames(); + + expect(names.length).toBe(new Set(names).size); + }); + + it('is the plain instrumented set, without the bundler-only additions in INSTRUMENTED_MODULE_NAMES', () => { + // `INSTRUMENTED_MODULE_NAMES` adds packages that must be force-bundled (e.g. `@remix-run/node`), + // which is the opposite of what a "keep external" caller wants. + expect(getInstrumentedModuleNames()).not.toContain('@remix-run/node'); + expect(INSTRUMENTED_MODULE_NAMES).toContain('@remix-run/node'); + expect(new Set(getInstrumentedModuleNames())).toEqual( + new Set(SENTRY_INSTRUMENTATIONS.map(instrumentation => instrumentation.module.name)), + ); + }); +}); + describe('orchestrion config — channel-subscriber coverage', () => { // The subscribe injection rides the real channel configs (the `tracingChannelImport` // override only runs on instrumented files), so a subscriber definition whose module is From 1046a5cec6bb2633703a24549d0848cf8dd7f094 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Wed, 9 Sep 2026 16:32:55 +0200 Subject: [PATCH 2/3] feat: Re-export getInstrumentedModuleNames from every runtime SDK Same footprint as eveConversationHook: added to the explicit re-export blocks of @sentry/bun, @sentry/aws-serverless and @sentry/google-cloud-serverless (from @sentry/node) and of @sentry/deno and @sentry/cloudflare (from @sentry/server-utils), so it is available wherever a server bundle is configured. Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/aws-serverless/src/index.ts | 1 + packages/bun/src/index.ts | 1 + packages/cloudflare/src/index.ts | 1 + packages/deno/src/index.ts | 1 + packages/google-cloud-serverless/src/index.ts | 1 + 5 files changed, 5 insertions(+) diff --git a/packages/aws-serverless/src/index.ts b/packages/aws-serverless/src/index.ts index e8077a9782b1..ba7b2e95e8ed 100644 --- a/packages/aws-serverless/src/index.ts +++ b/packages/aws-serverless/src/index.ts @@ -171,6 +171,7 @@ export { // oxlint-disable-next-line typescript/no-deprecated withStreamedSpan, eveConversationHook, + getInstrumentedModuleNames, } from '@sentry/node'; export { diff --git a/packages/bun/src/index.ts b/packages/bun/src/index.ts index 4daeac999485..90f62d956935 100644 --- a/packages/bun/src/index.ts +++ b/packages/bun/src/index.ts @@ -188,6 +188,7 @@ export { // oxlint-disable-next-line typescript/no-deprecated withStreamedSpan, eveConversationHook, + getInstrumentedModuleNames, } from '@sentry/node'; export { diff --git a/packages/cloudflare/src/index.ts b/packages/cloudflare/src/index.ts index a1043149b427..f75e4c429e78 100644 --- a/packages/cloudflare/src/index.ts +++ b/packages/cloudflare/src/index.ts @@ -135,6 +135,7 @@ export { instrumentCreateReactAgent, vercelAIIntegration, eveConversationHook, + getInstrumentedModuleNames, } from '@sentry/server-utils'; export { instrumentWorkflowWithSentry } from './workflows'; diff --git a/packages/deno/src/index.ts b/packages/deno/src/index.ts index bac6b216fb07..71bc53ea9a50 100644 --- a/packages/deno/src/index.ts +++ b/packages/deno/src/index.ts @@ -145,6 +145,7 @@ export { postgresJsIntegration, tediousIntegration, eveConversationHook, + getInstrumentedModuleNames, } from '@sentry/server-utils'; export { openTelemetryIntegration, getOtlpTracesEndpoint } from '@sentry/server-utils/no-diagnostic-channels'; // Deprecated aliases kept for back-compat. Each forwards to the shared diff --git a/packages/google-cloud-serverless/src/index.ts b/packages/google-cloud-serverless/src/index.ts index f8d6659f24d7..3d08265de05f 100644 --- a/packages/google-cloud-serverless/src/index.ts +++ b/packages/google-cloud-serverless/src/index.ts @@ -171,6 +171,7 @@ export { // oxlint-disable-next-line typescript/no-deprecated withStreamedSpan, eveConversationHook, + getInstrumentedModuleNames, } from '@sentry/node'; export { From 3dd340fa809f8522749b9b36e4ea96b44fe4c104 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Wed, 9 Sep 2026 16:51:32 +0200 Subject: [PATCH 3/3] feat(astro): Re-export getInstrumentedModuleNames Astro's runtime entry curates its `@sentry/node` re-exports (it can't `export *`), so it needs the explicit listing like the other SDKs. Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/astro/src/index.server.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/astro/src/index.server.ts b/packages/astro/src/index.server.ts index 2ff55bb1b8b8..8e22aa0f5dd4 100644 --- a/packages/astro/src/index.server.ts +++ b/packages/astro/src/index.server.ts @@ -185,6 +185,7 @@ export { withStreamedSpan, metrics, eveConversationHook, + getInstrumentedModuleNames, } from '@sentry/node'; export { init } from './server/sdk';