feat(core): Expose rewriteSources top level option - #20142

Merged
chargome merged 2 commits into
developfrom
cg/update-rewrite-sources
Apr 14, 2026
Merged

feat(core): Expose rewriteSources top level option #20142
chargome merged 2 commits into
developfrom
cg/update-rewrite-sources

Conversation

@chargome

@chargomechargome commented Apr 9, 2026

Copy link
Copy Markdown
Member
  • Adds rewriteSources to the base SourceMapsOptions interface so users can customize source path rewriting without the unstable_* escape hatch
  • Wires up the option in Nuxt, Next.js, and TanStack Start (SvelteKit, Astro, React Router already pass it through via spread)
  • Nuxt and Next.js preserve their default rewriting behavior when the option is not provided

bundler plugins ref getsentry/sentry-javascript-bundler-plugins#908
closes#20028

@chargomechargome self-assigned this Apr 9, 2026
@github-actions

github-actionsBot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

Cloudflare

  • Split alarms into multiple traces and link them by JPeer264 in #19373
  • Propagate traceparent to RPC calls - via fetch by JPeer264 in #19991

Core

  • Expose rewriteSources top level option by chargome in #20142
  • Automatically disable truncation when span streaming is enabled in LangGraph integration by andreiborza in #20231
  • Automatically disable truncation when span streaming is enabled in LangChain integration by andreiborza in #20230
  • Automatically disable truncation when span streaming is enabled in Google GenAI integration by andreiborza in #20229
  • Automatically disable truncation when span streaming is enabled in Anthropic AI integration by andreiborza in #20228
  • Automatically disable truncation when span streaming is enabled in Vercel AI integration by andreiborza in #20232
  • Automatically disable truncation when span streaming is enabled in OpenAI integration by andreiborza in #20227
  • Add enableTruncation option to Vercel AI integration by nicohrubec in #20195
  • Add enableTruncation option to Google GenAI integration by andreiborza in #20184
  • Add enableTruncation option to Anthropic AI integration by andreiborza in #20181
  • Add enableTruncation option to LangGraph integration by andreiborza in #20183
  • Add enableTruncation option to LangChain integration by andreiborza in #20182
  • Add enableTruncation option to OpenAI integration by andreiborza in #20167
  • Export a reusable function to add tracing headers by JPeer264 in #20076

Deps

  • Bump axios from 1.13.5 to 1.15.0 by dependabot in #20180
  • Bump hono from 4.12.7 to 4.12.12 by dependabot in #20118
  • Bump defu from 6.1.4 to 6.1.6 by dependabot in #20104

Other

  • (browser) Add View Hierarchy integration by timfish in #14981
  • (cloudflare,deno,vercel-edge) Add span streaming support by Lms24 in #20127
  • (node) Include global scope for eventLoopBlockIntegration by timfish in #20108
  • (node-native) Add support for V8 v14 (Node v25+) by timfish in #20125
  • (opentelemetry) Vendor AsyncLocalStorageContextManager by mydea in #20243

Bug Fixes 🐛

Deno

  • Handle reader.closed rejection from releaseLock() in streaming by andreiborza in #20187
  • Avoid inferring invalid span op from Deno tracer by Lms24 in #20128

Other

  • (ci) Prevent command injection in ci-metadata workflow by fix-it-felix-sentry in #19899
  • (core, node) Support loading Express options lazily by isaacs in #20211
  • (e2e) Add op check to waitForTransaction in React Router e2e tests by copilot-swe-agent in #20193
  • (e2e-tests) Remove flaky navigation breadcrumb assertions from parameterized-routes tests by copilot-swe-agent in #20202
  • (node-integration-tests) Fix flaky kafkajs test race condition by copilot-swe-agent in #20189

Internal Changes 🔧

Ci

  • Remove node-overhead GitHub Action by mydea in #20246
  • Bump dorny/paths-filter from v3.0.1 to v4.0.1 by mydea in #20251
  • Remove codecov steps from jobs that produce no coverage/JUnit data by mydea in #20244

Deps

  • Bump hono from 4.12.7 to 4.12.12 in /dev-packages/e2e-tests/test-applications/cloudflare-hono by dependabot in #20119
  • Bump axios from 1.13.5 to 1.15.0 in /dev-packages/e2e-tests/test-applications/nestjs-basic by dependabot in #20179

Deps Dev

  • Bump @sveltejs/kit from 2.53.3 to 2.57.1 by dependabot in #20216
  • Bump vite from 7.2.0 to 7.3.2 in /dev-packages/e2e-tests/test-applications/tanstackstart-react by dependabot in #20107

Other

  • (bugbot) Add rules to flag test-flake-provoking patterns by Lms24 in #20192
  • (react) Remove duplicated test mock by s1gr1d in #20200
  • (size-limit) Bump failing size limit scenario by Lms24 in #20186
  • Add PR review reminder workflow by copilot-swe-agent in #20175
  • Fix lint warnings by mydea in #20250
  • Fix flaky ANR test by increasing blocking duration by JPeer264 in #20239
  • Add automatic flaky test detector by nicohrubec in #18684

🤖 This preview updates automatically when you update the PR.

@github-actions

github-actionsBot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser25.73 kB--
@sentry/browser - with treeshaking flags24.22 kB--
@sentry/browser (incl. Tracing)42.72 kB--
@sentry/browser (incl. Tracing, Profiling)47.35 kB--
@sentry/browser (incl. Tracing, Replay)81.53 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags71.1 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)86.23 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)98.44 kB--
@sentry/browser (incl. Feedback)42.52 kB--
@sentry/browser (incl. sendFeedback)30.39 kB--
@sentry/browser (incl. FeedbackAsync)35.39 kB--
@sentry/browser (incl. Metrics)27.04 kB--
@sentry/browser (incl. Logs)27.19 kB--
@sentry/browser (incl. Metrics & Logs)27.86 kB--
@sentry/react27.48 kB--
@sentry/react (incl. Tracing)45.05 kB--
@sentry/vue30.56 kB--
@sentry/vue (incl. Tracing)44.58 kB--
@sentry/svelte25.75 kB--
CDN Bundle28.41 kB--
CDN Bundle (incl. Tracing)43.76 kB--
CDN Bundle (incl. Logs, Metrics)29.79 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)44.83 kB--
CDN Bundle (incl. Replay, Logs, Metrics)68.6 kB--
CDN Bundle (incl. Tracing, Replay)80.63 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)81.66 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)86.16 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)87.19 kB--
CDN Bundle - uncompressed83 kB--
CDN Bundle (incl. Tracing) - uncompressed129.79 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed87.14 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed133.2 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed210.13 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed246.67 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed250.07 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed259.58 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed262.97 kB--
@sentry/nextjs (client)47.47 kB--
@sentry/sveltekit (client)43.2 kB--
@sentry/node-core57.86 kB+0.02%+7 B 🔺
@sentry/node174.8 kB+0.01%+15 B 🔺
@sentry/node - without tracing97.63 kB+0.02%+11 B 🔺
@sentry/aws-serverless114.98 kB+0.01%+7 B 🔺

View base workflow run

@github-actions

Copy link
Copy Markdown
Contributor

node-overhead report 🧳

Note: This is a synthetic benchmark with a minimal express app and does not necessarily reflect the real-world performance impact in an application.

ScenarioRequests/s% of BaselinePrev. Requests/sChange %
GET Baseline9,450-9,316+1%
GET With Sentry1,83419%1,744+5%
GET With Sentry (error only)6,10765%5,995+2%
POST Baseline1,211-1,211-
POST With Sentry62852%624+1%
POST With Sentry (error only)1,06388%1,056+1%
MYSQL Baseline3,260-3,337-2%
MYSQL With Sentry52016%499+4%
MYSQL With Sentry (error only)2,68682%2,666+1%

View base workflow run

@chargome
chargome marked this pull request as ready for review April 9, 2026 11:08

@Lms24Lms24 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for adding this!
Just to confirm: We already bumped the plugins to the supported version? And sveltekit uses the interface from core directly?

Comment threadpackages/nuxt/src/vite/sourceMaps.ts Outdated

@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 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: No integration or E2E test for feature
    • Added integration tests in nextjs, nuxt, and tanstackstart-react packages that verify user-provided rewriteSources hooks are invoked with proper arguments and return correct results during the source map configuration flow.

Create PR

Or push these changes by commenting:

