feat(cloudflare): Add Spotlight integration for local dev event forwarding - #22490

Merged
JPeer264 merged 6 commits into
developfrom
feat/cloudflare-spotlight-integration
Jul 24, 2026
Merged

feat(cloudflare): Add Spotlight integration for local dev event forwarding#22490
JPeer264 merged 6 commits into
developfrom
feat/cloudflare-spotlight-integration

Conversation

@MathurAditya724

@MathurAditya724MathurAditya724 commented Jul 22, 2026

Copy link
Copy Markdown
Member

Summary

Add a fetch-based spotlightIntegration to @sentry/cloudflare that mirrors Sentry envelopes to a local Spotlight sidecar during development. Cloudflare Workers was the only server SDK without Spotlight support.

What this enables

  • spotlight: true in Sentry.init() (or withSentry) forwards all envelopes (errors, transactions, logs, AI spans) to http://localhost:8969/stream
  • SENTRY_SPOTLIGHT wrangler env binding (boolean or custom URL) for zero-code-change enablement
  • Works with sentry local serve from the Sentry CLI for a complete local dev experience

Changes

  • New integrationsrc/integrations/spotlight.ts — uses fetch with suppressTracing (CF instruments outbound fetch), consumes response body (Workers requirement), disables after >3 failures
  • sdk.ts — wire integration in init() behind a runtime if (options.spotlight) guard (matches the node-core pattern)
  • options.ts — read SENTRY_SPOTLIGHT from CF env binding with the same precedence as node-core's getSpotlightConfig (user option > env boolean > env URL)
  • client.ts — add spotlight?: boolean | string to BaseCloudflareOptions (CloudflareOptions extends Options/CoreOptions, which does not include ServerRuntimeOptions where spotlight normally lives)
  • index.ts — export spotlightIntegration
  • Tests — full coverage for integration (10 tests) + options (9 new tests), all existing tests still pass

Why fetch, not node:http

Cloudflare Workers don't have node:http. The integration mirrors the browser SDK's fetch-based approach but adds suppressTracing (like Node) since CF's fetchIntegration instruments all outbound fetch, and drains the response body (Workers-specific requirement, same as the CF transport).

Production behavior

Spotlight forwarding only activates when options.spotlight is truthy, which is falsy in production by default — identical to how @sentry/node, @sentry/bun, and all other server SDKs ship the spotlight integration. Each forwarded envelope counts as a Worker subrequest (cap 50 free / 1000 paid), documented in the integration JSDoc; keep it disabled in production.

…rding
Add a fetch-based spotlightIntegration to @sentry/cloudflare that mirrors
envelopes to a local Spotlight sidecar during development. This closes the
gap where Cloudflare Workers was the only server SDK without Spotlight support.
- New integration at src/integrations/spotlight.ts using fetch + suppressTracing
- Wire in sdk.ts init() with rollup-include-development-only markers (stripped from prod builds)
- Read SENTRY_SPOTLIGHT from CF env binding in getFinalOptions (boolean or URL)
- Export spotlightIntegration from package index
- Full test coverage (integration tests + options tests)
CloudflareOptions extends Options (CoreOptions) which does not include
ServerRuntimeOptions where spotlight is declared. Add the property
directly to BaseCloudflareOptions so TypeScript resolves it correctly.
@github-actions

github-actionsBot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

⚠️Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

PathSize% ChangeChange
@sentry/browser27.79 kB--
@sentry/browser - with treeshaking flags26.23 kB--
@sentry/browser (incl. Tracing)46.31 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.06 kB--
@sentry/browser (incl. Tracing, Profiling)51.12 kB--
@sentry/browser (incl. Tracing, Replay)85.59 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags75.23 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)90.29 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)102.97 kB--
@sentry/browser (incl. Feedback)44.96 kB--
@sentry/browser (incl. sendFeedback)32.59 kB--
@sentry/browser (incl. FeedbackAsync)37.64 kB--
@sentry/browser (incl. Metrics)28.88 kB--
@sentry/browser (incl. Logs)29.11 kB--
@sentry/browser (incl. Metrics & Logs)29.8 kB--
@sentry/react29.59 kB--
@sentry/react (incl. Tracing)48.6 kB--
@sentry/vue33.22 kB--
@sentry/vue (incl. Tracing)48.28 kB--
@sentry/svelte27.81 kB--
CDN Bundle30.11 kB--
CDN Bundle (incl. Tracing)48.14 kB--
CDN Bundle (incl. Logs, Metrics)31.69 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)49.47 kB--
CDN Bundle (incl. Replay, Logs, Metrics)70.95 kB--
CDN Bundle (incl. Tracing, Replay)85.72 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)87.01 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)91.49 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.78 kB--
CDN Bundle - uncompressed89.77 kB--
CDN Bundle (incl. Tracing) - uncompressed144.84 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed94.48 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed148.82 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed219.24 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed264.08 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed268.04 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed277.78 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed281.74 kB--
@sentry/nextjs (client)51.13 kB--
@sentry/sveltekit (client)46.75 kB--
@sentry/core/server79.92 kB--
@sentry/core/browser51.9 kB--
@sentry/node124.4 kB-0.01%-1 B 🔽
@sentry/node (incl. diagnostics channel injection)149.83 kB--
@sentry/node/import (ESM hook with diagnostics-channel injection)70.03 kB--
@sentry/node - without tracing74.66 kB+0.01%+1 B 🔺
@sentry/aws-serverless84.13 kB-0.01%-1 B 🔽
@sentry/cloudflare (withSentry) - minified196.61 kB--
@sentry/cloudflare (withSentry)483.18 kB+0.05%+210 B 🔺

View base workflow run

- Fix spotlight precedence to match node-core's getSpotlightConfig:
spotlight: true + env URL → uses env URL (not bare true)
- Fix accidental extra-space indent on instrumentPrototypeMethods
- Add tests: spotlight:true + env URL, 4xx/5xx status handling
@MathurAditya724
MathurAditya724 requested a review from BYKJuly 22, 2026 12:44
@MathurAditya724
MathurAditya724 marked this pull request as ready for review July 22, 2026 12:48
@MathurAditya724
MathurAditya724 requested a review from a team as a code ownerJuly 22, 2026 12:48
@MathurAditya724
MathurAditya724 requested review from JPeer264 and isaacs and removed request for a teamJuly 22, 2026 12:48
…ght wiring
The rollup-include-development-only markers only strip content when the
package build uses splitDevProd (only @sentry/browser does). The Cloudflare
package uses a single build without that plugin, so the markers were inert
and misleading. Match the node-core pattern: guard spotlight wiring with the
runtime 'if (options.spotlight)' check, which is falsy in production.
@timfish

timfish commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Spotlight forwarding only activates when options.spotlight is truthy, which is falsy in production by default

I had a quick look through the code so I might have missed it but how does options.spotlight end up falsy in prod by default?

identical to how @sentry/node, and all other server SDKs ship the spotlight integration

This is not true. The Node SDK specifically ship dev/prod output and strips this from production builds.

@timfish

Copy link
Copy Markdown
Collaborator

This is not true. The Node SDK specifically ship dev/prod output and strips this from production builds

I was wrong, we only do this in the browser:

/*! rollup-include-development-only */
if(options.spotlight){
if(!defaultIntegrations){
defaultIntegrations=[];
}
constargs=typeofoptions.spotlight==='string' ? {sidecarUrl: options.spotlight} : undefined;
defaultIntegrations.push(spotlightBrowserIntegration(args));
}
/*! rollup-include-development-only-end */

@JPeer264

Copy link
Copy Markdown
Member

I was wrong, we only do this in the browser:

Ya, I just went through and we only have this in the browser. @timfish if I'm not mistaken we only have to add splitDevProd in the rollup config and change the exports right?

@JPeer264

Copy link
Copy Markdown
Member

FYI. Gonna merge this latest tomorrow. If you want to test it you can yalc the Cloudflare package from this branch: https://github.com/getsentry/sentry-javascript/blob/develop/docs/using-yalc.md

@JPeer264
JPeer264force-pushed the feat/cloudflare-spotlight-integration branch from 4ebec68 to dff8f1bCompareJuly 23, 2026 17:03

@JPeer264JPeer264 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.

There is now a dev/prod build. I don't think that this works for wrangler, as they have special targets, but for Vite this is should work to use the correct bundle. 2kb is also not too bad unminified as the free tier now has 64MB, seems like they increased it

