Skip to content

feat: Polish the public tunnel API and export it on Server SDKs - #25783

Merged
logaretm merged 17 commits into
developfrom
awad/tunnel-server-exports
Oct 28, 2026
Merged

logaretm merged 17 commits into
developfrom
awad/tunnel-server-exports

Conversation

@logaretm

@logaretm logaretm commented Oct 27, 2026 •

Copy link
Copy Markdown
Member

This PR ties several loose ends, we wanted to allow custom setups to use our new core utilities. This means we need to export the APIs we have added and polish the call signatures for consistency.

Exported APIs

This exports APIs and types that are meant for public use, other APIs are marked as _INTERNAL_.

  • serveTunnelIntegration(options?): serves the managed tunnel for the browser's tunnel: true.
  • handleTunnelRequestIfMatched(request, options?) or (req, res, options?): forwards a tunnel request, returns undefined for anything else.
  • isTunnelRequest(request, options?): whether a request targets the tunnel, without reading the body.
  • getTunnelPath(dsn?): the tunnel path the browser derives from a DSN, the client's own by default.
  • handleTunnelRequest(request, options?) or (req, res, options?): forwards a request on a route you registered yourself.
  • ServerTunnelOptions: path, basePath, allowedDsns.
  • TunnelRequestLike: the request shape isTunnelRequest accepts, method and url.
  • HandleTunnelRequestOptions: allowedDsns, plus the deprecated request of the released form.

API Signature Changes

Some of these functions are getting a makeover to make them consistent with the new APIs and more ergonomic.

Global Args Fallbacks

The current utils were all over the place because we had two public APIs competing, the one shipped way back with Tanstack and the new one.

The idea is, if a user uses any of these utils themselves then we need to ensure no footguns exist, this is ensured by:

  1. Every util takes its options optionally. Without them it falls back to what serveTunnelIntegration is configured with, and without that to the server's own DSN.
  2. allowedDsns always merges the server's own DSN.
  3. Both fetch-style request and node-style request objects are handled.

The framework SDKs keep serving the tunnel only when serveTunnelIntegration is set up, they use internal versions without the fallbacks.

handleTunnelRequest

Now receives either a singular fetch request object and returns a response, OR a req, res pair to match connect style frameworks. This means it is now able to handle conversions between Web-fetch style requests and server-style requests. I deprecated the request option of the currently released signature but it is still there, will be dropped in v12.

This allows users to wire up their frameworks manually like so:

import express from 'express';
import * as Sentry from '@sentry/node';

const app = express();

// route handler style
app.post(Sentry.getTunnelPath()!, (req, res) => Sentry.handleTunnelRequest(req, res));

// middleware style
app.use((req, res, next) => Sentry.handleTunnelRequestIfMatched(req, res) ?? next());
// Nextjs
// proxy.ts file
import { NextResponse, type NextRequest } from 'next/server';
import * as Sentry from '@sentry/nextjs';

export function proxy(request: NextRequest) {
  return Sentry.handleTunnelRequestIfMatched(request) ?? NextResponse.next();
}

👀 How to review this?

Most file changes are direct copy/replace for handleServedTunnelRequest API, where the frameworks will use their own version with stricter conditions. Many other changes is just re-exports, so you can glance over these and look at the actual changes outlined above.

Files worth a close look:

@logaretm

Copy link
Copy Markdown
Member Author

bugbot run

@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

size-limit report 📦

Compared: 6c721ce (PR test merge using develop at c5c2a3a)

Against: c5c2a3a (develop) · base run #63956

✅ Baseline matches: this diff contains only this PR's changes.