@cursor push 0f5c31dc99
Preview (0f5c31dc99)
diff --git a/packages/nextjs/test/config/rewriteSources.integration.test.ts b/packages/nextjs/test/config/rewriteSources.integration.test.ts
new file mode 100644
--- /dev/null+++ b/packages/nextjs/test/config/rewriteSources.integration.test.ts@@ -1,0 +1,118 @@+import { describe, it, expect, vi } from 'vitest';+import { getBuildPluginOptions } from '../../src/config/getBuildPluginOptions';+import type { SentryBuildOptions } from '../../src/config/types';++const mockReleaseName = 'test-release';+const mockDistDirAbsPath = '/test/project/.next';++describe('rewriteSources integration', () => {+ it('invokes custom rewriteSources function with source paths', () => {+ const rewriteSourcesSpy = vi.fn((source: string) => {+ return source.replace(/^custom\//, 'rewritten/');+ });++ const sentryBuildOptions: SentryBuildOptions = {+ org: 'test-org',+ project: 'test-project',+ sourcemaps: {+ rewriteSources: rewriteSourcesSpy,+ },+ };++ const result = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-client',+ });++ const rewriteSources = result.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();+ expect(rewriteSources).toBe(rewriteSourcesSpy);++ const testPaths = [+ 'custom/src/pages/index.js',+ 'custom/components/Button.tsx',+ 'src/utils/helpers.js',+ 'custom/lib/api.ts',+ ];++ testPaths.forEach(path => {+ rewriteSources?.(path, {});+ });++ expect(rewriteSourcesSpy).toHaveBeenCalledTimes(4);+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(1, 'custom/src/pages/index.js', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(2, 'custom/components/Button.tsx', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(3, 'src/utils/helpers.js', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(4, 'custom/lib/api.ts', {});++ expect(rewriteSourcesSpy.mock.results[0]?.value).toBe('rewritten/src/pages/index.js');+ expect(rewriteSourcesSpy.mock.results[1]?.value).toBe('rewritten/components/Button.tsx');+ expect(rewriteSourcesSpy.mock.results[2]?.value).toBe('src/utils/helpers.js');+ expect(rewriteSourcesSpy.mock.results[3]?.value).toBe('rewritten/lib/api.ts');+ });++ it('invokes default rewriteSources function for webpack sources', () => {+ const sentryBuildOptions: SentryBuildOptions = {+ org: 'test-org',+ project: 'test-project',+ };++ const result = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-client',+ });++ const rewriteSources = result.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();++ const webpackPaths = [+ 'webpack://_N_E/src/pages/index.js',+ 'webpack://project/src/components/Button.js',+ 'src/utils/helpers.js',+ 'webpack://_N_E/./app/layout.tsx',+ ];++ const rewrittenPaths = webpackPaths.map(path => rewriteSources?.(path, {}));++ expect(rewrittenPaths).toEqual([+ 'src/pages/index.js',+ 'project/src/components/Button.js',+ 'src/utils/helpers.js',+ './app/layout.tsx',+ ]);+ });++ it('preserves rewriteSources function reference across multiple builds', () => {+ const customRewrite = (source: string) => source.replace(/^prefix\//, '');++ const sentryBuildOptions: SentryBuildOptions = {+ org: 'test-org',+ project: 'test-project',+ sourcemaps: {+ rewriteSources: customRewrite,+ },+ };++ const clientResult = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-client',+ });++ const serverResult = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-nodejs',+ });++ expect(clientResult.sourcemaps?.rewriteSources).toBe(customRewrite);+ expect(serverResult.sourcemaps?.rewriteSources).toBe(customRewrite);+ expect(clientResult.sourcemaps?.rewriteSources).toBe(serverResult.sourcemaps?.rewriteSources);+ });+});diff --git a/packages/nuxt/test/vite/rewriteSources.integration.test.ts b/packages/nuxt/test/vite/rewriteSources.integration.test.ts
new file mode 100644
--- /dev/null+++ b/packages/nuxt/test/vite/rewriteSources.integration.test.ts@@ -1,0 +1,72 @@+import { describe, it, expect, vi } from 'vitest';+import { getPluginOptions } from '../../src/vite/sourceMaps';+import type { SentryNuxtModuleOptions } from '../../src/common/types';++describe('rewriteSources integration', () => {+ it('invokes custom rewriteSources function with source paths', () => {+ const rewriteSourcesSpy = vi.fn((source: string) => {+ return source.replace(/^src\//, 'custom/');+ });++ const options = getPluginOptions({+ sourcemaps: {+ rewriteSources: rewriteSourcesSpy,+ },+ } as SentryNuxtModuleOptions);++ const rewriteSources = options.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();+ expect(rewriteSources).toBe(rewriteSourcesSpy);++ const testPaths = [+ 'src/pages/index.vue',+ 'src/components/Button.vue',+ 'lib/utils/helpers.ts',+ 'src/composables/useAuth.ts',+ ];++ testPaths.forEach(path => {+ rewriteSources?.(path);+ });++ expect(rewriteSourcesSpy).toHaveBeenCalledTimes(4);+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(1, 'src/pages/index.vue');+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(2, 'src/components/Button.vue');+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(3, 'lib/utils/helpers.ts');+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(4, 'src/composables/useAuth.ts');++ expect(rewriteSourcesSpy.mock.results[0]?.value).toBe('custom/pages/index.vue');+ expect(rewriteSourcesSpy.mock.results[1]?.value).toBe('custom/components/Button.vue');+ expect(rewriteSourcesSpy.mock.results[2]?.value).toBe('lib/utils/helpers.ts');+ expect(rewriteSourcesSpy.mock.results[3]?.value).toBe('custom/composables/useAuth.ts');+ });++ it('invokes default rewriteSources function to normalize paths', () => {+ const options = getPluginOptions({} as SentryNuxtModuleOptions);++ const rewriteSources = options.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();++ const paths = ['../../../foo/bar', '../../components/Layout.vue', './local', '../utils/api.ts'];++ const rewrittenPaths = paths.map(path => rewriteSources?.(path));++ expect(rewrittenPaths).toEqual(['./foo/bar', './components/Layout.vue', './local', './utils/api.ts']);+ });++ it('preserves rewriteSources function reference across configuration', () => {+ const customRewrite = (source: string) => source.replace(/^prefix\//, '');++ const options = getPluginOptions({+ sourcemaps: {+ rewriteSources: customRewrite,+ },+ } as SentryNuxtModuleOptions);++ expect(options.sourcemaps?.rewriteSources).toBe(customRewrite);++ const testSource = 'prefix/src/app.vue';+ const result = options.sourcemaps?.rewriteSources?.(testSource);+ expect(result).toBe('src/app.vue');+ });+});diff --git a/packages/tanstackstart-react/test/vite/rewriteSources.integration.test.ts b/packages/tanstackstart-react/test/vite/rewriteSources.integration.test.ts
new file mode 100644
--- /dev/null+++ b/packages/tanstackstart-react/test/vite/rewriteSources.integration.test.ts@@ -1,0 +1,83 @@+import { describe, it, expect, vi } from 'vitest';++const sentryVitePluginSpy = vi.fn(() => []);++vi.mock('@sentry/vite-plugin', () => ({+ sentryVitePlugin: vi.fn(() => []),+}));++import { makeAddSentryVitePlugin } from '../../src/vite/sourceMaps';+import { sentryVitePlugin } from '@sentry/vite-plugin';++vi.mocked(sentryVitePlugin).mockImplementation(sentryVitePluginSpy);++describe('rewriteSources integration', () => {+ it('invokes custom rewriteSources function with source paths', () => {+ const rewriteSourcesSpy = vi.fn((source: string) => {+ return source.replace(/^src\//, '');+ });++ makeAddSentryVitePlugin({+ org: 'my-org',+ authToken: 'my-token',+ sourcemaps: {+ rewriteSources: rewriteSourcesSpy,+ },+ });++ expect(sentryVitePluginSpy).toHaveBeenCalledWith(+ expect.objectContaining({+ sourcemaps: expect.objectContaining({+ rewriteSources: rewriteSourcesSpy,+ }),+ }),+ );++ const passedOptions = sentryVitePluginSpy.mock.calls[0]?.[0];+ const rewriteSources = passedOptions?.sourcemaps?.rewriteSources;++ expect(rewriteSources).toBe(rewriteSourcesSpy);++ const testPaths = ['src/routes/index.tsx', 'src/components/Button.tsx', 'lib/utils.ts', 'src/api/client.ts'];++ testPaths.forEach(path => {+ rewriteSources?.(path, {});+ });++ expect(rewriteSourcesSpy).toHaveBeenCalledTimes(4);+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(1, 'src/routes/index.tsx', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(2, 'src/components/Button.tsx', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(3, 'lib/utils.ts', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(4, 'src/api/client.ts', {});++ expect(rewriteSourcesSpy.mock.results[0]?.value).toBe('routes/index.tsx');+ expect(rewriteSourcesSpy.mock.results[1]?.value).toBe('components/Button.tsx');+ expect(rewriteSourcesSpy.mock.results[2]?.value).toBe('lib/utils.ts');+ expect(rewriteSourcesSpy.mock.results[3]?.value).toBe('api/client.ts');+ });++ it('preserves rewriteSources function reference in plugin options', () => {+ const customRewrite = (source: string) => source.replace(/^prefix\//, '');++ sentryVitePluginSpy.mockClear();++ makeAddSentryVitePlugin({+ org: 'my-org',+ authToken: 'my-token',+ sourcemaps: {+ rewriteSources: customRewrite,+ },+ });++ expect(sentryVitePluginSpy).toHaveBeenCalledWith(+ expect.objectContaining({+ sourcemaps: expect.objectContaining({+ rewriteSources: customRewrite,+ }),+ }),+ );++ const passedOptions = sentryVitePluginSpy.mock.calls[0]?.[0];+ expect(passedOptions?.sourcemaps?.rewriteSources).toBe(customRewrite);+ });+});

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2722abb. Configure here.

Comment threadpackages/nextjs/test/config/getBuildPluginOptions.test.ts
@chargome
chargome merged commit 13dc7a1 into developApr 14, 2026
467 of 477 checks passed
@chargome
chargome deleted the cg/update-rewrite-sources branch April 14, 2026 12:27
mydea pushed a commit that referenced this pull request Apr 15, 2026
- Adds `rewriteSources` to the base `SourceMapsOptions` interface so
users can customize source path rewriting without the `unstable_*`
escape hatch
- Wires up the option in Nuxt, Next.js, and TanStack Start (SvelteKit,
Astro, React Router already pass it through via spread)
- Nuxt and Next.js preserve their default rewriting behavior when the
option is not provided
bundler plugins ref
getsentry/sentry-javascript-bundler-plugins#908closes#20028
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.

Expose source map path/dir to rewriteSources hook

4 participants

@chargome@Lms24@nicohrubec@s1gr1d
, '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(core): Expose rewriteSources top level option - #20142

Merged
chargome merged 2 commits into
developfrom
cg/update-rewrite-sources
Apr 14, 2026
Merged

feat(core): Expose rewriteSources top level option #20142
chargome merged 2 commits into
developfrom
cg/update-rewrite-sources

Conversation

@chargome

@chargomechargome commented Apr 9, 2026

Copy link
Copy Markdown
Member
  • Adds rewriteSources to the base SourceMapsOptions interface so users can customize source path rewriting without the unstable_* escape hatch
  • Wires up the option in Nuxt, Next.js, and TanStack Start (SvelteKit, Astro, React Router already pass it through via spread)
  • Nuxt and Next.js preserve their default rewriting behavior when the option is not provided

bundler plugins ref getsentry/sentry-javascript-bundler-plugins#908
closes#20028

@chargomechargome self-assigned this Apr 9, 2026
@github-actions

github-actionsBot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

Cloudflare

  • Split alarms into multiple traces and link them by JPeer264 in #19373
  • Propagate traceparent to RPC calls - via fetch by JPeer264 in #19991

Core

  • Expose rewriteSources top level option by chargome in #20142
  • Automatically disable truncation when span streaming is enabled in LangGraph integration by andreiborza in #20231
  • Automatically disable truncation when span streaming is enabled in LangChain integration by andreiborza in #20230
  • Automatically disable truncation when span streaming is enabled in Google GenAI integration by andreiborza in #20229
  • Automatically disable truncation when span streaming is enabled in Anthropic AI integration by andreiborza in #20228
  • Automatically disable truncation when span streaming is enabled in Vercel AI integration by andreiborza in #20232
  • Automatically disable truncation when span streaming is enabled in OpenAI integration by andreiborza in #20227
  • Add enableTruncation option to Vercel AI integration by nicohrubec in #20195
  • Add enableTruncation option to Google GenAI integration by andreiborza in #20184
  • Add enableTruncation option to Anthropic AI integration by andreiborza in #20181
  • Add enableTruncation option to LangGraph integration by andreiborza in #20183
  • Add enableTruncation option to LangChain integration by andreiborza in #20182
  • Add enableTruncation option to OpenAI integration by andreiborza in #20167
  • Export a reusable function to add tracing headers by JPeer264 in #20076

Deps

  • Bump axios from 1.13.5 to 1.15.0 by dependabot in #20180
  • Bump hono from 4.12.7 to 4.12.12 by dependabot in #20118
  • Bump defu from 6.1.4 to 6.1.6 by dependabot in #20104

Other

  • (browser) Add View Hierarchy integration by timfish in #14981
  • (cloudflare,deno,vercel-edge) Add span streaming support by Lms24 in #20127
  • (node) Include global scope for eventLoopBlockIntegration by timfish in #20108
  • (node-native) Add support for V8 v14 (Node v25+) by timfish in #20125
  • (opentelemetry) Vendor AsyncLocalStorageContextManager by mydea in #20243

Bug Fixes 🐛

Deno

  • Handle reader.closed rejection from releaseLock() in streaming by andreiborza in #20187
  • Avoid inferring invalid span op from Deno tracer by Lms24 in #20128

Other

  • (ci) Prevent command injection in ci-metadata workflow by fix-it-felix-sentry in #19899
  • (core, node) Support loading Express options lazily by isaacs in #20211
  • (e2e) Add op check to waitForTransaction in React Router e2e tests by copilot-swe-agent in #20193
  • (e2e-tests) Remove flaky navigation breadcrumb assertions from parameterized-routes tests by copilot-swe-agent in #20202
  • (node-integration-tests) Fix flaky kafkajs test race condition by copilot-swe-agent in #20189

Internal Changes 🔧

Ci

  • Remove node-overhead GitHub Action by mydea in #20246
  • Bump dorny/paths-filter from v3.0.1 to v4.0.1 by mydea in #20251
  • Remove codecov steps from jobs that produce no coverage/JUnit data by mydea in #20244

Deps

  • Bump hono from 4.12.7 to 4.12.12 in /dev-packages/e2e-tests/test-applications/cloudflare-hono by dependabot in #20119
  • Bump axios from 1.13.5 to 1.15.0 in /dev-packages/e2e-tests/test-applications/nestjs-basic by dependabot in #20179

Deps Dev

  • Bump @sveltejs/kit from 2.53.3 to 2.57.1 by dependabot in #20216
  • Bump vite from 7.2.0 to 7.3.2 in /dev-packages/e2e-tests/test-applications/tanstackstart-react by dependabot in #20107

Other

  • (bugbot) Add rules to flag test-flake-provoking patterns by Lms24 in #20192
  • (react) Remove duplicated test mock by s1gr1d in #20200
  • (size-limit) Bump failing size limit scenario by Lms24 in #20186
  • Add PR review reminder workflow by copilot-swe-agent in #20175
  • Fix lint warnings by mydea in #20250
  • Fix flaky ANR test by increasing blocking duration by JPeer264 in #20239
  • Add automatic flaky test detector by nicohrubec in #18684

🤖 This preview updates automatically when you update the PR.

@github-actions

github-actionsBot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser25.73 kB--
@sentry/browser - with treeshaking flags24.22 kB--
@sentry/browser (incl. Tracing)42.72 kB--
@sentry/browser (incl. Tracing, Profiling)47.35 kB--
@sentry/browser (incl. Tracing, Replay)81.53 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags71.1 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)86.23 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)98.44 kB--
@sentry/browser (incl. Feedback)42.52 kB--
@sentry/browser (incl. sendFeedback)30.39 kB--
@sentry/browser (incl. FeedbackAsync)35.39 kB--
@sentry/browser (incl. Metrics)27.04 kB--
@sentry/browser (incl. Logs)27.19 kB--
@sentry/browser (incl. Metrics & Logs)27.86 kB--
@sentry/react27.48 kB--
@sentry/react (incl. Tracing)45.05 kB--
@sentry/vue30.56 kB--
@sentry/vue (incl. Tracing)44.58 kB--
@sentry/svelte25.75 kB--
CDN Bundle28.41 kB--
CDN Bundle (incl. Tracing)43.76 kB--
CDN Bundle (incl. Logs, Metrics)29.79 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)44.83 kB--
CDN Bundle (incl. Replay, Logs, Metrics)68.6 kB--
CDN Bundle (incl. Tracing, Replay)80.63 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)81.66 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)86.16 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)87.19 kB--
CDN Bundle - uncompressed83 kB--
CDN Bundle (incl. Tracing) - uncompressed129.79 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed87.14 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed133.2 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed210.13 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed246.67 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed250.07 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed259.58 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed262.97 kB--
@sentry/nextjs (client)47.47 kB--
@sentry/sveltekit (client)43.2 kB--
@sentry/node-core57.86 kB+0.02%+7 B 🔺
@sentry/node174.8 kB+0.01%+15 B 🔺
@sentry/node - without tracing97.63 kB+0.02%+11 B 🔺
@sentry/aws-serverless114.98 kB+0.01%+7 B 🔺

View base workflow run

@github-actions

Copy link
Copy Markdown
Contributor

node-overhead report 🧳

Note: This is a synthetic benchmark with a minimal express app and does not necessarily reflect the real-world performance impact in an application.

ScenarioRequests/s% of BaselinePrev. Requests/sChange %
GET Baseline9,450-9,316+1%
GET With Sentry1,83419%1,744+5%
GET With Sentry (error only)6,10765%5,995+2%
POST Baseline1,211-1,211-
POST With Sentry62852%624+1%
POST With Sentry (error only)1,06388%1,056+1%
MYSQL Baseline3,260-3,337-2%
MYSQL With Sentry52016%499+4%
MYSQL With Sentry (error only)2,68682%2,666+1%

View base workflow run

@chargome
chargome marked this pull request as ready for review April 9, 2026 11:08

@Lms24Lms24 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for adding this!
Just to confirm: We already bumped the plugins to the supported version? And sveltekit uses the interface from core directly?

Comment threadpackages/nuxt/src/vite/sourceMaps.ts Outdated

@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 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: No integration or E2E test for feature
    • Added integration tests in nextjs, nuxt, and tanstackstart-react packages that verify user-provided rewriteSources hooks are invoked with proper arguments and return correct results during the source map configuration flow.

Create PR

Or push these changes by commenting:

@cursor push 0f5c31dc99
Preview (0f5c31dc99)
diff --git a/packages/nextjs/test/config/rewriteSources.integration.test.ts b/packages/nextjs/test/config/rewriteSources.integration.test.ts
new file mode 100644
--- /dev/null+++ b/packages/nextjs/test/config/rewriteSources.integration.test.ts@@ -1,0 +1,118 @@+import { describe, it, expect, vi } from 'vitest';+import { getBuildPluginOptions } from '../../src/config/getBuildPluginOptions';+import type { SentryBuildOptions } from '../../src/config/types';++const mockReleaseName = 'test-release';+const mockDistDirAbsPath = '/test/project/.next';++describe('rewriteSources integration', () => {+ it('invokes custom rewriteSources function with source paths', () => {+ const rewriteSourcesSpy = vi.fn((source: string) => {+ return source.replace(/^custom\//, 'rewritten/');+ });++ const sentryBuildOptions: SentryBuildOptions = {+ org: 'test-org',+ project: 'test-project',+ sourcemaps: {+ rewriteSources: rewriteSourcesSpy,+ },+ };++ const result = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-client',+ });++ const rewriteSources = result.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();+ expect(rewriteSources).toBe(rewriteSourcesSpy);++ const testPaths = [+ 'custom/src/pages/index.js',+ 'custom/components/Button.tsx',+ 'src/utils/helpers.js',+ 'custom/lib/api.ts',+ ];++ testPaths.forEach(path => {+ rewriteSources?.(path, {});+ });++ expect(rewriteSourcesSpy).toHaveBeenCalledTimes(4);+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(1, 'custom/src/pages/index.js', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(2, 'custom/components/Button.tsx', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(3, 'src/utils/helpers.js', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(4, 'custom/lib/api.ts', {});++ expect(rewriteSourcesSpy.mock.results[0]?.value).toBe('rewritten/src/pages/index.js');+ expect(rewriteSourcesSpy.mock.results[1]?.value).toBe('rewritten/components/Button.tsx');+ expect(rewriteSourcesSpy.mock.results[2]?.value).toBe('src/utils/helpers.js');+ expect(rewriteSourcesSpy.mock.results[3]?.value).toBe('rewritten/lib/api.ts');+ });++ it('invokes default rewriteSources function for webpack sources', () => {+ const sentryBuildOptions: SentryBuildOptions = {+ org: 'test-org',+ project: 'test-project',+ };++ const result = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-client',+ });++ const rewriteSources = result.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();++ const webpackPaths = [+ 'webpack://_N_E/src/pages/index.js',+ 'webpack://project/src/components/Button.js',+ 'src/utils/helpers.js',+ 'webpack://_N_E/./app/layout.tsx',+ ];++ const rewrittenPaths = webpackPaths.map(path => rewriteSources?.(path, {}));++ expect(rewrittenPaths).toEqual([+ 'src/pages/index.js',+ 'project/src/components/Button.js',+ 'src/utils/helpers.js',+ './app/layout.tsx',+ ]);+ });++ it('preserves rewriteSources function reference across multiple builds', () => {+ const customRewrite = (source: string) => source.replace(/^prefix\//, '');++ const sentryBuildOptions: SentryBuildOptions = {+ org: 'test-org',+ project: 'test-project',+ sourcemaps: {+ rewriteSources: customRewrite,+ },+ };++ const clientResult = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-client',+ });++ const serverResult = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-nodejs',+ });++ expect(clientResult.sourcemaps?.rewriteSources).toBe(customRewrite);+ expect(serverResult.sourcemaps?.rewriteSources).toBe(customRewrite);+ expect(clientResult.sourcemaps?.rewriteSources).toBe(serverResult.sourcemaps?.rewriteSources);+ });+});diff --git a/packages/nuxt/test/vite/rewriteSources.integration.test.ts b/packages/nuxt/test/vite/rewriteSources.integration.test.ts
new file mode 100644
--- /dev/null+++ b/packages/nuxt/test/vite/rewriteSources.integration.test.ts@@ -1,0 +1,72 @@+import { describe, it, expect, vi } from 'vitest';+import { getPluginOptions } from '../../src/vite/sourceMaps';+import type { SentryNuxtModuleOptions } from '../../src/common/types';++describe('rewriteSources integration', () => {+ it('invokes custom rewriteSources function with source paths', () => {+ const rewriteSourcesSpy = vi.fn((source: string) => {+ return source.replace(/^src\//, 'custom/');+ });++ const options = getPluginOptions({+ sourcemaps: {+ rewriteSources: rewriteSourcesSpy,+ },+ } as SentryNuxtModuleOptions);++ const rewriteSources = options.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();+ expect(rewriteSources).toBe(rewriteSourcesSpy);++ const testPaths = [+ 'src/pages/index.vue',+ 'src/components/Button.vue',+ 'lib/utils/helpers.ts',+ 'src/composables/useAuth.ts',+ ];++ testPaths.forEach(path => {+ rewriteSources?.(path);+ });++ expect(rewriteSourcesSpy).toHaveBeenCalledTimes(4);+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(1, 'src/pages/index.vue');+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(2, 'src/components/Button.vue');+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(3, 'lib/utils/helpers.ts');+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(4, 'src/composables/useAuth.ts');++ expect(rewriteSourcesSpy.mock.results[0]?.value).toBe('custom/pages/index.vue');+ expect(rewriteSourcesSpy.mock.results[1]?.value).toBe('custom/components/Button.vue');+ expect(rewriteSourcesSpy.mock.results[2]?.value).toBe('lib/utils/helpers.ts');+ expect(rewriteSourcesSpy.mock.results[3]?.value).toBe('custom/composables/useAuth.ts');+ });++ it('invokes default rewriteSources function to normalize paths', () => {+ const options = getPluginOptions({} as SentryNuxtModuleOptions);++ const rewriteSources = options.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();++ const paths = ['../../../foo/bar', '../../components/Layout.vue', './local', '../utils/api.ts'];++ const rewrittenPaths = paths.map(path => rewriteSources?.(path));++ expect(rewrittenPaths).toEqual(['./foo/bar', './components/Layout.vue', './local', './utils/api.ts']);+ });++ it('preserves rewriteSources function reference across configuration', () => {+ const customRewrite = (source: string) => source.replace(/^prefix\//, '');++ const options = getPluginOptions({+ sourcemaps: {+ rewriteSources: customRewrite,+ },+ } as SentryNuxtModuleOptions);++ expect(options.sourcemaps?.rewriteSources).toBe(customRewrite);++ const testSource = 'prefix/src/app.vue';+ const result = options.sourcemaps?.rewriteSources?.(testSource);+ expect(result).toBe('src/app.vue');+ });+});diff --git a/packages/tanstackstart-react/test/vite/rewriteSources.integration.test.ts b/packages/tanstackstart-react/test/vite/rewriteSources.integration.test.ts
new file mode 100644
--- /dev/null+++ b/packages/tanstackstart-react/test/vite/rewriteSources.integration.test.ts@@ -1,0 +1,83 @@+import { describe, it, expect, vi } from 'vitest';++const sentryVitePluginSpy = vi.fn(() => []);++vi.mock('@sentry/vite-plugin', () => ({+ sentryVitePlugin: vi.fn(() => []),+}));++import { makeAddSentryVitePlugin } from '../../src/vite/sourceMaps';+import { sentryVitePlugin } from '@sentry/vite-plugin';++vi.mocked(sentryVitePlugin).mockImplementation(sentryVitePluginSpy);++describe('rewriteSources integration', () => {+ it('invokes custom rewriteSources function with source paths', () => {+ const rewriteSourcesSpy = vi.fn((source: string) => {+ return source.replace(/^src\//, '');+ });++ makeAddSentryVitePlugin({+ org: 'my-org',+ authToken: 'my-token',+ sourcemaps: {+ rewriteSources: rewriteSourcesSpy,+ },+ });++ expect(sentryVitePluginSpy).toHaveBeenCalledWith(+ expect.objectContaining({+ sourcemaps: expect.objectContaining({+ rewriteSources: rewriteSourcesSpy,+ }),+ }),+ );++ const passedOptions = sentryVitePluginSpy.mock.calls[0]?.[0];+ const rewriteSources = passedOptions?.sourcemaps?.rewriteSources;++ expect(rewriteSources).toBe(rewriteSourcesSpy);++ const testPaths = ['src/routes/index.tsx', 'src/components/Button.tsx', 'lib/utils.ts', 'src/api/client.ts'];++ testPaths.forEach(path => {+ rewriteSources?.(path, {});+ });++ expect(rewriteSourcesSpy).toHaveBeenCalledTimes(4);+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(1, 'src/routes/index.tsx', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(2, 'src/components/Button.tsx', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(3, 'lib/utils.ts', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(4, 'src/api/client.ts', {});++ expect(rewriteSourcesSpy.mock.results[0]?.value).toBe('routes/index.tsx');+ expect(rewriteSourcesSpy.mock.results[1]?.value).toBe('components/Button.tsx');+ expect(rewriteSourcesSpy.mock.results[2]?.value).toBe('lib/utils.ts');+ expect(rewriteSourcesSpy.mock.results[3]?.value).toBe('api/client.ts');+ });++ it('preserves rewriteSources function reference in plugin options', () => {+ const customRewrite = (source: string) => source.replace(/^prefix\//, '');++ sentryVitePluginSpy.mockClear();++ makeAddSentryVitePlugin({+ org: 'my-org',+ authToken: 'my-token',+ sourcemaps: {+ rewriteSources: customRewrite,+ },+ });++ expect(sentryVitePluginSpy).toHaveBeenCalledWith(+ expect.objectContaining({+ sourcemaps: expect.objectContaining({+ rewriteSources: customRewrite,+ }),+ }),+ );++ const passedOptions = sentryVitePluginSpy.mock.calls[0]?.[0];+ expect(passedOptions?.sourcemaps?.rewriteSources).toBe(customRewrite);+ });+});

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2722abb. Configure here.

Comment threadpackages/nextjs/test/config/getBuildPluginOptions.test.ts
@chargome
chargome merged commit 13dc7a1 into developApr 14, 2026
467 of 477 checks passed
@chargome
chargome deleted the cg/update-rewrite-sources branch April 14, 2026 12:27
mydea pushed a commit that referenced this pull request Apr 15, 2026
- Adds `rewriteSources` to the base `SourceMapsOptions` interface so
users can customize source path rewriting without the `unstable_*`
escape hatch
- Wires up the option in Nuxt, Next.js, and TanStack Start (SvelteKit,
Astro, React Router already pass it through via spread)
- Nuxt and Next.js preserve their default rewriting behavior when the
option is not provided
bundler plugins ref
getsentry/sentry-javascript-bundler-plugins#908closes#20028
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.

Expose source map path/dir to rewriteSources hook

4 participants

@chargome@Lms24@nicohrubec@s1gr1d
, '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(core): Expose rewriteSources top level option - #20142

Merged
chargome merged 2 commits into
developfrom
cg/update-rewrite-sources
Apr 14, 2026
Merged

feat(core): Expose rewriteSources top level option #20142
chargome merged 2 commits into
developfrom
cg/update-rewrite-sources

Conversation

@chargome

@chargomechargome commented Apr 9, 2026

Copy link
Copy Markdown
Member
  • Adds rewriteSources to the base SourceMapsOptions interface so users can customize source path rewriting without the unstable_* escape hatch
  • Wires up the option in Nuxt, Next.js, and TanStack Start (SvelteKit, Astro, React Router already pass it through via spread)
  • Nuxt and Next.js preserve their default rewriting behavior when the option is not provided

bundler plugins ref getsentry/sentry-javascript-bundler-plugins#908
closes#20028

@chargomechargome self-assigned this Apr 9, 2026
@github-actions

github-actionsBot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

Cloudflare

  • Split alarms into multiple traces and link them by JPeer264 in #19373
  • Propagate traceparent to RPC calls - via fetch by JPeer264 in #19991

Core

  • Expose rewriteSources top level option by chargome in #20142
  • Automatically disable truncation when span streaming is enabled in LangGraph integration by andreiborza in #20231
  • Automatically disable truncation when span streaming is enabled in LangChain integration by andreiborza in #20230
  • Automatically disable truncation when span streaming is enabled in Google GenAI integration by andreiborza in #20229
  • Automatically disable truncation when span streaming is enabled in Anthropic AI integration by andreiborza in #20228
  • Automatically disable truncation when span streaming is enabled in Vercel AI integration by andreiborza in #20232
  • Automatically disable truncation when span streaming is enabled in OpenAI integration by andreiborza in #20227
  • Add enableTruncation option to Vercel AI integration by nicohrubec in #20195
  • Add enableTruncation option to Google GenAI integration by andreiborza in #20184
  • Add enableTruncation option to Anthropic AI integration by andreiborza in #20181
  • Add enableTruncation option to LangGraph integration by andreiborza in #20183
  • Add enableTruncation option to LangChain integration by andreiborza in #20182
  • Add enableTruncation option to OpenAI integration by andreiborza in #20167
  • Export a reusable function to add tracing headers by JPeer264 in #20076

Deps

  • Bump axios from 1.13.5 to 1.15.0 by dependabot in #20180
  • Bump hono from 4.12.7 to 4.12.12 by dependabot in #20118
  • Bump defu from 6.1.4 to 6.1.6 by dependabot in #20104

Other

  • (browser) Add View Hierarchy integration by timfish in #14981
  • (cloudflare,deno,vercel-edge) Add span streaming support by Lms24 in #20127
  • (node) Include global scope for eventLoopBlockIntegration by timfish in #20108
  • (node-native) Add support for V8 v14 (Node v25+) by timfish in #20125
  • (opentelemetry) Vendor AsyncLocalStorageContextManager by mydea in #20243

Bug Fixes 🐛

Deno

  • Handle reader.closed rejection from releaseLock() in streaming by andreiborza in #20187
  • Avoid inferring invalid span op from Deno tracer by Lms24 in #20128

Other

  • (ci) Prevent command injection in ci-metadata workflow by fix-it-felix-sentry in #19899
  • (core, node) Support loading Express options lazily by isaacs in #20211
  • (e2e) Add op check to waitForTransaction in React Router e2e tests by copilot-swe-agent in #20193
  • (e2e-tests) Remove flaky navigation breadcrumb assertions from parameterized-routes tests by copilot-swe-agent in #20202
  • (node-integration-tests) Fix flaky kafkajs test race condition by copilot-swe-agent in #20189

Internal Changes 🔧

Ci

  • Remove node-overhead GitHub Action by mydea in #20246
  • Bump dorny/paths-filter from v3.0.1 to v4.0.1 by mydea in #20251
  • Remove codecov steps from jobs that produce no coverage/JUnit data by mydea in #20244

Deps

  • Bump hono from 4.12.7 to 4.12.12 in /dev-packages/e2e-tests/test-applications/cloudflare-hono by dependabot in #20119
  • Bump axios from 1.13.5 to 1.15.0 in /dev-packages/e2e-tests/test-applications/nestjs-basic by dependabot in #20179

Deps Dev

  • Bump @sveltejs/kit from 2.53.3 to 2.57.1 by dependabot in #20216
  • Bump vite from 7.2.0 to 7.3.2 in /dev-packages/e2e-tests/test-applications/tanstackstart-react by dependabot in #20107

Other

  • (bugbot) Add rules to flag test-flake-provoking patterns by Lms24 in #20192
  • (react) Remove duplicated test mock by s1gr1d in #20200
  • (size-limit) Bump failing size limit scenario by Lms24 in #20186
  • Add PR review reminder workflow by copilot-swe-agent in #20175
  • Fix lint warnings by mydea in #20250
  • Fix flaky ANR test by increasing blocking duration by JPeer264 in #20239
  • Add automatic flaky test detector by nicohrubec in #18684

🤖 This preview updates automatically when you update the PR.

@github-actions

github-actionsBot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser25.73 kB--
@sentry/browser - with treeshaking flags24.22 kB--
@sentry/browser (incl. Tracing)42.72 kB--
@sentry/browser (incl. Tracing, Profiling)47.35 kB--
@sentry/browser (incl. Tracing, Replay)81.53 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags71.1 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)86.23 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)98.44 kB--
@sentry/browser (incl. Feedback)42.52 kB--
@sentry/browser (incl. sendFeedback)30.39 kB--
@sentry/browser (incl. FeedbackAsync)35.39 kB--
@sentry/browser (incl. Metrics)27.04 kB--
@sentry/browser (incl. Logs)27.19 kB--
@sentry/browser (incl. Metrics & Logs)27.86 kB--
@sentry/react27.48 kB--
@sentry/react (incl. Tracing)45.05 kB--
@sentry/vue30.56 kB--
@sentry/vue (incl. Tracing)44.58 kB--
@sentry/svelte25.75 kB--
CDN Bundle28.41 kB--
CDN Bundle (incl. Tracing)43.76 kB--
CDN Bundle (incl. Logs, Metrics)29.79 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)44.83 kB--
CDN Bundle (incl. Replay, Logs, Metrics)68.6 kB--
CDN Bundle (incl. Tracing, Replay)80.63 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)81.66 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)86.16 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)87.19 kB--
CDN Bundle - uncompressed83 kB--
CDN Bundle (incl. Tracing) - uncompressed129.79 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed87.14 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed133.2 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed210.13 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed246.67 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed250.07 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed259.58 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed262.97 kB--
@sentry/nextjs (client)47.47 kB--
@sentry/sveltekit (client)43.2 kB--
@sentry/node-core57.86 kB+0.02%+7 B 🔺
@sentry/node174.8 kB+0.01%+15 B 🔺
@sentry/node - without tracing97.63 kB+0.02%+11 B 🔺
@sentry/aws-serverless114.98 kB+0.01%+7 B 🔺

View base workflow run

@github-actions

Copy link
Copy Markdown
Contributor

node-overhead report 🧳

Note: This is a synthetic benchmark with a minimal express app and does not necessarily reflect the real-world performance impact in an application.

ScenarioRequests/s% of BaselinePrev. Requests/sChange %
GET Baseline9,450-9,316+1%
GET With Sentry1,83419%1,744+5%
GET With Sentry (error only)6,10765%5,995+2%
POST Baseline1,211-1,211-
POST With Sentry62852%624+1%
POST With Sentry (error only)1,06388%1,056+1%
MYSQL Baseline3,260-3,337-2%
MYSQL With Sentry52016%499+4%
MYSQL With Sentry (error only)2,68682%2,666+1%

View base workflow run

@chargome
chargome marked this pull request as ready for review April 9, 2026 11:08

@Lms24Lms24 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for adding this!
Just to confirm: We already bumped the plugins to the supported version? And sveltekit uses the interface from core directly?

Comment threadpackages/nuxt/src/vite/sourceMaps.ts Outdated

@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 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: No integration or E2E test for feature
    • Added integration tests in nextjs, nuxt, and tanstackstart-react packages that verify user-provided rewriteSources hooks are invoked with proper arguments and return correct results during the source map configuration flow.

Create PR

Or push these changes by commenting:

@cursor push 0f5c31dc99
Preview (0f5c31dc99)
diff --git a/packages/nextjs/test/config/rewriteSources.integration.test.ts b/packages/nextjs/test/config/rewriteSources.integration.test.ts
new file mode 100644
--- /dev/null+++ b/packages/nextjs/test/config/rewriteSources.integration.test.ts@@ -1,0 +1,118 @@+import { describe, it, expect, vi } from 'vitest';+import { getBuildPluginOptions } from '../../src/config/getBuildPluginOptions';+import type { SentryBuildOptions } from '../../src/config/types';++const mockReleaseName = 'test-release';+const mockDistDirAbsPath = '/test/project/.next';++describe('rewriteSources integration', () => {+ it('invokes custom rewriteSources function with source paths', () => {+ const rewriteSourcesSpy = vi.fn((source: string) => {+ return source.replace(/^custom\//, 'rewritten/');+ });++ const sentryBuildOptions: SentryBuildOptions = {+ org: 'test-org',+ project: 'test-project',+ sourcemaps: {+ rewriteSources: rewriteSourcesSpy,+ },+ };++ const result = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-client',+ });++ const rewriteSources = result.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();+ expect(rewriteSources).toBe(rewriteSourcesSpy);++ const testPaths = [+ 'custom/src/pages/index.js',+ 'custom/components/Button.tsx',+ 'src/utils/helpers.js',+ 'custom/lib/api.ts',+ ];++ testPaths.forEach(path => {+ rewriteSources?.(path, {});+ });++ expect(rewriteSourcesSpy).toHaveBeenCalledTimes(4);+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(1, 'custom/src/pages/index.js', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(2, 'custom/components/Button.tsx', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(3, 'src/utils/helpers.js', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(4, 'custom/lib/api.ts', {});++ expect(rewriteSourcesSpy.mock.results[0]?.value).toBe('rewritten/src/pages/index.js');+ expect(rewriteSourcesSpy.mock.results[1]?.value).toBe('rewritten/components/Button.tsx');+ expect(rewriteSourcesSpy.mock.results[2]?.value).toBe('src/utils/helpers.js');+ expect(rewriteSourcesSpy.mock.results[3]?.value).toBe('rewritten/lib/api.ts');+ });++ it('invokes default rewriteSources function for webpack sources', () => {+ const sentryBuildOptions: SentryBuildOptions = {+ org: 'test-org',+ project: 'test-project',+ };++ const result = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-client',+ });++ const rewriteSources = result.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();++ const webpackPaths = [+ 'webpack://_N_E/src/pages/index.js',+ 'webpack://project/src/components/Button.js',+ 'src/utils/helpers.js',+ 'webpack://_N_E/./app/layout.tsx',+ ];++ const rewrittenPaths = webpackPaths.map(path => rewriteSources?.(path, {}));++ expect(rewrittenPaths).toEqual([+ 'src/pages/index.js',+ 'project/src/components/Button.js',+ 'src/utils/helpers.js',+ './app/layout.tsx',+ ]);+ });++ it('preserves rewriteSources function reference across multiple builds', () => {+ const customRewrite = (source: string) => source.replace(/^prefix\//, '');++ const sentryBuildOptions: SentryBuildOptions = {+ org: 'test-org',+ project: 'test-project',+ sourcemaps: {+ rewriteSources: customRewrite,+ },+ };++ const clientResult = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-client',+ });++ const serverResult = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-nodejs',+ });++ expect(clientResult.sourcemaps?.rewriteSources).toBe(customRewrite);+ expect(serverResult.sourcemaps?.rewriteSources).toBe(customRewrite);+ expect(clientResult.sourcemaps?.rewriteSources).toBe(serverResult.sourcemaps?.rewriteSources);+ });+});diff --git a/packages/nuxt/test/vite/rewriteSources.integration.test.ts b/packages/nuxt/test/vite/rewriteSources.integration.test.ts
new file mode 100644
--- /dev/null+++ b/packages/nuxt/test/vite/rewriteSources.integration.test.ts@@ -1,0 +1,72 @@+import { describe, it, expect, vi } from 'vitest';+import { getPluginOptions } from '../../src/vite/sourceMaps';+import type { SentryNuxtModuleOptions } from '../../src/common/types';++describe('rewriteSources integration', () => {+ it('invokes custom rewriteSources function with source paths', () => {+ const rewriteSourcesSpy = vi.fn((source: string) => {+ return source.replace(/^src\//, 'custom/');+ });++ const options = getPluginOptions({+ sourcemaps: {+ rewriteSources: rewriteSourcesSpy,+ },+ } as SentryNuxtModuleOptions);++ const rewriteSources = options.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();+ expect(rewriteSources).toBe(rewriteSourcesSpy);++ const testPaths = [+ 'src/pages/index.vue',+ 'src/components/Button.vue',+ 'lib/utils/helpers.ts',+ 'src/composables/useAuth.ts',+ ];++ testPaths.forEach(path => {+ rewriteSources?.(path);+ });++ expect(rewriteSourcesSpy).toHaveBeenCalledTimes(4);+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(1, 'src/pages/index.vue');+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(2, 'src/components/Button.vue');+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(3, 'lib/utils/helpers.ts');+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(4, 'src/composables/useAuth.ts');++ expect(rewriteSourcesSpy.mock.results[0]?.value).toBe('custom/pages/index.vue');+ expect(rewriteSourcesSpy.mock.results[1]?.value).toBe('custom/components/Button.vue');+ expect(rewriteSourcesSpy.mock.results[2]?.value).toBe('lib/utils/helpers.ts');+ expect(rewriteSourcesSpy.mock.results[3]?.value).toBe('custom/composables/useAuth.ts');+ });++ it('invokes default rewriteSources function to normalize paths', () => {+ const options = getPluginOptions({} as SentryNuxtModuleOptions);++ const rewriteSources = options.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();++ const paths = ['../../../foo/bar', '../../components/Layout.vue', './local', '../utils/api.ts'];++ const rewrittenPaths = paths.map(path => rewriteSources?.(path));++ expect(rewrittenPaths).toEqual(['./foo/bar', './components/Layout.vue', './local', './utils/api.ts']);+ });++ it('preserves rewriteSources function reference across configuration', () => {+ const customRewrite = (source: string) => source.replace(/^prefix\//, '');++ const options = getPluginOptions({+ sourcemaps: {+ rewriteSources: customRewrite,+ },+ } as SentryNuxtModuleOptions);++ expect(options.sourcemaps?.rewriteSources).toBe(customRewrite);++ const testSource = 'prefix/src/app.vue';+ const result = options.sourcemaps?.rewriteSources?.(testSource);+ expect(result).toBe('src/app.vue');+ });+});diff --git a/packages/tanstackstart-react/test/vite/rewriteSources.integration.test.ts b/packages/tanstackstart-react/test/vite/rewriteSources.integration.test.ts
new file mode 100644
--- /dev/null+++ b/packages/tanstackstart-react/test/vite/rewriteSources.integration.test.ts@@ -1,0 +1,83 @@+import { describe, it, expect, vi } from 'vitest';++const sentryVitePluginSpy = vi.fn(() => []);++vi.mock('@sentry/vite-plugin', () => ({+ sentryVitePlugin: vi.fn(() => []),+}));++import { makeAddSentryVitePlugin } from '../../src/vite/sourceMaps';+import { sentryVitePlugin } from '@sentry/vite-plugin';++vi.mocked(sentryVitePlugin).mockImplementation(sentryVitePluginSpy);++describe('rewriteSources integration', () => {+ it('invokes custom rewriteSources function with source paths', () => {+ const rewriteSourcesSpy = vi.fn((source: string) => {+ return source.replace(/^src\//, '');+ });++ makeAddSentryVitePlugin({+ org: 'my-org',+ authToken: 'my-token',+ sourcemaps: {+ rewriteSources: rewriteSourcesSpy,+ },+ });++ expect(sentryVitePluginSpy).toHaveBeenCalledWith(+ expect.objectContaining({+ sourcemaps: expect.objectContaining({+ rewriteSources: rewriteSourcesSpy,+ }),+ }),+ );++ const passedOptions = sentryVitePluginSpy.mock.calls[0]?.[0];+ const rewriteSources = passedOptions?.sourcemaps?.rewriteSources;++ expect(rewriteSources).toBe(rewriteSourcesSpy);++ const testPaths = ['src/routes/index.tsx', 'src/components/Button.tsx', 'lib/utils.ts', 'src/api/client.ts'];++ testPaths.forEach(path => {+ rewriteSources?.(path, {});+ });++ expect(rewriteSourcesSpy).toHaveBeenCalledTimes(4);+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(1, 'src/routes/index.tsx', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(2, 'src/components/Button.tsx', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(3, 'lib/utils.ts', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(4, 'src/api/client.ts', {});++ expect(rewriteSourcesSpy.mock.results[0]?.value).toBe('routes/index.tsx');+ expect(rewriteSourcesSpy.mock.results[1]?.value).toBe('components/Button.tsx');+ expect(rewriteSourcesSpy.mock.results[2]?.value).toBe('lib/utils.ts');+ expect(rewriteSourcesSpy.mock.results[3]?.value).toBe('api/client.ts');+ });++ it('preserves rewriteSources function reference in plugin options', () => {+ const customRewrite = (source: string) => source.replace(/^prefix\//, '');++ sentryVitePluginSpy.mockClear();++ makeAddSentryVitePlugin({+ org: 'my-org',+ authToken: 'my-token',+ sourcemaps: {+ rewriteSources: customRewrite,+ },+ });++ expect(sentryVitePluginSpy).toHaveBeenCalledWith(+ expect.objectContaining({+ sourcemaps: expect.objectContaining({+ rewriteSources: customRewrite,+ }),+ }),+ );++ const passedOptions = sentryVitePluginSpy.mock.calls[0]?.[0];+ expect(passedOptions?.sourcemaps?.rewriteSources).toBe(customRewrite);+ });+});

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2722abb. Configure here.

Comment threadpackages/nextjs/test/config/getBuildPluginOptions.test.ts
@chargome
chargome merged commit 13dc7a1 into developApr 14, 2026
467 of 477 checks passed
@chargome
chargome deleted the cg/update-rewrite-sources branch April 14, 2026 12:27
mydea pushed a commit that referenced this pull request Apr 15, 2026
- Adds `rewriteSources` to the base `SourceMapsOptions` interface so
users can customize source path rewriting without the `unstable_*`
escape hatch
- Wires up the option in Nuxt, Next.js, and TanStack Start (SvelteKit,
Astro, React Router already pass it through via spread)
- Nuxt and Next.js preserve their default rewriting behavior when the
option is not provided
bundler plugins ref
getsentry/sentry-javascript-bundler-plugins#908closes#20028
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.

Expose source map path/dir to rewriteSources hook

4 participants

@chargome@Lms24@nicohrubec@s1gr1d
, '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(core): Expose rewriteSources top level option - #20142

Merged
chargome merged 2 commits into
developfrom
cg/update-rewrite-sources
Apr 14, 2026
Merged

feat(core): Expose rewriteSources top level option #20142
chargome merged 2 commits into
developfrom
cg/update-rewrite-sources

Conversation

@chargome

@chargomechargome commented Apr 9, 2026

Copy link
Copy Markdown
Member
  • Adds rewriteSources to the base SourceMapsOptions interface so users can customize source path rewriting without the unstable_* escape hatch
  • Wires up the option in Nuxt, Next.js, and TanStack Start (SvelteKit, Astro, React Router already pass it through via spread)
  • Nuxt and Next.js preserve their default rewriting behavior when the option is not provided

bundler plugins ref getsentry/sentry-javascript-bundler-plugins#908
closes#20028

@chargomechargome self-assigned this Apr 9, 2026
@github-actions

github-actionsBot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

Cloudflare

  • Split alarms into multiple traces and link them by JPeer264 in #19373
  • Propagate traceparent to RPC calls - via fetch by JPeer264 in #19991

Core

  • Expose rewriteSources top level option by chargome in #20142
  • Automatically disable truncation when span streaming is enabled in LangGraph integration by andreiborza in #20231
  • Automatically disable truncation when span streaming is enabled in LangChain integration by andreiborza in #20230
  • Automatically disable truncation when span streaming is enabled in Google GenAI integration by andreiborza in #20229
  • Automatically disable truncation when span streaming is enabled in Anthropic AI integration by andreiborza in #20228
  • Automatically disable truncation when span streaming is enabled in Vercel AI integration by andreiborza in #20232
  • Automatically disable truncation when span streaming is enabled in OpenAI integration by andreiborza in #20227
  • Add enableTruncation option to Vercel AI integration by nicohrubec in #20195
  • Add enableTruncation option to Google GenAI integration by andreiborza in #20184
  • Add enableTruncation option to Anthropic AI integration by andreiborza in #20181
  • Add enableTruncation option to LangGraph integration by andreiborza in #20183
  • Add enableTruncation option to LangChain integration by andreiborza in #20182
  • Add enableTruncation option to OpenAI integration by andreiborza in #20167
  • Export a reusable function to add tracing headers by JPeer264 in #20076

Deps

  • Bump axios from 1.13.5 to 1.15.0 by dependabot in #20180
  • Bump hono from 4.12.7 to 4.12.12 by dependabot in #20118
  • Bump defu from 6.1.4 to 6.1.6 by dependabot in #20104

Other

  • (browser) Add View Hierarchy integration by timfish in #14981
  • (cloudflare,deno,vercel-edge) Add span streaming support by Lms24 in #20127
  • (node) Include global scope for eventLoopBlockIntegration by timfish in #20108
  • (node-native) Add support for V8 v14 (Node v25+) by timfish in #20125
  • (opentelemetry) Vendor AsyncLocalStorageContextManager by mydea in #20243

Bug Fixes 🐛

Deno

  • Handle reader.closed rejection from releaseLock() in streaming by andreiborza in #20187
  • Avoid inferring invalid span op from Deno tracer by Lms24 in #20128

Other

  • (ci) Prevent command injection in ci-metadata workflow by fix-it-felix-sentry in #19899
  • (core, node) Support loading Express options lazily by isaacs in #20211
  • (e2e) Add op check to waitForTransaction in React Router e2e tests by copilot-swe-agent in #20193
  • (e2e-tests) Remove flaky navigation breadcrumb assertions from parameterized-routes tests by copilot-swe-agent in #20202
  • (node-integration-tests) Fix flaky kafkajs test race condition by copilot-swe-agent in #20189

Internal Changes 🔧

Ci

  • Remove node-overhead GitHub Action by mydea in #20246
  • Bump dorny/paths-filter from v3.0.1 to v4.0.1 by mydea in #20251
  • Remove codecov steps from jobs that produce no coverage/JUnit data by mydea in #20244

Deps

  • Bump hono from 4.12.7 to 4.12.12 in /dev-packages/e2e-tests/test-applications/cloudflare-hono by dependabot in #20119
  • Bump axios from 1.13.5 to 1.15.0 in /dev-packages/e2e-tests/test-applications/nestjs-basic by dependabot in #20179

Deps Dev

  • Bump @sveltejs/kit from 2.53.3 to 2.57.1 by dependabot in #20216
  • Bump vite from 7.2.0 to 7.3.2 in /dev-packages/e2e-tests/test-applications/tanstackstart-react by dependabot in #20107

Other

  • (bugbot) Add rules to flag test-flake-provoking patterns by Lms24 in #20192
  • (react) Remove duplicated test mock by s1gr1d in #20200
  • (size-limit) Bump failing size limit scenario by Lms24 in #20186
  • Add PR review reminder workflow by copilot-swe-agent in #20175
  • Fix lint warnings by mydea in #20250
  • Fix flaky ANR test by increasing blocking duration by JPeer264 in #20239
  • Add automatic flaky test detector by nicohrubec in #18684

🤖 This preview updates automatically when you update the PR.

@github-actions

github-actionsBot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser25.73 kB--
@sentry/browser - with treeshaking flags24.22 kB--
@sentry/browser (incl. Tracing)42.72 kB--
@sentry/browser (incl. Tracing, Profiling)47.35 kB--
@sentry/browser (incl. Tracing, Replay)81.53 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags71.1 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)86.23 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)98.44 kB--
@sentry/browser (incl. Feedback)42.52 kB--
@sentry/browser (incl. sendFeedback)30.39 kB--
@sentry/browser (incl. FeedbackAsync)35.39 kB--
@sentry/browser (incl. Metrics)27.04 kB--
@sentry/browser (incl. Logs)27.19 kB--
@sentry/browser (incl. Metrics & Logs)27.86 kB--
@sentry/react27.48 kB--
@sentry/react (incl. Tracing)45.05 kB--
@sentry/vue30.56 kB--
@sentry/vue (incl. Tracing)44.58 kB--
@sentry/svelte25.75 kB--
CDN Bundle28.41 kB--
CDN Bundle (incl. Tracing)43.76 kB--
CDN Bundle (incl. Logs, Metrics)29.79 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)44.83 kB--
CDN Bundle (incl. Replay, Logs, Metrics)68.6 kB--
CDN Bundle (incl. Tracing, Replay)80.63 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)81.66 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)86.16 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)87.19 kB--
CDN Bundle - uncompressed83 kB--
CDN Bundle (incl. Tracing) - uncompressed129.79 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed87.14 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed133.2 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed210.13 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed246.67 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed250.07 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed259.58 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed262.97 kB--
@sentry/nextjs (client)47.47 kB--
@sentry/sveltekit (client)43.2 kB--
@sentry/node-core57.86 kB+0.02%+7 B 🔺
@sentry/node174.8 kB+0.01%+15 B 🔺
@sentry/node - without tracing97.63 kB+0.02%+11 B 🔺
@sentry/aws-serverless114.98 kB+0.01%+7 B 🔺

View base workflow run

@github-actions

Copy link
Copy Markdown
Contributor

node-overhead report 🧳

Note: This is a synthetic benchmark with a minimal express app and does not necessarily reflect the real-world performance impact in an application.

ScenarioRequests/s% of BaselinePrev. Requests/sChange %
GET Baseline9,450-9,316+1%
GET With Sentry1,83419%1,744+5%
GET With Sentry (error only)6,10765%5,995+2%
POST Baseline1,211-1,211-
POST With Sentry62852%624+1%
POST With Sentry (error only)1,06388%1,056+1%
MYSQL Baseline3,260-3,337-2%
MYSQL With Sentry52016%499+4%
MYSQL With Sentry (error only)2,68682%2,666+1%

View base workflow run

@chargome
chargome marked this pull request as ready for review April 9, 2026 11:08

@Lms24Lms24 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for adding this!
Just to confirm: We already bumped the plugins to the supported version? And sveltekit uses the interface from core directly?

Comment threadpackages/nuxt/src/vite/sourceMaps.ts Outdated

@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 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: No integration or E2E test for feature
    • Added integration tests in nextjs, nuxt, and tanstackstart-react packages that verify user-provided rewriteSources hooks are invoked with proper arguments and return correct results during the source map configuration flow.

Create PR

Or push these changes by commenting:

@cursor push 0f5c31dc99
Preview (0f5c31dc99)
diff --git a/packages/nextjs/test/config/rewriteSources.integration.test.ts b/packages/nextjs/test/config/rewriteSources.integration.test.ts
new file mode 100644
--- /dev/null+++ b/packages/nextjs/test/config/rewriteSources.integration.test.ts@@ -1,0 +1,118 @@+import { describe, it, expect, vi } from 'vitest';+import { getBuildPluginOptions } from '../../src/config/getBuildPluginOptions';+import type { SentryBuildOptions } from '../../src/config/types';++const mockReleaseName = 'test-release';+const mockDistDirAbsPath = '/test/project/.next';++describe('rewriteSources integration', () => {+ it('invokes custom rewriteSources function with source paths', () => {+ const rewriteSourcesSpy = vi.fn((source: string) => {+ return source.replace(/^custom\//, 'rewritten/');+ });++ const sentryBuildOptions: SentryBuildOptions = {+ org: 'test-org',+ project: 'test-project',+ sourcemaps: {+ rewriteSources: rewriteSourcesSpy,+ },+ };++ const result = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-client',+ });++ const rewriteSources = result.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();+ expect(rewriteSources).toBe(rewriteSourcesSpy);++ const testPaths = [+ 'custom/src/pages/index.js',+ 'custom/components/Button.tsx',+ 'src/utils/helpers.js',+ 'custom/lib/api.ts',+ ];++ testPaths.forEach(path => {+ rewriteSources?.(path, {});+ });++ expect(rewriteSourcesSpy).toHaveBeenCalledTimes(4);+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(1, 'custom/src/pages/index.js', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(2, 'custom/components/Button.tsx', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(3, 'src/utils/helpers.js', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(4, 'custom/lib/api.ts', {});++ expect(rewriteSourcesSpy.mock.results[0]?.value).toBe('rewritten/src/pages/index.js');+ expect(rewriteSourcesSpy.mock.results[1]?.value).toBe('rewritten/components/Button.tsx');+ expect(rewriteSourcesSpy.mock.results[2]?.value).toBe('src/utils/helpers.js');+ expect(rewriteSourcesSpy.mock.results[3]?.value).toBe('rewritten/lib/api.ts');+ });++ it('invokes default rewriteSources function for webpack sources', () => {+ const sentryBuildOptions: SentryBuildOptions = {+ org: 'test-org',+ project: 'test-project',+ };++ const result = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-client',+ });++ const rewriteSources = result.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();++ const webpackPaths = [+ 'webpack://_N_E/src/pages/index.js',+ 'webpack://project/src/components/Button.js',+ 'src/utils/helpers.js',+ 'webpack://_N_E/./app/layout.tsx',+ ];++ const rewrittenPaths = webpackPaths.map(path => rewriteSources?.(path, {}));++ expect(rewrittenPaths).toEqual([+ 'src/pages/index.js',+ 'project/src/components/Button.js',+ 'src/utils/helpers.js',+ './app/layout.tsx',+ ]);+ });++ it('preserves rewriteSources function reference across multiple builds', () => {+ const customRewrite = (source: string) => source.replace(/^prefix\//, '');++ const sentryBuildOptions: SentryBuildOptions = {+ org: 'test-org',+ project: 'test-project',+ sourcemaps: {+ rewriteSources: customRewrite,+ },+ };++ const clientResult = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-client',+ });++ const serverResult = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-nodejs',+ });++ expect(clientResult.sourcemaps?.rewriteSources).toBe(customRewrite);+ expect(serverResult.sourcemaps?.rewriteSources).toBe(customRewrite);+ expect(clientResult.sourcemaps?.rewriteSources).toBe(serverResult.sourcemaps?.rewriteSources);+ });+});diff --git a/packages/nuxt/test/vite/rewriteSources.integration.test.ts b/packages/nuxt/test/vite/rewriteSources.integration.test.ts
new file mode 100644
--- /dev/null+++ b/packages/nuxt/test/vite/rewriteSources.integration.test.ts@@ -1,0 +1,72 @@+import { describe, it, expect, vi } from 'vitest';+import { getPluginOptions } from '../../src/vite/sourceMaps';+import type { SentryNuxtModuleOptions } from '../../src/common/types';++describe('rewriteSources integration', () => {+ it('invokes custom rewriteSources function with source paths', () => {+ const rewriteSourcesSpy = vi.fn((source: string) => {+ return source.replace(/^src\//, 'custom/');+ });++ const options = getPluginOptions({+ sourcemaps: {+ rewriteSources: rewriteSourcesSpy,+ },+ } as SentryNuxtModuleOptions);++ const rewriteSources = options.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();+ expect(rewriteSources).toBe(rewriteSourcesSpy);++ const testPaths = [+ 'src/pages/index.vue',+ 'src/components/Button.vue',+ 'lib/utils/helpers.ts',+ 'src/composables/useAuth.ts',+ ];++ testPaths.forEach(path => {+ rewriteSources?.(path);+ });++ expect(rewriteSourcesSpy).toHaveBeenCalledTimes(4);+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(1, 'src/pages/index.vue');+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(2, 'src/components/Button.vue');+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(3, 'lib/utils/helpers.ts');+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(4, 'src/composables/useAuth.ts');++ expect(rewriteSourcesSpy.mock.results[0]?.value).toBe('custom/pages/index.vue');+ expect(rewriteSourcesSpy.mock.results[1]?.value).toBe('custom/components/Button.vue');+ expect(rewriteSourcesSpy.mock.results[2]?.value).toBe('lib/utils/helpers.ts');+ expect(rewriteSourcesSpy.mock.results[3]?.value).toBe('custom/composables/useAuth.ts');+ });++ it('invokes default rewriteSources function to normalize paths', () => {+ const options = getPluginOptions({} as SentryNuxtModuleOptions);++ const rewriteSources = options.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();++ const paths = ['../../../foo/bar', '../../components/Layout.vue', './local', '../utils/api.ts'];++ const rewrittenPaths = paths.map(path => rewriteSources?.(path));++ expect(rewrittenPaths).toEqual(['./foo/bar', './components/Layout.vue', './local', './utils/api.ts']);+ });++ it('preserves rewriteSources function reference across configuration', () => {+ const customRewrite = (source: string) => source.replace(/^prefix\//, '');++ const options = getPluginOptions({+ sourcemaps: {+ rewriteSources: customRewrite,+ },+ } as SentryNuxtModuleOptions);++ expect(options.sourcemaps?.rewriteSources).toBe(customRewrite);++ const testSource = 'prefix/src/app.vue';+ const result = options.sourcemaps?.rewriteSources?.(testSource);+ expect(result).toBe('src/app.vue');+ });+});diff --git a/packages/tanstackstart-react/test/vite/rewriteSources.integration.test.ts b/packages/tanstackstart-react/test/vite/rewriteSources.integration.test.ts
new file mode 100644
--- /dev/null+++ b/packages/tanstackstart-react/test/vite/rewriteSources.integration.test.ts@@ -1,0 +1,83 @@+import { describe, it, expect, vi } from 'vitest';++const sentryVitePluginSpy = vi.fn(() => []);++vi.mock('@sentry/vite-plugin', () => ({+ sentryVitePlugin: vi.fn(() => []),+}));++import { makeAddSentryVitePlugin } from '../../src/vite/sourceMaps';+import { sentryVitePlugin } from '@sentry/vite-plugin';++vi.mocked(sentryVitePlugin).mockImplementation(sentryVitePluginSpy);++describe('rewriteSources integration', () => {+ it('invokes custom rewriteSources function with source paths', () => {+ const rewriteSourcesSpy = vi.fn((source: string) => {+ return source.replace(/^src\//, '');+ });++ makeAddSentryVitePlugin({+ org: 'my-org',+ authToken: 'my-token',+ sourcemaps: {+ rewriteSources: rewriteSourcesSpy,+ },+ });++ expect(sentryVitePluginSpy).toHaveBeenCalledWith(+ expect.objectContaining({+ sourcemaps: expect.objectContaining({+ rewriteSources: rewriteSourcesSpy,+ }),+ }),+ );++ const passedOptions = sentryVitePluginSpy.mock.calls[0]?.[0];+ const rewriteSources = passedOptions?.sourcemaps?.rewriteSources;++ expect(rewriteSources).toBe(rewriteSourcesSpy);++ const testPaths = ['src/routes/index.tsx', 'src/components/Button.tsx', 'lib/utils.ts', 'src/api/client.ts'];++ testPaths.forEach(path => {+ rewriteSources?.(path, {});+ });++ expect(rewriteSourcesSpy).toHaveBeenCalledTimes(4);+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(1, 'src/routes/index.tsx', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(2, 'src/components/Button.tsx', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(3, 'lib/utils.ts', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(4, 'src/api/client.ts', {});++ expect(rewriteSourcesSpy.mock.results[0]?.value).toBe('routes/index.tsx');+ expect(rewriteSourcesSpy.mock.results[1]?.value).toBe('components/Button.tsx');+ expect(rewriteSourcesSpy.mock.results[2]?.value).toBe('lib/utils.ts');+ expect(rewriteSourcesSpy.mock.results[3]?.value).toBe('api/client.ts');+ });++ it('preserves rewriteSources function reference in plugin options', () => {+ const customRewrite = (source: string) => source.replace(/^prefix\//, '');++ sentryVitePluginSpy.mockClear();++ makeAddSentryVitePlugin({+ org: 'my-org',+ authToken: 'my-token',+ sourcemaps: {+ rewriteSources: customRewrite,+ },+ });++ expect(sentryVitePluginSpy).toHaveBeenCalledWith(+ expect.objectContaining({+ sourcemaps: expect.objectContaining({+ rewriteSources: customRewrite,+ }),+ }),+ );++ const passedOptions = sentryVitePluginSpy.mock.calls[0]?.[0];+ expect(passedOptions?.sourcemaps?.rewriteSources).toBe(customRewrite);+ });+});

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2722abb. Configure here.

Comment threadpackages/nextjs/test/config/getBuildPluginOptions.test.ts
@chargome
chargome merged commit 13dc7a1 into developApr 14, 2026
467 of 477 checks passed
@chargome
chargome deleted the cg/update-rewrite-sources branch April 14, 2026 12:27
mydea pushed a commit that referenced this pull request Apr 15, 2026
- Adds `rewriteSources` to the base `SourceMapsOptions` interface so
users can customize source path rewriting without the `unstable_*`
escape hatch
- Wires up the option in Nuxt, Next.js, and TanStack Start (SvelteKit,
Astro, React Router already pass it through via spread)
- Nuxt and Next.js preserve their default rewriting behavior when the
option is not provided
bundler plugins ref
getsentry/sentry-javascript-bundler-plugins#908closes#20028
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.

Expose source map path/dir to rewriteSources hook

4 participants

@chargome@Lms24@nicohrubec@s1gr1d
, '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(core): Expose rewriteSources top level option - #20142

Merged
chargome merged 2 commits into
developfrom
cg/update-rewrite-sources
Apr 14, 2026
Merged

feat(core): Expose rewriteSources top level option #20142
chargome merged 2 commits into
developfrom
cg/update-rewrite-sources

Conversation

@chargome

@chargomechargome commented Apr 9, 2026

Copy link
Copy Markdown
Member
  • Adds rewriteSources to the base SourceMapsOptions interface so users can customize source path rewriting without the unstable_* escape hatch
  • Wires up the option in Nuxt, Next.js, and TanStack Start (SvelteKit, Astro, React Router already pass it through via spread)
  • Nuxt and Next.js preserve their default rewriting behavior when the option is not provided

bundler plugins ref getsentry/sentry-javascript-bundler-plugins#908
closes#20028

@chargomechargome self-assigned this Apr 9, 2026
@github-actions

github-actionsBot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

Cloudflare

  • Split alarms into multiple traces and link them by JPeer264 in #19373
  • Propagate traceparent to RPC calls - via fetch by JPeer264 in #19991

Core

  • Expose rewriteSources top level option by chargome in #20142
  • Automatically disable truncation when span streaming is enabled in LangGraph integration by andreiborza in #20231
  • Automatically disable truncation when span streaming is enabled in LangChain integration by andreiborza in #20230
  • Automatically disable truncation when span streaming is enabled in Google GenAI integration by andreiborza in #20229
  • Automatically disable truncation when span streaming is enabled in Anthropic AI integration by andreiborza in #20228
  • Automatically disable truncation when span streaming is enabled in Vercel AI integration by andreiborza in #20232
  • Automatically disable truncation when span streaming is enabled in OpenAI integration by andreiborza in #20227
  • Add enableTruncation option to Vercel AI integration by nicohrubec in #20195
  • Add enableTruncation option to Google GenAI integration by andreiborza in #20184
  • Add enableTruncation option to Anthropic AI integration by andreiborza in #20181
  • Add enableTruncation option to LangGraph integration by andreiborza in #20183
  • Add enableTruncation option to LangChain integration by andreiborza in #20182
  • Add enableTruncation option to OpenAI integration by andreiborza in #20167
  • Export a reusable function to add tracing headers by JPeer264 in #20076

Deps

  • Bump axios from 1.13.5 to 1.15.0 by dependabot in #20180
  • Bump hono from 4.12.7 to 4.12.12 by dependabot in #20118
  • Bump defu from 6.1.4 to 6.1.6 by dependabot in #20104

Other

  • (browser) Add View Hierarchy integration by timfish in #14981
  • (cloudflare,deno,vercel-edge) Add span streaming support by Lms24 in #20127
  • (node) Include global scope for eventLoopBlockIntegration by timfish in #20108
  • (node-native) Add support for V8 v14 (Node v25+) by timfish in #20125
  • (opentelemetry) Vendor AsyncLocalStorageContextManager by mydea in #20243

Bug Fixes 🐛

Deno

  • Handle reader.closed rejection from releaseLock() in streaming by andreiborza in #20187
  • Avoid inferring invalid span op from Deno tracer by Lms24 in #20128

Other

  • (ci) Prevent command injection in ci-metadata workflow by fix-it-felix-sentry in #19899
  • (core, node) Support loading Express options lazily by isaacs in #20211
  • (e2e) Add op check to waitForTransaction in React Router e2e tests by copilot-swe-agent in #20193
  • (e2e-tests) Remove flaky navigation breadcrumb assertions from parameterized-routes tests by copilot-swe-agent in #20202
  • (node-integration-tests) Fix flaky kafkajs test race condition by copilot-swe-agent in #20189

Internal Changes 🔧

Ci

  • Remove node-overhead GitHub Action by mydea in #20246
  • Bump dorny/paths-filter from v3.0.1 to v4.0.1 by mydea in #20251
  • Remove codecov steps from jobs that produce no coverage/JUnit data by mydea in #20244

Deps

  • Bump hono from 4.12.7 to 4.12.12 in /dev-packages/e2e-tests/test-applications/cloudflare-hono by dependabot in #20119
  • Bump axios from 1.13.5 to 1.15.0 in /dev-packages/e2e-tests/test-applications/nestjs-basic by dependabot in #20179

Deps Dev

  • Bump @sveltejs/kit from 2.53.3 to 2.57.1 by dependabot in #20216
  • Bump vite from 7.2.0 to 7.3.2 in /dev-packages/e2e-tests/test-applications/tanstackstart-react by dependabot in #20107

Other

  • (bugbot) Add rules to flag test-flake-provoking patterns by Lms24 in #20192
  • (react) Remove duplicated test mock by s1gr1d in #20200
  • (size-limit) Bump failing size limit scenario by Lms24 in #20186
  • Add PR review reminder workflow by copilot-swe-agent in #20175
  • Fix lint warnings by mydea in #20250
  • Fix flaky ANR test by increasing blocking duration by JPeer264 in #20239
  • Add automatic flaky test detector by nicohrubec in #18684

🤖 This preview updates automatically when you update the PR.

@github-actions

github-actionsBot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser25.73 kB--
@sentry/browser - with treeshaking flags24.22 kB--
@sentry/browser (incl. Tracing)42.72 kB--
@sentry/browser (incl. Tracing, Profiling)47.35 kB--
@sentry/browser (incl. Tracing, Replay)81.53 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags71.1 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)86.23 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)98.44 kB--
@sentry/browser (incl. Feedback)42.52 kB--
@sentry/browser (incl. sendFeedback)30.39 kB--
@sentry/browser (incl. FeedbackAsync)35.39 kB--
@sentry/browser (incl. Metrics)27.04 kB--
@sentry/browser (incl. Logs)27.19 kB--
@sentry/browser (incl. Metrics & Logs)27.86 kB--
@sentry/react27.48 kB--
@sentry/react (incl. Tracing)45.05 kB--
@sentry/vue30.56 kB--
@sentry/vue (incl. Tracing)44.58 kB--
@sentry/svelte25.75 kB--
CDN Bundle28.41 kB--
CDN Bundle (incl. Tracing)43.76 kB--
CDN Bundle (incl. Logs, Metrics)29.79 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)44.83 kB--
CDN Bundle (incl. Replay, Logs, Metrics)68.6 kB--
CDN Bundle (incl. Tracing, Replay)80.63 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)81.66 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)86.16 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)87.19 kB--
CDN Bundle - uncompressed83 kB--
CDN Bundle (incl. Tracing) - uncompressed129.79 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed87.14 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed133.2 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed210.13 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed246.67 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed250.07 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed259.58 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed262.97 kB--
@sentry/nextjs (client)47.47 kB--
@sentry/sveltekit (client)43.2 kB--
@sentry/node-core57.86 kB+0.02%+7 B 🔺
@sentry/node174.8 kB+0.01%+15 B 🔺
@sentry/node - without tracing97.63 kB+0.02%+11 B 🔺
@sentry/aws-serverless114.98 kB+0.01%+7 B 🔺

View base workflow run

@github-actions

Copy link
Copy Markdown
Contributor

node-overhead report 🧳

Note: This is a synthetic benchmark with a minimal express app and does not necessarily reflect the real-world performance impact in an application.

ScenarioRequests/s% of BaselinePrev. Requests/sChange %
GET Baseline9,450-9,316+1%
GET With Sentry1,83419%1,744+5%
GET With Sentry (error only)6,10765%5,995+2%
POST Baseline1,211-1,211-
POST With Sentry62852%624+1%
POST With Sentry (error only)1,06388%1,056+1%
MYSQL Baseline3,260-3,337-2%
MYSQL With Sentry52016%499+4%
MYSQL With Sentry (error only)2,68682%2,666+1%

View base workflow run

@chargome
chargome marked this pull request as ready for review April 9, 2026 11:08

@Lms24Lms24 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for adding this!
Just to confirm: We already bumped the plugins to the supported version? And sveltekit uses the interface from core directly?

Comment threadpackages/nuxt/src/vite/sourceMaps.ts Outdated

@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 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: No integration or E2E test for feature
    • Added integration tests in nextjs, nuxt, and tanstackstart-react packages that verify user-provided rewriteSources hooks are invoked with proper arguments and return correct results during the source map configuration flow.

Create PR

Or push these changes by commenting:

@cursor push 0f5c31dc99
Preview (0f5c31dc99)
diff --git a/packages/nextjs/test/config/rewriteSources.integration.test.ts b/packages/nextjs/test/config/rewriteSources.integration.test.ts
new file mode 100644
--- /dev/null+++ b/packages/nextjs/test/config/rewriteSources.integration.test.ts@@ -1,0 +1,118 @@+import { describe, it, expect, vi } from 'vitest';+import { getBuildPluginOptions } from '../../src/config/getBuildPluginOptions';+import type { SentryBuildOptions } from '../../src/config/types';++const mockReleaseName = 'test-release';+const mockDistDirAbsPath = '/test/project/.next';++describe('rewriteSources integration', () => {+ it('invokes custom rewriteSources function with source paths', () => {+ const rewriteSourcesSpy = vi.fn((source: string) => {+ return source.replace(/^custom\//, 'rewritten/');+ });++ const sentryBuildOptions: SentryBuildOptions = {+ org: 'test-org',+ project: 'test-project',+ sourcemaps: {+ rewriteSources: rewriteSourcesSpy,+ },+ };++ const result = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-client',+ });++ const rewriteSources = result.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();+ expect(rewriteSources).toBe(rewriteSourcesSpy);++ const testPaths = [+ 'custom/src/pages/index.js',+ 'custom/components/Button.tsx',+ 'src/utils/helpers.js',+ 'custom/lib/api.ts',+ ];++ testPaths.forEach(path => {+ rewriteSources?.(path, {});+ });++ expect(rewriteSourcesSpy).toHaveBeenCalledTimes(4);+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(1, 'custom/src/pages/index.js', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(2, 'custom/components/Button.tsx', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(3, 'src/utils/helpers.js', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(4, 'custom/lib/api.ts', {});++ expect(rewriteSourcesSpy.mock.results[0]?.value).toBe('rewritten/src/pages/index.js');+ expect(rewriteSourcesSpy.mock.results[1]?.value).toBe('rewritten/components/Button.tsx');+ expect(rewriteSourcesSpy.mock.results[2]?.value).toBe('src/utils/helpers.js');+ expect(rewriteSourcesSpy.mock.results[3]?.value).toBe('rewritten/lib/api.ts');+ });++ it('invokes default rewriteSources function for webpack sources', () => {+ const sentryBuildOptions: SentryBuildOptions = {+ org: 'test-org',+ project: 'test-project',+ };++ const result = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-client',+ });++ const rewriteSources = result.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();++ const webpackPaths = [+ 'webpack://_N_E/src/pages/index.js',+ 'webpack://project/src/components/Button.js',+ 'src/utils/helpers.js',+ 'webpack://_N_E/./app/layout.tsx',+ ];++ const rewrittenPaths = webpackPaths.map(path => rewriteSources?.(path, {}));++ expect(rewrittenPaths).toEqual([+ 'src/pages/index.js',+ 'project/src/components/Button.js',+ 'src/utils/helpers.js',+ './app/layout.tsx',+ ]);+ });++ it('preserves rewriteSources function reference across multiple builds', () => {+ const customRewrite = (source: string) => source.replace(/^prefix\//, '');++ const sentryBuildOptions: SentryBuildOptions = {+ org: 'test-org',+ project: 'test-project',+ sourcemaps: {+ rewriteSources: customRewrite,+ },+ };++ const clientResult = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-client',+ });++ const serverResult = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-nodejs',+ });++ expect(clientResult.sourcemaps?.rewriteSources).toBe(customRewrite);+ expect(serverResult.sourcemaps?.rewriteSources).toBe(customRewrite);+ expect(clientResult.sourcemaps?.rewriteSources).toBe(serverResult.sourcemaps?.rewriteSources);+ });+});diff --git a/packages/nuxt/test/vite/rewriteSources.integration.test.ts b/packages/nuxt/test/vite/rewriteSources.integration.test.ts
new file mode 100644
--- /dev/null+++ b/packages/nuxt/test/vite/rewriteSources.integration.test.ts@@ -1,0 +1,72 @@+import { describe, it, expect, vi } from 'vitest';+import { getPluginOptions } from '../../src/vite/sourceMaps';+import type { SentryNuxtModuleOptions } from '../../src/common/types';++describe('rewriteSources integration', () => {+ it('invokes custom rewriteSources function with source paths', () => {+ const rewriteSourcesSpy = vi.fn((source: string) => {+ return source.replace(/^src\//, 'custom/');+ });++ const options = getPluginOptions({+ sourcemaps: {+ rewriteSources: rewriteSourcesSpy,+ },+ } as SentryNuxtModuleOptions);++ const rewriteSources = options.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();+ expect(rewriteSources).toBe(rewriteSourcesSpy);++ const testPaths = [+ 'src/pages/index.vue',+ 'src/components/Button.vue',+ 'lib/utils/helpers.ts',+ 'src/composables/useAuth.ts',+ ];++ testPaths.forEach(path => {+ rewriteSources?.(path);+ });++ expect(rewriteSourcesSpy).toHaveBeenCalledTimes(4);+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(1, 'src/pages/index.vue');+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(2, 'src/components/Button.vue');+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(3, 'lib/utils/helpers.ts');+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(4, 'src/composables/useAuth.ts');++ expect(rewriteSourcesSpy.mock.results[0]?.value).toBe('custom/pages/index.vue');+ expect(rewriteSourcesSpy.mock.results[1]?.value).toBe('custom/components/Button.vue');+ expect(rewriteSourcesSpy.mock.results[2]?.value).toBe('lib/utils/helpers.ts');+ expect(rewriteSourcesSpy.mock.results[3]?.value).toBe('custom/composables/useAuth.ts');+ });++ it('invokes default rewriteSources function to normalize paths', () => {+ const options = getPluginOptions({} as SentryNuxtModuleOptions);++ const rewriteSources = options.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();++ const paths = ['../../../foo/bar', '../../components/Layout.vue', './local', '../utils/api.ts'];++ const rewrittenPaths = paths.map(path => rewriteSources?.(path));++ expect(rewrittenPaths).toEqual(['./foo/bar', './components/Layout.vue', './local', './utils/api.ts']);+ });++ it('preserves rewriteSources function reference across configuration', () => {+ const customRewrite = (source: string) => source.replace(/^prefix\//, '');++ const options = getPluginOptions({+ sourcemaps: {+ rewriteSources: customRewrite,+ },+ } as SentryNuxtModuleOptions);++ expect(options.sourcemaps?.rewriteSources).toBe(customRewrite);++ const testSource = 'prefix/src/app.vue';+ const result = options.sourcemaps?.rewriteSources?.(testSource);+ expect(result).toBe('src/app.vue');+ });+});diff --git a/packages/tanstackstart-react/test/vite/rewriteSources.integration.test.ts b/packages/tanstackstart-react/test/vite/rewriteSources.integration.test.ts
new file mode 100644
--- /dev/null+++ b/packages/tanstackstart-react/test/vite/rewriteSources.integration.test.ts@@ -1,0 +1,83 @@+import { describe, it, expect, vi } from 'vitest';++const sentryVitePluginSpy = vi.fn(() => []);++vi.mock('@sentry/vite-plugin', () => ({+ sentryVitePlugin: vi.fn(() => []),+}));++import { makeAddSentryVitePlugin } from '../../src/vite/sourceMaps';+import { sentryVitePlugin } from '@sentry/vite-plugin';++vi.mocked(sentryVitePlugin).mockImplementation(sentryVitePluginSpy);++describe('rewriteSources integration', () => {+ it('invokes custom rewriteSources function with source paths', () => {+ const rewriteSourcesSpy = vi.fn((source: string) => {+ return source.replace(/^src\//, '');+ });++ makeAddSentryVitePlugin({+ org: 'my-org',+ authToken: 'my-token',+ sourcemaps: {+ rewriteSources: rewriteSourcesSpy,+ },+ });++ expect(sentryVitePluginSpy).toHaveBeenCalledWith(+ expect.objectContaining({+ sourcemaps: expect.objectContaining({+ rewriteSources: rewriteSourcesSpy,+ }),+ }),+ );++ const passedOptions = sentryVitePluginSpy.mock.calls[0]?.[0];+ const rewriteSources = passedOptions?.sourcemaps?.rewriteSources;++ expect(rewriteSources).toBe(rewriteSourcesSpy);++ const testPaths = ['src/routes/index.tsx', 'src/components/Button.tsx', 'lib/utils.ts', 'src/api/client.ts'];++ testPaths.forEach(path => {+ rewriteSources?.(path, {});+ });++ expect(rewriteSourcesSpy).toHaveBeenCalledTimes(4);+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(1, 'src/routes/index.tsx', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(2, 'src/components/Button.tsx', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(3, 'lib/utils.ts', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(4, 'src/api/client.ts', {});++ expect(rewriteSourcesSpy.mock.results[0]?.value).toBe('routes/index.tsx');+ expect(rewriteSourcesSpy.mock.results[1]?.value).toBe('components/Button.tsx');+ expect(rewriteSourcesSpy.mock.results[2]?.value).toBe('lib/utils.ts');+ expect(rewriteSourcesSpy.mock.results[3]?.value).toBe('api/client.ts');+ });++ it('preserves rewriteSources function reference in plugin options', () => {+ const customRewrite = (source: string) => source.replace(/^prefix\//, '');++ sentryVitePluginSpy.mockClear();++ makeAddSentryVitePlugin({+ org: 'my-org',+ authToken: 'my-token',+ sourcemaps: {+ rewriteSources: customRewrite,+ },+ });++ expect(sentryVitePluginSpy).toHaveBeenCalledWith(+ expect.objectContaining({+ sourcemaps: expect.objectContaining({+ rewriteSources: customRewrite,+ }),+ }),+ );++ const passedOptions = sentryVitePluginSpy.mock.calls[0]?.[0];+ expect(passedOptions?.sourcemaps?.rewriteSources).toBe(customRewrite);+ });+});

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2722abb. Configure here.

Comment threadpackages/nextjs/test/config/getBuildPluginOptions.test.ts
@chargome
chargome merged commit 13dc7a1 into developApr 14, 2026
467 of 477 checks passed
@chargome
chargome deleted the cg/update-rewrite-sources branch April 14, 2026 12:27
mydea pushed a commit that referenced this pull request Apr 15, 2026
- Adds `rewriteSources` to the base `SourceMapsOptions` interface so
users can customize source path rewriting without the `unstable_*`
escape hatch
- Wires up the option in Nuxt, Next.js, and TanStack Start (SvelteKit,
Astro, React Router already pass it through via spread)
- Nuxt and Next.js preserve their default rewriting behavior when the
option is not provided
bundler plugins ref
getsentry/sentry-javascript-bundler-plugins#908closes#20028
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.

Expose source map path/dir to rewriteSources hook

4 participants

@chargome@Lms24@nicohrubec@s1gr1d
, '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(core): Expose rewriteSources top level option - #20142

Merged
chargome merged 2 commits into
developfrom
cg/update-rewrite-sources
Apr 14, 2026
Merged

feat(core): Expose rewriteSources top level option #20142
chargome merged 2 commits into
developfrom
cg/update-rewrite-sources

Conversation

@chargome

@chargomechargome commented Apr 9, 2026

Copy link
Copy Markdown
Member
  • Adds rewriteSources to the base SourceMapsOptions interface so users can customize source path rewriting without the unstable_* escape hatch
  • Wires up the option in Nuxt, Next.js, and TanStack Start (SvelteKit, Astro, React Router already pass it through via spread)
  • Nuxt and Next.js preserve their default rewriting behavior when the option is not provided

bundler plugins ref getsentry/sentry-javascript-bundler-plugins#908
closes#20028

@chargomechargome self-assigned this Apr 9, 2026
@github-actions

github-actionsBot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

Cloudflare

  • Split alarms into multiple traces and link them by JPeer264 in #19373
  • Propagate traceparent to RPC calls - via fetch by JPeer264 in #19991

Core

  • Expose rewriteSources top level option by chargome in #20142
  • Automatically disable truncation when span streaming is enabled in LangGraph integration by andreiborza in #20231
  • Automatically disable truncation when span streaming is enabled in LangChain integration by andreiborza in #20230
  • Automatically disable truncation when span streaming is enabled in Google GenAI integration by andreiborza in #20229
  • Automatically disable truncation when span streaming is enabled in Anthropic AI integration by andreiborza in #20228
  • Automatically disable truncation when span streaming is enabled in Vercel AI integration by andreiborza in #20232
  • Automatically disable truncation when span streaming is enabled in OpenAI integration by andreiborza in #20227
  • Add enableTruncation option to Vercel AI integration by nicohrubec in #20195
  • Add enableTruncation option to Google GenAI integration by andreiborza in #20184
  • Add enableTruncation option to Anthropic AI integration by andreiborza in #20181
  • Add enableTruncation option to LangGraph integration by andreiborza in #20183
  • Add enableTruncation option to LangChain integration by andreiborza in #20182
  • Add enableTruncation option to OpenAI integration by andreiborza in #20167
  • Export a reusable function to add tracing headers by JPeer264 in #20076

Deps

  • Bump axios from 1.13.5 to 1.15.0 by dependabot in #20180
  • Bump hono from 4.12.7 to 4.12.12 by dependabot in #20118
  • Bump defu from 6.1.4 to 6.1.6 by dependabot in #20104

Other

  • (browser) Add View Hierarchy integration by timfish in #14981
  • (cloudflare,deno,vercel-edge) Add span streaming support by Lms24 in #20127
  • (node) Include global scope for eventLoopBlockIntegration by timfish in #20108
  • (node-native) Add support for V8 v14 (Node v25+) by timfish in #20125
  • (opentelemetry) Vendor AsyncLocalStorageContextManager by mydea in #20243

Bug Fixes 🐛

Deno

  • Handle reader.closed rejection from releaseLock() in streaming by andreiborza in #20187
  • Avoid inferring invalid span op from Deno tracer by Lms24 in #20128

Other

  • (ci) Prevent command injection in ci-metadata workflow by fix-it-felix-sentry in #19899
  • (core, node) Support loading Express options lazily by isaacs in #20211
  • (e2e) Add op check to waitForTransaction in React Router e2e tests by copilot-swe-agent in #20193
  • (e2e-tests) Remove flaky navigation breadcrumb assertions from parameterized-routes tests by copilot-swe-agent in #20202
  • (node-integration-tests) Fix flaky kafkajs test race condition by copilot-swe-agent in #20189

Internal Changes 🔧

Ci

  • Remove node-overhead GitHub Action by mydea in #20246
  • Bump dorny/paths-filter from v3.0.1 to v4.0.1 by mydea in #20251
  • Remove codecov steps from jobs that produce no coverage/JUnit data by mydea in #20244

Deps

  • Bump hono from 4.12.7 to 4.12.12 in /dev-packages/e2e-tests/test-applications/cloudflare-hono by dependabot in #20119
  • Bump axios from 1.13.5 to 1.15.0 in /dev-packages/e2e-tests/test-applications/nestjs-basic by dependabot in #20179

Deps Dev

  • Bump @sveltejs/kit from 2.53.3 to 2.57.1 by dependabot in #20216
  • Bump vite from 7.2.0 to 7.3.2 in /dev-packages/e2e-tests/test-applications/tanstackstart-react by dependabot in #20107

Other

  • (bugbot) Add rules to flag test-flake-provoking patterns by Lms24 in #20192
  • (react) Remove duplicated test mock by s1gr1d in #20200
  • (size-limit) Bump failing size limit scenario by Lms24 in #20186
  • Add PR review reminder workflow by copilot-swe-agent in #20175
  • Fix lint warnings by mydea in #20250
  • Fix flaky ANR test by increasing blocking duration by JPeer264 in #20239
  • Add automatic flaky test detector by nicohrubec in #18684

🤖 This preview updates automatically when you update the PR.

@github-actions

github-actionsBot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser25.73 kB--
@sentry/browser - with treeshaking flags24.22 kB--
@sentry/browser (incl. Tracing)42.72 kB--
@sentry/browser (incl. Tracing, Profiling)47.35 kB--
@sentry/browser (incl. Tracing, Replay)81.53 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags71.1 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)86.23 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)98.44 kB--
@sentry/browser (incl. Feedback)42.52 kB--
@sentry/browser (incl. sendFeedback)30.39 kB--
@sentry/browser (incl. FeedbackAsync)35.39 kB--
@sentry/browser (incl. Metrics)27.04 kB--
@sentry/browser (incl. Logs)27.19 kB--
@sentry/browser (incl. Metrics & Logs)27.86 kB--
@sentry/react27.48 kB--
@sentry/react (incl. Tracing)45.05 kB--
@sentry/vue30.56 kB--
@sentry/vue (incl. Tracing)44.58 kB--
@sentry/svelte25.75 kB--
CDN Bundle28.41 kB--
CDN Bundle (incl. Tracing)43.76 kB--
CDN Bundle (incl. Logs, Metrics)29.79 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)44.83 kB--
CDN Bundle (incl. Replay, Logs, Metrics)68.6 kB--
CDN Bundle (incl. Tracing, Replay)80.63 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)81.66 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)86.16 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)87.19 kB--
CDN Bundle - uncompressed83 kB--
CDN Bundle (incl. Tracing) - uncompressed129.79 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed87.14 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed133.2 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed210.13 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed246.67 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed250.07 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed259.58 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed262.97 kB--
@sentry/nextjs (client)47.47 kB--
@sentry/sveltekit (client)43.2 kB--
@sentry/node-core57.86 kB+0.02%+7 B 🔺
@sentry/node174.8 kB+0.01%+15 B 🔺
@sentry/node - without tracing97.63 kB+0.02%+11 B 🔺
@sentry/aws-serverless114.98 kB+0.01%+7 B 🔺

View base workflow run

@github-actions

Copy link
Copy Markdown
Contributor

node-overhead report 🧳

Note: This is a synthetic benchmark with a minimal express app and does not necessarily reflect the real-world performance impact in an application.

ScenarioRequests/s% of BaselinePrev. Requests/sChange %
GET Baseline9,450-9,316+1%
GET With Sentry1,83419%1,744+5%
GET With Sentry (error only)6,10765%5,995+2%
POST Baseline1,211-1,211-
POST With Sentry62852%624+1%
POST With Sentry (error only)1,06388%1,056+1%
MYSQL Baseline3,260-3,337-2%
MYSQL With Sentry52016%499+4%
MYSQL With Sentry (error only)2,68682%2,666+1%

View base workflow run

@chargome
chargome marked this pull request as ready for review April 9, 2026 11:08

@Lms24Lms24 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for adding this!
Just to confirm: We already bumped the plugins to the supported version? And sveltekit uses the interface from core directly?

Comment threadpackages/nuxt/src/vite/sourceMaps.ts Outdated

@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 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: No integration or E2E test for feature
    • Added integration tests in nextjs, nuxt, and tanstackstart-react packages that verify user-provided rewriteSources hooks are invoked with proper arguments and return correct results during the source map configuration flow.

Create PR

Or push these changes by commenting:

@cursor push 0f5c31dc99
Preview (0f5c31dc99)
diff --git a/packages/nextjs/test/config/rewriteSources.integration.test.ts b/packages/nextjs/test/config/rewriteSources.integration.test.ts
new file mode 100644
--- /dev/null+++ b/packages/nextjs/test/config/rewriteSources.integration.test.ts@@ -1,0 +1,118 @@+import { describe, it, expect, vi } from 'vitest';+import { getBuildPluginOptions } from '../../src/config/getBuildPluginOptions';+import type { SentryBuildOptions } from '../../src/config/types';++const mockReleaseName = 'test-release';+const mockDistDirAbsPath = '/test/project/.next';++describe('rewriteSources integration', () => {+ it('invokes custom rewriteSources function with source paths', () => {+ const rewriteSourcesSpy = vi.fn((source: string) => {+ return source.replace(/^custom\//, 'rewritten/');+ });++ const sentryBuildOptions: SentryBuildOptions = {+ org: 'test-org',+ project: 'test-project',+ sourcemaps: {+ rewriteSources: rewriteSourcesSpy,+ },+ };++ const result = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-client',+ });++ const rewriteSources = result.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();+ expect(rewriteSources).toBe(rewriteSourcesSpy);++ const testPaths = [+ 'custom/src/pages/index.js',+ 'custom/components/Button.tsx',+ 'src/utils/helpers.js',+ 'custom/lib/api.ts',+ ];++ testPaths.forEach(path => {+ rewriteSources?.(path, {});+ });++ expect(rewriteSourcesSpy).toHaveBeenCalledTimes(4);+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(1, 'custom/src/pages/index.js', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(2, 'custom/components/Button.tsx', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(3, 'src/utils/helpers.js', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(4, 'custom/lib/api.ts', {});++ expect(rewriteSourcesSpy.mock.results[0]?.value).toBe('rewritten/src/pages/index.js');+ expect(rewriteSourcesSpy.mock.results[1]?.value).toBe('rewritten/components/Button.tsx');+ expect(rewriteSourcesSpy.mock.results[2]?.value).toBe('src/utils/helpers.js');+ expect(rewriteSourcesSpy.mock.results[3]?.value).toBe('rewritten/lib/api.ts');+ });++ it('invokes default rewriteSources function for webpack sources', () => {+ const sentryBuildOptions: SentryBuildOptions = {+ org: 'test-org',+ project: 'test-project',+ };++ const result = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-client',+ });++ const rewriteSources = result.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();++ const webpackPaths = [+ 'webpack://_N_E/src/pages/index.js',+ 'webpack://project/src/components/Button.js',+ 'src/utils/helpers.js',+ 'webpack://_N_E/./app/layout.tsx',+ ];++ const rewrittenPaths = webpackPaths.map(path => rewriteSources?.(path, {}));++ expect(rewrittenPaths).toEqual([+ 'src/pages/index.js',+ 'project/src/components/Button.js',+ 'src/utils/helpers.js',+ './app/layout.tsx',+ ]);+ });++ it('preserves rewriteSources function reference across multiple builds', () => {+ const customRewrite = (source: string) => source.replace(/^prefix\//, '');++ const sentryBuildOptions: SentryBuildOptions = {+ org: 'test-org',+ project: 'test-project',+ sourcemaps: {+ rewriteSources: customRewrite,+ },+ };++ const clientResult = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-client',+ });++ const serverResult = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-nodejs',+ });++ expect(clientResult.sourcemaps?.rewriteSources).toBe(customRewrite);+ expect(serverResult.sourcemaps?.rewriteSources).toBe(customRewrite);+ expect(clientResult.sourcemaps?.rewriteSources).toBe(serverResult.sourcemaps?.rewriteSources);+ });+});diff --git a/packages/nuxt/test/vite/rewriteSources.integration.test.ts b/packages/nuxt/test/vite/rewriteSources.integration.test.ts
new file mode 100644
--- /dev/null+++ b/packages/nuxt/test/vite/rewriteSources.integration.test.ts@@ -1,0 +1,72 @@+import { describe, it, expect, vi } from 'vitest';+import { getPluginOptions } from '../../src/vite/sourceMaps';+import type { SentryNuxtModuleOptions } from '../../src/common/types';++describe('rewriteSources integration', () => {+ it('invokes custom rewriteSources function with source paths', () => {+ const rewriteSourcesSpy = vi.fn((source: string) => {+ return source.replace(/^src\//, 'custom/');+ });++ const options = getPluginOptions({+ sourcemaps: {+ rewriteSources: rewriteSourcesSpy,+ },+ } as SentryNuxtModuleOptions);++ const rewriteSources = options.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();+ expect(rewriteSources).toBe(rewriteSourcesSpy);++ const testPaths = [+ 'src/pages/index.vue',+ 'src/components/Button.vue',+ 'lib/utils/helpers.ts',+ 'src/composables/useAuth.ts',+ ];++ testPaths.forEach(path => {+ rewriteSources?.(path);+ });++ expect(rewriteSourcesSpy).toHaveBeenCalledTimes(4);+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(1, 'src/pages/index.vue');+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(2, 'src/components/Button.vue');+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(3, 'lib/utils/helpers.ts');+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(4, 'src/composables/useAuth.ts');++ expect(rewriteSourcesSpy.mock.results[0]?.value).toBe('custom/pages/index.vue');+ expect(rewriteSourcesSpy.mock.results[1]?.value).toBe('custom/components/Button.vue');+ expect(rewriteSourcesSpy.mock.results[2]?.value).toBe('lib/utils/helpers.ts');+ expect(rewriteSourcesSpy.mock.results[3]?.value).toBe('custom/composables/useAuth.ts');+ });++ it('invokes default rewriteSources function to normalize paths', () => {+ const options = getPluginOptions({} as SentryNuxtModuleOptions);++ const rewriteSources = options.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();++ const paths = ['../../../foo/bar', '../../components/Layout.vue', './local', '../utils/api.ts'];++ const rewrittenPaths = paths.map(path => rewriteSources?.(path));++ expect(rewrittenPaths).toEqual(['./foo/bar', './components/Layout.vue', './local', './utils/api.ts']);+ });++ it('preserves rewriteSources function reference across configuration', () => {+ const customRewrite = (source: string) => source.replace(/^prefix\//, '');++ const options = getPluginOptions({+ sourcemaps: {+ rewriteSources: customRewrite,+ },+ } as SentryNuxtModuleOptions);++ expect(options.sourcemaps?.rewriteSources).toBe(customRewrite);++ const testSource = 'prefix/src/app.vue';+ const result = options.sourcemaps?.rewriteSources?.(testSource);+ expect(result).toBe('src/app.vue');+ });+});diff --git a/packages/tanstackstart-react/test/vite/rewriteSources.integration.test.ts b/packages/tanstackstart-react/test/vite/rewriteSources.integration.test.ts
new file mode 100644
--- /dev/null+++ b/packages/tanstackstart-react/test/vite/rewriteSources.integration.test.ts@@ -1,0 +1,83 @@+import { describe, it, expect, vi } from 'vitest';++const sentryVitePluginSpy = vi.fn(() => []);++vi.mock('@sentry/vite-plugin', () => ({+ sentryVitePlugin: vi.fn(() => []),+}));++import { makeAddSentryVitePlugin } from '../../src/vite/sourceMaps';+import { sentryVitePlugin } from '@sentry/vite-plugin';++vi.mocked(sentryVitePlugin).mockImplementation(sentryVitePluginSpy);++describe('rewriteSources integration', () => {+ it('invokes custom rewriteSources function with source paths', () => {+ const rewriteSourcesSpy = vi.fn((source: string) => {+ return source.replace(/^src\//, '');+ });++ makeAddSentryVitePlugin({+ org: 'my-org',+ authToken: 'my-token',+ sourcemaps: {+ rewriteSources: rewriteSourcesSpy,+ },+ });++ expect(sentryVitePluginSpy).toHaveBeenCalledWith(+ expect.objectContaining({+ sourcemaps: expect.objectContaining({+ rewriteSources: rewriteSourcesSpy,+ }),+ }),+ );++ const passedOptions = sentryVitePluginSpy.mock.calls[0]?.[0];+ const rewriteSources = passedOptions?.sourcemaps?.rewriteSources;++ expect(rewriteSources).toBe(rewriteSourcesSpy);++ const testPaths = ['src/routes/index.tsx', 'src/components/Button.tsx', 'lib/utils.ts', 'src/api/client.ts'];++ testPaths.forEach(path => {+ rewriteSources?.(path, {});+ });++ expect(rewriteSourcesSpy).toHaveBeenCalledTimes(4);+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(1, 'src/routes/index.tsx', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(2, 'src/components/Button.tsx', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(3, 'lib/utils.ts', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(4, 'src/api/client.ts', {});++ expect(rewriteSourcesSpy.mock.results[0]?.value).toBe('routes/index.tsx');+ expect(rewriteSourcesSpy.mock.results[1]?.value).toBe('components/Button.tsx');+ expect(rewriteSourcesSpy.mock.results[2]?.value).toBe('lib/utils.ts');+ expect(rewriteSourcesSpy.mock.results[3]?.value).toBe('api/client.ts');+ });++ it('preserves rewriteSources function reference in plugin options', () => {+ const customRewrite = (source: string) => source.replace(/^prefix\//, '');++ sentryVitePluginSpy.mockClear();++ makeAddSentryVitePlugin({+ org: 'my-org',+ authToken: 'my-token',+ sourcemaps: {+ rewriteSources: customRewrite,+ },+ });++ expect(sentryVitePluginSpy).toHaveBeenCalledWith(+ expect.objectContaining({+ sourcemaps: expect.objectContaining({+ rewriteSources: customRewrite,+ }),+ }),+ );++ const passedOptions = sentryVitePluginSpy.mock.calls[0]?.[0];+ expect(passedOptions?.sourcemaps?.rewriteSources).toBe(customRewrite);+ });+});

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2722abb. Configure here.

Comment threadpackages/nextjs/test/config/getBuildPluginOptions.test.ts
@chargome
chargome merged commit 13dc7a1 into developApr 14, 2026
467 of 477 checks passed
@chargome
chargome deleted the cg/update-rewrite-sources branch April 14, 2026 12:27
mydea pushed a commit that referenced this pull request Apr 15, 2026
- Adds `rewriteSources` to the base `SourceMapsOptions` interface so
users can customize source path rewriting without the `unstable_*`
escape hatch
- Wires up the option in Nuxt, Next.js, and TanStack Start (SvelteKit,
Astro, React Router already pass it through via spread)
- Nuxt and Next.js preserve their default rewriting behavior when the
option is not provided
bundler plugins ref
getsentry/sentry-javascript-bundler-plugins#908closes#20028
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.

Expose source map path/dir to rewriteSources hook

4 participants

@chargome@Lms24@nicohrubec@s1gr1d
, '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(core): Expose rewriteSources top level option - #20142

Merged
chargome merged 2 commits into
developfrom
cg/update-rewrite-sources
Apr 14, 2026
Merged

feat(core): Expose rewriteSources top level option #20142
chargome merged 2 commits into
developfrom
cg/update-rewrite-sources

Conversation

@chargome

@chargomechargome commented Apr 9, 2026

Copy link
Copy Markdown
Member
  • Adds rewriteSources to the base SourceMapsOptions interface so users can customize source path rewriting without the unstable_* escape hatch
  • Wires up the option in Nuxt, Next.js, and TanStack Start (SvelteKit, Astro, React Router already pass it through via spread)
  • Nuxt and Next.js preserve their default rewriting behavior when the option is not provided

bundler plugins ref getsentry/sentry-javascript-bundler-plugins#908
closes#20028

@chargomechargome self-assigned this Apr 9, 2026
@github-actions

github-actionsBot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

Cloudflare

  • Split alarms into multiple traces and link them by JPeer264 in #19373
  • Propagate traceparent to RPC calls - via fetch by JPeer264 in #19991

Core

  • Expose rewriteSources top level option by chargome in #20142
  • Automatically disable truncation when span streaming is enabled in LangGraph integration by andreiborza in #20231
  • Automatically disable truncation when span streaming is enabled in LangChain integration by andreiborza in #20230
  • Automatically disable truncation when span streaming is enabled in Google GenAI integration by andreiborza in #20229
  • Automatically disable truncation when span streaming is enabled in Anthropic AI integration by andreiborza in #20228
  • Automatically disable truncation when span streaming is enabled in Vercel AI integration by andreiborza in #20232
  • Automatically disable truncation when span streaming is enabled in OpenAI integration by andreiborza in #20227
  • Add enableTruncation option to Vercel AI integration by nicohrubec in #20195
  • Add enableTruncation option to Google GenAI integration by andreiborza in #20184
  • Add enableTruncation option to Anthropic AI integration by andreiborza in #20181
  • Add enableTruncation option to LangGraph integration by andreiborza in #20183
  • Add enableTruncation option to LangChain integration by andreiborza in #20182
  • Add enableTruncation option to OpenAI integration by andreiborza in #20167
  • Export a reusable function to add tracing headers by JPeer264 in #20076

Deps

  • Bump axios from 1.13.5 to 1.15.0 by dependabot in #20180
  • Bump hono from 4.12.7 to 4.12.12 by dependabot in #20118
  • Bump defu from 6.1.4 to 6.1.6 by dependabot in #20104

Other

  • (browser) Add View Hierarchy integration by timfish in #14981
  • (cloudflare,deno,vercel-edge) Add span streaming support by Lms24 in #20127
  • (node) Include global scope for eventLoopBlockIntegration by timfish in #20108
  • (node-native) Add support for V8 v14 (Node v25+) by timfish in #20125
  • (opentelemetry) Vendor AsyncLocalStorageContextManager by mydea in #20243

Bug Fixes 🐛

Deno

  • Handle reader.closed rejection from releaseLock() in streaming by andreiborza in #20187
  • Avoid inferring invalid span op from Deno tracer by Lms24 in #20128

Other

  • (ci) Prevent command injection in ci-metadata workflow by fix-it-felix-sentry in #19899
  • (core, node) Support loading Express options lazily by isaacs in #20211
  • (e2e) Add op check to waitForTransaction in React Router e2e tests by copilot-swe-agent in #20193
  • (e2e-tests) Remove flaky navigation breadcrumb assertions from parameterized-routes tests by copilot-swe-agent in #20202
  • (node-integration-tests) Fix flaky kafkajs test race condition by copilot-swe-agent in #20189

Internal Changes 🔧

Ci

  • Remove node-overhead GitHub Action by mydea in #20246
  • Bump dorny/paths-filter from v3.0.1 to v4.0.1 by mydea in #20251
  • Remove codecov steps from jobs that produce no coverage/JUnit data by mydea in #20244

Deps

  • Bump hono from 4.12.7 to 4.12.12 in /dev-packages/e2e-tests/test-applications/cloudflare-hono by dependabot in #20119
  • Bump axios from 1.13.5 to 1.15.0 in /dev-packages/e2e-tests/test-applications/nestjs-basic by dependabot in #20179

Deps Dev

  • Bump @sveltejs/kit from 2.53.3 to 2.57.1 by dependabot in #20216
  • Bump vite from 7.2.0 to 7.3.2 in /dev-packages/e2e-tests/test-applications/tanstackstart-react by dependabot in #20107

Other

  • (bugbot) Add rules to flag test-flake-provoking patterns by Lms24 in #20192
  • (react) Remove duplicated test mock by s1gr1d in #20200
  • (size-limit) Bump failing size limit scenario by Lms24 in #20186
  • Add PR review reminder workflow by copilot-swe-agent in #20175
  • Fix lint warnings by mydea in #20250
  • Fix flaky ANR test by increasing blocking duration by JPeer264 in #20239
  • Add automatic flaky test detector by nicohrubec in #18684

🤖 This preview updates automatically when you update the PR.

@github-actions

github-actionsBot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser25.73 kB--
@sentry/browser - with treeshaking flags24.22 kB--
@sentry/browser (incl. Tracing)42.72 kB--
@sentry/browser (incl. Tracing, Profiling)47.35 kB--
@sentry/browser (incl. Tracing, Replay)81.53 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags71.1 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)86.23 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)98.44 kB--
@sentry/browser (incl. Feedback)42.52 kB--
@sentry/browser (incl. sendFeedback)30.39 kB--
@sentry/browser (incl. FeedbackAsync)35.39 kB--
@sentry/browser (incl. Metrics)27.04 kB--
@sentry/browser (incl. Logs)27.19 kB--
@sentry/browser (incl. Metrics & Logs)27.86 kB--
@sentry/react27.48 kB--
@sentry/react (incl. Tracing)45.05 kB--
@sentry/vue30.56 kB--
@sentry/vue (incl. Tracing)44.58 kB--
@sentry/svelte25.75 kB--
CDN Bundle28.41 kB--
CDN Bundle (incl. Tracing)43.76 kB--
CDN Bundle (incl. Logs, Metrics)29.79 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)44.83 kB--
CDN Bundle (incl. Replay, Logs, Metrics)68.6 kB--
CDN Bundle (incl. Tracing, Replay)80.63 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)81.66 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)86.16 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)87.19 kB--
CDN Bundle - uncompressed83 kB--
CDN Bundle (incl. Tracing) - uncompressed129.79 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed87.14 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed133.2 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed210.13 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed246.67 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed250.07 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed259.58 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed262.97 kB--
@sentry/nextjs (client)47.47 kB--
@sentry/sveltekit (client)43.2 kB--
@sentry/node-core57.86 kB+0.02%+7 B 🔺
@sentry/node174.8 kB+0.01%+15 B 🔺
@sentry/node - without tracing97.63 kB+0.02%+11 B 🔺
@sentry/aws-serverless114.98 kB+0.01%+7 B 🔺

View base workflow run

@github-actions

Copy link
Copy Markdown
Contributor

node-overhead report 🧳

Note: This is a synthetic benchmark with a minimal express app and does not necessarily reflect the real-world performance impact in an application.

ScenarioRequests/s% of BaselinePrev. Requests/sChange %
GET Baseline9,450-9,316+1%
GET With Sentry1,83419%1,744+5%
GET With Sentry (error only)6,10765%5,995+2%
POST Baseline1,211-1,211-
POST With Sentry62852%624+1%
POST With Sentry (error only)1,06388%1,056+1%
MYSQL Baseline3,260-3,337-2%
MYSQL With Sentry52016%499+4%
MYSQL With Sentry (error only)2,68682%2,666+1%

View base workflow run

@chargome
chargome marked this pull request as ready for review April 9, 2026 11:08

@Lms24Lms24 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for adding this!
Just to confirm: We already bumped the plugins to the supported version? And sveltekit uses the interface from core directly?

Comment threadpackages/nuxt/src/vite/sourceMaps.ts Outdated

@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 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: No integration or E2E test for feature
    • Added integration tests in nextjs, nuxt, and tanstackstart-react packages that verify user-provided rewriteSources hooks are invoked with proper arguments and return correct results during the source map configuration flow.

Create PR

Or push these changes by commenting:

@cursor push 0f5c31dc99
Preview (0f5c31dc99)
diff --git a/packages/nextjs/test/config/rewriteSources.integration.test.ts b/packages/nextjs/test/config/rewriteSources.integration.test.ts
new file mode 100644
--- /dev/null+++ b/packages/nextjs/test/config/rewriteSources.integration.test.ts@@ -1,0 +1,118 @@+import { describe, it, expect, vi } from 'vitest';+import { getBuildPluginOptions } from '../../src/config/getBuildPluginOptions';+import type { SentryBuildOptions } from '../../src/config/types';++const mockReleaseName = 'test-release';+const mockDistDirAbsPath = '/test/project/.next';++describe('rewriteSources integration', () => {+ it('invokes custom rewriteSources function with source paths', () => {+ const rewriteSourcesSpy = vi.fn((source: string) => {+ return source.replace(/^custom\//, 'rewritten/');+ });++ const sentryBuildOptions: SentryBuildOptions = {+ org: 'test-org',+ project: 'test-project',+ sourcemaps: {+ rewriteSources: rewriteSourcesSpy,+ },+ };++ const result = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-client',+ });++ const rewriteSources = result.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();+ expect(rewriteSources).toBe(rewriteSourcesSpy);++ const testPaths = [+ 'custom/src/pages/index.js',+ 'custom/components/Button.tsx',+ 'src/utils/helpers.js',+ 'custom/lib/api.ts',+ ];++ testPaths.forEach(path => {+ rewriteSources?.(path, {});+ });++ expect(rewriteSourcesSpy).toHaveBeenCalledTimes(4);+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(1, 'custom/src/pages/index.js', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(2, 'custom/components/Button.tsx', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(3, 'src/utils/helpers.js', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(4, 'custom/lib/api.ts', {});++ expect(rewriteSourcesSpy.mock.results[0]?.value).toBe('rewritten/src/pages/index.js');+ expect(rewriteSourcesSpy.mock.results[1]?.value).toBe('rewritten/components/Button.tsx');+ expect(rewriteSourcesSpy.mock.results[2]?.value).toBe('src/utils/helpers.js');+ expect(rewriteSourcesSpy.mock.results[3]?.value).toBe('rewritten/lib/api.ts');+ });++ it('invokes default rewriteSources function for webpack sources', () => {+ const sentryBuildOptions: SentryBuildOptions = {+ org: 'test-org',+ project: 'test-project',+ };++ const result = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-client',+ });++ const rewriteSources = result.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();++ const webpackPaths = [+ 'webpack://_N_E/src/pages/index.js',+ 'webpack://project/src/components/Button.js',+ 'src/utils/helpers.js',+ 'webpack://_N_E/./app/layout.tsx',+ ];++ const rewrittenPaths = webpackPaths.map(path => rewriteSources?.(path, {}));++ expect(rewrittenPaths).toEqual([+ 'src/pages/index.js',+ 'project/src/components/Button.js',+ 'src/utils/helpers.js',+ './app/layout.tsx',+ ]);+ });++ it('preserves rewriteSources function reference across multiple builds', () => {+ const customRewrite = (source: string) => source.replace(/^prefix\//, '');++ const sentryBuildOptions: SentryBuildOptions = {+ org: 'test-org',+ project: 'test-project',+ sourcemaps: {+ rewriteSources: customRewrite,+ },+ };++ const clientResult = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-client',+ });++ const serverResult = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-nodejs',+ });++ expect(clientResult.sourcemaps?.rewriteSources).toBe(customRewrite);+ expect(serverResult.sourcemaps?.rewriteSources).toBe(customRewrite);+ expect(clientResult.sourcemaps?.rewriteSources).toBe(serverResult.sourcemaps?.rewriteSources);+ });+});diff --git a/packages/nuxt/test/vite/rewriteSources.integration.test.ts b/packages/nuxt/test/vite/rewriteSources.integration.test.ts
new file mode 100644
--- /dev/null+++ b/packages/nuxt/test/vite/rewriteSources.integration.test.ts@@ -1,0 +1,72 @@+import { describe, it, expect, vi } from 'vitest';+import { getPluginOptions } from '../../src/vite/sourceMaps';+import type { SentryNuxtModuleOptions } from '../../src/common/types';++describe('rewriteSources integration', () => {+ it('invokes custom rewriteSources function with source paths', () => {+ const rewriteSourcesSpy = vi.fn((source: string) => {+ return source.replace(/^src\//, 'custom/');+ });++ const options = getPluginOptions({+ sourcemaps: {+ rewriteSources: rewriteSourcesSpy,+ },+ } as SentryNuxtModuleOptions);++ const rewriteSources = options.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();+ expect(rewriteSources).toBe(rewriteSourcesSpy);++ const testPaths = [+ 'src/pages/index.vue',+ 'src/components/Button.vue',+ 'lib/utils/helpers.ts',+ 'src/composables/useAuth.ts',+ ];++ testPaths.forEach(path => {+ rewriteSources?.(path);+ });++ expect(rewriteSourcesSpy).toHaveBeenCalledTimes(4);+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(1, 'src/pages/index.vue');+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(2, 'src/components/Button.vue');+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(3, 'lib/utils/helpers.ts');+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(4, 'src/composables/useAuth.ts');++ expect(rewriteSourcesSpy.mock.results[0]?.value).toBe('custom/pages/index.vue');+ expect(rewriteSourcesSpy.mock.results[1]?.value).toBe('custom/components/Button.vue');+ expect(rewriteSourcesSpy.mock.results[2]?.value).toBe('lib/utils/helpers.ts');+ expect(rewriteSourcesSpy.mock.results[3]?.value).toBe('custom/composables/useAuth.ts');+ });++ it('invokes default rewriteSources function to normalize paths', () => {+ const options = getPluginOptions({} as SentryNuxtModuleOptions);++ const rewriteSources = options.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();++ const paths = ['../../../foo/bar', '../../components/Layout.vue', './local', '../utils/api.ts'];++ const rewrittenPaths = paths.map(path => rewriteSources?.(path));++ expect(rewrittenPaths).toEqual(['./foo/bar', './components/Layout.vue', './local', './utils/api.ts']);+ });++ it('preserves rewriteSources function reference across configuration', () => {+ const customRewrite = (source: string) => source.replace(/^prefix\//, '');++ const options = getPluginOptions({+ sourcemaps: {+ rewriteSources: customRewrite,+ },+ } as SentryNuxtModuleOptions);++ expect(options.sourcemaps?.rewriteSources).toBe(customRewrite);++ const testSource = 'prefix/src/app.vue';+ const result = options.sourcemaps?.rewriteSources?.(testSource);+ expect(result).toBe('src/app.vue');+ });+});diff --git a/packages/tanstackstart-react/test/vite/rewriteSources.integration.test.ts b/packages/tanstackstart-react/test/vite/rewriteSources.integration.test.ts
new file mode 100644
--- /dev/null+++ b/packages/tanstackstart-react/test/vite/rewriteSources.integration.test.ts@@ -1,0 +1,83 @@+import { describe, it, expect, vi } from 'vitest';++const sentryVitePluginSpy = vi.fn(() => []);++vi.mock('@sentry/vite-plugin', () => ({+ sentryVitePlugin: vi.fn(() => []),+}));++import { makeAddSentryVitePlugin } from '../../src/vite/sourceMaps';+import { sentryVitePlugin } from '@sentry/vite-plugin';++vi.mocked(sentryVitePlugin).mockImplementation(sentryVitePluginSpy);++describe('rewriteSources integration', () => {+ it('invokes custom rewriteSources function with source paths', () => {+ const rewriteSourcesSpy = vi.fn((source: string) => {+ return source.replace(/^src\//, '');+ });++ makeAddSentryVitePlugin({+ org: 'my-org',+ authToken: 'my-token',+ sourcemaps: {+ rewriteSources: rewriteSourcesSpy,+ },+ });++ expect(sentryVitePluginSpy).toHaveBeenCalledWith(+ expect.objectContaining({+ sourcemaps: expect.objectContaining({+ rewriteSources: rewriteSourcesSpy,+ }),+ }),+ );++ const passedOptions = sentryVitePluginSpy.mock.calls[0]?.[0];+ const rewriteSources = passedOptions?.sourcemaps?.rewriteSources;++ expect(rewriteSources).toBe(rewriteSourcesSpy);++ const testPaths = ['src/routes/index.tsx', 'src/components/Button.tsx', 'lib/utils.ts', 'src/api/client.ts'];++ testPaths.forEach(path => {+ rewriteSources?.(path, {});+ });++ expect(rewriteSourcesSpy).toHaveBeenCalledTimes(4);+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(1, 'src/routes/index.tsx', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(2, 'src/components/Button.tsx', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(3, 'lib/utils.ts', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(4, 'src/api/client.ts', {});++ expect(rewriteSourcesSpy.mock.results[0]?.value).toBe('routes/index.tsx');+ expect(rewriteSourcesSpy.mock.results[1]?.value).toBe('components/Button.tsx');+ expect(rewriteSourcesSpy.mock.results[2]?.value).toBe('lib/utils.ts');+ expect(rewriteSourcesSpy.mock.results[3]?.value).toBe('api/client.ts');+ });++ it('preserves rewriteSources function reference in plugin options', () => {+ const customRewrite = (source: string) => source.replace(/^prefix\//, '');++ sentryVitePluginSpy.mockClear();++ makeAddSentryVitePlugin({+ org: 'my-org',+ authToken: 'my-token',+ sourcemaps: {+ rewriteSources: customRewrite,+ },+ });++ expect(sentryVitePluginSpy).toHaveBeenCalledWith(+ expect.objectContaining({+ sourcemaps: expect.objectContaining({+ rewriteSources: customRewrite,+ }),+ }),+ );++ const passedOptions = sentryVitePluginSpy.mock.calls[0]?.[0];+ expect(passedOptions?.sourcemaps?.rewriteSources).toBe(customRewrite);+ });+});

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2722abb. Configure here.

Comment threadpackages/nextjs/test/config/getBuildPluginOptions.test.ts
@chargome
chargome merged commit 13dc7a1 into developApr 14, 2026
467 of 477 checks passed
@chargome
chargome deleted the cg/update-rewrite-sources branch April 14, 2026 12:27
mydea pushed a commit that referenced this pull request Apr 15, 2026
- Adds `rewriteSources` to the base `SourceMapsOptions` interface so
users can customize source path rewriting without the `unstable_*`
escape hatch
- Wires up the option in Nuxt, Next.js, and TanStack Start (SvelteKit,
Astro, React Router already pass it through via spread)
- Nuxt and Next.js preserve their default rewriting behavior when the
option is not provided
bundler plugins ref
getsentry/sentry-javascript-bundler-plugins#908closes#20028
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.

Expose source map path/dir to rewriteSources hook

4 participants

@chargome@Lms24@nicohrubec@s1gr1d
, '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(core): Expose rewriteSources top level option - #20142

Merged
chargome merged 2 commits into
developfrom
cg/update-rewrite-sources
Apr 14, 2026
Merged

feat(core): Expose rewriteSources top level option #20142
chargome merged 2 commits into
developfrom
cg/update-rewrite-sources

Conversation

@chargome

@chargomechargome commented Apr 9, 2026

Copy link
Copy Markdown
Member
  • Adds rewriteSources to the base SourceMapsOptions interface so users can customize source path rewriting without the unstable_* escape hatch
  • Wires up the option in Nuxt, Next.js, and TanStack Start (SvelteKit, Astro, React Router already pass it through via spread)
  • Nuxt and Next.js preserve their default rewriting behavior when the option is not provided

bundler plugins ref getsentry/sentry-javascript-bundler-plugins#908
closes#20028

@chargomechargome self-assigned this Apr 9, 2026
@github-actions

github-actionsBot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

Cloudflare

  • Split alarms into multiple traces and link them by JPeer264 in #19373
  • Propagate traceparent to RPC calls - via fetch by JPeer264 in #19991

Core

  • Expose rewriteSources top level option by chargome in #20142
  • Automatically disable truncation when span streaming is enabled in LangGraph integration by andreiborza in #20231
  • Automatically disable truncation when span streaming is enabled in LangChain integration by andreiborza in #20230
  • Automatically disable truncation when span streaming is enabled in Google GenAI integration by andreiborza in #20229
  • Automatically disable truncation when span streaming is enabled in Anthropic AI integration by andreiborza in #20228
  • Automatically disable truncation when span streaming is enabled in Vercel AI integration by andreiborza in #20232
  • Automatically disable truncation when span streaming is enabled in OpenAI integration by andreiborza in #20227
  • Add enableTruncation option to Vercel AI integration by nicohrubec in #20195
  • Add enableTruncation option to Google GenAI integration by andreiborza in #20184
  • Add enableTruncation option to Anthropic AI integration by andreiborza in #20181
  • Add enableTruncation option to LangGraph integration by andreiborza in #20183
  • Add enableTruncation option to LangChain integration by andreiborza in #20182
  • Add enableTruncation option to OpenAI integration by andreiborza in #20167
  • Export a reusable function to add tracing headers by JPeer264 in #20076

Deps

  • Bump axios from 1.13.5 to 1.15.0 by dependabot in #20180
  • Bump hono from 4.12.7 to 4.12.12 by dependabot in #20118
  • Bump defu from 6.1.4 to 6.1.6 by dependabot in #20104

Other

  • (browser) Add View Hierarchy integration by timfish in #14981
  • (cloudflare,deno,vercel-edge) Add span streaming support by Lms24 in #20127
  • (node) Include global scope for eventLoopBlockIntegration by timfish in #20108
  • (node-native) Add support for V8 v14 (Node v25+) by timfish in #20125
  • (opentelemetry) Vendor AsyncLocalStorageContextManager by mydea in #20243

Bug Fixes 🐛

Deno

  • Handle reader.closed rejection from releaseLock() in streaming by andreiborza in #20187
  • Avoid inferring invalid span op from Deno tracer by Lms24 in #20128

Other

  • (ci) Prevent command injection in ci-metadata workflow by fix-it-felix-sentry in #19899
  • (core, node) Support loading Express options lazily by isaacs in #20211
  • (e2e) Add op check to waitForTransaction in React Router e2e tests by copilot-swe-agent in #20193
  • (e2e-tests) Remove flaky navigation breadcrumb assertions from parameterized-routes tests by copilot-swe-agent in #20202
  • (node-integration-tests) Fix flaky kafkajs test race condition by copilot-swe-agent in #20189

Internal Changes 🔧

Ci

  • Remove node-overhead GitHub Action by mydea in #20246
  • Bump dorny/paths-filter from v3.0.1 to v4.0.1 by mydea in #20251
  • Remove codecov steps from jobs that produce no coverage/JUnit data by mydea in #20244

Deps

  • Bump hono from 4.12.7 to 4.12.12 in /dev-packages/e2e-tests/test-applications/cloudflare-hono by dependabot in #20119
  • Bump axios from 1.13.5 to 1.15.0 in /dev-packages/e2e-tests/test-applications/nestjs-basic by dependabot in #20179

Deps Dev

  • Bump @sveltejs/kit from 2.53.3 to 2.57.1 by dependabot in #20216
  • Bump vite from 7.2.0 to 7.3.2 in /dev-packages/e2e-tests/test-applications/tanstackstart-react by dependabot in #20107

Other

  • (bugbot) Add rules to flag test-flake-provoking patterns by Lms24 in #20192
  • (react) Remove duplicated test mock by s1gr1d in #20200
  • (size-limit) Bump failing size limit scenario by Lms24 in #20186
  • Add PR review reminder workflow by copilot-swe-agent in #20175
  • Fix lint warnings by mydea in #20250
  • Fix flaky ANR test by increasing blocking duration by JPeer264 in #20239
  • Add automatic flaky test detector by nicohrubec in #18684

🤖 This preview updates automatically when you update the PR.

@github-actions

github-actionsBot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser25.73 kB--
@sentry/browser - with treeshaking flags24.22 kB--
@sentry/browser (incl. Tracing)42.72 kB--
@sentry/browser (incl. Tracing, Profiling)47.35 kB--
@sentry/browser (incl. Tracing, Replay)81.53 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags71.1 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)86.23 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)98.44 kB--
@sentry/browser (incl. Feedback)42.52 kB--
@sentry/browser (incl. sendFeedback)30.39 kB--
@sentry/browser (incl. FeedbackAsync)35.39 kB--
@sentry/browser (incl. Metrics)27.04 kB--
@sentry/browser (incl. Logs)27.19 kB--
@sentry/browser (incl. Metrics & Logs)27.86 kB--
@sentry/react27.48 kB--
@sentry/react (incl. Tracing)45.05 kB--
@sentry/vue30.56 kB--
@sentry/vue (incl. Tracing)44.58 kB--
@sentry/svelte25.75 kB--
CDN Bundle28.41 kB--
CDN Bundle (incl. Tracing)43.76 kB--
CDN Bundle (incl. Logs, Metrics)29.79 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)44.83 kB--
CDN Bundle (incl. Replay, Logs, Metrics)68.6 kB--
CDN Bundle (incl. Tracing, Replay)80.63 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)81.66 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)86.16 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)87.19 kB--
CDN Bundle - uncompressed83 kB--
CDN Bundle (incl. Tracing) - uncompressed129.79 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed87.14 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed133.2 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed210.13 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed246.67 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed250.07 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed259.58 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed262.97 kB--
@sentry/nextjs (client)47.47 kB--
@sentry/sveltekit (client)43.2 kB--
@sentry/node-core57.86 kB+0.02%+7 B 🔺
@sentry/node174.8 kB+0.01%+15 B 🔺
@sentry/node - without tracing97.63 kB+0.02%+11 B 🔺
@sentry/aws-serverless114.98 kB+0.01%+7 B 🔺

View base workflow run

@github-actions

Copy link
Copy Markdown
Contributor

node-overhead report 🧳

Note: This is a synthetic benchmark with a minimal express app and does not necessarily reflect the real-world performance impact in an application.

ScenarioRequests/s% of BaselinePrev. Requests/sChange %
GET Baseline9,450-9,316+1%
GET With Sentry1,83419%1,744+5%
GET With Sentry (error only)6,10765%5,995+2%
POST Baseline1,211-1,211-
POST With Sentry62852%624+1%
POST With Sentry (error only)1,06388%1,056+1%
MYSQL Baseline3,260-3,337-2%
MYSQL With Sentry52016%499+4%
MYSQL With Sentry (error only)2,68682%2,666+1%

View base workflow run

@chargome
chargome marked this pull request as ready for review April 9, 2026 11:08

@Lms24Lms24 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for adding this!
Just to confirm: We already bumped the plugins to the supported version? And sveltekit uses the interface from core directly?

Comment threadpackages/nuxt/src/vite/sourceMaps.ts Outdated

@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 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: No integration or E2E test for feature
    • Added integration tests in nextjs, nuxt, and tanstackstart-react packages that verify user-provided rewriteSources hooks are invoked with proper arguments and return correct results during the source map configuration flow.

Create PR

Or push these changes by commenting:

@cursor push 0f5c31dc99
Preview (0f5c31dc99)
diff --git a/packages/nextjs/test/config/rewriteSources.integration.test.ts b/packages/nextjs/test/config/rewriteSources.integration.test.ts
new file mode 100644
--- /dev/null+++ b/packages/nextjs/test/config/rewriteSources.integration.test.ts@@ -1,0 +1,118 @@+import { describe, it, expect, vi } from 'vitest';+import { getBuildPluginOptions } from '../../src/config/getBuildPluginOptions';+import type { SentryBuildOptions } from '../../src/config/types';++const mockReleaseName = 'test-release';+const mockDistDirAbsPath = '/test/project/.next';++describe('rewriteSources integration', () => {+ it('invokes custom rewriteSources function with source paths', () => {+ const rewriteSourcesSpy = vi.fn((source: string) => {+ return source.replace(/^custom\//, 'rewritten/');+ });++ const sentryBuildOptions: SentryBuildOptions = {+ org: 'test-org',+ project: 'test-project',+ sourcemaps: {+ rewriteSources: rewriteSourcesSpy,+ },+ };++ const result = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-client',+ });++ const rewriteSources = result.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();+ expect(rewriteSources).toBe(rewriteSourcesSpy);++ const testPaths = [+ 'custom/src/pages/index.js',+ 'custom/components/Button.tsx',+ 'src/utils/helpers.js',+ 'custom/lib/api.ts',+ ];++ testPaths.forEach(path => {+ rewriteSources?.(path, {});+ });++ expect(rewriteSourcesSpy).toHaveBeenCalledTimes(4);+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(1, 'custom/src/pages/index.js', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(2, 'custom/components/Button.tsx', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(3, 'src/utils/helpers.js', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(4, 'custom/lib/api.ts', {});++ expect(rewriteSourcesSpy.mock.results[0]?.value).toBe('rewritten/src/pages/index.js');+ expect(rewriteSourcesSpy.mock.results[1]?.value).toBe('rewritten/components/Button.tsx');+ expect(rewriteSourcesSpy.mock.results[2]?.value).toBe('src/utils/helpers.js');+ expect(rewriteSourcesSpy.mock.results[3]?.value).toBe('rewritten/lib/api.ts');+ });++ it('invokes default rewriteSources function for webpack sources', () => {+ const sentryBuildOptions: SentryBuildOptions = {+ org: 'test-org',+ project: 'test-project',+ };++ const result = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-client',+ });++ const rewriteSources = result.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();++ const webpackPaths = [+ 'webpack://_N_E/src/pages/index.js',+ 'webpack://project/src/components/Button.js',+ 'src/utils/helpers.js',+ 'webpack://_N_E/./app/layout.tsx',+ ];++ const rewrittenPaths = webpackPaths.map(path => rewriteSources?.(path, {}));++ expect(rewrittenPaths).toEqual([+ 'src/pages/index.js',+ 'project/src/components/Button.js',+ 'src/utils/helpers.js',+ './app/layout.tsx',+ ]);+ });++ it('preserves rewriteSources function reference across multiple builds', () => {+ const customRewrite = (source: string) => source.replace(/^prefix\//, '');++ const sentryBuildOptions: SentryBuildOptions = {+ org: 'test-org',+ project: 'test-project',+ sourcemaps: {+ rewriteSources: customRewrite,+ },+ };++ const clientResult = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-client',+ });++ const serverResult = getBuildPluginOptions({+ sentryBuildOptions,+ releaseName: mockReleaseName,+ distDirAbsPath: mockDistDirAbsPath,+ buildTool: 'webpack-nodejs',+ });++ expect(clientResult.sourcemaps?.rewriteSources).toBe(customRewrite);+ expect(serverResult.sourcemaps?.rewriteSources).toBe(customRewrite);+ expect(clientResult.sourcemaps?.rewriteSources).toBe(serverResult.sourcemaps?.rewriteSources);+ });+});diff --git a/packages/nuxt/test/vite/rewriteSources.integration.test.ts b/packages/nuxt/test/vite/rewriteSources.integration.test.ts
new file mode 100644
--- /dev/null+++ b/packages/nuxt/test/vite/rewriteSources.integration.test.ts@@ -1,0 +1,72 @@+import { describe, it, expect, vi } from 'vitest';+import { getPluginOptions } from '../../src/vite/sourceMaps';+import type { SentryNuxtModuleOptions } from '../../src/common/types';++describe('rewriteSources integration', () => {+ it('invokes custom rewriteSources function with source paths', () => {+ const rewriteSourcesSpy = vi.fn((source: string) => {+ return source.replace(/^src\//, 'custom/');+ });++ const options = getPluginOptions({+ sourcemaps: {+ rewriteSources: rewriteSourcesSpy,+ },+ } as SentryNuxtModuleOptions);++ const rewriteSources = options.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();+ expect(rewriteSources).toBe(rewriteSourcesSpy);++ const testPaths = [+ 'src/pages/index.vue',+ 'src/components/Button.vue',+ 'lib/utils/helpers.ts',+ 'src/composables/useAuth.ts',+ ];++ testPaths.forEach(path => {+ rewriteSources?.(path);+ });++ expect(rewriteSourcesSpy).toHaveBeenCalledTimes(4);+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(1, 'src/pages/index.vue');+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(2, 'src/components/Button.vue');+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(3, 'lib/utils/helpers.ts');+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(4, 'src/composables/useAuth.ts');++ expect(rewriteSourcesSpy.mock.results[0]?.value).toBe('custom/pages/index.vue');+ expect(rewriteSourcesSpy.mock.results[1]?.value).toBe('custom/components/Button.vue');+ expect(rewriteSourcesSpy.mock.results[2]?.value).toBe('lib/utils/helpers.ts');+ expect(rewriteSourcesSpy.mock.results[3]?.value).toBe('custom/composables/useAuth.ts');+ });++ it('invokes default rewriteSources function to normalize paths', () => {+ const options = getPluginOptions({} as SentryNuxtModuleOptions);++ const rewriteSources = options.sourcemaps?.rewriteSources;+ expect(rewriteSources).toBeDefined();++ const paths = ['../../../foo/bar', '../../components/Layout.vue', './local', '../utils/api.ts'];++ const rewrittenPaths = paths.map(path => rewriteSources?.(path));++ expect(rewrittenPaths).toEqual(['./foo/bar', './components/Layout.vue', './local', './utils/api.ts']);+ });++ it('preserves rewriteSources function reference across configuration', () => {+ const customRewrite = (source: string) => source.replace(/^prefix\//, '');++ const options = getPluginOptions({+ sourcemaps: {+ rewriteSources: customRewrite,+ },+ } as SentryNuxtModuleOptions);++ expect(options.sourcemaps?.rewriteSources).toBe(customRewrite);++ const testSource = 'prefix/src/app.vue';+ const result = options.sourcemaps?.rewriteSources?.(testSource);+ expect(result).toBe('src/app.vue');+ });+});diff --git a/packages/tanstackstart-react/test/vite/rewriteSources.integration.test.ts b/packages/tanstackstart-react/test/vite/rewriteSources.integration.test.ts
new file mode 100644
--- /dev/null+++ b/packages/tanstackstart-react/test/vite/rewriteSources.integration.test.ts@@ -1,0 +1,83 @@+import { describe, it, expect, vi } from 'vitest';++const sentryVitePluginSpy = vi.fn(() => []);++vi.mock('@sentry/vite-plugin', () => ({+ sentryVitePlugin: vi.fn(() => []),+}));++import { makeAddSentryVitePlugin } from '../../src/vite/sourceMaps';+import { sentryVitePlugin } from '@sentry/vite-plugin';++vi.mocked(sentryVitePlugin).mockImplementation(sentryVitePluginSpy);++describe('rewriteSources integration', () => {+ it('invokes custom rewriteSources function with source paths', () => {+ const rewriteSourcesSpy = vi.fn((source: string) => {+ return source.replace(/^src\//, '');+ });++ makeAddSentryVitePlugin({+ org: 'my-org',+ authToken: 'my-token',+ sourcemaps: {+ rewriteSources: rewriteSourcesSpy,+ },+ });++ expect(sentryVitePluginSpy).toHaveBeenCalledWith(+ expect.objectContaining({+ sourcemaps: expect.objectContaining({+ rewriteSources: rewriteSourcesSpy,+ }),+ }),+ );++ const passedOptions = sentryVitePluginSpy.mock.calls[0]?.[0];+ const rewriteSources = passedOptions?.sourcemaps?.rewriteSources;++ expect(rewriteSources).toBe(rewriteSourcesSpy);++ const testPaths = ['src/routes/index.tsx', 'src/components/Button.tsx', 'lib/utils.ts', 'src/api/client.ts'];++ testPaths.forEach(path => {+ rewriteSources?.(path, {});+ });++ expect(rewriteSourcesSpy).toHaveBeenCalledTimes(4);+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(1, 'src/routes/index.tsx', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(2, 'src/components/Button.tsx', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(3, 'lib/utils.ts', {});+ expect(rewriteSourcesSpy).toHaveBeenNthCalledWith(4, 'src/api/client.ts', {});++ expect(rewriteSourcesSpy.mock.results[0]?.value).toBe('routes/index.tsx');+ expect(rewriteSourcesSpy.mock.results[1]?.value).toBe('components/Button.tsx');+ expect(rewriteSourcesSpy.mock.results[2]?.value).toBe('lib/utils.ts');+ expect(rewriteSourcesSpy.mock.results[3]?.value).toBe('api/client.ts');+ });++ it('preserves rewriteSources function reference in plugin options', () => {+ const customRewrite = (source: string) => source.replace(/^prefix\//, '');++ sentryVitePluginSpy.mockClear();++ makeAddSentryVitePlugin({+ org: 'my-org',+ authToken: 'my-token',+ sourcemaps: {+ rewriteSources: customRewrite,+ },+ });++ expect(sentryVitePluginSpy).toHaveBeenCalledWith(+ expect.objectContaining({+ sourcemaps: expect.objectContaining({+ rewriteSources: customRewrite,+ }),+ }),+ );++ const passedOptions = sentryVitePluginSpy.mock.calls[0]?.[0];+ expect(passedOptions?.sourcemaps?.rewriteSources).toBe(customRewrite);+ });+});

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2722abb. Configure here.

Comment threadpackages/nextjs/test/config/getBuildPluginOptions.test.ts
@chargome
chargome merged commit 13dc7a1 into developApr 14, 2026
467 of 477 checks passed
@chargome
chargome deleted the cg/update-rewrite-sources branch April 14, 2026 12:27
mydea pushed a commit that referenced this pull request Apr 15, 2026
- Adds `rewriteSources` to the base `SourceMapsOptions` interface so
users can customize source path rewriting without the `unstable_*`
escape hatch
- Wires up the option in Nuxt, Next.js, and TanStack Start (SvelteKit,
Astro, React Router already pass it through via spread)
- Nuxt and Next.js preserve their default rewriting behavior when the
option is not provided
bundler plugins ref
getsentry/sentry-javascript-bundler-plugins#908closes#20028
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.

Expose source map path/dir to rewriteSources hook

4 participants

@chargome@Lms24@nicohrubec@s1gr1d