feat(nextjs): Add opt-in for orchestrion instrumentation - #22043

Merged
chargome merged 13 commits into
developfrom
cg/nextjs-orchestrion-support
Jul 10, 2026
Merged

feat(nextjs): Add opt-in for orchestrion instrumentation#22043
chargome merged 13 commits into
developfrom
cg/nextjs-orchestrion-support

Conversation

@chargome

@chargomechargome commented Jul 8, 2026

Copy link
Copy Markdown
Member

Adds an experimental option for opting in to orchestrion instrumentation.

  • Once the opt-in flag is set we:
    • Un-externalize the bundle-safe instrumented packages; bundle-unsafe ones (mysql) stay external and are instrumented by the runtime module hook, which works because we also externalize the @apm-js-collab/* transformer packages.
    • transpilePackages for the ones that Next externalizes by default (e.g. pg) — removing them from serverExternalPackages isn't enough for Next's own defaults.
    • Inject the code-transform loader as a Turbopack rule and as a webpack plugin.
  • The webpack loader gets exported from our server utils package (which can be used for turbopack too)

Verified in e2e

closes#22009

@chargomechargome self-assigned this Jul 8, 2026
chargomeand others added 6 commits July 8, 2026 10:06
Two fixes for diagnostics-channel injection under Turbopack:
Externalize @apm-js-collab/tracing-hooks and @apm-js-collab/code-transformer
when the flag is on. Bundled, the code transformer's parser breaks
('a.parse is not a function'), so the runtime module hook silently returned
untransformed sources and externalized packages never produced spans.
Keep mysql in serverExternalPackages instead of force-bundling it. Turbopack
cannot bundle mysql 2.x correctly (the wire-protocol handshake fails with
'Received packet in the wrong sequence' even untransformed). External, it is
now instrumented by the working runtime hook instead of the build-time loader.
Also drop the bundler marker from the orchestrion webpack plugin: it made
registerDiagnosticsChannelInjection() skip the runtime hook, but the hybrid
setup (loader for bundled deps, runtime hook for external ones) needs both.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Instead of un-externalizing every instrumented package and forcing the
Next-default-external ones through transpilePackages, only packages on an
explicit bundle-safe allowlist (currently ioredis) are removed from Sentry's
own serverExternalPackages defaults. Everything else — Next's defaults, the
user's externals, the rest of Sentry's defaults — stays external and is
instrumented by the runtime module hook on require, which works since the
orchestrion machinery is externalized.
This is safer: bundling a server package changes real behavior (mysql 2.x
corrupts its wire protocol when bundled by Turbopack), and new upstream
instrumentations (e.g. hapi) now default to the external/runtime-hook path
instead of silently becoming bundled. It also removes the Next
server-external-packages list parsing and the pg-native webpack workaround,
both only needed to support force-bundling.
Verified in the nextjs-16-orchestrion e2e: ioredis via the build-time loader,
pg and mysql via the runtime hook.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t@13
Use createRequire(__filename) instead of aliasing the CJS require in the
orchestrion webpack bundler module. Next.js 13 bundles @sentry/nextjs into
the server build, and webpack flags the aliased require with 'Critical
dependency: require function is used in a way in which dependencies cannot
be statically extracted', which the nextjs-app-dir e2e treats as a failure.
Add isDiagnosticsChannelInjectionEnabled to the consistent-exports ignore
list: like its companions experimentalUseDiagnosticsChannelInjection and
diagnosticsChannelInjectionIntegrations, the Node-runtime-only opt-in is not
surfaced through the framework / serverless SDKs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chargome

Copy link
Copy Markdown
MemberAuthor

bugbot run

Comment threadpackages/nextjs/src/config/webpack.ts
The plugin previously ran for all server compilations, including the edge
runtime, which diagnostics-channel injection does not target. Gate it on the
already-derived runtime instead of isServer and add unit tests covering the
server/edge/client/flag-off cases.
Also trim down the orchestrion-related comments.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chargome

Copy link
Copy Markdown
MemberAuthor

bugbot run

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a3a3595. Configure here.

Comment threadpackages/nextjs/src/server/index.ts
expect(externals).toContain('mysql');
expect(externals).not.toContain('@apm-js-collab/tracing-hooks');
});
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feat lacks integration or E2E

Medium Severity

This feature PR adds unit and webpack config tests but no integration or E2E test in the diff, though the description references separate e2e verification. Review guidelines expect at least one integration or E2E test for feat changes.

Fix in CursorFix in Web

Triggered by project rule: PR Review Guidelines for Cursor Bot

Reviewed by Cursor Bugbot for commit a3a3595. Configure here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handled in #22080

// module don't emit a "Critical dependency" warning.
function getOrchestrionRequire(): ReturnType<typeof createRequire> {
let nodeRequire: ReturnType<typeof createRequire>;
/*! rollup-include-cjs-only */

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

big thanks to @timfish for this :D

@chargome
chargome marked this pull request as ready for review July 9, 2026 08:34
@chargome
chargome requested review from a team as code ownersJuly 9, 2026 08:34
@chargome
chargome requested review from a team, JPeer264, andreiborza, logaretm, mydea, nicohrubec, s1gr1d and timfish and removed request for a team, JPeer264, andreiborza and s1gr1dJuly 9, 2026 08:34
Comment threadpackages/nextjs/src/config/webpack.ts
When the SDK is bundled into a Next.js server build, the runtime module
hook's bare require of @apm-js-collab/tracing-hooks resolves relative to
the emitted chunk, which fails under isolated installs (pnpm) where the
package is not linked at the app root — the hook silently no-ops and
externalized packages (pg, mysql) lose their spans.
Resolve the package location in withSentryConfig, where the SDK is a
real on-disk package, and inline it as a build-time env value that the
runtime prefers over the bare specifier. Also construct nodeRequire via
createRequire in both build flavors so bundlers don't statically trace
the call (Turbopack otherwise tries to resolve the injected absolute
path at build time).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@timfishtimfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a webpack plugin in this PR:

Why is the createRequire needed?

@github-actions

github-actionsBot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser27.6 kB--
@sentry/browser - with treeshaking flags26.04 kB--
@sentry/browser (incl. Tracing)46.35 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.14 kB--
@sentry/browser (incl. Tracing, Profiling)51.13 kB--
@sentry/browser (incl. Tracing, Replay)85.62 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags75.26 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)90.33 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)102.99 kB--
@sentry/browser (incl. Feedback)44.78 kB--
@sentry/browser (incl. sendFeedback)32.4 kB--
@sentry/browser (incl. FeedbackAsync)37.53 kB--
@sentry/browser (incl. Metrics)28.68 kB--
@sentry/browser (incl. Logs)28.93 kB--
@sentry/browser (incl. Metrics & Logs)29.61 kB--
@sentry/react29.39 kB-0.01%-1 B 🔽
@sentry/react (incl. Tracing)48.62 kB--
@sentry/vue33.03 kB-0.01%-1 B 🔽
@sentry/vue (incl. Tracing)48.33 kB--
@sentry/svelte27.63 kB--
CDN Bundle30 kB--
CDN Bundle (incl. Tracing)48.33 kB--
CDN Bundle (incl. Logs, Metrics)31.58 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)49.65 kB--
CDN Bundle (incl. Replay, Logs, Metrics)70.82 kB--
CDN Bundle (incl. Tracing, Replay)85.84 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)87.16 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)91.64 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.92 kB--
CDN Bundle - uncompressed89.33 kB--
CDN Bundle (incl. Tracing) - uncompressed146.07 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed94.03 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed150.05 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed218.76 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed265.28 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed269.24 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed278.98 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed282.93 kB--
@sentry/nextjs (client)51.17 kB--
@sentry/sveltekit (client)46.8 kB--
@sentry/core/server78.42 kB-0.01%-2 B 🔽
@sentry/core/browser64.77 kB--
@sentry/node-core62.72 kB-0.01%-1 B 🔽
@sentry/node125.35 kB-0.01%-1 B 🔽
@sentry/node (incl. diagnostics channel injection)138.67 kB+0.05%+65 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)69.95 kB--
@sentry/node/light50.73 kB-0.01%-1 B 🔽
@sentry/node - without tracing74.04 kB-0.01%-2 B 🔽
@sentry/aws-serverless83.26 kB--
@sentry/cloudflare (withSentry) - minified181.47 kB--
@sentry/cloudflare (withSentry)448.98 kB--

View base workflow run

Comment threadpackages/server-utils/src/orchestrion/runtime/register.ts Outdated
@timfish

Copy link
Copy Markdown
Collaborator

I guess the createRequire and then the require obfuscation are to stop webpack including all the orchestrion stuff in a bundle when that feature is not used?

Ideally we should avoid using config options to enable orchestrion mode because it requires all these hacks. I also think this will result in webpack not including the orchestrion code in a bundle even if you use it and result in a runtime error when it can't be loaded.

For the Node SDK we have Sentry.experimentalUseDiagnosticsChannelInjection() which ensures the code is only included in the bundle if you actually reference that.

The only valid use I've seen for createRequire is to ensure that tracing-hooks isn't directly in our import graph because it's ESM and this needs require(esm) to load it from CJS (ie. Node v20.19). The argument could be made that these new usages stop webpack from building a bundle that fails to run on Node v18 but I suspect webpack removes the require(esm) issue anyway.

@timfishtimfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getTracingHooksSpecifier obfuscation will mean that no other bundler will be able to resolve and bundle @apm-js-collab/tracing-hooks/hook-sync.mjs. This will force us (and users) to make it external everywhere.

I think in the long term we need to solve the Turbopack issue (happy to take a look!) but for now to get this merged I would create a nextjs specific registerDiagnosticsChannelInjection() so only nextjs has this obfuscation!

chargomeand others added 3 commits July 9, 2026 14:30
registerDiagnosticsChannelInjection() now takes an optional tracingHooksDir
and by default loads @apm-js-collab/tracing-hooks via its bare specifier
again, keeping the require statically analyzable for bundlers. Only the
Next.js SDK — whose server builds bundle the SDK and can't resolve the bare
specifier under pnpm — passes the build-time-resolved package location
(via a Next.js-specific experimentalUseDiagnosticsChannelInjection wrapper),
where loading switches to an opaque createRequire.
webpack ignore-comments were evaluated as an alternative: turbopackIgnore
works on require(), but webpack only honors webpackIgnore on import() and
compiles the call to a broken module stub, so createRequire it is.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chargome

chargome commented Jul 9, 2026

Copy link
Copy Markdown
MemberAuthor

@timfish

I reworked the pr so we have a nextjs specific registerDiagnosticsChannelInjection call which takes an optional tracingHooksDir that uses the createRequire path in the nextjs case. This enables us to not bundle in the transformer hook in turbopack and keep the hybrid buildtime/runtime approach as long as this breaks.

On magic comments: require(/* webpackIgnore: true */ /* turbopackIgnore: true */ specifier); did work for turbopack but not for webpack (apparently webpackIgnore only works for import). I'd rather have a solution that works uniformly across bundlers in nextjs which is createRequire.

Once the transformer becomes bundle-safe, we can update the code and internalize all the modules for both bundlers.

@timfishtimfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great bundler wrangling!

@chargome
chargome merged commit 4951504 into developJul 10, 2026
215 checks passed
@chargome
chargome deleted the cg/nextjs-orchestrion-support branch July 10, 2026 10:53
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support orchestrion auto-instrumentation in turbopack

3 participants