@JPeer264
JPeer264 enabled auto-merge (squash) July 24, 2026 06:59
@JPeer264
JPeer264 merged commit fe90ccd into developJul 24, 2026
549 of 551 checks passed
@JPeer264
JPeer264 deleted the feat/cloudflare-spotlight-integration branch July 24, 2026 07:58
JPeer264 added a commit that referenced this pull request Jul 29, 2026
…orwarding (#22796)
Backport of: #22490
And reverted dev/prod bundle split, as `wrangler` on its own doesn't
support that
---------
Co-authored-by: Aditya Mathur <57684218+MathurAditya724@users.noreply.github.com>
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.

3 participants

@MathurAditya724@timfish@JPeer264
, '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(cloudflare): Add Spotlight integration for local dev event forwarding - #22490

Merged
JPeer264 merged 6 commits into
developfrom
feat/cloudflare-spotlight-integration
Jul 24, 2026
Merged

feat(cloudflare): Add Spotlight integration for local dev event forwarding#22490
JPeer264 merged 6 commits into
developfrom
feat/cloudflare-spotlight-integration

Conversation

@MathurAditya724

@MathurAditya724MathurAditya724 commented Jul 22, 2026

Copy link
Copy Markdown
Member

Summary

Add a fetch-based spotlightIntegration to @sentry/cloudflare that mirrors Sentry envelopes to a local Spotlight sidecar during development. Cloudflare Workers was the only server SDK without Spotlight support.

What this enables

  • spotlight: true in Sentry.init() (or withSentry) forwards all envelopes (errors, transactions, logs, AI spans) to http://localhost:8969/stream
  • SENTRY_SPOTLIGHT wrangler env binding (boolean or custom URL) for zero-code-change enablement
  • Works with sentry local serve from the Sentry CLI for a complete local dev experience

Changes

  • New integrationsrc/integrations/spotlight.ts — uses fetch with suppressTracing (CF instruments outbound fetch), consumes response body (Workers requirement), disables after >3 failures
  • sdk.ts — wire integration in init() behind a runtime if (options.spotlight) guard (matches the node-core pattern)
  • options.ts — read SENTRY_SPOTLIGHT from CF env binding with the same precedence as node-core's getSpotlightConfig (user option > env boolean > env URL)
  • client.ts — add spotlight?: boolean | string to BaseCloudflareOptions (CloudflareOptions extends Options/CoreOptions, which does not include ServerRuntimeOptions where spotlight normally lives)
  • index.ts — export spotlightIntegration
  • Tests — full coverage for integration (10 tests) + options (9 new tests), all existing tests still pass

Why fetch, not node:http

Cloudflare Workers don't have node:http. The integration mirrors the browser SDK's fetch-based approach but adds suppressTracing (like Node) since CF's fetchIntegration instruments all outbound fetch, and drains the response body (Workers-specific requirement, same as the CF transport).

Production behavior

Spotlight forwarding only activates when options.spotlight is truthy, which is falsy in production by default — identical to how @sentry/node, @sentry/bun, and all other server SDKs ship the spotlight integration. Each forwarded envelope counts as a Worker subrequest (cap 50 free / 1000 paid), documented in the integration JSDoc; keep it disabled in production.

…rding
Add a fetch-based spotlightIntegration to @sentry/cloudflare that mirrors
envelopes to a local Spotlight sidecar during development. This closes the
gap where Cloudflare Workers was the only server SDK without Spotlight support.
- New integration at src/integrations/spotlight.ts using fetch + suppressTracing
- Wire in sdk.ts init() with rollup-include-development-only markers (stripped from prod builds)
- Read SENTRY_SPOTLIGHT from CF env binding in getFinalOptions (boolean or URL)
- Export spotlightIntegration from package index
- Full test coverage (integration tests + options tests)
CloudflareOptions extends Options (CoreOptions) which does not include
ServerRuntimeOptions where spotlight is declared. Add the property
directly to BaseCloudflareOptions so TypeScript resolves it correctly.
@github-actions

github-actionsBot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

⚠️Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

PathSize% ChangeChange
@sentry/browser27.79 kB--
@sentry/browser - with treeshaking flags26.23 kB--
@sentry/browser (incl. Tracing)46.31 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.06 kB--
@sentry/browser (incl. Tracing, Profiling)51.12 kB--
@sentry/browser (incl. Tracing, Replay)85.59 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags75.23 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)90.29 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)102.97 kB--
@sentry/browser (incl. Feedback)44.96 kB--
@sentry/browser (incl. sendFeedback)32.59 kB--
@sentry/browser (incl. FeedbackAsync)37.64 kB--
@sentry/browser (incl. Metrics)28.88 kB--
@sentry/browser (incl. Logs)29.11 kB--
@sentry/browser (incl. Metrics & Logs)29.8 kB--
@sentry/react29.59 kB--
@sentry/react (incl. Tracing)48.6 kB--
@sentry/vue33.22 kB--
@sentry/vue (incl. Tracing)48.28 kB--
@sentry/svelte27.81 kB--
CDN Bundle30.11 kB--
CDN Bundle (incl. Tracing)48.14 kB--
CDN Bundle (incl. Logs, Metrics)31.69 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)49.47 kB--
CDN Bundle (incl. Replay, Logs, Metrics)70.95 kB--
CDN Bundle (incl. Tracing, Replay)85.72 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)87.01 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)91.49 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.78 kB--
CDN Bundle - uncompressed89.77 kB--
CDN Bundle (incl. Tracing) - uncompressed144.84 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed94.48 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed148.82 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed219.24 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed264.08 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed268.04 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed277.78 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed281.74 kB--
@sentry/nextjs (client)51.13 kB--
@sentry/sveltekit (client)46.75 kB--
@sentry/core/server79.92 kB--
@sentry/core/browser51.9 kB--
@sentry/node124.4 kB-0.01%-1 B 🔽
@sentry/node (incl. diagnostics channel injection)149.83 kB--
@sentry/node/import (ESM hook with diagnostics-channel injection)70.03 kB--
@sentry/node - without tracing74.66 kB+0.01%+1 B 🔺
@sentry/aws-serverless84.13 kB-0.01%-1 B 🔽
@sentry/cloudflare (withSentry) - minified196.61 kB--
@sentry/cloudflare (withSentry)483.18 kB+0.05%+210 B 🔺

View base workflow run

- Fix spotlight precedence to match node-core's getSpotlightConfig:
spotlight: true + env URL → uses env URL (not bare true)
- Fix accidental extra-space indent on instrumentPrototypeMethods
- Add tests: spotlight:true + env URL, 4xx/5xx status handling
@MathurAditya724
MathurAditya724 requested a review from BYKJuly 22, 2026 12:44
@MathurAditya724
MathurAditya724 marked this pull request as ready for review July 22, 2026 12:48
@MathurAditya724
MathurAditya724 requested a review from a team as a code ownerJuly 22, 2026 12:48
@MathurAditya724
MathurAditya724 requested review from JPeer264 and isaacs and removed request for a teamJuly 22, 2026 12:48
…ght wiring
The rollup-include-development-only markers only strip content when the
package build uses splitDevProd (only @sentry/browser does). The Cloudflare
package uses a single build without that plugin, so the markers were inert
and misleading. Match the node-core pattern: guard spotlight wiring with the
runtime 'if (options.spotlight)' check, which is falsy in production.
@timfish

timfish commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Spotlight forwarding only activates when options.spotlight is truthy, which is falsy in production by default

I had a quick look through the code so I might have missed it but how does options.spotlight end up falsy in prod by default?

identical to how @sentry/node, and all other server SDKs ship the spotlight integration

This is not true. The Node SDK specifically ship dev/prod output and strips this from production builds.

@timfish

Copy link
Copy Markdown
Collaborator

This is not true. The Node SDK specifically ship dev/prod output and strips this from production builds

I was wrong, we only do this in the browser:

/*! rollup-include-development-only */
if(options.spotlight){
if(!defaultIntegrations){
defaultIntegrations=[];
}
constargs=typeofoptions.spotlight==='string' ? {sidecarUrl: options.spotlight} : undefined;
defaultIntegrations.push(spotlightBrowserIntegration(args));
}
/*! rollup-include-development-only-end */

@JPeer264

Copy link
Copy Markdown
Member

I was wrong, we only do this in the browser:

Ya, I just went through and we only have this in the browser. @timfish if I'm not mistaken we only have to add splitDevProd in the rollup config and change the exports right?

@JPeer264

Copy link
Copy Markdown
Member

FYI. Gonna merge this latest tomorrow. If you want to test it you can yalc the Cloudflare package from this branch: https://github.com/getsentry/sentry-javascript/blob/develop/docs/using-yalc.md

@JPeer264
JPeer264force-pushed the feat/cloudflare-spotlight-integration branch from 4ebec68 to dff8f1bCompareJuly 23, 2026 17:03

@JPeer264JPeer264 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.

There is now a dev/prod build. I don't think that this works for wrangler, as they have special targets, but for Vite this is should work to use the correct bundle. 2kb is also not too bad unminified as the free tier now has 64MB, seems like they increased it