Path Size % Change Change
@sentry/browser 30.34 kB +0.04% +12 B 🔺
@sentry/browser - with treeshaking flags 28.46 kB +0.05% +12 B 🔺
@sentry/browser - with treeshaking flags tracing without tracing 28.35 kB +0.04% +11 B 🔺
@sentry/browser (incl. Tracing) 52.61 kB +0.03% +11 B 🔺
@sentry/browser (incl. Tracing + Span Streaming) 52.62 kB +0.03% +12 B 🔺
@sentry/browser (incl. Tracing, Profiling) 55.54 kB +0.02% +10 B 🔺
@sentry/browser (incl. Tracing, Replay) 92.81 kB +0.02% +11 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 81.43 kB +0.02% +15 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas) 97.54 kB +0.02% +11 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback) 110.52 kB +0.01% +11 B 🔺
@sentry/browser (incl. Feedback) 47.9 kB +0.02% +8 B 🔺
@sentry/browser (incl. sendFeedback) 35.41 kB +0.04% +12 B 🔺
@sentry/browser (incl. FeedbackAsync) 40.52 kB +0.04% +13 B 🔺
@sentry/browser (incl. Metrics) 31.35 kB +0.04% +12 B 🔺
@sentry/browser (incl. Logs) 31.63 kB +0.04% +11 B 🔺
@sentry/browser (incl. Metrics & Logs) 32.31 kB +0.04% +12 B 🔺
@sentry/react 32.16 kB +0.04% +11 B 🔺
@sentry/react (incl. Tracing) 54.89 kB +0.02% +6 B 🔺
@sentry/vue 38.61 kB +0.04% +12 B 🔺
@sentry/vue (incl. Tracing) 55.5 kB +0.02% +10 B 🔺
@sentry/svelte 30.37 kB +0.04% +12 B 🔺
@sentry/remix (Remix 3 client bundle) 57.65 kB +0.02% +7 B 🔺
CDN Bundle 32.07 kB +0.03% +9 B 🔺
CDN Bundle (incl. Tracing) 53.1 kB +0.03% +11 B 🔺
CDN Bundle (incl. Logs, Metrics) 34.27 kB +0.03% +9 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) 55.05 kB +0.02% +8 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) 75.69 kB +0.02% +10 B 🔺
CDN Bundle (incl. Tracing, Replay) 91.23 kB +0.01% +9 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 93.17 kB +0.01% +8 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) 97.36 kB +0.01% +9 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 99.34 kB +0.02% +11 B 🔺
CDN Bundle - uncompressed 94.31 kB +0.02% +17 B 🔺
CDN Bundle (incl. Tracing) - uncompressed 157.3 kB +0.02% +17 B 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed 100.86 kB +0.02% +17 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 163.27 kB +0.02% +17 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 232.36 kB +0.01% +17 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed 278.93 kB +0.01% +17 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 284.88 kB +0.01% +17 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 292.63 kB +0.01% +17 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 298.58 kB +0.01% +17 B 🔺
@sentry/nextjs (client) 57.75 kB +0.02% +10 B 🔺
@sentry/sveltekit (client) 53.06 kB +0.03% +12 B 🔺
@sentry/core/server 44.29 kB +1.05% +456 B 🔺
@sentry/core/browser 13.95 kB - -
@sentry/node 154.76 kB +0.04% +49 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 81.61 kB -0.01% -2 B 🔽
@sentry/node - without tracing 95.72 kB +0.04% +29 B 🔺
@sentry/node - without channel injection 135.39 kB +0.04% +46 B 🔺
@sentry/aws-serverless 103.89 kB +0.04% +37 B 🔺
@sentry/cloudflare (withSentry) - minified 197.89 kB +0.06% +118 B 🔺
@sentry/cloudflare (withSentry) 491.82 kB +0.07% +316 B 🔺
@sentry/nextjs/cloudflare (withSentry) - minified 218.47 kB +0.06% +118 B 🔺

View base workflow run

@cursor cursor Bot 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.

Stale Bugbot comment from a previous run.

Comment thread packages/astro/src/index.server.ts
Comment thread packages/node/test/tunnelExports.test.ts
@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Optional E2E tests

Some optional checks were not successful. They do not block the merge. 3 not successful, 64 successful. See the run.