@chargome@timfish@andreiborza
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(nextjs): Add opt-in for orchestrion instrumentation - #22043

Merged
chargome merged 13 commits into
developfrom
cg/nextjs-orchestrion-support
Jul 10, 2026
Merged

feat(nextjs): Add opt-in for orchestrion instrumentation#22043
chargome merged 13 commits into
developfrom
cg/nextjs-orchestrion-support

Conversation

@chargome

@chargomechargome commented Jul 8, 2026

Copy link
Copy Markdown
Member

Adds an experimental option for opting in to orchestrion instrumentation.

  • Once the opt-in flag is set we:
    • Un-externalize the bundle-safe instrumented packages; bundle-unsafe ones (mysql) stay external and are instrumented by the runtime module hook, which works because we also externalize the @apm-js-collab/* transformer packages.
    • transpilePackages for the ones that Next externalizes by default (e.g. pg) — removing them from serverExternalPackages isn't enough for Next's own defaults.
    • Inject the code-transform loader as a Turbopack rule and as a webpack plugin.
  • The webpack loader gets exported from our server utils package (which can be used for turbopack too)

Verified in e2e

closes#22009

@chargomechargome self-assigned this Jul 8, 2026
chargomeand others added 6 commits July 8, 2026 10:06
Two fixes for diagnostics-channel injection under Turbopack:
Externalize @apm-js-collab/tracing-hooks and @apm-js-collab/code-transformer
when the flag is on. Bundled, the code transformer's parser breaks
('a.parse is not a function'), so the runtime module hook silently returned
untransformed sources and externalized packages never produced spans.
Keep mysql in serverExternalPackages instead of force-bundling it. Turbopack
cannot bundle mysql 2.x correctly (the wire-protocol handshake fails with
'Received packet in the wrong sequence' even untransformed). External, it is
now instrumented by the working runtime hook instead of the build-time loader.
Also drop the bundler marker from the orchestrion webpack plugin: it made
registerDiagnosticsChannelInjection() skip the runtime hook, but the hybrid
setup (loader for bundled deps, runtime hook for external ones) needs both.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Instead of un-externalizing every instrumented package and forcing the
Next-default-external ones through transpilePackages, only packages on an
explicit bundle-safe allowlist (currently ioredis) are removed from Sentry's
own serverExternalPackages defaults. Everything else — Next's defaults, the
user's externals, the rest of Sentry's defaults — stays external and is
instrumented by the runtime module hook on require, which works since the
orchestrion machinery is externalized.
This is safer: bundling a server package changes real behavior (mysql 2.x
corrupts its wire protocol when bundled by Turbopack), and new upstream
instrumentations (e.g. hapi) now default to the external/runtime-hook path
instead of silently becoming bundled. It also removes the Next
server-external-packages list parsing and the pg-native webpack workaround,
both only needed to support force-bundling.
Verified in the nextjs-16-orchestrion e2e: ioredis via the build-time loader,
pg and mysql via the runtime hook.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t@13
Use createRequire(__filename) instead of aliasing the CJS require in the
orchestrion webpack bundler module. Next.js 13 bundles @sentry/nextjs into
the server build, and webpack flags the aliased require with 'Critical
dependency: require function is used in a way in which dependencies cannot
be statically extracted', which the nextjs-app-dir e2e treats as a failure.
Add isDiagnosticsChannelInjectionEnabled to the consistent-exports ignore
list: like its companions experimentalUseDiagnosticsChannelInjection and
diagnosticsChannelInjectionIntegrations, the Node-runtime-only opt-in is not
surfaced through the framework / serverless SDKs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chargome

Copy link
Copy Markdown
MemberAuthor

bugbot run

Comment threadpackages/nextjs/src/config/webpack.ts
The plugin previously ran for all server compilations, including the edge
runtime, which diagnostics-channel injection does not target. Gate it on the
already-derived runtime instead of isServer and add unit tests covering the
server/edge/client/flag-off cases.
Also trim down the orchestrion-related comments.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chargome

Copy link
Copy Markdown
MemberAuthor

bugbot run

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a3a3595. Configure here.

Comment threadpackages/nextjs/src/server/index.ts
expect(externals).toContain('mysql');
expect(externals).not.toContain('@apm-js-collab/tracing-hooks');
});
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feat lacks integration or E2E

Medium Severity

This feature PR adds unit and webpack config tests but no integration or E2E test in the diff, though the description references separate e2e verification. Review guidelines expect at least one integration or E2E test for feat changes.

Fix in CursorFix in Web

Triggered by project rule: PR Review Guidelines for Cursor Bot

Reviewed by Cursor Bugbot for commit a3a3595. Configure here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handled in #22080

// module don't emit a "Critical dependency" warning.
function getOrchestrionRequire(): ReturnType<typeof createRequire> {
let nodeRequire: ReturnType<typeof createRequire>;
/*! rollup-include-cjs-only */

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

big thanks to @timfish for this :D

@chargome
chargome marked this pull request as ready for review July 9, 2026 08:34
@chargome
chargome requested review from a team as code ownersJuly 9, 2026 08:34
@chargome
chargome requested review from a team, JPeer264, andreiborza, logaretm, mydea, nicohrubec, s1gr1d and timfish and removed request for a team, JPeer264, andreiborza and s1gr1dJuly 9, 2026 08:34
Comment threadpackages/nextjs/src/config/webpack.ts
When the SDK is bundled into a Next.js server build, the runtime module
hook's bare require of @apm-js-collab/tracing-hooks resolves relative to
the emitted chunk, which fails under isolated installs (pnpm) where the
package is not linked at the app root — the hook silently no-ops and
externalized packages (pg, mysql) lose their spans.
Resolve the package location in withSentryConfig, where the SDK is a
real on-disk package, and inline it as a build-time env value that the
runtime prefers over the bare specifier. Also construct nodeRequire via
createRequire in both build flavors so bundlers don't statically trace
the call (Turbopack otherwise tries to resolve the injected absolute
path at build time).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@timfishtimfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a webpack plugin in this PR:

Why is the createRequire needed?

@github-actions

github-actionsBot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser27.6 kB--
@sentry/browser - with treeshaking flags26.04 kB--
@sentry/browser (incl. Tracing)46.35 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.14 kB--
@sentry/browser (incl. Tracing, Profiling)51.13 kB--
@sentry/browser (incl. Tracing, Replay)85.62 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags75.26 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)90.33 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)102.99 kB--
@sentry/browser (incl. Feedback)44.78 kB--
@sentry/browser (incl. sendFeedback)32.4 kB--
@sentry/browser (incl. FeedbackAsync)37.53 kB--
@sentry/browser (incl. Metrics)28.68 kB--
@sentry/browser (incl. Logs)28.93 kB--
@sentry/browser (incl. Metrics & Logs)29.61 kB--
@sentry/react29.39 kB-0.01%-1 B 🔽
@sentry/react (incl. Tracing)48.62 kB--
@sentry/vue33.03 kB-0.01%-1 B 🔽
@sentry/vue (incl. Tracing)48.33 kB--
@sentry/svelte27.63 kB--
CDN Bundle30 kB--
CDN Bundle (incl. Tracing)48.33 kB--
CDN Bundle (incl. Logs, Metrics)31.58 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)49.65 kB--
CDN Bundle (incl. Replay, Logs, Metrics)70.82 kB--
CDN Bundle (incl. Tracing, Replay)85.84 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)87.16 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)91.64 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.92 kB--
CDN Bundle - uncompressed89.33 kB--
CDN Bundle (incl. Tracing) - uncompressed146.07 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed94.03 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed150.05 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed218.76 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed265.28 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed269.24 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed278.98 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed282.93 kB--
@sentry/nextjs (client)51.17 kB--
@sentry/sveltekit (client)46.8 kB--
@sentry/core/server78.42 kB-0.01%-2 B 🔽
@sentry/core/browser64.77 kB--
@sentry/node-core62.72 kB-0.01%-1 B 🔽
@sentry/node125.35 kB-0.01%-1 B 🔽
@sentry/node (incl. diagnostics channel injection)138.67 kB+0.05%+65 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)69.95 kB--
@sentry/node/light50.73 kB-0.01%-1 B 🔽
@sentry/node - without tracing74.04 kB-0.01%-2 B 🔽
@sentry/aws-serverless83.26 kB--
@sentry/cloudflare (withSentry) - minified181.47 kB--
@sentry/cloudflare (withSentry)448.98 kB--

View base workflow run

Comment threadpackages/server-utils/src/orchestrion/runtime/register.ts Outdated
@timfish

Copy link
Copy Markdown
Collaborator

I guess the createRequire and then the require obfuscation are to stop webpack including all the orchestrion stuff in a bundle when that feature is not used?

Ideally we should avoid using config options to enable orchestrion mode because it requires all these hacks. I also think this will result in webpack not including the orchestrion code in a bundle even if you use it and result in a runtime error when it can't be loaded.

For the Node SDK we have Sentry.experimentalUseDiagnosticsChannelInjection() which ensures the code is only included in the bundle if you actually reference that.

The only valid use I've seen for createRequire is to ensure that tracing-hooks isn't directly in our import graph because it's ESM and this needs require(esm) to load it from CJS (ie. Node v20.19). The argument could be made that these new usages stop webpack from building a bundle that fails to run on Node v18 but I suspect webpack removes the require(esm) issue anyway.

@timfishtimfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getTracingHooksSpecifier obfuscation will mean that no other bundler will be able to resolve and bundle @apm-js-collab/tracing-hooks/hook-sync.mjs. This will force us (and users) to make it external everywhere.

I think in the long term we need to solve the Turbopack issue (happy to take a look!) but for now to get this merged I would create a nextjs specific registerDiagnosticsChannelInjection() so only nextjs has this obfuscation!

chargomeand others added 3 commits July 9, 2026 14:30
registerDiagnosticsChannelInjection() now takes an optional tracingHooksDir
and by default loads @apm-js-collab/tracing-hooks via its bare specifier
again, keeping the require statically analyzable for bundlers. Only the
Next.js SDK — whose server builds bundle the SDK and can't resolve the bare
specifier under pnpm — passes the build-time-resolved package location
(via a Next.js-specific experimentalUseDiagnosticsChannelInjection wrapper),
where loading switches to an opaque createRequire.
webpack ignore-comments were evaluated as an alternative: turbopackIgnore
works on require(), but webpack only honors webpackIgnore on import() and
compiles the call to a broken module stub, so createRequire it is.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chargome

chargome commented Jul 9, 2026

Copy link
Copy Markdown
MemberAuthor

@timfish

I reworked the pr so we have a nextjs specific registerDiagnosticsChannelInjection call which takes an optional tracingHooksDir that uses the createRequire path in the nextjs case. This enables us to not bundle in the transformer hook in turbopack and keep the hybrid buildtime/runtime approach as long as this breaks.

On magic comments: require(/* webpackIgnore: true */ /* turbopackIgnore: true */ specifier); did work for turbopack but not for webpack (apparently webpackIgnore only works for import). I'd rather have a solution that works uniformly across bundlers in nextjs which is createRequire.

Once the transformer becomes bundle-safe, we can update the code and internalize all the modules for both bundlers.

@timfishtimfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great bundler wrangling!

@chargome
chargome merged commit 4951504 into developJul 10, 2026
215 checks passed
@chargome
chargome deleted the cg/nextjs-orchestrion-support branch July 10, 2026 10:53
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support orchestrion auto-instrumentation in turbopack

3 participants