@JPeer264
JPeer264 enabled auto-merge (squash) July 24, 2026 06:59
@JPeer264
JPeer264 merged commit fe90ccd into developJul 24, 2026
549 of 551 checks passed
@JPeer264
JPeer264 deleted the feat/cloudflare-spotlight-integration branch July 24, 2026 07:58
JPeer264 added a commit that referenced this pull request Jul 29, 2026
…orwarding (#22796)
Backport of: #22490
And reverted dev/prod bundle split, as `wrangler` on its own doesn't
support that
---------
Co-authored-by: Aditya Mathur <57684218+MathurAditya724@users.noreply.github.com>
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.

3 participants

@MathurAditya724@timfish@JPeer264
, '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(cloudflare): Add Spotlight integration for local dev event forwarding - #22490

Merged
JPeer264 merged 6 commits into
developfrom
feat/cloudflare-spotlight-integration
Jul 24, 2026
Merged

feat(cloudflare): Add Spotlight integration for local dev event forwarding#22490
JPeer264 merged 6 commits into
developfrom
feat/cloudflare-spotlight-integration

Conversation

@MathurAditya724

@MathurAditya724MathurAditya724 commented Jul 22, 2026

Copy link
Copy Markdown
Member

Summary

Add a fetch-based spotlightIntegration to @sentry/cloudflare that mirrors Sentry envelopes to a local Spotlight sidecar during development. Cloudflare Workers was the only server SDK without Spotlight support.

What this enables

  • spotlight: true in Sentry.init() (or withSentry) forwards all envelopes (errors, transactions, logs, AI spans) to http://localhost:8969/stream
  • SENTRY_SPOTLIGHT wrangler env binding (boolean or custom URL) for zero-code-change enablement
  • Works with sentry local serve from the Sentry CLI for a complete local dev experience

Changes

  • New integrationsrc/integrations/spotlight.ts — uses fetch with suppressTracing (CF instruments outbound fetch), consumes response body (Workers requirement), disables after >3 failures
  • sdk.ts — wire integration in init() behind a runtime if (options.spotlight) guard (matches the node-core pattern)
  • options.ts — read SENTRY_SPOTLIGHT from CF env binding with the same precedence as node-core's getSpotlightConfig (user option > env boolean > env URL)
  • client.ts — add spotlight?: boolean | string to BaseCloudflareOptions (CloudflareOptions extends Options/CoreOptions, which does not include ServerRuntimeOptions where spotlight normally lives)
  • index.ts — export spotlightIntegration
  • Tests — full coverage for integration (10 tests) + options (9 new tests), all existing tests still pass

Why fetch, not node:http

Cloudflare Workers don't have node:http. The integration mirrors the browser SDK's fetch-based approach but adds suppressTracing (like Node) since CF's fetchIntegration instruments all outbound fetch, and drains the response body (Workers-specific requirement, same as the CF transport).

Production behavior

Spotlight forwarding only activates when options.spotlight is truthy, which is falsy in production by default — identical to how @sentry/node, @sentry/bun, and all other server SDKs ship the spotlight integration. Each forwarded envelope counts as a Worker subrequest (cap 50 free / 1000 paid), documented in the integration JSDoc; keep it disabled in production.

…rding
Add a fetch-based spotlightIntegration to @sentry/cloudflare that mirrors
envelopes to a local Spotlight sidecar during development. This closes the
gap where Cloudflare Workers was the only server SDK without Spotlight support.
- New integration at src/integrations/spotlight.ts using fetch + suppressTracing
- Wire in sdk.ts init() with rollup-include-development-only markers (stripped from prod builds)
- Read SENTRY_SPOTLIGHT from CF env binding in getFinalOptions (boolean or URL)
- Export spotlightIntegration from package index
- Full test coverage (integration tests + options tests)
CloudflareOptions extends Options (CoreOptions) which does not include
ServerRuntimeOptions where spotlight is declared. Add the property
directly to BaseCloudflareOptions so TypeScript resolves it correctly.
@github-actions

github-actionsBot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

⚠️Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

PathSize% ChangeChange
@sentry/browser27.79 kB--
@sentry/browser - with treeshaking flags26.23 kB--
@sentry/browser (incl. Tracing)46.31 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.06 kB--
@sentry/browser (incl. Tracing, Profiling)51.12 kB--
@sentry/browser (incl. Tracing, Replay)85.59 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags75.23 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)90.29 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)102.97 kB--
@sentry/browser (incl. Feedback)44.96 kB--
@sentry/browser (incl. sendFeedback)32.59 kB--
@sentry/browser (incl. FeedbackAsync)37.64 kB--
@sentry/browser (incl. Metrics)28.88 kB--
@sentry/browser (incl. Logs)29.11 kB--
@sentry/browser (incl. Metrics & Logs)29.8 kB--
@sentry/react29.59 kB--
@sentry/react (incl. Tracing)48.6 kB--
@sentry/vue33.22 kB--
@sentry/vue (incl. Tracing)48.28 kB--
@sentry/svelte27.81 kB--
CDN Bundle30.11 kB--
CDN Bundle (incl. Tracing)48.14 kB--
CDN Bundle (incl. Logs, Metrics)31.69 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)49.47 kB--
CDN Bundle (incl. Replay, Logs, Metrics)70.95 kB--
CDN Bundle (incl. Tracing, Replay)85.72 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)87.01 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)91.49 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.78 kB--
CDN Bundle - uncompressed89.77 kB--
CDN Bundle (incl. Tracing) - uncompressed144.84 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed94.48 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed148.82 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed219.24 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed264.08 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed268.04 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed277.78 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed281.74 kB--
@sentry/nextjs (client)51.13 kB--
@sentry/sveltekit (client)46.75 kB--
@sentry/core/server79.92 kB--
@sentry/core/browser51.9 kB--
@sentry/node124.4 kB-0.01%-1 B 🔽
@sentry/node (incl. diagnostics channel injection)149.83 kB--
@sentry/node/import (ESM hook with diagnostics-channel injection)70.03 kB--
@sentry/node - without tracing74.66 kB+0.01%+1 B 🔺
@sentry/aws-serverless84.13 kB-0.01%-1 B 🔽
@sentry/cloudflare (withSentry) - minified196.61 kB--
@sentry/cloudflare (withSentry)483.18 kB+0.05%+210 B 🔺

View base workflow run

- Fix spotlight precedence to match node-core's getSpotlightConfig:
spotlight: true + env URL → uses env URL (not bare true)
- Fix accidental extra-space indent on instrumentPrototypeMethods
- Add tests: spotlight:true + env URL, 4xx/5xx status handling
@MathurAditya724
MathurAditya724 requested a review from BYKJuly 22, 2026 12:44
@MathurAditya724
MathurAditya724 marked this pull request as ready for review July 22, 2026 12:48
@MathurAditya724
MathurAditya724 requested a review from a team as a code ownerJuly 22, 2026 12:48
@MathurAditya724
MathurAditya724 requested review from JPeer264 and isaacs and removed request for a teamJuly 22, 2026 12:48
…ght wiring
The rollup-include-development-only markers only strip content when the
package build uses splitDevProd (only @sentry/browser does). The Cloudflare
package uses a single build without that plugin, so the markers were inert
and misleading. Match the node-core pattern: guard spotlight wiring with the
runtime 'if (options.spotlight)' check, which is falsy in production.
@timfish

timfish commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Spotlight forwarding only activates when options.spotlight is truthy, which is falsy in production by default

I had a quick look through the code so I might have missed it but how does options.spotlight end up falsy in prod by default?

identical to how @sentry/node, and all other server SDKs ship the spotlight integration

This is not true. The Node SDK specifically ship dev/prod output and strips this from production builds.

@timfish

Copy link
Copy Markdown
Collaborator

This is not true. The Node SDK specifically ship dev/prod output and strips this from production builds

I was wrong, we only do this in the browser:

/*! rollup-include-development-only */
if(options.spotlight){
if(!defaultIntegrations){
defaultIntegrations=[];
}
constargs=typeofoptions.spotlight==='string' ? {sidecarUrl: options.spotlight} : undefined;
defaultIntegrations.push(spotlightBrowserIntegration(args));
}
/*! rollup-include-development-only-end */

@JPeer264

Copy link
Copy Markdown
Member

I was wrong, we only do this in the browser:

Ya, I just went through and we only have this in the browser. @timfish if I'm not mistaken we only have to add splitDevProd in the rollup config and change the exports right?

@JPeer264

Copy link
Copy Markdown
Member

FYI. Gonna merge this latest tomorrow. If you want to test it you can yalc the Cloudflare package from this branch: https://github.com/getsentry/sentry-javascript/blob/develop/docs/using-yalc.md

@JPeer264
JPeer264force-pushed the feat/cloudflare-spotlight-integration branch from 4ebec68 to dff8f1bCompareJuly 23, 2026 17:03

@JPeer264JPeer264 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.

There is now a dev/prod build. I don't think that this works for wrangler, as they have special targets, but for Vite this is should work to use the correct bundle. 2kb is also not too bad unminified as the free tier now has 64MB, seems like they increased it