Check Status
❌ CI: Optional E2E Tests / E2E node-eve (latest) Test (optional) Failing after 2m Details
❌ CI: Optional E2E Tests / E2E node-anthropic-send-to-sentry (latest) Test (optional) Failing after 3m Details
❌ CI: Optional E2E Tests / E2E node-mastra Test (optional) Failing after 20s Details
Successful checks
Check Status
✅ CI: Optional E2E Tests / E2E cloudflare-agent Test (optional) Successful in 22s Details
✅ CI: Optional E2E Tests / E2E node-flue Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E cloudflare-workers-send-to-sentry Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E nuxt-4-static (canary) Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E nuxt-4 (canary) Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E node-eve Test (optional) Successful in 45s Details
✅ CI: Optional E2E Tests / E2E cloudflare-think Test (optional) Successful in 54s Details
✅ CI: Optional E2E Tests / E2E cloudflare-pi-durable Test (optional) Successful in 27s Details
✅ CI: Optional E2E Tests / E2E angular (canary) Test (optional) Successful in 54s Details
✅ CI: Optional E2E Tests / E2E cloudflare-flue-send-to-sentry Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E cloudflare-pi-durable (latest) Test (optional) Successful in 26s Details
✅ CI: Optional E2E Tests / E2E tanstackstart-react-cloudflare Test (optional) Successful in 34s Details
✅ CI: Optional E2E Tests / E2E deno (latest) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E nextjs-14 (latest) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E node-openai-decisions-send-to-sentry Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E node-pi-durable (latest) Test (optional) Successful in 25s Details
✅ CI: Optional E2E Tests / E2E node-openai-send-to-sentry Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E nextjs-16 (deno) Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E nextjs-14 (canary) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E solidstart-2 Test (optional) Successful in 38s Details
✅ CI: Optional E2E Tests / E2E angular (canary) Test (optional) Successful in 59s Details
✅ CI: Optional E2E Tests / E2E node-pi-durable Test (optional) Successful in 26s Details
✅ CI: Optional E2E Tests / E2E nextjs-16 (canary, webpack) Test (optional) Successful in 5m Details
✅ CI: Optional E2E Tests / E2E node-flue (latest) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E cloudflare-mastra Test (optional) Successful in 42s Details
✅ CI: Optional E2E Tests / E2E debug-id-sourcemaps Test (optional) Successful in 30s Details
✅ CI: Optional E2E Tests / E2E nextjs-15 (turbo) Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E gen-ai-libraries (cloudflare) Test (optional) Successful in 23s Details
✅ CI: Optional E2E Tests / E2E astro-7 Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E nuxt-5 Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E nextjs-15 (latest) Test (optional) Successful in 3m Details
✅ CI: Optional E2E Tests / E2E cloudflare-think (ai v6) Test (optional) Successful in 36s Details
✅ CI: Optional E2E Tests / E2E node-openai-decisions-send-to-sentry (latest) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E astro-7-static Test (optional) Successful in 55s Details
✅ CI: Optional E2E Tests / E2E gen-ai-libraries Test (optional) Successful in 34s Details
✅ CI: Optional E2E Tests / E2E nuxt-5-sourcemaps (static / nuxt generate) Test (optional) Successful in 21s Details
✅ CI: Optional E2E Tests / E2E react-send-to-sentry Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E supabase-nextjs Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E node-express-send-to-sentry Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E nextjs-16 (cloudflare, latest) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E nextjs-16 (vinext) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E node-eve (orchestrion) Test (optional) Successful in 39s Details
✅ CI: Optional E2E Tests / E2E nextjs-16 (vinext, bun) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E node-express-send-to-sentry-static Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E node-fastify-6 (tracer provider) Test (optional) Successful in 20s Details
✅ CI: Optional E2E Tests / E2E node-express-mcp-v1 Test (optional) Successful in 25s Details
✅ CI: Optional E2E Tests / E2E node-flue-send-to-sentry (latest) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E nextjs-16-tunnel (cloudflare) Test (optional) Successful in 41s Details
✅ CI: Optional E2E Tests / E2E node-mastra (latest) Test (optional) Successful in 53s Details
✅ CI: Optional E2E Tests / E2E nextjs-16-standalone (canary, turbopack) Test (optional) Successful in 30s Details
✅ CI: Optional E2E Tests / E2E node-openai-send-to-sentry (latest) Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E nextjs-16 (canary, turbopack) Test (optional) Successful in 3m Details
✅ CI: Optional E2E Tests / E2E node-anthropic-send-to-sentry Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E node-flue-send-to-sentry Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E node-fastify-6 Test (optional) Successful in 21s Details
✅ CI: Optional E2E Tests / E2E node-express-mcp-v2 Test (optional) Successful in 19s Details
✅ CI: Optional E2E Tests / E2E nextjs-orpc Test (optional) Successful in 57s Details
✅ CI: Optional E2E Tests / E2E nuxt-5-sourcemaps (client source maps kept) Test (optional) Successful in 23s Details
✅ CI: Optional E2E Tests / E2E nuxt-5-sourcemaps Test (optional) Successful in 22s Details
✅ CI: Optional E2E Tests / E2E nuxt-3 (canary) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E react-router-7-framework-instrumentation Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E vue-3 (no Options API) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E tanstackstart-react-sourcemaps Test (optional) Successful in 24s Details
✅ CI: Optional E2E Tests / E2E vue-3 (canary) Test (optional) Successful in 53s Details

@logaretm logaretm changed the title feat: Export the managed tunnel API from every server SDK feat: Polish the public tunnel API and export it on Server SDKs Oct 27, 2026
@logaretm

Copy link
Copy Markdown
Member Author