@chargome@timfish@andreiborza
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(nextjs): Add opt-in for orchestrion instrumentation - #22043

Merged
chargome merged 13 commits into
developfrom
cg/nextjs-orchestrion-support
Jul 10, 2026
Merged

feat(nextjs): Add opt-in for orchestrion instrumentation#22043
chargome merged 13 commits into
developfrom
cg/nextjs-orchestrion-support

Conversation

@chargome

@chargomechargome commented Jul 8, 2026

Copy link
Copy Markdown
Member

Adds an experimental option for opting in to orchestrion instrumentation.

  • Once the opt-in flag is set we:
    • Un-externalize the bundle-safe instrumented packages; bundle-unsafe ones (mysql) stay external and are instrumented by the runtime module hook, which works because we also externalize the @apm-js-collab/* transformer packages.
    • transpilePackages for the ones that Next externalizes by default (e.g. pg) — removing them from serverExternalPackages isn't enough for Next's own defaults.
    • Inject the code-transform loader as a Turbopack rule and as a webpack plugin.
  • The webpack loader gets exported from our server utils package (which can be used for turbopack too)

Verified in e2e

closes#22009

@chargomechargome self-assigned this Jul 8, 2026
chargomeand others added 6 commits July 8, 2026 10:06
Two fixes for diagnostics-channel injection under Turbopack:
Externalize @apm-js-collab/tracing-hooks and @apm-js-collab/code-transformer
when the flag is on. Bundled, the code transformer's parser breaks
('a.parse is not a function'), so the runtime module hook silently returned
untransformed sources and externalized packages never produced spans.
Keep mysql in serverExternalPackages instead of force-bundling it. Turbopack
cannot bundle mysql 2.x correctly (the wire-protocol handshake fails with
'Received packet in the wrong sequence' even untransformed). External, it is
now instrumented by the working runtime hook instead of the build-time loader.
Also drop the bundler marker from the orchestrion webpack plugin: it made
registerDiagnosticsChannelInjection() skip the runtime hook, but the hybrid
setup (loader for bundled deps, runtime hook for external ones) needs both.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Instead of un-externalizing every instrumented package and forcing the
Next-default-external ones through transpilePackages, only packages on an
explicit bundle-safe allowlist (currently ioredis) are removed from Sentry's
own serverExternalPackages defaults. Everything else — Next's defaults, the
user's externals, the rest of Sentry's defaults — stays external and is
instrumented by the runtime module hook on require, which works since the
orchestrion machinery is externalized.
This is safer: bundling a server package changes real behavior (mysql 2.x
corrupts its wire protocol when bundled by Turbopack), and new upstream
instrumentations (e.g. hapi) now default to the external/runtime-hook path
instead of silently becoming bundled. It also removes the Next
server-external-packages list parsing and the pg-native webpack workaround,
both only needed to support force-bundling.
Verified in the nextjs-16-orchestrion e2e: ioredis via the build-time loader,
pg and mysql via the runtime hook.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t@13
Use createRequire(__filename) instead of aliasing the CJS require in the
orchestrion webpack bundler module. Next.js 13 bundles @sentry/nextjs into
the server build, and webpack flags the aliased require with 'Critical
dependency: require function is used in a way in which dependencies cannot
be statically extracted', which the nextjs-app-dir e2e treats as a failure.
Add isDiagnosticsChannelInjectionEnabled to the consistent-exports ignore
list: like its companions experimentalUseDiagnosticsChannelInjection and
diagnosticsChannelInjectionIntegrations, the Node-runtime-only opt-in is not
surfaced through the framework / serverless SDKs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chargome

Copy link
Copy Markdown
MemberAuthor

bugbot run

Comment threadpackages/nextjs/src/config/webpack.ts
The plugin previously ran for all server compilations, including the edge
runtime, which diagnostics-channel injection does not target. Gate it on the
already-derived runtime instead of isServer and add unit tests covering the
server/edge/client/flag-off cases.
Also trim down the orchestrion-related comments.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chargome

Copy link
Copy Markdown
MemberAuthor

bugbot run

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a3a3595. Configure here.

Comment threadpackages/nextjs/src/server/index.ts
expect(externals).toContain('mysql');
expect(externals).not.toContain('@apm-js-collab/tracing-hooks');
});
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feat lacks integration or E2E

Medium Severity

This feature PR adds unit and webpack config tests but no integration or E2E test in the diff, though the description references separate e2e verification. Review guidelines expect at least one integration or E2E test for feat changes.

Fix in CursorFix in Web

Triggered by project rule: PR Review Guidelines for Cursor Bot

Reviewed by Cursor Bugbot for commit a3a3595. Configure here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handled in #22080

// module don't emit a "Critical dependency" warning.
function getOrchestrionRequire(): ReturnType<typeof createRequire> {
let nodeRequire: ReturnType<typeof createRequire>;
/*! rollup-include-cjs-only */

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

big thanks to @timfish for this :D

@chargome
chargome marked this pull request as ready for review July 9, 2026 08:34
@chargome
chargome requested review from a team as code ownersJuly 9, 2026 08:34
@chargome
chargome requested review from a team, JPeer264, andreiborza, logaretm, mydea, nicohrubec, s1gr1d and timfish and removed request for a team, JPeer264, andreiborza and s1gr1dJuly 9, 2026 08:34
Comment threadpackages/nextjs/src/config/webpack.ts
When the SDK is bundled into a Next.js server build, the runtime module
hook's bare require of @apm-js-collab/tracing-hooks resolves relative to
the emitted chunk, which fails under isolated installs (pnpm) where the
package is not linked at the app root — the hook silently no-ops and
externalized packages (pg, mysql) lose their spans.
Resolve the package location in withSentryConfig, where the SDK is a
real on-disk package, and inline it as a build-time env value that the
runtime prefers over the bare specifier. Also construct nodeRequire via
createRequire in both build flavors so bundlers don't statically trace
the call (Turbopack otherwise tries to resolve the injected absolute
path at build time).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@timfishtimfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a webpack plugin in this PR:

Why is the createRequire needed?

@github-actions

github-actionsBot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser27.6 kB--
@sentry/browser - with treeshaking flags26.04 kB--
@sentry/browser (incl. Tracing)46.35 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.14 kB--
@sentry/browser (incl. Tracing, Profiling)51.13 kB--
@sentry/browser (incl. Tracing, Replay)85.62 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags75.26 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)90.33 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)102.99 kB--
@sentry/browser (incl. Feedback)44.78 kB--
@sentry/browser (incl. sendFeedback)32.4 kB--
@sentry/browser (incl. FeedbackAsync)37.53 kB--
@sentry/browser (incl. Metrics)28.68 kB--
@sentry/browser (incl. Logs)28.93 kB--
@sentry/browser (incl. Metrics & Logs)29.61 kB--
@sentry/react29.39 kB-0.01%-1 B 🔽
@sentry/react (incl. Tracing)48.62 kB--
@sentry/vue33.03 kB-0.01%-1 B 🔽
@sentry/vue (incl. Tracing)48.33 kB--
@sentry/svelte27.63 kB--
CDN Bundle30 kB--
CDN Bundle (incl. Tracing)48.33 kB--
CDN Bundle (incl. Logs, Metrics)31.58 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)49.65 kB--
CDN Bundle (incl. Replay, Logs, Metrics)70.82 kB--
CDN Bundle (incl. Tracing, Replay)85.84 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)87.16 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)91.64 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.92 kB--
CDN Bundle - uncompressed89.33 kB--
CDN Bundle (incl. Tracing) - uncompressed146.07 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed94.03 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed150.05 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed218.76 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed265.28 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed269.24 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed278.98 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed282.93 kB--
@sentry/nextjs (client)51.17 kB--
@sentry/sveltekit (client)46.8 kB--
@sentry/core/server78.42 kB-0.01%-2 B 🔽
@sentry/core/browser64.77 kB--
@sentry/node-core62.72 kB-0.01%-1 B 🔽
@sentry/node125.35 kB-0.01%-1 B 🔽
@sentry/node (incl. diagnostics channel injection)138.67 kB+0.05%+65 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)69.95 kB--
@sentry/node/light50.73 kB-0.01%-1 B 🔽
@sentry/node - without tracing74.04 kB-0.01%-2 B 🔽
@sentry/aws-serverless83.26 kB--
@sentry/cloudflare (withSentry) - minified181.47 kB--
@sentry/cloudflare (withSentry)448.98 kB--

View base workflow run

Comment threadpackages/server-utils/src/orchestrion/runtime/register.ts Outdated
@timfish

Copy link
Copy Markdown
Collaborator

I guess the createRequire and then the require obfuscation are to stop webpack including all the orchestrion stuff in a bundle when that feature is not used?

Ideally we should avoid using config options to enable orchestrion mode because it requires all these hacks. I also think this will result in webpack not including the orchestrion code in a bundle even if you use it and result in a runtime error when it can't be loaded.

For the Node SDK we have Sentry.experimentalUseDiagnosticsChannelInjection() which ensures the code is only included in the bundle if you actually reference that.

The only valid use I've seen for createRequire is to ensure that tracing-hooks isn't directly in our import graph because it's ESM and this needs require(esm) to load it from CJS (ie. Node v20.19). The argument could be made that these new usages stop webpack from building a bundle that fails to run on Node v18 but I suspect webpack removes the require(esm) issue anyway.

@timfishtimfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getTracingHooksSpecifier obfuscation will mean that no other bundler will be able to resolve and bundle @apm-js-collab/tracing-hooks/hook-sync.mjs. This will force us (and users) to make it external everywhere.

I think in the long term we need to solve the Turbopack issue (happy to take a look!) but for now to get this merged I would create a nextjs specific registerDiagnosticsChannelInjection() so only nextjs has this obfuscation!

chargomeand others added 3 commits July 9, 2026 14:30
registerDiagnosticsChannelInjection() now takes an optional tracingHooksDir
and by default loads @apm-js-collab/tracing-hooks via its bare specifier
again, keeping the require statically analyzable for bundlers. Only the
Next.js SDK — whose server builds bundle the SDK and can't resolve the bare
specifier under pnpm — passes the build-time-resolved package location
(via a Next.js-specific experimentalUseDiagnosticsChannelInjection wrapper),
where loading switches to an opaque createRequire.
webpack ignore-comments were evaluated as an alternative: turbopackIgnore
works on require(), but webpack only honors webpackIgnore on import() and
compiles the call to a broken module stub, so createRequire it is.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chargome

chargome commented Jul 9, 2026

Copy link
Copy Markdown
MemberAuthor

@timfish

I reworked the pr so we have a nextjs specific registerDiagnosticsChannelInjection call which takes an optional tracingHooksDir that uses the createRequire path in the nextjs case. This enables us to not bundle in the transformer hook in turbopack and keep the hybrid buildtime/runtime approach as long as this breaks.

On magic comments: require(/* webpackIgnore: true */ /* turbopackIgnore: true */ specifier); did work for turbopack but not for webpack (apparently webpackIgnore only works for import). I'd rather have a solution that works uniformly across bundlers in nextjs which is createRequire.

Once the transformer becomes bundle-safe, we can update the code and internalize all the modules for both bundlers.

@timfishtimfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great bundler wrangling!

@chargome
chargome merged commit 4951504 into developJul 10, 2026
215 checks passed
@chargome
chargome deleted the cg/nextjs-orchestrion-support branch July 10, 2026 10:53
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support orchestrion auto-instrumentation in turbopack

3 participants