@JPeer264
JPeer264 enabled auto-merge (squash) July 24, 2026 06:59
@JPeer264
JPeer264 merged commit fe90ccd into developJul 24, 2026
549 of 551 checks passed
@JPeer264
JPeer264 deleted the feat/cloudflare-spotlight-integration branch July 24, 2026 07:58
JPeer264 added a commit that referenced this pull request Jul 29, 2026
…orwarding (#22796)
Backport of: #22490
And reverted dev/prod bundle split, as `wrangler` on its own doesn't
support that
---------
Co-authored-by: Aditya Mathur <57684218+MathurAditya724@users.noreply.github.com>
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.

3 participants

@MathurAditya724@timfish@JPeer264
, '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(cloudflare): Add Spotlight integration for local dev event forwarding - #22490

Merged
JPeer264 merged 6 commits into
developfrom
feat/cloudflare-spotlight-integration
Jul 24, 2026
Merged

feat(cloudflare): Add Spotlight integration for local dev event forwarding#22490
JPeer264 merged 6 commits into
developfrom
feat/cloudflare-spotlight-integration

Conversation

@MathurAditya724

@MathurAditya724MathurAditya724 commented Jul 22, 2026

Copy link
Copy Markdown
Member

Summary

Add a fetch-based spotlightIntegration to @sentry/cloudflare that mirrors Sentry envelopes to a local Spotlight sidecar during development. Cloudflare Workers was the only server SDK without Spotlight support.

What this enables

  • spotlight: true in Sentry.init() (or withSentry) forwards all envelopes (errors, transactions, logs, AI spans) to http://localhost:8969/stream
  • SENTRY_SPOTLIGHT wrangler env binding (boolean or custom URL) for zero-code-change enablement
  • Works with sentry local serve from the Sentry CLI for a complete local dev experience

Changes

  • New integrationsrc/integrations/spotlight.ts — uses fetch with suppressTracing (CF instruments outbound fetch), consumes response body (Workers requirement), disables after >3 failures
  • sdk.ts — wire integration in init() behind a runtime if (options.spotlight) guard (matches the node-core pattern)
  • options.ts — read SENTRY_SPOTLIGHT from CF env binding with the same precedence as node-core's getSpotlightConfig (user option > env boolean > env URL)
  • client.ts — add spotlight?: boolean | string to BaseCloudflareOptions (CloudflareOptions extends Options/CoreOptions, which does not include ServerRuntimeOptions where spotlight normally lives)
  • index.ts — export spotlightIntegration
  • Tests — full coverage for integration (10 tests) + options (9 new tests), all existing tests still pass

Why fetch, not node:http

Cloudflare Workers don't have node:http. The integration mirrors the browser SDK's fetch-based approach but adds suppressTracing (like Node) since CF's fetchIntegration instruments all outbound fetch, and drains the response body (Workers-specific requirement, same as the CF transport).

Production behavior

Spotlight forwarding only activates when options.spotlight is truthy, which is falsy in production by default — identical to how @sentry/node, @sentry/bun, and all other server SDKs ship the spotlight integration. Each forwarded envelope counts as a Worker subrequest (cap 50 free / 1000 paid), documented in the integration JSDoc; keep it disabled in production.

…rding
Add a fetch-based spotlightIntegration to @sentry/cloudflare that mirrors
envelopes to a local Spotlight sidecar during development. This closes the
gap where Cloudflare Workers was the only server SDK without Spotlight support.
- New integration at src/integrations/spotlight.ts using fetch + suppressTracing
- Wire in sdk.ts init() with rollup-include-development-only markers (stripped from prod builds)
- Read SENTRY_SPOTLIGHT from CF env binding in getFinalOptions (boolean or URL)
- Export spotlightIntegration from package index
- Full test coverage (integration tests + options tests)
CloudflareOptions extends Options (CoreOptions) which does not include
ServerRuntimeOptions where spotlight is declared. Add the property
directly to BaseCloudflareOptions so TypeScript resolves it correctly.
@github-actions

github-actionsBot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

⚠️Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

PathSize% ChangeChange
@sentry/browser27.79 kB--
@sentry/browser - with treeshaking flags26.23 kB--
@sentry/browser (incl. Tracing)46.31 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.06 kB--
@sentry/browser (incl. Tracing, Profiling)51.12 kB--
@sentry/browser (incl. Tracing, Replay)85.59 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags75.23 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)90.29 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)102.97 kB--
@sentry/browser (incl. Feedback)44.96 kB--
@sentry/browser (incl. sendFeedback)32.59 kB--
@sentry/browser (incl. FeedbackAsync)37.64 kB--
@sentry/browser (incl. Metrics)28.88 kB--
@sentry/browser (incl. Logs)29.11 kB--
@sentry/browser (incl. Metrics & Logs)29.8 kB--
@sentry/react29.59 kB--
@sentry/react (incl. Tracing)48.6 kB--
@sentry/vue33.22 kB--
@sentry/vue (incl. Tracing)48.28 kB--
@sentry/svelte27.81 kB--
CDN Bundle30.11 kB--
CDN Bundle (incl. Tracing)48.14 kB--
CDN Bundle (incl. Logs, Metrics)31.69 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)49.47 kB--
CDN Bundle (incl. Replay, Logs, Metrics)70.95 kB--
CDN Bundle (incl. Tracing, Replay)85.72 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)87.01 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)91.49 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.78 kB--
CDN Bundle - uncompressed89.77 kB--
CDN Bundle (incl. Tracing) - uncompressed144.84 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed94.48 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed148.82 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed219.24 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed264.08 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed268.04 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed277.78 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed281.74 kB--
@sentry/nextjs (client)51.13 kB--
@sentry/sveltekit (client)46.75 kB--
@sentry/core/server79.92 kB--
@sentry/core/browser51.9 kB--
@sentry/node124.4 kB-0.01%-1 B 🔽
@sentry/node (incl. diagnostics channel injection)149.83 kB--
@sentry/node/import (ESM hook with diagnostics-channel injection)70.03 kB--
@sentry/node - without tracing74.66 kB+0.01%+1 B 🔺
@sentry/aws-serverless84.13 kB-0.01%-1 B 🔽
@sentry/cloudflare (withSentry) - minified196.61 kB--
@sentry/cloudflare (withSentry)483.18 kB+0.05%+210 B 🔺

View base workflow run

- Fix spotlight precedence to match node-core's getSpotlightConfig:
spotlight: true + env URL → uses env URL (not bare true)
- Fix accidental extra-space indent on instrumentPrototypeMethods
- Add tests: spotlight:true + env URL, 4xx/5xx status handling
@MathurAditya724
MathurAditya724 requested a review from BYKJuly 22, 2026 12:44
@MathurAditya724
MathurAditya724 marked this pull request as ready for review July 22, 2026 12:48
@MathurAditya724
MathurAditya724 requested a review from a team as a code ownerJuly 22, 2026 12:48
@MathurAditya724
MathurAditya724 requested review from JPeer264 and isaacs and removed request for a teamJuly 22, 2026 12:48
…ght wiring
The rollup-include-development-only markers only strip content when the
package build uses splitDevProd (only @sentry/browser does). The Cloudflare
package uses a single build without that plugin, so the markers were inert
and misleading. Match the node-core pattern: guard spotlight wiring with the
runtime 'if (options.spotlight)' check, which is falsy in production.
@timfish

timfish commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Spotlight forwarding only activates when options.spotlight is truthy, which is falsy in production by default

I had a quick look through the code so I might have missed it but how does options.spotlight end up falsy in prod by default?

identical to how @sentry/node, and all other server SDKs ship the spotlight integration

This is not true. The Node SDK specifically ship dev/prod output and strips this from production builds.

@timfish

Copy link
Copy Markdown
Collaborator

This is not true. The Node SDK specifically ship dev/prod output and strips this from production builds

I was wrong, we only do this in the browser:

/*! rollup-include-development-only */
if(options.spotlight){
if(!defaultIntegrations){
defaultIntegrations=[];
}
constargs=typeofoptions.spotlight==='string' ? {sidecarUrl: options.spotlight} : undefined;
defaultIntegrations.push(spotlightBrowserIntegration(args));
}
/*! rollup-include-development-only-end */

@JPeer264

Copy link
Copy Markdown
Member

I was wrong, we only do this in the browser:

Ya, I just went through and we only have this in the browser. @timfish if I'm not mistaken we only have to add splitDevProd in the rollup config and change the exports right?

@JPeer264

Copy link
Copy Markdown
Member

FYI. Gonna merge this latest tomorrow. If you want to test it you can yalc the Cloudflare package from this branch: https://github.com/getsentry/sentry-javascript/blob/develop/docs/using-yalc.md

@JPeer264
JPeer264force-pushed the feat/cloudflare-spotlight-integration branch from 4ebec68 to dff8f1bCompareJuly 23, 2026 17:03

@JPeer264JPeer264 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.

There is now a dev/prod build. I don't think that this works for wrangler, as they have special targets, but for Vite this is should work to use the correct bundle. 2kb is also not too bad unminified as the free tier now has 64MB, seems like they increased it

@JPeer264
JPeer264 enabled auto-merge (squash) July 24, 2026 06:59
@JPeer264
JPeer264 merged commit fe90ccd into developJul 24, 2026
549 of 551 checks passed
@JPeer264
JPeer264 deleted the feat/cloudflare-spotlight-integration branch July 24, 2026 07:58
JPeer264 added a commit that referenced this pull request Jul 29, 2026
…orwarding (#22796)
Backport of: #22490
And reverted dev/prod bundle split, as `wrangler` on its own doesn't
support that
---------
Co-authored-by: Aditya Mathur <57684218+MathurAditya724@users.noreply.github.com>
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.

3 participants

@MathurAditya724@timfish@JPeer264
, '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(cloudflare): Add Spotlight integration for local dev event forwarding - #22490

Merged
JPeer264 merged 6 commits into
developfrom
feat/cloudflare-spotlight-integration
Jul 24, 2026
Merged

feat(cloudflare): Add Spotlight integration for local dev event forwarding#22490
JPeer264 merged 6 commits into
developfrom
feat/cloudflare-spotlight-integration

Conversation

@MathurAditya724

@MathurAditya724MathurAditya724 commented Jul 22, 2026

Copy link
Copy Markdown
Member

Summary

Add a fetch-based spotlightIntegration to @sentry/cloudflare that mirrors Sentry envelopes to a local Spotlight sidecar during development. Cloudflare Workers was the only server SDK without Spotlight support.

What this enables

  • spotlight: true in Sentry.init() (or withSentry) forwards all envelopes (errors, transactions, logs, AI spans) to http://localhost:8969/stream
  • SENTRY_SPOTLIGHT wrangler env binding (boolean or custom URL) for zero-code-change enablement
  • Works with sentry local serve from the Sentry CLI for a complete local dev experience

Changes

  • New integrationsrc/integrations/spotlight.ts — uses fetch with suppressTracing (CF instruments outbound fetch), consumes response body (Workers requirement), disables after >3 failures
  • sdk.ts — wire integration in init() behind a runtime if (options.spotlight) guard (matches the node-core pattern)
  • options.ts — read SENTRY_SPOTLIGHT from CF env binding with the same precedence as node-core's getSpotlightConfig (user option > env boolean > env URL)
  • client.ts — add spotlight?: boolean | string to BaseCloudflareOptions (CloudflareOptions extends Options/CoreOptions, which does not include ServerRuntimeOptions where spotlight normally lives)
  • index.ts — export spotlightIntegration
  • Tests — full coverage for integration (10 tests) + options (9 new tests), all existing tests still pass

Why fetch, not node:http

Cloudflare Workers don't have node:http. The integration mirrors the browser SDK's fetch-based approach but adds suppressTracing (like Node) since CF's fetchIntegration instruments all outbound fetch, and drains the response body (Workers-specific requirement, same as the CF transport).

Production behavior

Spotlight forwarding only activates when options.spotlight is truthy, which is falsy in production by default — identical to how @sentry/node, @sentry/bun, and all other server SDKs ship the spotlight integration. Each forwarded envelope counts as a Worker subrequest (cap 50 free / 1000 paid), documented in the integration JSDoc; keep it disabled in production.

…rding
Add a fetch-based spotlightIntegration to @sentry/cloudflare that mirrors
envelopes to a local Spotlight sidecar during development. This closes the
gap where Cloudflare Workers was the only server SDK without Spotlight support.
- New integration at src/integrations/spotlight.ts using fetch + suppressTracing
- Wire in sdk.ts init() with rollup-include-development-only markers (stripped from prod builds)
- Read SENTRY_SPOTLIGHT from CF env binding in getFinalOptions (boolean or URL)
- Export spotlightIntegration from package index
- Full test coverage (integration tests + options tests)
CloudflareOptions extends Options (CoreOptions) which does not include
ServerRuntimeOptions where spotlight is declared. Add the property
directly to BaseCloudflareOptions so TypeScript resolves it correctly.
@github-actions

github-actionsBot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

⚠️Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

PathSize% ChangeChange
@sentry/browser27.79 kB--
@sentry/browser - with treeshaking flags26.23 kB--
@sentry/browser (incl. Tracing)46.31 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.06 kB--
@sentry/browser (incl. Tracing, Profiling)51.12 kB--
@sentry/browser (incl. Tracing, Replay)85.59 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags75.23 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)90.29 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)102.97 kB--
@sentry/browser (incl. Feedback)44.96 kB--
@sentry/browser (incl. sendFeedback)32.59 kB--
@sentry/browser (incl. FeedbackAsync)37.64 kB--
@sentry/browser (incl. Metrics)28.88 kB--
@sentry/browser (incl. Logs)29.11 kB--
@sentry/browser (incl. Metrics & Logs)29.8 kB--
@sentry/react29.59 kB--
@sentry/react (incl. Tracing)48.6 kB--
@sentry/vue33.22 kB--
@sentry/vue (incl. Tracing)48.28 kB--
@sentry/svelte27.81 kB--
CDN Bundle30.11 kB--
CDN Bundle (incl. Tracing)48.14 kB--
CDN Bundle (incl. Logs, Metrics)31.69 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)49.47 kB--
CDN Bundle (incl. Replay, Logs, Metrics)70.95 kB--
CDN Bundle (incl. Tracing, Replay)85.72 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)87.01 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)91.49 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.78 kB--
CDN Bundle - uncompressed89.77 kB--
CDN Bundle (incl. Tracing) - uncompressed144.84 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed94.48 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed148.82 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed219.24 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed264.08 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed268.04 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed277.78 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed281.74 kB--
@sentry/nextjs (client)51.13 kB--
@sentry/sveltekit (client)46.75 kB--
@sentry/core/server79.92 kB--
@sentry/core/browser51.9 kB--
@sentry/node124.4 kB-0.01%-1 B 🔽
@sentry/node (incl. diagnostics channel injection)149.83 kB--
@sentry/node/import (ESM hook with diagnostics-channel injection)70.03 kB--
@sentry/node - without tracing74.66 kB+0.01%+1 B 🔺
@sentry/aws-serverless84.13 kB-0.01%-1 B 🔽
@sentry/cloudflare (withSentry) - minified196.61 kB--
@sentry/cloudflare (withSentry)483.18 kB+0.05%+210 B 🔺

View base workflow run

- Fix spotlight precedence to match node-core's getSpotlightConfig:
spotlight: true + env URL → uses env URL (not bare true)
- Fix accidental extra-space indent on instrumentPrototypeMethods
- Add tests: spotlight:true + env URL, 4xx/5xx status handling
@MathurAditya724
MathurAditya724 requested a review from BYKJuly 22, 2026 12:44
@MathurAditya724
MathurAditya724 marked this pull request as ready for review July 22, 2026 12:48
@MathurAditya724
MathurAditya724 requested a review from a team as a code ownerJuly 22, 2026 12:48
@MathurAditya724
MathurAditya724 requested review from JPeer264 and isaacs and removed request for a teamJuly 22, 2026 12:48
…ght wiring
The rollup-include-development-only markers only strip content when the
package build uses splitDevProd (only @sentry/browser does). The Cloudflare
package uses a single build without that plugin, so the markers were inert
and misleading. Match the node-core pattern: guard spotlight wiring with the
runtime 'if (options.spotlight)' check, which is falsy in production.
@timfish

timfish commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Spotlight forwarding only activates when options.spotlight is truthy, which is falsy in production by default

I had a quick look through the code so I might have missed it but how does options.spotlight end up falsy in prod by default?

identical to how @sentry/node, and all other server SDKs ship the spotlight integration

This is not true. The Node SDK specifically ship dev/prod output and strips this from production builds.

@timfish

Copy link
Copy Markdown
Collaborator

This is not true. The Node SDK specifically ship dev/prod output and strips this from production builds

I was wrong, we only do this in the browser:

/*! rollup-include-development-only */
if(options.spotlight){
if(!defaultIntegrations){
defaultIntegrations=[];
}
constargs=typeofoptions.spotlight==='string' ? {sidecarUrl: options.spotlight} : undefined;
defaultIntegrations.push(spotlightBrowserIntegration(args));
}
/*! rollup-include-development-only-end */

@JPeer264

Copy link
Copy Markdown
Member

I was wrong, we only do this in the browser:

Ya, I just went through and we only have this in the browser. @timfish if I'm not mistaken we only have to add splitDevProd in the rollup config and change the exports right?

@JPeer264

Copy link
Copy Markdown
Member

FYI. Gonna merge this latest tomorrow. If you want to test it you can yalc the Cloudflare package from this branch: https://github.com/getsentry/sentry-javascript/blob/develop/docs/using-yalc.md

@JPeer264
JPeer264force-pushed the feat/cloudflare-spotlight-integration branch from 4ebec68 to dff8f1bCompareJuly 23, 2026 17:03

@JPeer264JPeer264 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.

There is now a dev/prod build. I don't think that this works for wrangler, as they have special targets, but for Vite this is should work to use the correct bundle. 2kb is also not too bad unminified as the free tier now has 64MB, seems like they increased it

