Repository navigation
feat(node,bun,deno): Answer managed tunnel requests in the runtime - #25998
Conversation
size-limit report 📦Compared: 46cffcf (PR test merge using develop at 31201fb) Against: 2f26678 (develop) · base run #65292
|
Optional E2E testsSome optional checks were not successful. They do not block the merge. 6 not successful, 50 successful. See the run.
Successful checks
|
86e706c to
0a00c73
Compare
ff78a0d to
a92ac02
Compare
a92ac02 to
e35e392
Compare
e35e392 to
f3239a8
Compare
| if (options.intercept !== false) { | ||
| instrumentTunnelServers(); | ||
| instrumentRuntimeServers?.(); | ||
| } |
There was a problem hiding this comment.
Bugbot raised this somewhat weirdly phrased imo, but there's a real issue here.
setupOnce runs once per integration name per process. If the first serveTunnelIntegration set up has intercept: false, then a later client or addIntegration with intercept: true won't install the hooks. Its tunnel requests then go to the app. The reverse case is harmless.
The gate doesn't buy much. Each hook reads intercept per request through getServedTunnelMatch, so with intercept: false installed hooks hand every request to the app anyway. TanStack already installs the hooks without any gate.
Suggestion: drop the if and always install in setupOnce. If the cost of a no-op emit wrapper with intercept: false matters, install from setup(client) when that client's intercept is not false. The install functions already keep their own "done once" flags.
| if (options.intercept !== false) { | |
| instrumentTunnelServers(); | |
| instrumentRuntimeServers?.(); | |
| } | |
| instrumentTunnelServers(); | |
| instrumentRuntimeServers?.(); |
There was a problem hiding this comment.
This would only be relevant if multiple clients exist, so that's why I ignored it.
Having multiple clients with two active tunnel configurations is not valid. I would have did it but Bun's hook adds a POST route for the tunnel path, which means we cannot have it both ways.
So this behaviors makes it uniform across runtimes, but leaving this edge case as a gap.
Happy to revisit if you think otherwise.
f3239a8 to
1ce6dab
Compare
32a4161 to
2918f88
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 2918f88. Configure here.
|
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. |
`serveTunnelIntegration` answers tunnel requests on every `node:http` server (through the `http.server.request.start` diagnostics channel), `Bun.serve` and `Deno.serve` before the app sees them. TanStack Start's `init` installs the hooks itself, since `tunnelRoute` enables the tunnel after `init`. Every runtime gets the same set of malformed and hostile requests, to prove the app keeps working.
…s emit wrapper With both wrappers on `emit`, the HTTP one wrapped again on the second request, so every request passed two HTTP proxies, and anything replacing `emit` later dropped the tunnel. The HTTP wrapper now answers tunnel requests itself, and the separate tunnel wrapper is only installed without it.
The tunnel wrapped `fetch` and replaced `routes` on the app's own options object, so passing the same object to `server.reload` wrapped our wrappers again. Options a class gives through a `fetch` getter threw on the assignment and got no tunnel.
Core's HTTP server instrumentation imported the tunnel's Node handler, so every Node and Cloudflare bundle carried it. The tunnel now registers the handler in the tunnel store when its hooks install, and the instrumentation calls it from there.
033d6b4 to
c7c0c3f
Compare

This PR actually delivers the approach outlined in #25997 by hooking up the new utilities and wiring them up to the runtimes appropriately.
The implementation introduces a new integration factory that creates an integration for each runtime, it accepts a runtime-specific server patcher, and it applies the HTTP patch itself.
For Node.js / http module
For Node, and other runtimes that emit the http module's diagnostic channel, we patch the server emit and handle the request before it ever hits the user application.
For Deno / Bun
It does the same thing as in Node, but also adds a
*.servepatch in-case the user is using the native runtime servers.Note for reviewers: if you are lacking context on this PR, check #25997