@chargome@timfish@andreiborza
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(nextjs): Add opt-in for orchestrion instrumentation - #22043

Merged
chargome merged 13 commits into
developfrom
cg/nextjs-orchestrion-support
Jul 10, 2026
Merged

feat(nextjs): Add opt-in for orchestrion instrumentation#22043
chargome merged 13 commits into
developfrom
cg/nextjs-orchestrion-support

Conversation

@chargome

@chargomechargome commented Jul 8, 2026

Copy link
Copy Markdown
Member

Adds an experimental option for opting in to orchestrion instrumentation.

  • Once the opt-in flag is set we:
    • Un-externalize the bundle-safe instrumented packages; bundle-unsafe ones (mysql) stay external and are instrumented by the runtime module hook, which works because we also externalize the @apm-js-collab/* transformer packages.
    • transpilePackages for the ones that Next externalizes by default (e.g. pg) — removing them from serverExternalPackages isn't enough for Next's own defaults.
    • Inject the code-transform loader as a Turbopack rule and as a webpack plugin.
  • The webpack loader gets exported from our server utils package (which can be used for turbopack too)

Verified in e2e

closes#22009

@chargomechargome self-assigned this Jul 8, 2026
chargomeand others added 6 commits July 8, 2026 10:06
Two fixes for diagnostics-channel injection under Turbopack:
Externalize @apm-js-collab/tracing-hooks and @apm-js-collab/code-transformer
when the flag is on. Bundled, the code transformer's parser breaks
('a.parse is not a function'), so the runtime module hook silently returned
untransformed sources and externalized packages never produced spans.
Keep mysql in serverExternalPackages instead of force-bundling it. Turbopack
cannot bundle mysql 2.x correctly (the wire-protocol handshake fails with
'Received packet in the wrong sequence' even untransformed). External, it is
now instrumented by the working runtime hook instead of the build-time loader.
Also drop the bundler marker from the orchestrion webpack plugin: it made
registerDiagnosticsChannelInjection() skip the runtime hook, but the hybrid
setup (loader for bundled deps, runtime hook for external ones) needs both.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Instead of un-externalizing every instrumented package and forcing the
Next-default-external ones through transpilePackages, only packages on an
explicit bundle-safe allowlist (currently ioredis) are removed from Sentry's
own serverExternalPackages defaults. Everything else — Next's defaults, the
user's externals, the rest of Sentry's defaults — stays external and is
instrumented by the runtime module hook on require, which works since the
orchestrion machinery is externalized.
This is safer: bundling a server package changes real behavior (mysql 2.x
corrupts its wire protocol when bundled by Turbopack), and new upstream
instrumentations (e.g. hapi) now default to the external/runtime-hook path
instead of silently becoming bundled. It also removes the Next
server-external-packages list parsing and the pg-native webpack workaround,
both only needed to support force-bundling.
Verified in the nextjs-16-orchestrion e2e: ioredis via the build-time loader,
pg and mysql via the runtime hook.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t@13
Use createRequire(__filename) instead of aliasing the CJS require in the
orchestrion webpack bundler module. Next.js 13 bundles @sentry/nextjs into
the server build, and webpack flags the aliased require with 'Critical
dependency: require function is used in a way in which dependencies cannot
be statically extracted', which the nextjs-app-dir e2e treats as a failure.
Add isDiagnosticsChannelInjectionEnabled to the consistent-exports ignore
list: like its companions experimentalUseDiagnosticsChannelInjection and
diagnosticsChannelInjectionIntegrations, the Node-runtime-only opt-in is not
surfaced through the framework / serverless SDKs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chargome

Copy link
Copy Markdown
MemberAuthor

bugbot run

Comment threadpackages/nextjs/src/config/webpack.ts
The plugin previously ran for all server compilations, including the edge
runtime, which diagnostics-channel injection does not target. Gate it on the
already-derived runtime instead of isServer and add unit tests covering the
server/edge/client/flag-off cases.
Also trim down the orchestrion-related comments.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chargome

Copy link
Copy Markdown
MemberAuthor

bugbot run

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a3a3595. Configure here.

Comment threadpackages/nextjs/src/server/index.ts
expect(externals).toContain('mysql');
expect(externals).not.toContain('@apm-js-collab/tracing-hooks');
});
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feat lacks integration or E2E

Medium Severity

This feature PR adds unit and webpack config tests but no integration or E2E test in the diff, though the description references separate e2e verification. Review guidelines expect at least one integration or E2E test for feat changes.

Fix in CursorFix in Web

Triggered by project rule: PR Review Guidelines for Cursor Bot

Reviewed by Cursor Bugbot for commit a3a3595. Configure here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handled in #22080

// module don't emit a "Critical dependency" warning.
function getOrchestrionRequire(): ReturnType<typeof createRequire> {
let nodeRequire: ReturnType<typeof createRequire>;
/*! rollup-include-cjs-only */

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

big thanks to @timfish for this :D

@chargome
chargome marked this pull request as ready for review July 9, 2026 08:34
@chargome
chargome requested review from a team as code ownersJuly 9, 2026 08:34
@chargome
chargome requested review from a team, JPeer264, andreiborza, logaretm, mydea, nicohrubec, s1gr1d and timfish and removed request for a team, JPeer264, andreiborza and s1gr1dJuly 9, 2026 08:34
Comment threadpackages/nextjs/src/config/webpack.ts
When the SDK is bundled into a Next.js server build, the runtime module
hook's bare require of @apm-js-collab/tracing-hooks resolves relative to
the emitted chunk, which fails under isolated installs (pnpm) where the
package is not linked at the app root — the hook silently no-ops and
externalized packages (pg, mysql) lose their spans.
Resolve the package location in withSentryConfig, where the SDK is a
real on-disk package, and inline it as a build-time env value that the
runtime prefers over the bare specifier. Also construct nodeRequire via
createRequire in both build flavors so bundlers don't statically trace
the call (Turbopack otherwise tries to resolve the injected absolute
path at build time).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@timfishtimfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a webpack plugin in this PR:

Why is the createRequire needed?

@github-actions

github-actionsBot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser27.6 kB--
@sentry/browser - with treeshaking flags26.04 kB--
@sentry/browser (incl. Tracing)46.35 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.14 kB--
@sentry/browser (incl. Tracing, Profiling)51.13 kB--
@sentry/browser (incl. Tracing, Replay)85.62 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags75.26 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)90.33 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)102.99 kB--
@sentry/browser (incl. Feedback)44.78 kB--
@sentry/browser (incl. sendFeedback)32.4 kB--
@sentry/browser (incl. FeedbackAsync)37.53 kB--
@sentry/browser (incl. Metrics)28.68 kB--
@sentry/browser (incl. Logs)28.93 kB--
@sentry/browser (incl. Metrics & Logs)29.61 kB--
@sentry/react29.39 kB-0.01%-1 B 🔽
@sentry/react (incl. Tracing)48.62 kB--
@sentry/vue33.03 kB-0.01%-1 B 🔽
@sentry/vue (incl. Tracing)48.33 kB--
@sentry/svelte27.63 kB--
CDN Bundle30 kB--
CDN Bundle (incl. Tracing)48.33 kB--
CDN Bundle (incl. Logs, Metrics)31.58 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)49.65 kB--
CDN Bundle (incl. Replay, Logs, Metrics)70.82 kB--
CDN Bundle (incl. Tracing, Replay)85.84 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)87.16 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)91.64 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.92 kB--
CDN Bundle - uncompressed89.33 kB--
CDN Bundle (incl. Tracing) - uncompressed146.07 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed94.03 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed150.05 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed218.76 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed265.28 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed269.24 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed278.98 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed282.93 kB--
@sentry/nextjs (client)51.17 kB--
@sentry/sveltekit (client)46.8 kB--
@sentry/core/server78.42 kB-0.01%-2 B 🔽
@sentry/core/browser64.77 kB--
@sentry/node-core62.72 kB-0.01%-1 B 🔽
@sentry/node125.35 kB-0.01%-1 B 🔽
@sentry/node (incl. diagnostics channel injection)138.67 kB+0.05%+65 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)69.95 kB--
@sentry/node/light50.73 kB-0.01%-1 B 🔽
@sentry/node - without tracing74.04 kB-0.01%-2 B 🔽
@sentry/aws-serverless83.26 kB--
@sentry/cloudflare (withSentry) - minified181.47 kB--
@sentry/cloudflare (withSentry)448.98 kB--

View base workflow run

Comment threadpackages/server-utils/src/orchestrion/runtime/register.ts Outdated
@timfish

Copy link
Copy Markdown
Collaborator

I guess the createRequire and then the require obfuscation are to stop webpack including all the orchestrion stuff in a bundle when that feature is not used?

Ideally we should avoid using config options to enable orchestrion mode because it requires all these hacks. I also think this will result in webpack not including the orchestrion code in a bundle even if you use it and result in a runtime error when it can't be loaded.

For the Node SDK we have Sentry.experimentalUseDiagnosticsChannelInjection() which ensures the code is only included in the bundle if you actually reference that.

The only valid use I've seen for createRequire is to ensure that tracing-hooks isn't directly in our import graph because it's ESM and this needs require(esm) to load it from CJS (ie. Node v20.19). The argument could be made that these new usages stop webpack from building a bundle that fails to run on Node v18 but I suspect webpack removes the require(esm) issue anyway.

@timfishtimfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getTracingHooksSpecifier obfuscation will mean that no other bundler will be able to resolve and bundle @apm-js-collab/tracing-hooks/hook-sync.mjs. This will force us (and users) to make it external everywhere.

I think in the long term we need to solve the Turbopack issue (happy to take a look!) but for now to get this merged I would create a nextjs specific registerDiagnosticsChannelInjection() so only nextjs has this obfuscation!

chargomeand others added 3 commits July 9, 2026 14:30
registerDiagnosticsChannelInjection() now takes an optional tracingHooksDir
and by default loads @apm-js-collab/tracing-hooks via its bare specifier
again, keeping the require statically analyzable for bundlers. Only the
Next.js SDK — whose server builds bundle the SDK and can't resolve the bare
specifier under pnpm — passes the build-time-resolved package location
(via a Next.js-specific experimentalUseDiagnosticsChannelInjection wrapper),
where loading switches to an opaque createRequire.
webpack ignore-comments were evaluated as an alternative: turbopackIgnore
works on require(), but webpack only honors webpackIgnore on import() and
compiles the call to a broken module stub, so createRequire it is.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chargome

chargome commented Jul 9, 2026

Copy link
Copy Markdown
MemberAuthor

@timfish

I reworked the pr so we have a nextjs specific registerDiagnosticsChannelInjection call which takes an optional tracingHooksDir that uses the createRequire path in the nextjs case. This enables us to not bundle in the transformer hook in turbopack and keep the hybrid buildtime/runtime approach as long as this breaks.

On magic comments: require(/* webpackIgnore: true */ /* turbopackIgnore: true */ specifier); did work for turbopack but not for webpack (apparently webpackIgnore only works for import). I'd rather have a solution that works uniformly across bundlers in nextjs which is createRequire.

Once the transformer becomes bundle-safe, we can update the code and internalize all the modules for both bundlers.

@timfishtimfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great bundler wrangling!

@chargome
chargome merged commit 4951504 into developJul 10, 2026
215 checks passed
@chargome
chargome deleted the cg/nextjs-orchestrion-support branch July 10, 2026 10:53
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support orchestrion auto-instrumentation in turbopack

3 participants