bugbot run

@cursor cursor Bot 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.

Stale Bugbot comment from a previous run.

Comment thread packages/core/src/utils/tunnelForward.ts Outdated
Comment thread packages/core/src/utils/tunnelForward.ts
Comment thread packages/core/test/lib/utils/tunnel.test.ts
logaretm added a commit to getsentry/sentry-docs that referenced this pull request Oct 27, 2026
Covers the tunnel helpers every server SDK exports and the new
handleTunnelRequest signature from getsentry/sentry-javascript#25783.
@github-actions

Copy link
Copy Markdown
Contributor

One or more gzipped bundles increased by more than 500 bytes. If this increase is intentional, add the Accept Bundlesize Increase label to this PR to rerun and accept the check.

@logaretm

Copy link
Copy Markdown
Member Author

bugbot run

@cursor cursor Bot 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.

Stale Bugbot comment from a previous run.

Comment thread dev-packages/cloudflare-integration-tests/suites/tunnel/index.ts Outdated
Comment thread dev-packages/node-integration-tests/suites/tunnel/manual-wiring/test.ts Outdated
@logaretm

Copy link
Copy Markdown
Member Author

bugbot run

@cursor cursor Bot 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit e794fd6. Configure here.

@logaretm
logaretm force-pushed the awad/tunnel-server-exports branch 2 times, most recently from 31401af to 9bbc09d Compare October 27, 2026 21:59
@logaretm
logaretm added this pull request to stack #25786 October 27, 2026 21:59
@logaretm
logaretm marked this pull request as ready for review October 27, 2026 23:43
@logaretm
logaretm requested review from a team as code owners October 27, 2026 23:43
@logaretm
logaretm requested review from chargome, isaacs, mydea and nicohrubec and removed request for a team October 27, 2026 23:43
@logaretm

Copy link
Copy Markdown
Member Author

@Lms24 I think we shouldn't fallback to a fixed path because the browser wouldn't know if the server serves it or not. I think we can document the middleware style for random paths and keep the fixed path for specific paths so it would work both ways.

DSN being unset would be safe to use in the middleware style. WDYT?

@logaretm
logaretm requested review from Lms24 and chargome October 28, 2026 13:31
@Lms24

Lms24 commented Oct 28, 2026

Copy link
Copy Markdown
Member

I think we shouldn't fallback to a fixed path because the browser wouldn't know if the server serves it or not. I think we can document the middleware style for random paths and keep the fixed path for specific paths so it would work both ways.

yeah I'm fine with handling this on a documentation level. Sounds good, thx!

Users wiring the tunnel by hand could only import these from `@sentry/core`, which strict pnpm installs do not resolve.
…pt a Node request and response

The released options object form keeps working and is deprecated.
…erveTunnelIntegration`, then the own DSN

`isTunnelRequest`, `handleTunnelRequestIfMatched` and `getTunnelPath` work without the integration now, and `handleTunnelRequestIfMatched` also takes a Node request and response. The framework SDKs keep serving only with the integration, through internal served-only versions.
…eTunnelRequest` overload

The deprecated overload made every re-export of the function fail the deprecation lint.
… did not set it up

The Node form skips HTTP/2 pseudo-headers, which fetch rejects. A copy of core without the served tunnel, like the one in a Next.js middleware bundle, rebuilds it from the client config and the integration.
…l request

HTTP/2 servers reject connection-specific headers like keep-alive, which
Sentry's answer carries.
Reuse resolveTunnelMatch for the allowed DSNs and the served match, stream the
Node body with ReadableStream.from, and write back only the headers the browser
SDK reads.
Caches the served tunnel a second module copy rebuilds, stores its base path so it matches directly, and splits the Node tunnel path into request and response conversions so both entry points forward with DSNs they already resolved.
…nt's Host header

Node accepts Host headers that aren't valid URL hosts, which made the Request
constructor throw.
…TunnelRequest options type, and accept HTTP/2 responses
Unread body bytes stalled the next request on a keep-alive connection.
@logaretm
logaretm force-pushed the awad/tunnel-server-exports branch from 087a1bf to 1c77ce4 Compare October 28, 2026 14:18
import type { ServerTunnelOptions } from '../types/options';
import { enableManagedTunnel, SERVE_TUNNEL_INTEGRATION_NAME } from '../utils/managedTunnelDefaults';
import { enableManagedTunnel } from '../utils/managedTunnelDefaults';
import { SERVE_TUNNEL_INTEGRATION_NAME } from '../utils/tunnel';

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.