@JPeer264
JPeer264 enabled auto-merge (squash) July 24, 2026 06:59
@JPeer264
JPeer264 merged commit fe90ccd into developJul 24, 2026
549 of 551 checks passed
@JPeer264
JPeer264 deleted the feat/cloudflare-spotlight-integration branch July 24, 2026 07:58
JPeer264 added a commit that referenced this pull request Jul 29, 2026
…orwarding (#22796)
Backport of: #22490
And reverted dev/prod bundle split, as `wrangler` on its own doesn't
support that
---------
Co-authored-by: Aditya Mathur <57684218+MathurAditya724@users.noreply.github.com>
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.

3 participants

@MathurAditya724@timfish@JPeer264
, '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(cloudflare): Add Spotlight integration for local dev event forwarding - #22490

Merged
JPeer264 merged 6 commits into
developfrom
feat/cloudflare-spotlight-integration
Jul 24, 2026
Merged

feat(cloudflare): Add Spotlight integration for local dev event forwarding#22490
JPeer264 merged 6 commits into
developfrom
feat/cloudflare-spotlight-integration

Conversation

@MathurAditya724

@MathurAditya724MathurAditya724 commented Jul 22, 2026

Copy link
Copy Markdown
Member

Summary

Add a fetch-based spotlightIntegration to @sentry/cloudflare that mirrors Sentry envelopes to a local Spotlight sidecar during development. Cloudflare Workers was the only server SDK without Spotlight support.

What this enables

  • spotlight: true in Sentry.init() (or withSentry) forwards all envelopes (errors, transactions, logs, AI spans) to http://localhost:8969/stream
  • SENTRY_SPOTLIGHT wrangler env binding (boolean or custom URL) for zero-code-change enablement
  • Works with sentry local serve from the Sentry CLI for a complete local dev experience

Changes

  • New integrationsrc/integrations/spotlight.ts — uses fetch with suppressTracing (CF instruments outbound fetch), consumes response body (Workers requirement), disables after >3 failures
  • sdk.ts — wire integration in init() behind a runtime if (options.spotlight) guard (matches the node-core pattern)
  • options.ts — read SENTRY_SPOTLIGHT from CF env binding with the same precedence as node-core's getSpotlightConfig (user option > env boolean > env URL)
  • client.ts — add spotlight?: boolean | string to BaseCloudflareOptions (CloudflareOptions extends Options/CoreOptions, which does not include ServerRuntimeOptions where spotlight normally lives)
  • index.ts — export spotlightIntegration
  • Tests — full coverage for integration (10 tests) + options (9 new tests), all existing tests still pass

Why fetch, not node:http

Cloudflare Workers don't have node:http. The integration mirrors the browser SDK's fetch-based approach but adds suppressTracing (like Node) since CF's fetchIntegration instruments all outbound fetch, and drains the response body (Workers-specific requirement, same as the CF transport).

Production behavior

Spotlight forwarding only activates when options.spotlight is truthy, which is falsy in production by default — identical to how @sentry/node, @sentry/bun, and all other server SDKs ship the spotlight integration. Each forwarded envelope counts as a Worker subrequest (cap 50 free / 1000 paid), documented in the integration JSDoc; keep it disabled in production.

…rding
Add a fetch-based spotlightIntegration to @sentry/cloudflare that mirrors
envelopes to a local Spotlight sidecar during development. This closes the
gap where Cloudflare Workers was the only server SDK without Spotlight support.
- New integration at src/integrations/spotlight.ts using fetch + suppressTracing
- Wire in sdk.ts init() with rollup-include-development-only markers (stripped from prod builds)
- Read SENTRY_SPOTLIGHT from CF env binding in getFinalOptions (boolean or URL)
- Export spotlightIntegration from package index
- Full test coverage (integration tests + options tests)
CloudflareOptions extends Options (CoreOptions) which does not include
ServerRuntimeOptions where spotlight is declared. Add the property
directly to BaseCloudflareOptions so TypeScript resolves it correctly.
@github-actions

github-actionsBot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

⚠️Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

PathSize% ChangeChange
@sentry/browser27.79 kB--
@sentry/browser - with treeshaking flags26.23 kB--
@sentry/browser (incl. Tracing)46.31 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.06 kB--
@sentry/browser (incl. Tracing, Profiling)51.12 kB--
@sentry/browser (incl. Tracing, Replay)85.59 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags75.23 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)90.29 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)102.97 kB--
@sentry/browser (incl. Feedback)44.96 kB--
@sentry/browser (incl. sendFeedback)32.59 kB--
@sentry/browser (incl. FeedbackAsync)37.64 kB--
@sentry/browser (incl. Metrics)28.88 kB--
@sentry/browser (incl. Logs)29.11 kB--
@sentry/browser (incl. Metrics & Logs)29.8 kB--
@sentry/react29.59 kB--
@sentry/react (incl. Tracing)48.6 kB--
@sentry/vue33.22 kB--
@sentry/vue (incl. Tracing)48.28 kB--
@sentry/svelte27.81 kB--
CDN Bundle30.11 kB--
CDN Bundle (incl. Tracing)48.14 kB--
CDN Bundle (incl. Logs, Metrics)31.69 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)49.47 kB--
CDN Bundle (incl. Replay, Logs, Metrics)70.95 kB--
CDN Bundle (incl. Tracing, Replay)85.72 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)87.01 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)91.49 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.78 kB--
CDN Bundle - uncompressed89.77 kB--
CDN Bundle (incl. Tracing) - uncompressed144.84 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed94.48 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed148.82 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed219.24 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed264.08 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed268.04 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed277.78 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed281.74 kB--
@sentry/nextjs (client)51.13 kB--
@sentry/sveltekit (client)46.75 kB--
@sentry/core/server79.92 kB--
@sentry/core/browser51.9 kB--
@sentry/node124.4 kB-0.01%-1 B 🔽
@sentry/node (incl. diagnostics channel injection)149.83 kB--
@sentry/node/import (ESM hook with diagnostics-channel injection)70.03 kB--
@sentry/node - without tracing74.66 kB+0.01%+1 B 🔺
@sentry/aws-serverless84.13 kB-0.01%-1 B 🔽
@sentry/cloudflare (withSentry) - minified196.61 kB--
@sentry/cloudflare (withSentry)483.18 kB+0.05%+210 B 🔺

View base workflow run

- Fix spotlight precedence to match node-core's getSpotlightConfig:
spotlight: true + env URL → uses env URL (not bare true)
- Fix accidental extra-space indent on instrumentPrototypeMethods
- Add tests: spotlight:true + env URL, 4xx/5xx status handling
@MathurAditya724
MathurAditya724 requested a review from BYKJuly 22, 2026 12:44
@MathurAditya724
MathurAditya724 marked this pull request as ready for review July 22, 2026 12:48
@MathurAditya724
MathurAditya724 requested a review from a team as a code ownerJuly 22, 2026 12:48
@MathurAditya724
MathurAditya724 requested review from JPeer264 and isaacs and removed request for a teamJuly 22, 2026 12:48
…ght wiring
The rollup-include-development-only markers only strip content when the
package build uses splitDevProd (only @sentry/browser does). The Cloudflare
package uses a single build without that plugin, so the markers were inert
and misleading. Match the node-core pattern: guard spotlight wiring with the
runtime 'if (options.spotlight)' check, which is falsy in production.
@timfish

timfish commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Spotlight forwarding only activates when options.spotlight is truthy, which is falsy in production by default

I had a quick look through the code so I might have missed it but how does options.spotlight end up falsy in prod by default?

identical to how @sentry/node, and all other server SDKs ship the spotlight integration

This is not true. The Node SDK specifically ship dev/prod output and strips this from production builds.

@timfish

Copy link
Copy Markdown
Collaborator

This is not true. The Node SDK specifically ship dev/prod output and strips this from production builds

I was wrong, we only do this in the browser:

/*! rollup-include-development-only */
if(options.spotlight){
if(!defaultIntegrations){
defaultIntegrations=[];
}
constargs=typeofoptions.spotlight==='string' ? {sidecarUrl: options.spotlight} : undefined;
defaultIntegrations.push(spotlightBrowserIntegration(args));
}
/*! rollup-include-development-only-end */

@JPeer264

Copy link
Copy Markdown
Member

I was wrong, we only do this in the browser:

Ya, I just went through and we only have this in the browser. @timfish if I'm not mistaken we only have to add splitDevProd in the rollup config and change the exports right?

@JPeer264

Copy link
Copy Markdown
Member

FYI. Gonna merge this latest tomorrow. If you want to test it you can yalc the Cloudflare package from this branch: https://github.com/getsentry/sentry-javascript/blob/develop/docs/using-yalc.md

@JPeer264
JPeer264force-pushed the feat/cloudflare-spotlight-integration branch from 4ebec68 to dff8f1bCompareJuly 23, 2026 17:03

@JPeer264JPeer264 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.

There is now a dev/prod build. I don't think that this works for wrangler, as they have special targets, but for Vite this is should work to use the correct bundle. 2kb is also not too bad unminified as the free tier now has 64MB, seems like they increased it

@JPeer264
JPeer264 enabled auto-merge (squash) July 24, 2026 06:59
@JPeer264
JPeer264 merged commit fe90ccd into developJul 24, 2026
549 of 551 checks passed
@JPeer264
JPeer264 deleted the feat/cloudflare-spotlight-integration branch July 24, 2026 07:58
JPeer264 added a commit that referenced this pull request Jul 29, 2026
…orwarding (#22796)
Backport of: #22490
And reverted dev/prod bundle split, as `wrangler` on its own doesn't
support that
---------
Co-authored-by: Aditya Mathur <57684218+MathurAditya724@users.noreply.github.com>
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.

3 participants

@MathurAditya724@timfish@JPeer264
, '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(cloudflare): Add Spotlight integration for local dev event forwarding - #22490

Merged
JPeer264 merged 6 commits into
developfrom
feat/cloudflare-spotlight-integration
Jul 24, 2026
Merged

feat(cloudflare): Add Spotlight integration for local dev event forwarding#22490
JPeer264 merged 6 commits into
developfrom
feat/cloudflare-spotlight-integration

Conversation

@MathurAditya724

@MathurAditya724MathurAditya724 commented Jul 22, 2026

Copy link
Copy Markdown
Member

Summary

Add a fetch-based spotlightIntegration to @sentry/cloudflare that mirrors Sentry envelopes to a local Spotlight sidecar during development. Cloudflare Workers was the only server SDK without Spotlight support.

What this enables

  • spotlight: true in Sentry.init() (or withSentry) forwards all envelopes (errors, transactions, logs, AI spans) to http://localhost:8969/stream
  • SENTRY_SPOTLIGHT wrangler env binding (boolean or custom URL) for zero-code-change enablement
  • Works with sentry local serve from the Sentry CLI for a complete local dev experience

Changes

  • New integrationsrc/integrations/spotlight.ts — uses fetch with suppressTracing (CF instruments outbound fetch), consumes response body (Workers requirement), disables after >3 failures
  • sdk.ts — wire integration in init() behind a runtime if (options.spotlight) guard (matches the node-core pattern)
  • options.ts — read SENTRY_SPOTLIGHT from CF env binding with the same precedence as node-core's getSpotlightConfig (user option > env boolean > env URL)
  • client.ts — add spotlight?: boolean | string to BaseCloudflareOptions (CloudflareOptions extends Options/CoreOptions, which does not include ServerRuntimeOptions where spotlight normally lives)
  • index.ts — export spotlightIntegration
  • Tests — full coverage for integration (10 tests) + options (9 new tests), all existing tests still pass

Why fetch, not node:http

Cloudflare Workers don't have node:http. The integration mirrors the browser SDK's fetch-based approach but adds suppressTracing (like Node) since CF's fetchIntegration instruments all outbound fetch, and drains the response body (Workers-specific requirement, same as the CF transport).

Production behavior

Spotlight forwarding only activates when options.spotlight is truthy, which is falsy in production by default — identical to how @sentry/node, @sentry/bun, and all other server SDKs ship the spotlight integration. Each forwarded envelope counts as a Worker subrequest (cap 50 free / 1000 paid), documented in the integration JSDoc; keep it disabled in production.

…rding
Add a fetch-based spotlightIntegration to @sentry/cloudflare that mirrors
envelopes to a local Spotlight sidecar during development. This closes the
gap where Cloudflare Workers was the only server SDK without Spotlight support.
- New integration at src/integrations/spotlight.ts using fetch + suppressTracing
- Wire in sdk.ts init() with rollup-include-development-only markers (stripped from prod builds)
- Read SENTRY_SPOTLIGHT from CF env binding in getFinalOptions (boolean or URL)
- Export spotlightIntegration from package index
- Full test coverage (integration tests + options tests)
CloudflareOptions extends Options (CoreOptions) which does not include
ServerRuntimeOptions where spotlight is declared. Add the property
directly to BaseCloudflareOptions so TypeScript resolves it correctly.
@github-actions

github-actionsBot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

⚠️Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

PathSize% ChangeChange
@sentry/browser27.79 kB--
@sentry/browser - with treeshaking flags26.23 kB--
@sentry/browser (incl. Tracing)46.31 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.06 kB--
@sentry/browser (incl. Tracing, Profiling)51.12 kB--
@sentry/browser (incl. Tracing, Replay)85.59 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags75.23 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)90.29 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)102.97 kB--
@sentry/browser (incl. Feedback)44.96 kB--
@sentry/browser (incl. sendFeedback)32.59 kB--
@sentry/browser (incl. FeedbackAsync)37.64 kB--
@sentry/browser (incl. Metrics)28.88 kB--
@sentry/browser (incl. Logs)29.11 kB--
@sentry/browser (incl. Metrics & Logs)29.8 kB--
@sentry/react29.59 kB--
@sentry/react (incl. Tracing)48.6 kB--
@sentry/vue33.22 kB--
@sentry/vue (incl. Tracing)48.28 kB--
@sentry/svelte27.81 kB--
CDN Bundle30.11 kB--
CDN Bundle (incl. Tracing)48.14 kB--
CDN Bundle (incl. Logs, Metrics)31.69 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)49.47 kB--
CDN Bundle (incl. Replay, Logs, Metrics)70.95 kB--
CDN Bundle (incl. Tracing, Replay)85.72 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)87.01 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)91.49 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.78 kB--
CDN Bundle - uncompressed89.77 kB--
CDN Bundle (incl. Tracing) - uncompressed144.84 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed94.48 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed148.82 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed219.24 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed264.08 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed268.04 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed277.78 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed281.74 kB--
@sentry/nextjs (client)51.13 kB--
@sentry/sveltekit (client)46.75 kB--
@sentry/core/server79.92 kB--
@sentry/core/browser51.9 kB--
@sentry/node124.4 kB-0.01%-1 B 🔽
@sentry/node (incl. diagnostics channel injection)149.83 kB--
@sentry/node/import (ESM hook with diagnostics-channel injection)70.03 kB--
@sentry/node - without tracing74.66 kB+0.01%+1 B 🔺
@sentry/aws-serverless84.13 kB-0.01%-1 B 🔽
@sentry/cloudflare (withSentry) - minified196.61 kB--
@sentry/cloudflare (withSentry)483.18 kB+0.05%+210 B 🔺

View base workflow run

- Fix spotlight precedence to match node-core's getSpotlightConfig:
spotlight: true + env URL → uses env URL (not bare true)
- Fix accidental extra-space indent on instrumentPrototypeMethods
- Add tests: spotlight:true + env URL, 4xx/5xx status handling
@MathurAditya724
MathurAditya724 requested a review from BYKJuly 22, 2026 12:44
@MathurAditya724
MathurAditya724 marked this pull request as ready for review July 22, 2026 12:48
@MathurAditya724
MathurAditya724 requested a review from a team as a code ownerJuly 22, 2026 12:48
@MathurAditya724
MathurAditya724 requested review from JPeer264 and isaacs and removed request for a teamJuly 22, 2026 12:48
…ght wiring
The rollup-include-development-only markers only strip content when the
package build uses splitDevProd (only @sentry/browser does). The Cloudflare
package uses a single build without that plugin, so the markers were inert
and misleading. Match the node-core pattern: guard spotlight wiring with the
runtime 'if (options.spotlight)' check, which is falsy in production.
@timfish

timfish commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Spotlight forwarding only activates when options.spotlight is truthy, which is falsy in production by default

I had a quick look through the code so I might have missed it but how does options.spotlight end up falsy in prod by default?

identical to how @sentry/node, and all other server SDKs ship the spotlight integration

This is not true. The Node SDK specifically ship dev/prod output and strips this from production builds.

@timfish

Copy link
Copy Markdown
Collaborator

This is not true. The Node SDK specifically ship dev/prod output and strips this from production builds

I was wrong, we only do this in the browser:

/*! rollup-include-development-only */
if(options.spotlight){
if(!defaultIntegrations){
defaultIntegrations=[];
}
constargs=typeofoptions.spotlight==='string' ? {sidecarUrl: options.spotlight} : undefined;
defaultIntegrations.push(spotlightBrowserIntegration(args));
}
/*! rollup-include-development-only-end */

@JPeer264

Copy link
Copy Markdown
Member

I was wrong, we only do this in the browser:

Ya, I just went through and we only have this in the browser. @timfish if I'm not mistaken we only have to add splitDevProd in the rollup config and change the exports right?

@JPeer264

Copy link
Copy Markdown
Member

FYI. Gonna merge this latest tomorrow. If you want to test it you can yalc the Cloudflare package from this branch: https://github.com/getsentry/sentry-javascript/blob/develop/docs/using-yalc.md

@JPeer264
JPeer264force-pushed the feat/cloudflare-spotlight-integration branch from 4ebec68 to dff8f1bCompareJuly 23, 2026 17:03

@JPeer264JPeer264 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.

There is now a dev/prod build. I don't think that this works for wrangler, as they have special targets, but for Vite this is should work to use the correct bundle. 2kb is also not too bad unminified as the free tier now has 64MB, seems like they increased it

@JPeer264
JPeer264 enabled auto-merge (squash) July 24, 2026 06:59
@JPeer264
JPeer264 merged commit fe90ccd into developJul 24, 2026
549 of 551 checks passed
@JPeer264
JPeer264 deleted the feat/cloudflare-spotlight-integration branch July 24, 2026 07:58
JPeer264 added a commit that referenced this pull request Jul 29, 2026
…orwarding (#22796)
Backport of: #22490
And reverted dev/prod bundle split, as `wrangler` on its own doesn't
support that
---------
Co-authored-by: Aditya Mathur <57684218+MathurAditya724@users.noreply.github.com>
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.

3 participants

@MathurAditya724@timfish@JPeer264
, '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(cloudflare): Add Spotlight integration for local dev event forwarding - #22490

Merged
JPeer264 merged 6 commits into
developfrom
feat/cloudflare-spotlight-integration
Jul 24, 2026
Merged

feat(cloudflare): Add Spotlight integration for local dev event forwarding#22490
JPeer264 merged 6 commits into
developfrom
feat/cloudflare-spotlight-integration

Conversation

@MathurAditya724

@MathurAditya724MathurAditya724 commented Jul 22, 2026

Copy link
Copy Markdown
Member

Summary

Add a fetch-based spotlightIntegration to @sentry/cloudflare that mirrors Sentry envelopes to a local Spotlight sidecar during development. Cloudflare Workers was the only server SDK without Spotlight support.

What this enables

  • spotlight: true in Sentry.init() (or withSentry) forwards all envelopes (errors, transactions, logs, AI spans) to http://localhost:8969/stream
  • SENTRY_SPOTLIGHT wrangler env binding (boolean or custom URL) for zero-code-change enablement
  • Works with sentry local serve from the Sentry CLI for a complete local dev experience

Changes

  • New integrationsrc/integrations/spotlight.ts — uses fetch with suppressTracing (CF instruments outbound fetch), consumes response body (Workers requirement), disables after >3 failures
  • sdk.ts — wire integration in init() behind a runtime if (options.spotlight) guard (matches the node-core pattern)
  • options.ts — read SENTRY_SPOTLIGHT from CF env binding with the same precedence as node-core's getSpotlightConfig (user option > env boolean > env URL)
  • client.ts — add spotlight?: boolean | string to BaseCloudflareOptions (CloudflareOptions extends Options/CoreOptions, which does not include ServerRuntimeOptions where spotlight normally lives)
  • index.ts — export spotlightIntegration
  • Tests — full coverage for integration (10 tests) + options (9 new tests), all existing tests still pass

Why fetch, not node:http

Cloudflare Workers don't have node:http. The integration mirrors the browser SDK's fetch-based approach but adds suppressTracing (like Node) since CF's fetchIntegration instruments all outbound fetch, and drains the response body (Workers-specific requirement, same as the CF transport).

Production behavior

Spotlight forwarding only activates when options.spotlight is truthy, which is falsy in production by default — identical to how @sentry/node, @sentry/bun, and all other server SDKs ship the spotlight integration. Each forwarded envelope counts as a Worker subrequest (cap 50 free / 1000 paid), documented in the integration JSDoc; keep it disabled in production.

…rding
Add a fetch-based spotlightIntegration to @sentry/cloudflare that mirrors
envelopes to a local Spotlight sidecar during development. This closes the
gap where Cloudflare Workers was the only server SDK without Spotlight support.
- New integration at src/integrations/spotlight.ts using fetch + suppressTracing
- Wire in sdk.ts init() with rollup-include-development-only markers (stripped from prod builds)
- Read SENTRY_SPOTLIGHT from CF env binding in getFinalOptions (boolean or URL)
- Export spotlightIntegration from package index
- Full test coverage (integration tests + options tests)
CloudflareOptions extends Options (CoreOptions) which does not include
ServerRuntimeOptions where spotlight is declared. Add the property
directly to BaseCloudflareOptions so TypeScript resolves it correctly.
@github-actions

github-actionsBot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

⚠️Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

PathSize% ChangeChange
@sentry/browser27.79 kB--
@sentry/browser - with treeshaking flags26.23 kB--
@sentry/browser (incl. Tracing)46.31 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.06 kB--
@sentry/browser (incl. Tracing, Profiling)51.12 kB--
@sentry/browser (incl. Tracing, Replay)85.59 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags75.23 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)90.29 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)102.97 kB--
@sentry/browser (incl. Feedback)44.96 kB--
@sentry/browser (incl. sendFeedback)32.59 kB--
@sentry/browser (incl. FeedbackAsync)37.64 kB--
@sentry/browser (incl. Metrics)28.88 kB--
@sentry/browser (incl. Logs)29.11 kB--
@sentry/browser (incl. Metrics & Logs)29.8 kB--
@sentry/react29.59 kB--
@sentry/react (incl. Tracing)48.6 kB--
@sentry/vue33.22 kB--
@sentry/vue (incl. Tracing)48.28 kB--
@sentry/svelte27.81 kB--
CDN Bundle30.11 kB--
CDN Bundle (incl. Tracing)48.14 kB--
CDN Bundle (incl. Logs, Metrics)31.69 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)49.47 kB--
CDN Bundle (incl. Replay, Logs, Metrics)70.95 kB--
CDN Bundle (incl. Tracing, Replay)85.72 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)87.01 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)91.49 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.78 kB--
CDN Bundle - uncompressed89.77 kB--
CDN Bundle (incl. Tracing) - uncompressed144.84 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed94.48 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed148.82 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed219.24 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed264.08 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed268.04 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed277.78 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed281.74 kB--
@sentry/nextjs (client)51.13 kB--
@sentry/sveltekit (client)46.75 kB--
@sentry/core/server79.92 kB--
@sentry/core/browser51.9 kB--
@sentry/node124.4 kB-0.01%-1 B 🔽
@sentry/node (incl. diagnostics channel injection)149.83 kB--
@sentry/node/import (ESM hook with diagnostics-channel injection)70.03 kB--
@sentry/node - without tracing74.66 kB+0.01%+1 B 🔺
@sentry/aws-serverless84.13 kB-0.01%-1 B 🔽
@sentry/cloudflare (withSentry) - minified196.61 kB--
@sentry/cloudflare (withSentry)483.18 kB+0.05%+210 B 🔺

View base workflow run

- Fix spotlight precedence to match node-core's getSpotlightConfig:
spotlight: true + env URL → uses env URL (not bare true)
- Fix accidental extra-space indent on instrumentPrototypeMethods
- Add tests: spotlight:true + env URL, 4xx/5xx status handling
@MathurAditya724
MathurAditya724 requested a review from BYKJuly 22, 2026 12:44
@MathurAditya724
MathurAditya724 marked this pull request as ready for review July 22, 2026 12:48
@MathurAditya724
MathurAditya724 requested a review from a team as a code ownerJuly 22, 2026 12:48
@MathurAditya724
MathurAditya724 requested review from JPeer264 and isaacs and removed request for a teamJuly 22, 2026 12:48
…ght wiring
The rollup-include-development-only markers only strip content when the
package build uses splitDevProd (only @sentry/browser does). The Cloudflare
package uses a single build without that plugin, so the markers were inert
and misleading. Match the node-core pattern: guard spotlight wiring with the
runtime 'if (options.spotlight)' check, which is falsy in production.
@timfish

timfish commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Spotlight forwarding only activates when options.spotlight is truthy, which is falsy in production by default

I had a quick look through the code so I might have missed it but how does options.spotlight end up falsy in prod by default?

identical to how @sentry/node, and all other server SDKs ship the spotlight integration

This is not true. The Node SDK specifically ship dev/prod output and strips this from production builds.

@timfish

Copy link
Copy Markdown
Collaborator

This is not true. The Node SDK specifically ship dev/prod output and strips this from production builds

I was wrong, we only do this in the browser:

/*! rollup-include-development-only */
if(options.spotlight){
if(!defaultIntegrations){
defaultIntegrations=[];
}
constargs=typeofoptions.spotlight==='string' ? {sidecarUrl: options.spotlight} : undefined;
defaultIntegrations.push(spotlightBrowserIntegration(args));
}
/*! rollup-include-development-only-end */

@JPeer264

Copy link
Copy Markdown
Member

I was wrong, we only do this in the browser:

Ya, I just went through and we only have this in the browser. @timfish if I'm not mistaken we only have to add splitDevProd in the rollup config and change the exports right?

@JPeer264

Copy link
Copy Markdown
Member

FYI. Gonna merge this latest tomorrow. If you want to test it you can yalc the Cloudflare package from this branch: https://github.com/getsentry/sentry-javascript/blob/develop/docs/using-yalc.md

@JPeer264
JPeer264force-pushed the feat/cloudflare-spotlight-integration branch from 4ebec68 to dff8f1bCompareJuly 23, 2026 17:03

@JPeer264JPeer264 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.

There is now a dev/prod build. I don't think that this works for wrangler, as they have special targets, but for Vite this is should work to use the correct bundle. 2kb is also not too bad unminified as the free tier now has 64MB, seems like they increased it

@JPeer264
JPeer264 enabled auto-merge (squash) July 24, 2026 06:59
@JPeer264
JPeer264 merged commit fe90ccd into developJul 24, 2026
549 of 551 checks passed
@JPeer264
JPeer264 deleted the feat/cloudflare-spotlight-integration branch July 24, 2026 07:58
JPeer264 added a commit that referenced this pull request Jul 29, 2026
…orwarding (#22796)
Backport of: #22490
And reverted dev/prod bundle split, as `wrangler` on its own doesn't
support that
---------
Co-authored-by: Aditya Mathur <57684218+MathurAditya724@users.noreply.github.com>
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.

3 participants

@MathurAditya724@timfish@JPeer264