@chargome@timfish@andreiborza
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat(nextjs): Add opt-in for orchestrion instrumentation - #22043

Merged
chargome merged 13 commits into
developfrom
cg/nextjs-orchestrion-support
Jul 10, 2026
Merged

feat(nextjs): Add opt-in for orchestrion instrumentation#22043
chargome merged 13 commits into
developfrom
cg/nextjs-orchestrion-support

Conversation

@chargome

@chargomechargome commented Jul 8, 2026

Copy link
Copy Markdown
Member

Adds an experimental option for opting in to orchestrion instrumentation.

  • Once the opt-in flag is set we:
    • Un-externalize the bundle-safe instrumented packages; bundle-unsafe ones (mysql) stay external and are instrumented by the runtime module hook, which works because we also externalize the @apm-js-collab/* transformer packages.
    • transpilePackages for the ones that Next externalizes by default (e.g. pg) — removing them from serverExternalPackages isn't enough for Next's own defaults.
    • Inject the code-transform loader as a Turbopack rule and as a webpack plugin.
  • The webpack loader gets exported from our server utils package (which can be used for turbopack too)

Verified in e2e

closes#22009

@chargomechargome self-assigned this Jul 8, 2026
chargomeand others added 6 commits July 8, 2026 10:06
Two fixes for diagnostics-channel injection under Turbopack:
Externalize @apm-js-collab/tracing-hooks and @apm-js-collab/code-transformer
when the flag is on. Bundled, the code transformer's parser breaks
('a.parse is not a function'), so the runtime module hook silently returned
untransformed sources and externalized packages never produced spans.
Keep mysql in serverExternalPackages instead of force-bundling it. Turbopack
cannot bundle mysql 2.x correctly (the wire-protocol handshake fails with
'Received packet in the wrong sequence' even untransformed). External, it is
now instrumented by the working runtime hook instead of the build-time loader.
Also drop the bundler marker from the orchestrion webpack plugin: it made
registerDiagnosticsChannelInjection() skip the runtime hook, but the hybrid
setup (loader for bundled deps, runtime hook for external ones) needs both.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Instead of un-externalizing every instrumented package and forcing the
Next-default-external ones through transpilePackages, only packages on an
explicit bundle-safe allowlist (currently ioredis) are removed from Sentry's
own serverExternalPackages defaults. Everything else — Next's defaults, the
user's externals, the rest of Sentry's defaults — stays external and is
instrumented by the runtime module hook on require, which works since the
orchestrion machinery is externalized.
This is safer: bundling a server package changes real behavior (mysql 2.x
corrupts its wire protocol when bundled by Turbopack), and new upstream
instrumentations (e.g. hapi) now default to the external/runtime-hook path
instead of silently becoming bundled. It also removes the Next
server-external-packages list parsing and the pg-native webpack workaround,
both only needed to support force-bundling.
Verified in the nextjs-16-orchestrion e2e: ioredis via the build-time loader,
pg and mysql via the runtime hook.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t@13
Use createRequire(__filename) instead of aliasing the CJS require in the
orchestrion webpack bundler module. Next.js 13 bundles @sentry/nextjs into
the server build, and webpack flags the aliased require with 'Critical
dependency: require function is used in a way in which dependencies cannot
be statically extracted', which the nextjs-app-dir e2e treats as a failure.
Add isDiagnosticsChannelInjectionEnabled to the consistent-exports ignore
list: like its companions experimentalUseDiagnosticsChannelInjection and
diagnosticsChannelInjectionIntegrations, the Node-runtime-only opt-in is not
surfaced through the framework / serverless SDKs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chargome

Copy link
Copy Markdown
MemberAuthor

bugbot run

Comment threadpackages/nextjs/src/config/webpack.ts
The plugin previously ran for all server compilations, including the edge
runtime, which diagnostics-channel injection does not target. Gate it on the
already-derived runtime instead of isServer and add unit tests covering the
server/edge/client/flag-off cases.
Also trim down the orchestrion-related comments.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chargome

Copy link
Copy Markdown
MemberAuthor

bugbot run

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a3a3595. Configure here.

Comment threadpackages/nextjs/src/server/index.ts
expect(externals).toContain('mysql');
expect(externals).not.toContain('@apm-js-collab/tracing-hooks');
});
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feat lacks integration or E2E

Medium Severity

This feature PR adds unit and webpack config tests but no integration or E2E test in the diff, though the description references separate e2e verification. Review guidelines expect at least one integration or E2E test for feat changes.

Fix in CursorFix in Web

Triggered by project rule: PR Review Guidelines for Cursor Bot

Reviewed by Cursor Bugbot for commit a3a3595. Configure here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handled in #22080

// module don't emit a "Critical dependency" warning.
function getOrchestrionRequire(): ReturnType<typeof createRequire> {
let nodeRequire: ReturnType<typeof createRequire>;
/*! rollup-include-cjs-only */

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

big thanks to @timfish for this :D

@chargome
chargome marked this pull request as ready for review July 9, 2026 08:34
@chargome
chargome requested review from a team as code ownersJuly 9, 2026 08:34
@chargome
chargome requested review from a team, JPeer264, andreiborza, logaretm, mydea, nicohrubec, s1gr1d and timfish and removed request for a team, JPeer264, andreiborza and s1gr1dJuly 9, 2026 08:34
Comment threadpackages/nextjs/src/config/webpack.ts
When the SDK is bundled into a Next.js server build, the runtime module
hook's bare require of @apm-js-collab/tracing-hooks resolves relative to
the emitted chunk, which fails under isolated installs (pnpm) where the
package is not linked at the app root — the hook silently no-ops and
externalized packages (pg, mysql) lose their spans.
Resolve the package location in withSentryConfig, where the SDK is a
real on-disk package, and inline it as a build-time env value that the
runtime prefers over the bare specifier. Also construct nodeRequire via
createRequire in both build flavors so bundlers don't statically trace
the call (Turbopack otherwise tries to resolve the injected absolute
path at build time).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@timfishtimfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a webpack plugin in this PR:

Why is the createRequire needed?

@github-actions

github-actionsBot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser27.6 kB--
@sentry/browser - with treeshaking flags26.04 kB--
@sentry/browser (incl. Tracing)46.35 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.14 kB--
@sentry/browser (incl. Tracing, Profiling)51.13 kB--
@sentry/browser (incl. Tracing, Replay)85.62 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags75.26 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)90.33 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)102.99 kB--
@sentry/browser (incl. Feedback)44.78 kB--
@sentry/browser (incl. sendFeedback)32.4 kB--
@sentry/browser (incl. FeedbackAsync)37.53 kB--
@sentry/browser (incl. Metrics)28.68 kB--
@sentry/browser (incl. Logs)28.93 kB--
@sentry/browser (incl. Metrics & Logs)29.61 kB--
@sentry/react29.39 kB-0.01%-1 B 🔽
@sentry/react (incl. Tracing)48.62 kB--
@sentry/vue33.03 kB-0.01%-1 B 🔽
@sentry/vue (incl. Tracing)48.33 kB--
@sentry/svelte27.63 kB--
CDN Bundle30 kB--
CDN Bundle (incl. Tracing)48.33 kB--
CDN Bundle (incl. Logs, Metrics)31.58 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)49.65 kB--
CDN Bundle (incl. Replay, Logs, Metrics)70.82 kB--
CDN Bundle (incl. Tracing, Replay)85.84 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)87.16 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)91.64 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.92 kB--
CDN Bundle - uncompressed89.33 kB--
CDN Bundle (incl. Tracing) - uncompressed146.07 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed94.03 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed150.05 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed218.76 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed265.28 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed269.24 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed278.98 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed282.93 kB--
@sentry/nextjs (client)51.17 kB--
@sentry/sveltekit (client)46.8 kB--
@sentry/core/server78.42 kB-0.01%-2 B 🔽
@sentry/core/browser64.77 kB--
@sentry/node-core62.72 kB-0.01%-1 B 🔽
@sentry/node125.35 kB-0.01%-1 B 🔽
@sentry/node (incl. diagnostics channel injection)138.67 kB+0.05%+65 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)69.95 kB--
@sentry/node/light50.73 kB-0.01%-1 B 🔽
@sentry/node - without tracing74.04 kB-0.01%-2 B 🔽
@sentry/aws-serverless83.26 kB--
@sentry/cloudflare (withSentry) - minified181.47 kB--
@sentry/cloudflare (withSentry)448.98 kB--

View base workflow run

Comment threadpackages/server-utils/src/orchestrion/runtime/register.ts Outdated
@timfish

Copy link
Copy Markdown
Collaborator

I guess the createRequire and then the require obfuscation are to stop webpack including all the orchestrion stuff in a bundle when that feature is not used?

Ideally we should avoid using config options to enable orchestrion mode because it requires all these hacks. I also think this will result in webpack not including the orchestrion code in a bundle even if you use it and result in a runtime error when it can't be loaded.

For the Node SDK we have Sentry.experimentalUseDiagnosticsChannelInjection() which ensures the code is only included in the bundle if you actually reference that.

The only valid use I've seen for createRequire is to ensure that tracing-hooks isn't directly in our import graph because it's ESM and this needs require(esm) to load it from CJS (ie. Node v20.19). The argument could be made that these new usages stop webpack from building a bundle that fails to run on Node v18 but I suspect webpack removes the require(esm) issue anyway.

@timfishtimfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getTracingHooksSpecifier obfuscation will mean that no other bundler will be able to resolve and bundle @apm-js-collab/tracing-hooks/hook-sync.mjs. This will force us (and users) to make it external everywhere.

I think in the long term we need to solve the Turbopack issue (happy to take a look!) but for now to get this merged I would create a nextjs specific registerDiagnosticsChannelInjection() so only nextjs has this obfuscation!

chargomeand others added 3 commits July 9, 2026 14:30
registerDiagnosticsChannelInjection() now takes an optional tracingHooksDir
and by default loads @apm-js-collab/tracing-hooks via its bare specifier
again, keeping the require statically analyzable for bundlers. Only the
Next.js SDK — whose server builds bundle the SDK and can't resolve the bare
specifier under pnpm — passes the build-time-resolved package location
(via a Next.js-specific experimentalUseDiagnosticsChannelInjection wrapper),
where loading switches to an opaque createRequire.
webpack ignore-comments were evaluated as an alternative: turbopackIgnore
works on require(), but webpack only honors webpackIgnore on import() and
compiles the call to a broken module stub, so createRequire it is.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chargome

chargome commented Jul 9, 2026

Copy link
Copy Markdown
MemberAuthor

@timfish

I reworked the pr so we have a nextjs specific registerDiagnosticsChannelInjection call which takes an optional tracingHooksDir that uses the createRequire path in the nextjs case. This enables us to not bundle in the transformer hook in turbopack and keep the hybrid buildtime/runtime approach as long as this breaks.

On magic comments: require(/* webpackIgnore: true */ /* turbopackIgnore: true */ specifier); did work for turbopack but not for webpack (apparently webpackIgnore only works for import). I'd rather have a solution that works uniformly across bundlers in nextjs which is createRequire.

Once the transformer becomes bundle-safe, we can update the code and internalize all the modules for both bundlers.

@timfishtimfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great bundler wrangling!

@chargome
chargome merged commit 4951504 into developJul 10, 2026
215 checks passed
@chargome
chargome deleted the cg/nextjs-orchestrion-support branch July 10, 2026 10:53
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support orchestrion auto-instrumentation in turbopack

3 participants

@chargome@timfish@andreiborza
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(nextjs): Add opt-in for orchestrion instrumentation - #22043

Merged
chargome merged 13 commits into
developfrom
cg/nextjs-orchestrion-support
Jul 10, 2026
Merged

feat(nextjs): Add opt-in for orchestrion instrumentation#22043
chargome merged 13 commits into
developfrom
cg/nextjs-orchestrion-support

Conversation

@chargome

@chargomechargome commented Jul 8, 2026

Copy link
Copy Markdown
Member

Adds an experimental option for opting in to orchestrion instrumentation.

  • Once the opt-in flag is set we:
    • Un-externalize the bundle-safe instrumented packages; bundle-unsafe ones (mysql) stay external and are instrumented by the runtime module hook, which works because we also externalize the @apm-js-collab/* transformer packages.
    • transpilePackages for the ones that Next externalizes by default (e.g. pg) — removing them from serverExternalPackages isn't enough for Next's own defaults.
    • Inject the code-transform loader as a Turbopack rule and as a webpack plugin.
  • The webpack loader gets exported from our server utils package (which can be used for turbopack too)

Verified in e2e

closes#22009

@chargomechargome self-assigned this Jul 8, 2026
chargomeand others added 6 commits July 8, 2026 10:06
Two fixes for diagnostics-channel injection under Turbopack:
Externalize @apm-js-collab/tracing-hooks and @apm-js-collab/code-transformer
when the flag is on. Bundled, the code transformer's parser breaks
('a.parse is not a function'), so the runtime module hook silently returned
untransformed sources and externalized packages never produced spans.
Keep mysql in serverExternalPackages instead of force-bundling it. Turbopack
cannot bundle mysql 2.x correctly (the wire-protocol handshake fails with
'Received packet in the wrong sequence' even untransformed). External, it is
now instrumented by the working runtime hook instead of the build-time loader.
Also drop the bundler marker from the orchestrion webpack plugin: it made
registerDiagnosticsChannelInjection() skip the runtime hook, but the hybrid
setup (loader for bundled deps, runtime hook for external ones) needs both.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Instead of un-externalizing every instrumented package and forcing the
Next-default-external ones through transpilePackages, only packages on an
explicit bundle-safe allowlist (currently ioredis) are removed from Sentry's
own serverExternalPackages defaults. Everything else — Next's defaults, the
user's externals, the rest of Sentry's defaults — stays external and is
instrumented by the runtime module hook on require, which works since the
orchestrion machinery is externalized.
This is safer: bundling a server package changes real behavior (mysql 2.x
corrupts its wire protocol when bundled by Turbopack), and new upstream
instrumentations (e.g. hapi) now default to the external/runtime-hook path
instead of silently becoming bundled. It also removes the Next
server-external-packages list parsing and the pg-native webpack workaround,
both only needed to support force-bundling.
Verified in the nextjs-16-orchestrion e2e: ioredis via the build-time loader,
pg and mysql via the runtime hook.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t@13
Use createRequire(__filename) instead of aliasing the CJS require in the
orchestrion webpack bundler module. Next.js 13 bundles @sentry/nextjs into
the server build, and webpack flags the aliased require with 'Critical
dependency: require function is used in a way in which dependencies cannot
be statically extracted', which the nextjs-app-dir e2e treats as a failure.
Add isDiagnosticsChannelInjectionEnabled to the consistent-exports ignore
list: like its companions experimentalUseDiagnosticsChannelInjection and
diagnosticsChannelInjectionIntegrations, the Node-runtime-only opt-in is not
surfaced through the framework / serverless SDKs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chargome

Copy link
Copy Markdown
MemberAuthor

bugbot run

Comment threadpackages/nextjs/src/config/webpack.ts
The plugin previously ran for all server compilations, including the edge
runtime, which diagnostics-channel injection does not target. Gate it on the
already-derived runtime instead of isServer and add unit tests covering the
server/edge/client/flag-off cases.
Also trim down the orchestrion-related comments.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chargome

Copy link
Copy Markdown
MemberAuthor

bugbot run

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a3a3595. Configure here.

Comment threadpackages/nextjs/src/server/index.ts
expect(externals).toContain('mysql');
expect(externals).not.toContain('@apm-js-collab/tracing-hooks');
});
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feat lacks integration or E2E

Medium Severity

This feature PR adds unit and webpack config tests but no integration or E2E test in the diff, though the description references separate e2e verification. Review guidelines expect at least one integration or E2E test for feat changes.

Fix in CursorFix in Web

Triggered by project rule: PR Review Guidelines for Cursor Bot

Reviewed by Cursor Bugbot for commit a3a3595. Configure here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handled in #22080

// module don't emit a "Critical dependency" warning.
function getOrchestrionRequire(): ReturnType<typeof createRequire> {
let nodeRequire: ReturnType<typeof createRequire>;
/*! rollup-include-cjs-only */

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

big thanks to @timfish for this :D

@chargome
chargome marked this pull request as ready for review July 9, 2026 08:34
@chargome
chargome requested review from a team as code ownersJuly 9, 2026 08:34
@chargome
chargome requested review from a team, JPeer264, andreiborza, logaretm, mydea, nicohrubec, s1gr1d and timfish and removed request for a team, JPeer264, andreiborza and s1gr1dJuly 9, 2026 08:34
Comment threadpackages/nextjs/src/config/webpack.ts
When the SDK is bundled into a Next.js server build, the runtime module
hook's bare require of @apm-js-collab/tracing-hooks resolves relative to
the emitted chunk, which fails under isolated installs (pnpm) where the
package is not linked at the app root — the hook silently no-ops and
externalized packages (pg, mysql) lose their spans.
Resolve the package location in withSentryConfig, where the SDK is a
real on-disk package, and inline it as a build-time env value that the
runtime prefers over the bare specifier. Also construct nodeRequire via
createRequire in both build flavors so bundlers don't statically trace
the call (Turbopack otherwise tries to resolve the injected absolute
path at build time).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@timfishtimfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a webpack plugin in this PR:

Why is the createRequire needed?

@github-actions

github-actionsBot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser27.6 kB--
@sentry/browser - with treeshaking flags26.04 kB--
@sentry/browser (incl. Tracing)46.35 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.14 kB--
@sentry/browser (incl. Tracing, Profiling)51.13 kB--
@sentry/browser (incl. Tracing, Replay)85.62 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags75.26 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)90.33 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)102.99 kB--
@sentry/browser (incl. Feedback)44.78 kB--
@sentry/browser (incl. sendFeedback)32.4 kB--
@sentry/browser (incl. FeedbackAsync)37.53 kB--
@sentry/browser (incl. Metrics)28.68 kB--
@sentry/browser (incl. Logs)28.93 kB--
@sentry/browser (incl. Metrics & Logs)29.61 kB--
@sentry/react29.39 kB-0.01%-1 B 🔽
@sentry/react (incl. Tracing)48.62 kB--
@sentry/vue33.03 kB-0.01%-1 B 🔽
@sentry/vue (incl. Tracing)48.33 kB--
@sentry/svelte27.63 kB--
CDN Bundle30 kB--
CDN Bundle (incl. Tracing)48.33 kB--
CDN Bundle (incl. Logs, Metrics)31.58 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)49.65 kB--
CDN Bundle (incl. Replay, Logs, Metrics)70.82 kB--
CDN Bundle (incl. Tracing, Replay)85.84 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)87.16 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)91.64 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.92 kB--
CDN Bundle - uncompressed89.33 kB--
CDN Bundle (incl. Tracing) - uncompressed146.07 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed94.03 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed150.05 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed218.76 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed265.28 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed269.24 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed278.98 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed282.93 kB--
@sentry/nextjs (client)51.17 kB--
@sentry/sveltekit (client)46.8 kB--
@sentry/core/server78.42 kB-0.01%-2 B 🔽
@sentry/core/browser64.77 kB--
@sentry/node-core62.72 kB-0.01%-1 B 🔽
@sentry/node125.35 kB-0.01%-1 B 🔽
@sentry/node (incl. diagnostics channel injection)138.67 kB+0.05%+65 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)69.95 kB--
@sentry/node/light50.73 kB-0.01%-1 B 🔽
@sentry/node - without tracing74.04 kB-0.01%-2 B 🔽
@sentry/aws-serverless83.26 kB--
@sentry/cloudflare (withSentry) - minified181.47 kB--
@sentry/cloudflare (withSentry)448.98 kB--

View base workflow run

Comment threadpackages/server-utils/src/orchestrion/runtime/register.ts Outdated
@timfish

Copy link
Copy Markdown
Collaborator

I guess the createRequire and then the require obfuscation are to stop webpack including all the orchestrion stuff in a bundle when that feature is not used?

Ideally we should avoid using config options to enable orchestrion mode because it requires all these hacks. I also think this will result in webpack not including the orchestrion code in a bundle even if you use it and result in a runtime error when it can't be loaded.

For the Node SDK we have Sentry.experimentalUseDiagnosticsChannelInjection() which ensures the code is only included in the bundle if you actually reference that.

The only valid use I've seen for createRequire is to ensure that tracing-hooks isn't directly in our import graph because it's ESM and this needs require(esm) to load it from CJS (ie. Node v20.19). The argument could be made that these new usages stop webpack from building a bundle that fails to run on Node v18 but I suspect webpack removes the require(esm) issue anyway.

@timfishtimfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getTracingHooksSpecifier obfuscation will mean that no other bundler will be able to resolve and bundle @apm-js-collab/tracing-hooks/hook-sync.mjs. This will force us (and users) to make it external everywhere.

I think in the long term we need to solve the Turbopack issue (happy to take a look!) but for now to get this merged I would create a nextjs specific registerDiagnosticsChannelInjection() so only nextjs has this obfuscation!

chargomeand others added 3 commits July 9, 2026 14:30
registerDiagnosticsChannelInjection() now takes an optional tracingHooksDir
and by default loads @apm-js-collab/tracing-hooks via its bare specifier
again, keeping the require statically analyzable for bundlers. Only the
Next.js SDK — whose server builds bundle the SDK and can't resolve the bare
specifier under pnpm — passes the build-time-resolved package location
(via a Next.js-specific experimentalUseDiagnosticsChannelInjection wrapper),
where loading switches to an opaque createRequire.
webpack ignore-comments were evaluated as an alternative: turbopackIgnore
works on require(), but webpack only honors webpackIgnore on import() and
compiles the call to a broken module stub, so createRequire it is.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chargome

chargome commented Jul 9, 2026

Copy link
Copy Markdown
MemberAuthor

@timfish

I reworked the pr so we have a nextjs specific registerDiagnosticsChannelInjection call which takes an optional tracingHooksDir that uses the createRequire path in the nextjs case. This enables us to not bundle in the transformer hook in turbopack and keep the hybrid buildtime/runtime approach as long as this breaks.

On magic comments: require(/* webpackIgnore: true */ /* turbopackIgnore: true */ specifier); did work for turbopack but not for webpack (apparently webpackIgnore only works for import). I'd rather have a solution that works uniformly across bundlers in nextjs which is createRequire.

Once the transformer becomes bundle-safe, we can update the code and internalize all the modules for both bundlers.

@timfishtimfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great bundler wrangling!

@chargome
chargome merged commit 4951504 into developJul 10, 2026
215 checks passed
@chargome
chargome deleted the cg/nextjs-orchestrion-support branch July 10, 2026 10:53
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support orchestrion auto-instrumentation in turbopack

3 participants

@chargome@timfish@andreiborza
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(nextjs): Add opt-in for orchestrion instrumentation - #22043

Merged
chargome merged 13 commits into
developfrom
cg/nextjs-orchestrion-support
Jul 10, 2026
Merged

feat(nextjs): Add opt-in for orchestrion instrumentation#22043
chargome merged 13 commits into
developfrom
cg/nextjs-orchestrion-support

Conversation

@chargome

@chargomechargome commented Jul 8, 2026

Copy link
Copy Markdown
Member

Adds an experimental option for opting in to orchestrion instrumentation.

  • Once the opt-in flag is set we:
    • Un-externalize the bundle-safe instrumented packages; bundle-unsafe ones (mysql) stay external and are instrumented by the runtime module hook, which works because we also externalize the @apm-js-collab/* transformer packages.
    • transpilePackages for the ones that Next externalizes by default (e.g. pg) — removing them from serverExternalPackages isn't enough for Next's own defaults.
    • Inject the code-transform loader as a Turbopack rule and as a webpack plugin.
  • The webpack loader gets exported from our server utils package (which can be used for turbopack too)

Verified in e2e

closes#22009

@chargomechargome self-assigned this Jul 8, 2026
chargomeand others added 6 commits July 8, 2026 10:06
Two fixes for diagnostics-channel injection under Turbopack:
Externalize @apm-js-collab/tracing-hooks and @apm-js-collab/code-transformer
when the flag is on. Bundled, the code transformer's parser breaks
('a.parse is not a function'), so the runtime module hook silently returned
untransformed sources and externalized packages never produced spans.
Keep mysql in serverExternalPackages instead of force-bundling it. Turbopack
cannot bundle mysql 2.x correctly (the wire-protocol handshake fails with
'Received packet in the wrong sequence' even untransformed). External, it is
now instrumented by the working runtime hook instead of the build-time loader.
Also drop the bundler marker from the orchestrion webpack plugin: it made
registerDiagnosticsChannelInjection() skip the runtime hook, but the hybrid
setup (loader for bundled deps, runtime hook for external ones) needs both.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Instead of un-externalizing every instrumented package and forcing the
Next-default-external ones through transpilePackages, only packages on an
explicit bundle-safe allowlist (currently ioredis) are removed from Sentry's
own serverExternalPackages defaults. Everything else — Next's defaults, the
user's externals, the rest of Sentry's defaults — stays external and is
instrumented by the runtime module hook on require, which works since the
orchestrion machinery is externalized.
This is safer: bundling a server package changes real behavior (mysql 2.x
corrupts its wire protocol when bundled by Turbopack), and new upstream
instrumentations (e.g. hapi) now default to the external/runtime-hook path
instead of silently becoming bundled. It also removes the Next
server-external-packages list parsing and the pg-native webpack workaround,
both only needed to support force-bundling.
Verified in the nextjs-16-orchestrion e2e: ioredis via the build-time loader,
pg and mysql via the runtime hook.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t@13
Use createRequire(__filename) instead of aliasing the CJS require in the
orchestrion webpack bundler module. Next.js 13 bundles @sentry/nextjs into
the server build, and webpack flags the aliased require with 'Critical
dependency: require function is used in a way in which dependencies cannot
be statically extracted', which the nextjs-app-dir e2e treats as a failure.
Add isDiagnosticsChannelInjectionEnabled to the consistent-exports ignore
list: like its companions experimentalUseDiagnosticsChannelInjection and
diagnosticsChannelInjectionIntegrations, the Node-runtime-only opt-in is not
surfaced through the framework / serverless SDKs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chargome

Copy link
Copy Markdown
MemberAuthor

bugbot run

Comment threadpackages/nextjs/src/config/webpack.ts
The plugin previously ran for all server compilations, including the edge
runtime, which diagnostics-channel injection does not target. Gate it on the
already-derived runtime instead of isServer and add unit tests covering the
server/edge/client/flag-off cases.
Also trim down the orchestrion-related comments.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chargome

Copy link
Copy Markdown
MemberAuthor

bugbot run

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a3a3595. Configure here.

Comment threadpackages/nextjs/src/server/index.ts
expect(externals).toContain('mysql');
expect(externals).not.toContain('@apm-js-collab/tracing-hooks');
});
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feat lacks integration or E2E

Medium Severity

This feature PR adds unit and webpack config tests but no integration or E2E test in the diff, though the description references separate e2e verification. Review guidelines expect at least one integration or E2E test for feat changes.

Fix in CursorFix in Web

Triggered by project rule: PR Review Guidelines for Cursor Bot

Reviewed by Cursor Bugbot for commit a3a3595. Configure here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handled in #22080

// module don't emit a "Critical dependency" warning.
function getOrchestrionRequire(): ReturnType<typeof createRequire> {
let nodeRequire: ReturnType<typeof createRequire>;
/*! rollup-include-cjs-only */

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

big thanks to @timfish for this :D

@chargome
chargome marked this pull request as ready for review July 9, 2026 08:34
@chargome
chargome requested review from a team as code ownersJuly 9, 2026 08:34
@chargome
chargome requested review from a team, JPeer264, andreiborza, logaretm, mydea, nicohrubec, s1gr1d and timfish and removed request for a team, JPeer264, andreiborza and s1gr1dJuly 9, 2026 08:34
Comment threadpackages/nextjs/src/config/webpack.ts
When the SDK is bundled into a Next.js server build, the runtime module
hook's bare require of @apm-js-collab/tracing-hooks resolves relative to
the emitted chunk, which fails under isolated installs (pnpm) where the
package is not linked at the app root — the hook silently no-ops and
externalized packages (pg, mysql) lose their spans.
Resolve the package location in withSentryConfig, where the SDK is a
real on-disk package, and inline it as a build-time env value that the
runtime prefers over the bare specifier. Also construct nodeRequire via
createRequire in both build flavors so bundlers don't statically trace
the call (Turbopack otherwise tries to resolve the injected absolute
path at build time).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@timfishtimfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a webpack plugin in this PR:

Why is the createRequire needed?

@github-actions

github-actionsBot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser27.6 kB--
@sentry/browser - with treeshaking flags26.04 kB--
@sentry/browser (incl. Tracing)46.35 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.14 kB--
@sentry/browser (incl. Tracing, Profiling)51.13 kB--
@sentry/browser (incl. Tracing, Replay)85.62 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags75.26 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)90.33 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)102.99 kB--
@sentry/browser (incl. Feedback)44.78 kB--
@sentry/browser (incl. sendFeedback)32.4 kB--
@sentry/browser (incl. FeedbackAsync)37.53 kB--
@sentry/browser (incl. Metrics)28.68 kB--
@sentry/browser (incl. Logs)28.93 kB--
@sentry/browser (incl. Metrics & Logs)29.61 kB--
@sentry/react29.39 kB-0.01%-1 B 🔽
@sentry/react (incl. Tracing)48.62 kB--
@sentry/vue33.03 kB-0.01%-1 B 🔽
@sentry/vue (incl. Tracing)48.33 kB--
@sentry/svelte27.63 kB--
CDN Bundle30 kB--
CDN Bundle (incl. Tracing)48.33 kB--
CDN Bundle (incl. Logs, Metrics)31.58 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)49.65 kB--
CDN Bundle (incl. Replay, Logs, Metrics)70.82 kB--
CDN Bundle (incl. Tracing, Replay)85.84 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)87.16 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)91.64 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.92 kB--
CDN Bundle - uncompressed89.33 kB--
CDN Bundle (incl. Tracing) - uncompressed146.07 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed94.03 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed150.05 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed218.76 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed265.28 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed269.24 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed278.98 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed282.93 kB--
@sentry/nextjs (client)51.17 kB--
@sentry/sveltekit (client)46.8 kB--
@sentry/core/server78.42 kB-0.01%-2 B 🔽
@sentry/core/browser64.77 kB--
@sentry/node-core62.72 kB-0.01%-1 B 🔽
@sentry/node125.35 kB-0.01%-1 B 🔽
@sentry/node (incl. diagnostics channel injection)138.67 kB+0.05%+65 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)69.95 kB--
@sentry/node/light50.73 kB-0.01%-1 B 🔽
@sentry/node - without tracing74.04 kB-0.01%-2 B 🔽
@sentry/aws-serverless83.26 kB--
@sentry/cloudflare (withSentry) - minified181.47 kB--
@sentry/cloudflare (withSentry)448.98 kB--

View base workflow run

Comment threadpackages/server-utils/src/orchestrion/runtime/register.ts Outdated
@timfish

Copy link
Copy Markdown
Collaborator

I guess the createRequire and then the require obfuscation are to stop webpack including all the orchestrion stuff in a bundle when that feature is not used?

Ideally we should avoid using config options to enable orchestrion mode because it requires all these hacks. I also think this will result in webpack not including the orchestrion code in a bundle even if you use it and result in a runtime error when it can't be loaded.

For the Node SDK we have Sentry.experimentalUseDiagnosticsChannelInjection() which ensures the code is only included in the bundle if you actually reference that.

The only valid use I've seen for createRequire is to ensure that tracing-hooks isn't directly in our import graph because it's ESM and this needs require(esm) to load it from CJS (ie. Node v20.19). The argument could be made that these new usages stop webpack from building a bundle that fails to run on Node v18 but I suspect webpack removes the require(esm) issue anyway.

@timfishtimfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getTracingHooksSpecifier obfuscation will mean that no other bundler will be able to resolve and bundle @apm-js-collab/tracing-hooks/hook-sync.mjs. This will force us (and users) to make it external everywhere.

I think in the long term we need to solve the Turbopack issue (happy to take a look!) but for now to get this merged I would create a nextjs specific registerDiagnosticsChannelInjection() so only nextjs has this obfuscation!

chargomeand others added 3 commits July 9, 2026 14:30
registerDiagnosticsChannelInjection() now takes an optional tracingHooksDir
and by default loads @apm-js-collab/tracing-hooks via its bare specifier
again, keeping the require statically analyzable for bundlers. Only the
Next.js SDK — whose server builds bundle the SDK and can't resolve the bare
specifier under pnpm — passes the build-time-resolved package location
(via a Next.js-specific experimentalUseDiagnosticsChannelInjection wrapper),
where loading switches to an opaque createRequire.
webpack ignore-comments were evaluated as an alternative: turbopackIgnore
works on require(), but webpack only honors webpackIgnore on import() and
compiles the call to a broken module stub, so createRequire it is.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chargome

chargome commented Jul 9, 2026

Copy link
Copy Markdown
MemberAuthor

@timfish

I reworked the pr so we have a nextjs specific registerDiagnosticsChannelInjection call which takes an optional tracingHooksDir that uses the createRequire path in the nextjs case. This enables us to not bundle in the transformer hook in turbopack and keep the hybrid buildtime/runtime approach as long as this breaks.

On magic comments: require(/* webpackIgnore: true */ /* turbopackIgnore: true */ specifier); did work for turbopack but not for webpack (apparently webpackIgnore only works for import). I'd rather have a solution that works uniformly across bundlers in nextjs which is createRequire.

Once the transformer becomes bundle-safe, we can update the code and internalize all the modules for both bundlers.

@timfishtimfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great bundler wrangling!

@chargome
chargome merged commit 4951504 into developJul 10, 2026
215 checks passed
@chargome
chargome deleted the cg/nextjs-orchestrion-support branch July 10, 2026 10:53
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support orchestrion auto-instrumentation in turbopack

3 participants

@chargome@timfish@andreiborza
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat(nextjs): Add opt-in for orchestrion instrumentation - #22043

Merged
chargome merged 13 commits into
developfrom
cg/nextjs-orchestrion-support
Jul 10, 2026
Merged

feat(nextjs): Add opt-in for orchestrion instrumentation#22043
chargome merged 13 commits into
developfrom
cg/nextjs-orchestrion-support

Conversation

@chargome

@chargomechargome commented Jul 8, 2026

Copy link
Copy Markdown
Member

Adds an experimental option for opting in to orchestrion instrumentation.

  • Once the opt-in flag is set we:
    • Un-externalize the bundle-safe instrumented packages; bundle-unsafe ones (mysql) stay external and are instrumented by the runtime module hook, which works because we also externalize the @apm-js-collab/* transformer packages.
    • transpilePackages for the ones that Next externalizes by default (e.g. pg) — removing them from serverExternalPackages isn't enough for Next's own defaults.
    • Inject the code-transform loader as a Turbopack rule and as a webpack plugin.
  • The webpack loader gets exported from our server utils package (which can be used for turbopack too)

Verified in e2e

closes#22009

@chargomechargome self-assigned this Jul 8, 2026
chargomeand others added 6 commits July 8, 2026 10:06
Two fixes for diagnostics-channel injection under Turbopack:
Externalize @apm-js-collab/tracing-hooks and @apm-js-collab/code-transformer
when the flag is on. Bundled, the code transformer's parser breaks
('a.parse is not a function'), so the runtime module hook silently returned
untransformed sources and externalized packages never produced spans.
Keep mysql in serverExternalPackages instead of force-bundling it. Turbopack
cannot bundle mysql 2.x correctly (the wire-protocol handshake fails with
'Received packet in the wrong sequence' even untransformed). External, it is
now instrumented by the working runtime hook instead of the build-time loader.
Also drop the bundler marker from the orchestrion webpack plugin: it made
registerDiagnosticsChannelInjection() skip the runtime hook, but the hybrid
setup (loader for bundled deps, runtime hook for external ones) needs both.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Instead of un-externalizing every instrumented package and forcing the
Next-default-external ones through transpilePackages, only packages on an
explicit bundle-safe allowlist (currently ioredis) are removed from Sentry's
own serverExternalPackages defaults. Everything else — Next's defaults, the
user's externals, the rest of Sentry's defaults — stays external and is
instrumented by the runtime module hook on require, which works since the
orchestrion machinery is externalized.
This is safer: bundling a server package changes real behavior (mysql 2.x
corrupts its wire protocol when bundled by Turbopack), and new upstream
instrumentations (e.g. hapi) now default to the external/runtime-hook path
instead of silently becoming bundled. It also removes the Next
server-external-packages list parsing and the pg-native webpack workaround,
both only needed to support force-bundling.
Verified in the nextjs-16-orchestrion e2e: ioredis via the build-time loader,
pg and mysql via the runtime hook.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t@13
Use createRequire(__filename) instead of aliasing the CJS require in the
orchestrion webpack bundler module. Next.js 13 bundles @sentry/nextjs into
the server build, and webpack flags the aliased require with 'Critical
dependency: require function is used in a way in which dependencies cannot
be statically extracted', which the nextjs-app-dir e2e treats as a failure.
Add isDiagnosticsChannelInjectionEnabled to the consistent-exports ignore
list: like its companions experimentalUseDiagnosticsChannelInjection and
diagnosticsChannelInjectionIntegrations, the Node-runtime-only opt-in is not
surfaced through the framework / serverless SDKs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chargome

Copy link
Copy Markdown
MemberAuthor

bugbot run

Comment threadpackages/nextjs/src/config/webpack.ts
The plugin previously ran for all server compilations, including the edge
runtime, which diagnostics-channel injection does not target. Gate it on the
already-derived runtime instead of isServer and add unit tests covering the
server/edge/client/flag-off cases.
Also trim down the orchestrion-related comments.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chargome

Copy link
Copy Markdown
MemberAuthor

bugbot run

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a3a3595. Configure here.

Comment threadpackages/nextjs/src/server/index.ts
expect(externals).toContain('mysql');
expect(externals).not.toContain('@apm-js-collab/tracing-hooks');
});
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feat lacks integration or E2E

Medium Severity

This feature PR adds unit and webpack config tests but no integration or E2E test in the diff, though the description references separate e2e verification. Review guidelines expect at least one integration or E2E test for feat changes.

Fix in CursorFix in Web

Triggered by project rule: PR Review Guidelines for Cursor Bot

Reviewed by Cursor Bugbot for commit a3a3595. Configure here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handled in #22080

// module don't emit a "Critical dependency" warning.
function getOrchestrionRequire(): ReturnType<typeof createRequire> {
let nodeRequire: ReturnType<typeof createRequire>;
/*! rollup-include-cjs-only */

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

big thanks to @timfish for this :D

@chargome
chargome marked this pull request as ready for review July 9, 2026 08:34
@chargome
chargome requested review from a team as code ownersJuly 9, 2026 08:34
@chargome
chargome requested review from a team, JPeer264, andreiborza, logaretm, mydea, nicohrubec, s1gr1d and timfish and removed request for a team, JPeer264, andreiborza and s1gr1dJuly 9, 2026 08:34
Comment threadpackages/nextjs/src/config/webpack.ts
When the SDK is bundled into a Next.js server build, the runtime module
hook's bare require of @apm-js-collab/tracing-hooks resolves relative to
the emitted chunk, which fails under isolated installs (pnpm) where the
package is not linked at the app root — the hook silently no-ops and
externalized packages (pg, mysql) lose their spans.
Resolve the package location in withSentryConfig, where the SDK is a
real on-disk package, and inline it as a build-time env value that the
runtime prefers over the bare specifier. Also construct nodeRequire via
createRequire in both build flavors so bundlers don't statically trace
the call (Turbopack otherwise tries to resolve the injected absolute
path at build time).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@timfishtimfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a webpack plugin in this PR:

Why is the createRequire needed?

@github-actions

github-actionsBot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser27.6 kB--
@sentry/browser - with treeshaking flags26.04 kB--
@sentry/browser (incl. Tracing)46.35 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.14 kB--
@sentry/browser (incl. Tracing, Profiling)51.13 kB--
@sentry/browser (incl. Tracing, Replay)85.62 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags75.26 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)90.33 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)102.99 kB--
@sentry/browser (incl. Feedback)44.78 kB--
@sentry/browser (incl. sendFeedback)32.4 kB--
@sentry/browser (incl. FeedbackAsync)37.53 kB--
@sentry/browser (incl. Metrics)28.68 kB--
@sentry/browser (incl. Logs)28.93 kB--
@sentry/browser (incl. Metrics & Logs)29.61 kB--
@sentry/react29.39 kB-0.01%-1 B 🔽
@sentry/react (incl. Tracing)48.62 kB--
@sentry/vue33.03 kB-0.01%-1 B 🔽
@sentry/vue (incl. Tracing)48.33 kB--
@sentry/svelte27.63 kB--
CDN Bundle30 kB--
CDN Bundle (incl. Tracing)48.33 kB--
CDN Bundle (incl. Logs, Metrics)31.58 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)49.65 kB--
CDN Bundle (incl. Replay, Logs, Metrics)70.82 kB--
CDN Bundle (incl. Tracing, Replay)85.84 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)87.16 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)91.64 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.92 kB--
CDN Bundle - uncompressed89.33 kB--
CDN Bundle (incl. Tracing) - uncompressed146.07 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed94.03 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed150.05 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed218.76 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed265.28 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed269.24 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed278.98 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed282.93 kB--
@sentry/nextjs (client)51.17 kB--
@sentry/sveltekit (client)46.8 kB--
@sentry/core/server78.42 kB-0.01%-2 B 🔽
@sentry/core/browser64.77 kB--
@sentry/node-core62.72 kB-0.01%-1 B 🔽
@sentry/node125.35 kB-0.01%-1 B 🔽
@sentry/node (incl. diagnostics channel injection)138.67 kB+0.05%+65 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)69.95 kB--
@sentry/node/light50.73 kB-0.01%-1 B 🔽
@sentry/node - without tracing74.04 kB-0.01%-2 B 🔽
@sentry/aws-serverless83.26 kB--
@sentry/cloudflare (withSentry) - minified181.47 kB--
@sentry/cloudflare (withSentry)448.98 kB--

View base workflow run

Comment threadpackages/server-utils/src/orchestrion/runtime/register.ts Outdated
@timfish

Copy link
Copy Markdown
Collaborator

I guess the createRequire and then the require obfuscation are to stop webpack including all the orchestrion stuff in a bundle when that feature is not used?

Ideally we should avoid using config options to enable orchestrion mode because it requires all these hacks. I also think this will result in webpack not including the orchestrion code in a bundle even if you use it and result in a runtime error when it can't be loaded.

For the Node SDK we have Sentry.experimentalUseDiagnosticsChannelInjection() which ensures the code is only included in the bundle if you actually reference that.

The only valid use I've seen for createRequire is to ensure that tracing-hooks isn't directly in our import graph because it's ESM and this needs require(esm) to load it from CJS (ie. Node v20.19). The argument could be made that these new usages stop webpack from building a bundle that fails to run on Node v18 but I suspect webpack removes the require(esm) issue anyway.

@timfishtimfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getTracingHooksSpecifier obfuscation will mean that no other bundler will be able to resolve and bundle @apm-js-collab/tracing-hooks/hook-sync.mjs. This will force us (and users) to make it external everywhere.

I think in the long term we need to solve the Turbopack issue (happy to take a look!) but for now to get this merged I would create a nextjs specific registerDiagnosticsChannelInjection() so only nextjs has this obfuscation!

chargomeand others added 3 commits July 9, 2026 14:30
registerDiagnosticsChannelInjection() now takes an optional tracingHooksDir
and by default loads @apm-js-collab/tracing-hooks via its bare specifier
again, keeping the require statically analyzable for bundlers. Only the
Next.js SDK — whose server builds bundle the SDK and can't resolve the bare
specifier under pnpm — passes the build-time-resolved package location
(via a Next.js-specific experimentalUseDiagnosticsChannelInjection wrapper),
where loading switches to an opaque createRequire.
webpack ignore-comments were evaluated as an alternative: turbopackIgnore
works on require(), but webpack only honors webpackIgnore on import() and
compiles the call to a broken module stub, so createRequire it is.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chargome

chargome commented Jul 9, 2026

Copy link
Copy Markdown
MemberAuthor

@timfish

I reworked the pr so we have a nextjs specific registerDiagnosticsChannelInjection call which takes an optional tracingHooksDir that uses the createRequire path in the nextjs case. This enables us to not bundle in the transformer hook in turbopack and keep the hybrid buildtime/runtime approach as long as this breaks.

On magic comments: require(/* webpackIgnore: true */ /* turbopackIgnore: true */ specifier); did work for turbopack but not for webpack (apparently webpackIgnore only works for import). I'd rather have a solution that works uniformly across bundlers in nextjs which is createRequire.

Once the transformer becomes bundle-safe, we can update the code and internalize all the modules for both bundlers.

@timfishtimfish left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great bundler wrangling!

@chargome
chargome merged commit 4951504 into developJul 10, 2026
215 checks passed
@chargome
chargome deleted the cg/nextjs-orchestrion-support branch July 10, 2026 10:53
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support orchestrion auto-instrumentation in turbopack

3 participants

@chargome@timfish@andreiborza