L and out of scope of the changes of his PR: The JSDoc of serveTunnelIntegration doesn't read very user-facing, making it hard to understand when users should add it and how. I'd reword it a bit (can't make a direct suggestion since the change isn't part of this PR).

Here's a rough suggestion:

/**
 * Configures your Sentry server SDK to handle and forward requests from a Sentry browser SDK configured with `tunnel: true`.
 *
 * With this integration on the server and `tunnel: true` in the browser `Sentry.init`, the browser SDK sends its events to  * your own server instead, and your server sends them on to Sentry.
 *
 * In meta frameworks instrumented with Sentry (e.g. NextJS, Nuxt or SvelteKit), this integration is already enabled. For  standalone server applications (e.g. Express or Fastify), add this integration and call `handleTunnelRequestIfMatched` in your request handler.
 * 
 * @example
 * ```js
 * // Server
 * Sentry.init({
 *   dsn: '__DSN__',
 *   integrations: [Sentry.serveTunnelIntegration()],
 * });
 *
 * // Browser
 * Sentry.init({
 *   dsn: '__DSN__',
 *   tunnel: true,
 * });
 * ```
 */

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Can do, will do a follow up PR for this.

options?: TunnelForwardOptions,
): Promise<void>;
/**
* The released form, kept until v12. Its `request` option is deprecated: pass the request as the first argument.

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.

l: should this be deprecated?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yep, better than deprecating just one option in it.

Comment on lines +64 to +67
/**
* The released form, kept until v12. Its `request` option is deprecated: pass the request as the first argument.
*/
export function handleTunnelRequest(options: HandleTunnelRequestOptions): Promise<Response>;

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.

l: should this signature be deprecated?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I only deprecated the request object but deprecating the entire signature might be a better idea. doing that in a new PR.

getTunnelPath,
handleTunnelRequest,
handleTunnelRequestIfMatched,
isTunnelRequest,

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.

m: what's the use case for exporting isTunnelRequest publicly? Do users need it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

They can use it to match a request manually if they don't use handleTunnelRequestIfMatched so I would keep it.

Comment on lines +55 to +56
return client.getIntegrationByName?.<Integration & { forward?: TunnelForwarder }>(SERVE_TUNNEL_INTEGRATION_NAME)
?.forward;

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.

Q: I realize this isn't technically part of the PR but I was wondering why we put the forwarding function onto the integration? Would it be easier to store the forwarder directly on the client?

Kinda have the same question for the servedTunnels weakmap

@logaretm logaretm Oct 28, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Mostly bundle size, it's a server only feature so I didn't want it pulled into the client, which browser bundles share.

Happy to change them if u think the placement is worth the bundle size.

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.

ah ok, I thought this would only affect ServerRuntimeClient. If we'd need to put it into the core client, I agree, then we should keep it out.

@logaretm
logaretm merged commit 7e6148e into develop Oct 28, 2026
727 of 732 checks passed
@logaretm
logaretm deleted the awad/tunnel-server-exports branch October 28, 2026 14:41
logaretm added a commit that referenced this pull request Oct 28, 2026
…#25785)

The tunnel helpers from #25783 had no coverage of wiring them by hand in
real servers, fixed by adding integration and e2e suites for it.

The tests forward through [node:http, HTTP/2, Express, Fastify and
Koa](https://github.com/getsentry/sentry-javascript/pull/25785/files#diff-8ffa4f06f4098a472634e418d2eec8119f1aa655d522b6844e6deaf923fab111)
(also run on Bun and Deno), [Cloudflare in and outside
`withSentry`](https://github.com/getsentry/sentry-javascript/pull/25785/files#diff-1f61417f22ea29705f73430b63ae3f242896453a76e865bfb4911fe12e286ae7),
and [a Next.js Pages API route and middleware under
basePath](https://github.com/getsentry/sentry-javascript/pull/25785/files#diff-d1b8cf7eae9cf02cbe35a8dcf5ed4ab459f9b17cdd951730ca2fba703b0ca93f).
logaretm added a commit that referenced this pull request Oct 28, 2026
…rd the `serveTunnelIntegration` docs (#25844)

Lukas pointed out after #25783 merged that the `serveTunnelIntegration`
docs did not say when to add it and that the released
`handleTunnelRequest({ request })` form was not marked deprecated, fixed
by rewording the JSDoc and tagging that overload `@deprecated`, plus an
oxlint allow entry since `no-deprecated` otherwise flags every SDK
re-export of it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants