feat: deliver devtools events from isolated server runtimes via Vite HotChannel#384
Conversation
…er runtimes Addresses the issue where devtools events are lost when server code runs in isolated environments (Nitro v3 worker threads, Cloudflare Workers, etc.) that don't share globalThis with the Vite main thread.
Fix problem description precision, URL matching and handleNewConnection signature issues, POST handler routing, placeholder convention, triplicate interface sync, queue preservation, and multi-worker echo safety.
Disambiguate that both standalone and external server POST/upgrade handlers need updates, and that only WebSocket URL matching needs prefix change (not SSE/POST URLs).
7-task plan covering: interface updates, ServerEventBus bridge support, POST handler routing, RingBuffer utility, EventClient network transport detection, WebSocket connection/emit/receive, and integration tests.
- Accept WebSocket connections with ?bridge=server query parameter - Track bridge clients separately for proper routing - Bridge messages route through emit() (broadcast to WS clients + EventTarget) - Regular browser messages route through emitToServer() (EventTarget only) - Clean up bridge client tracking on disconnect and stop()
When EventClient detects it is in an isolated server environment (no shared globalThis.__TANSTACK_EVENT_TARGET__, no window), it automatically connects to ServerEventBus via WebSocket. Bidirectional: events emitted in the worker reach the devtools panel, and events from the devtools panel reach listeners in the worker. Includes echo prevention via 200-entry ring buffer, exponential backoff reconnection, HTTP POST fallback, and event queuing.
- Add scheduleReconnect() call in error handler for non-browser runtimes where 'close' may not follow 'error' - Reset wsGaveUp, wsReconnectAttempts, wsReconnectDelay in ___destroyNetworkTransport for safe reuse
|
|
View your CI Pipeline Execution ↗ for commit 8c973f6
☁️ Nx Cloud last updated this comment at |
Two minimal examples for manually testing the network transport fallback: - examples/react/start-nitro — TanStack Start + Nitro v3 (worker threads) - examples/react/start-cloudflare — TanStack Start + Cloudflare Workers Both emit devtools events from server functions and display them in a custom "Server Events" devtools panel. If events appear in the panel, the network transport fallback is working correctly.
|
Warning Review limit reached
More reviews will be available in 14 minutes and 5 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds a dev-only Vite HotChannel bridge ( ChangesRuntime Bridge Core and Example Apps
Sequence DiagramsequenceDiagram
participant ServerFn as Server Function (Worker/Nitro)
participant EventClient as EventClient (injected bridge)
participant HotChannel as Vite HotChannel (import.meta.hot)
participant Plugin as devtools-vite plugin
participant ServerEventBus as ServerEventBus (Vite process)
participant DevtoolsUI as TanStackDevtools UI
ServerFn->>EventClient: emitServerEvent("server-fn-called", data)
EventClient->>EventClient: dispatches tanstack-dispatch-event on globalThis.__TANSTACK_EVENT_TARGET__
EventClient->>HotChannel: import.meta.hot.send("tsd:to-server", payload)
HotChannel->>Plugin: wireRuntimeBridgeChannels tsd:to-server handler
Plugin->>ServerEventBus: dispatches tanstack-dispatch-event on EventTarget
ServerEventBus->>HotChannel: EventTarget dispatches tanstack-devtools-global
HotChannel->>EventClient: import.meta.hot.on("tsd:to-client") re-dispatches CustomEvent
ServerEventBus->>DevtoolsUI: ServerEventsPanel receives server-fn-called event
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ❌ 2❌ Failed checks (2 warnings)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (8)
examples/react/start-cloudflare/.gitignore (1)
6-11: Consider adding env variant ignores to reduce secret leak risk.You already ignore
.env; adding.env.*and Cloudflare local secret files (for example.dev.vars/.dev.vars.*) would provide better protection against accidental commits in local/dev workflows.Proposed update
.env +.env.* +.dev.vars +.dev.vars.* .nitro .tanstack .wrangler🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/react/start-cloudflare/.gitignore` around lines 6 - 11, Update the .gitignore entry that currently lists ".env" to also ignore environment file variants and common Cloudflare local secret files: add patterns like ".env.*", "*.env.local", ".dev.vars" and ".dev.vars.*" (or other local secret naming used in this project) so accidental commits of local/dev secrets are prevented; ensure these patterns are added alongside existing entries (".env", ".nitro", ".tanstack", ".wrangler", ".output", ".vinxi") in the examples/react/start-cloudflare/.gitignore file.examples/react/start-cloudflare/package.json (1)
12-20: Consider moving build-time Vite plugins to devDependencies.The following packages are Vite plugins used only during development/build and do not run at runtime in Cloudflare Workers:
@cloudflare/vite-plugin(line 12)@tanstack/router-plugin(line 17)vite-tsconfig-paths(line 20)Moving them to
devDependenciesmore accurately reflects their purpose and can reduce confusion about what runs in the deployed worker.♻️ Proposed fix
"dependencies": { - "@cloudflare/vite-plugin": "^1.13.8", "@tanstack/devtools-event-client": "workspace:*", "@tanstack/react-devtools": "workspace:*", "@tanstack/react-router": "^1.132.0", "@tanstack/react-start": "^1.132.0", - "@tanstack/router-plugin": "^1.132.0", "react": "^19.2.0", - "react-dom": "^19.2.0", - "vite-tsconfig-paths": "^6.0.2" + "react-dom": "^19.2.0" }, "devDependencies": { + "@cloudflare/vite-plugin": "^1.13.8", "@tanstack/devtools-vite": "workspace:*", + "@tanstack/router-plugin": "^1.132.0", "@types/node": "^22.15.2", "@types/react": "^19.2.0", "@types/react-dom": "^19.2.0", "@vitejs/plugin-react": "^5.0.4", "typescript": "~5.9.2", "vite": "^7.1.7", + "vite-tsconfig-paths": "^6.0.2", "wrangler": "^4.40.3" }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/react/start-cloudflare/package.json` around lines 12 - 20, The package.json currently lists build-time Vite plugins as runtime dependencies; remove "@cloudflare/vite-plugin", "@tanstack/router-plugin", and "vite-tsconfig-paths" from the "dependencies" section and add them to "devDependencies" with the same version strings so they are only installed for build/dev. Ensure you update the package.json keys accordingly (keep other packages untouched) and run your package manager to refresh lockfiles so the change is reflected in installs.examples/react/start-nitro/package.json (1)
16-16: Pinnitroto a specific version instead of usinglatest.At Line 16,
"nitro": "latest"is inconsistent with the rest of the dependencies and makes this example non-reproducible. The lockfile resolves tonitro@3.0.1-alpha.2; pin the dependency to this version or a specific stable release:- "nitro": "latest", + "nitro": "^3.0.1-alpha.2",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/react/start-nitro/package.json` at line 16, The dependency entry "nitro": "latest" in package.json makes the example non-reproducible; update the "nitro" dependency (the "nitro" property in package.json) to a specific version (e.g., "3.0.1-alpha.2" or a chosen stable release) so the example and lockfile remain deterministic, then run npm/yarn install to refresh the lockfile.examples/react/start-cloudflare/src/routes/index.tsx (1)
60-63: Consider adding error handling for server function calls.The async button handlers call server functions without try/catch. If a server function fails (network error, worker crash), the promise rejection will be unhandled.
💡 Example error handling
onClick={async () => { + try { const msg = await greet() addResult(msg) + } catch (e) { + addResult(`Error: ${e instanceof Error ? e.message : 'Unknown error'}`) + } }}Also applies to: 78-81, 96-99
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/react/start-cloudflare/src/routes/index.tsx` around lines 60 - 63, Wrap each async server-call handler (the onClick callbacks that call greet(), and similar handlers at the other spots) in a try/catch: call the server function inside try, await the result, and then call addResult(msg) on success; in catch, log the error and surface a user-friendly message (e.g., via addResult or an error state) so promise rejections from greet() (or other server functions) are handled and do not stay unhandled. Ensure you update all three handlers (the one invoking greet() and the other handlers at the 78-81 and 96-99 locations) and reference the existing functions greet() and addResult when implementing the try/catch handling.examples/react/start-cloudflare/src/devtools/ServerEventsPanel.tsx (1)
105-107: Consider a more unique key for event list items.Using
${ev.timestamp}-${index}as the key could cause React reconciliation issues if events arrive with identical timestamps (possible with rapid emission). Since events are prepended, the index of existing items shifts on each new event, which combined with timestamp collisions could lead to incorrect DOM updates.💡 Consider adding a unique identifier
If events gain an
eventIdfield from the transport layer (as mentioned in the PR objectives), use that instead:- <div - key={`${ev.timestamp}-${index}`} + <div + key={ev.eventId ?? `${ev.timestamp}-${index}`}Alternatively, generate a client-side ID when storing events.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/react/start-cloudflare/src/devtools/ServerEventsPanel.tsx` around lines 105 - 107, The list key for events in ServerEventsPanel (events.map -> key={`${ev.timestamp}-${index}`}) is unstable; change it to use a stable unique id: prefer using ev.eventId (if provided by the transport) or a client-generated id assigned when the event is created/added (e.g., set ev.id or event.id inside the event insertion logic). Update the mapping to use that stable id (key={ev.eventId || ev.id}) so prepending new events won't shift keys and break React reconciliation.packages/event-bus/src/server/server.ts (1)
156-167: Extract the POST bridge routing into one helper.Both server modes now duplicate the same
source === 'server-bridge'branch. Pulling that into a shared helper will keep standalone and piggyback mode from drifting the next time the bridge protocol changes.Also applies to: 274-288
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/event-bus/src/server/server.ts` around lines 156 - 167, Extract the duplicated POST bridge routing into a single helper (e.g. handleDevtoolsSendRequest) that performs the body collection, calls parseWithBigInt, logs via this.debugLog('Received event from client', msg) and then calls either this.emit(msg) or this.emitToServer(msg) based on msg.source === 'server-bridge'; replace the duplicated blocks currently handling req.url === '/__devtools/send' in both server modes (the block around the parseWithBigInt/this.debugLog/this.emit/this.emitToServer sequence and the similar block at the other location) with calls to this new helper so both modes share the same logic.packages/event-bus-client/src/plugin.ts (1)
581-595: Use plain comments for these private helpers.
@internalJSDoc on private methods will still flow into generated docs, which makes the website output noisy for implementation details. A normal//comment here is a better fit.As per coding guidelines,
**/*.{js,ts,jsx,tsx}: JSDoc should read like documentation as it gets converted to markdown docs for the website.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/event-bus-client/src/plugin.ts` around lines 581 - 595, The JSDoc `@internal` on private helper methods is leaking into generated docs; replace the /** `@internal` */ JSDoc blocks on the private helpers (specifically ___enableNetworkTransport and ___destroyNetworkTransport) with plain single-line comments (e.g., // internal — only for testing) so they are treated as implementation comments rather than documentation and won’t be emitted into the site output.examples/react/start-nitro/src/devtools/ServerEventsPanel.tsx (1)
20-28: Consider hydration safety for time formatting.
toLocaleTimeStringcan produce different output on server vs client due to locale/timezone differences, potentially causing hydration mismatches. Since this is a devtools panel (client-only rendering context), this is likely fine, but worth noting.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/react/start-nitro/src/devtools/ServerEventsPanel.tsx` around lines 20 - 28, formatTime currently uses toLocaleTimeString which can differ between server and client and cause hydration mismatches; make the output deterministic by specifying an explicit timeZone (e.g., add timeZone: 'UTC' to the options) or move formatting to run only on the client (compute inside a useEffect or when rendering client-only components) so timezone/locale differences won't cause hydration errors — update the formatTime function to use Intl.DateTimeFormat or toLocaleTimeString with a fixed timeZone option, or ensure calls to formatTime occur only on the client side.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@examples/react/start-nitro/package.json`:
- Line 19: Move the "vite-tsconfig-paths" dependency from runtime dependencies
into devDependencies: remove the "vite-tsconfig-paths": "^6.0.2" entry under
dependencies in package.json and add the same entry under devDependencies (so
build-time only tools used by vite.config.ts are not bundled at runtime); ensure
package-lock/yarn lock is updated by reinstalling or running the package manager
after the change.
In `@packages/event-bus-client/src/plugin.ts`:
- Around line 50-54: The event ID generation (generateEventId using
globalEventIdCounter) is only unique per module instance and can collide across
workers; create a process-scoped random/unique prefix (e.g., instancePrefix
generated once at module init using crypto.randomBytes or a high-entropy
Math.random hex) and prepend it to the existing
`${++globalEventIdCounter}-${Date.now()}` scheme so generateEventId returns
`${instancePrefix}-${++globalEventIdCounter}-${Date.now()}`, ensuring
instance-scoped entropy and avoiding cross-worker collisions; ensure
instancePrefix is initialized once (top-level) and referenced in
generateEventId.
- Around line 475-483: The branch inside the this.#useNetworkTransport path
incorrectly queues events when the client has given up on WebSocket; update the
logic in the block that calls createEventPayload so that if this.#wsGaveUp is
true you bypass queuing and connectWebSocket(), and immediately call
sendViaNetwork (or sendViaHttp for non-network transport) instead; specifically,
in the code handling this.#useNetworkTransport, check this.#wsGaveUp before
pushing to this.#queuedEvents and short-circuit to sendViaNetwork/sendViaHttp,
leaving connectWebSocket() only for cases where a reconnect attempt should
actually be made.
- Around line 304-308: The bridge currently uses JSON.parse/JSON.stringify which
drops BigInt values; change the ws message handler in
ws.addEventListener('message') to call parseWithBigInt(data) instead of
JSON.parse(data), and update the bridge send methods (the functions around the
send calls at the locations corresponding to lines 408 and 428) to use
stringifyWithBigInt(payload) instead of JSON.stringify(payload). Ensure
parseWithBigInt and stringifyWithBigInt are imported from the event-bus
utilities and replace all plain JSON.parse/JSON.stringify usages in those
handlers/sendters to maintain BigInt-safe wire format compatibility.
In `@packages/event-bus-client/src/ring-buffer.ts`:
- Around line 7-11: The RingBuffer constructor must validate the incoming
capacity and fail fast for non-positive or non-integer values: in the
constructor (class RingBuffer) add a guard that checks capacity is a positive
integer (capacity > 0 && Number.isInteger(capacity)) and throw a clear
RangeError or TypeError if not, so fields like `#capacity`, `#buffer` and `#index`
remain valid and add() cannot enter a broken state; update the constructor that
currently sets this.#capacity, this.#buffer and this.#set to perform this
validation before initializing those fields.
---
Nitpick comments:
In `@examples/react/start-cloudflare/.gitignore`:
- Around line 6-11: Update the .gitignore entry that currently lists ".env" to
also ignore environment file variants and common Cloudflare local secret files:
add patterns like ".env.*", "*.env.local", ".dev.vars" and ".dev.vars.*" (or
other local secret naming used in this project) so accidental commits of
local/dev secrets are prevented; ensure these patterns are added alongside
existing entries (".env", ".nitro", ".tanstack", ".wrangler", ".output",
".vinxi") in the examples/react/start-cloudflare/.gitignore file.
In `@examples/react/start-cloudflare/package.json`:
- Around line 12-20: The package.json currently lists build-time Vite plugins as
runtime dependencies; remove "@cloudflare/vite-plugin",
"@tanstack/router-plugin", and "vite-tsconfig-paths" from the "dependencies"
section and add them to "devDependencies" with the same version strings so they
are only installed for build/dev. Ensure you update the package.json keys
accordingly (keep other packages untouched) and run your package manager to
refresh lockfiles so the change is reflected in installs.
In `@examples/react/start-cloudflare/src/devtools/ServerEventsPanel.tsx`:
- Around line 105-107: The list key for events in ServerEventsPanel (events.map
-> key={`${ev.timestamp}-${index}`}) is unstable; change it to use a stable
unique id: prefer using ev.eventId (if provided by the transport) or a
client-generated id assigned when the event is created/added (e.g., set ev.id or
event.id inside the event insertion logic). Update the mapping to use that
stable id (key={ev.eventId || ev.id}) so prepending new events won't shift keys
and break React reconciliation.
In `@examples/react/start-cloudflare/src/routes/index.tsx`:
- Around line 60-63: Wrap each async server-call handler (the onClick callbacks
that call greet(), and similar handlers at the other spots) in a try/catch: call
the server function inside try, await the result, and then call addResult(msg)
on success; in catch, log the error and surface a user-friendly message (e.g.,
via addResult or an error state) so promise rejections from greet() (or other
server functions) are handled and do not stay unhandled. Ensure you update all
three handlers (the one invoking greet() and the other handlers at the 78-81 and
96-99 locations) and reference the existing functions greet() and addResult when
implementing the try/catch handling.
In `@examples/react/start-nitro/package.json`:
- Line 16: The dependency entry "nitro": "latest" in package.json makes the
example non-reproducible; update the "nitro" dependency (the "nitro" property in
package.json) to a specific version (e.g., "3.0.1-alpha.2" or a chosen stable
release) so the example and lockfile remain deterministic, then run npm/yarn
install to refresh the lockfile.
In `@examples/react/start-nitro/src/devtools/ServerEventsPanel.tsx`:
- Around line 20-28: formatTime currently uses toLocaleTimeString which can
differ between server and client and cause hydration mismatches; make the output
deterministic by specifying an explicit timeZone (e.g., add timeZone: 'UTC' to
the options) or move formatting to run only on the client (compute inside a
useEffect or when rendering client-only components) so timezone/locale
differences won't cause hydration errors — update the formatTime function to use
Intl.DateTimeFormat or toLocaleTimeString with a fixed timeZone option, or
ensure calls to formatTime occur only on the client side.
In `@packages/event-bus-client/src/plugin.ts`:
- Around line 581-595: The JSDoc `@internal` on private helper methods is leaking
into generated docs; replace the /** `@internal` */ JSDoc blocks on the private
helpers (specifically ___enableNetworkTransport and ___destroyNetworkTransport)
with plain single-line comments (e.g., // internal — only for testing) so they
are treated as implementation comments rather than documentation and won’t be
emitted into the site output.
In `@packages/event-bus/src/server/server.ts`:
- Around line 156-167: Extract the duplicated POST bridge routing into a single
helper (e.g. handleDevtoolsSendRequest) that performs the body collection, calls
parseWithBigInt, logs via this.debugLog('Received event from client', msg) and
then calls either this.emit(msg) or this.emitToServer(msg) based on msg.source
=== 'server-bridge'; replace the duplicated blocks currently handling req.url
=== '/__devtools/send' in both server modes (the block around the
parseWithBigInt/this.debugLog/this.emit/this.emitToServer sequence and the
similar block at the other location) with calls to this new helper so both modes
share the same logic.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 23c57afd-2ae3-40ad-89fd-677242f015bd
📒 Files selected for processing (32)
docs/superpowers/plans/2026-03-12-network-transport-fallback.mddocs/superpowers/specs/2026-03-12-network-transport-fallback-design.mdexamples/react/start-cloudflare/.gitignoreexamples/react/start-cloudflare/package.jsonexamples/react/start-cloudflare/src/devtools/ServerEventsPanel.tsxexamples/react/start-cloudflare/src/devtools/index.tsexamples/react/start-cloudflare/src/devtools/server-event-client.tsexamples/react/start-cloudflare/src/router.tsxexamples/react/start-cloudflare/src/routes/__root.tsxexamples/react/start-cloudflare/src/routes/index.tsxexamples/react/start-cloudflare/tsconfig.jsonexamples/react/start-cloudflare/vite.config.tsexamples/react/start-cloudflare/wrangler.jsoncexamples/react/start-nitro/.gitignoreexamples/react/start-nitro/package.jsonexamples/react/start-nitro/src/devtools/ServerEventsPanel.tsxexamples/react/start-nitro/src/devtools/index.tsexamples/react/start-nitro/src/devtools/server-event-client.tsexamples/react/start-nitro/src/router.tsxexamples/react/start-nitro/src/routes/__root.tsxexamples/react/start-nitro/src/routes/index.tsxexamples/react/start-nitro/tsconfig.jsonexamples/react/start-nitro/vite.config.tspackages/event-bus-client/src/index.tspackages/event-bus-client/src/plugin.tspackages/event-bus-client/src/ring-buffer.tspackages/event-bus-client/tests/integration.test.tspackages/event-bus-client/tests/network-transport.test.tspackages/event-bus-client/tests/ring-buffer.test.tspackages/event-bus/src/client/client.tspackages/event-bus/src/server/server.tspackages/event-bus/tests/server.test.ts
| "nitro": "latest", | ||
| "react": "^19.2.0", | ||
| "react-dom": "^19.2.0", | ||
| "vite-tsconfig-paths": "^6.0.2" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Search for vite-tsconfig-paths usage:"
rg -n 'vite-tsconfig-paths|tsconfigPaths\s*\(' --glob '**/*.{ts,tsx,js,jsx,mjs,cjs,mts,cts}'Repository: TanStack/devtools
Length of output: 484
🏁 Script executed:
cat examples/react/start-nitro/package.jsonRepository: TanStack/devtools
Length of output: 860
Move vite-tsconfig-paths to devDependencies.
This package is imported only in vite.config.ts (build-time configuration). It should not be in dependencies since it's not used at runtime.
Proposed change
"dependencies": {
"@tanstack/devtools-event-client": "workspace:*",
"@tanstack/react-devtools": "workspace:*",
"@tanstack/react-router": "^1.132.0",
"@tanstack/react-start": "^1.132.0",
"@tanstack/router-plugin": "^1.132.0",
"nitro": "latest",
"react": "^19.2.0",
"react-dom": "^19.2.0",
- "vite-tsconfig-paths": "^6.0.2"
},
"devDependencies": {
"@tanstack/devtools-vite": "workspace:*",
"@types/node": "^22.15.2",
"@types/react": "^19.2.0",
"@types/react-dom": "^19.2.0",
"@vitejs/plugin-react": "^5.0.4",
+ "vite-tsconfig-paths": "^6.0.2",
"typescript": "~5.9.2",
"vite": "^7.1.7"
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "vite-tsconfig-paths": "^6.0.2" | |
| "dependencies": { | |
| "@tanstack/devtools-event-client": "workspace:*", | |
| "@tanstack/react-devtools": "workspace:*", | |
| "@tanstack/react-router": "^1.132.0", | |
| "@tanstack/react-start": "^1.132.0", | |
| "@tanstack/router-plugin": "^1.132.0", | |
| "nitro": "latest", | |
| "react": "^19.2.0", | |
| "react-dom": "^19.2.0" | |
| }, | |
| "devDependencies": { | |
| "@tanstack/devtools-vite": "workspace:*", | |
| "@types/node": "^22.15.2", | |
| "@types/react": "^19.2.0", | |
| "@types/react-dom": "^19.2.0", | |
| "@vitejs/plugin-react": "^5.0.4", | |
| "vite-tsconfig-paths": "^6.0.2", | |
| "typescript": "~5.9.2", | |
| "vite": "^7.1.7" | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/react/start-nitro/package.json` at line 19, Move the
"vite-tsconfig-paths" dependency from runtime dependencies into devDependencies:
remove the "vite-tsconfig-paths": "^6.0.2" entry under dependencies in
package.json and add the same entry under devDependencies (so build-time only
tools used by vite.config.ts are not bundled at runtime); ensure
package-lock/yarn lock is updated by reinstalling or running the package manager
after the change.
| constructor(capacity: number) { | ||
| this.#capacity = capacity | ||
| this.#buffer = new Array(capacity).fill('') | ||
| this.#set = new Set() | ||
| } |
There was a problem hiding this comment.
Reject invalid capacities in the constructor.
capacity <= 0 leaves add() in a broken state (#index becomes NaN) and negative values surface a generic Array() error. Since RingBuffer is exported, fail fast with a clear positive-integer check.
🛡️ Proposed guard
constructor(capacity: number) {
+ if (!Number.isInteger(capacity) || capacity <= 0) {
+ throw new RangeError('RingBuffer capacity must be a positive integer')
+ }
this.#capacity = capacity
this.#buffer = new Array(capacity).fill('')
this.#set = new Set()
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| constructor(capacity: number) { | |
| this.#capacity = capacity | |
| this.#buffer = new Array(capacity).fill('') | |
| this.#set = new Set() | |
| } | |
| constructor(capacity: number) { | |
| if (!Number.isInteger(capacity) || capacity <= 0) { | |
| throw new RangeError('RingBuffer capacity must be a positive integer') | |
| } | |
| this.#capacity = capacity | |
| this.#buffer = new Array(capacity).fill('') | |
| this.#set = new Set() | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/event-bus-client/src/ring-buffer.ts` around lines 7 - 11, The
RingBuffer constructor must validate the incoming capacity and fail fast for
non-positive or non-integer values: in the constructor (class RingBuffer) add a
guard that checks capacity is a positive integer (capacity > 0 &&
Number.isInteger(capacity)) and throw a clear RangeError or TypeError if not, so
fields like `#capacity`, `#buffer` and `#index` remain valid and add() cannot enter a
broken state; update the constructor that currently sets this.#capacity,
this.#buffer and this.#set to perform this validation before initializing those
fields.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
examples/react/start-nitro/src/routes/__root.tsx (1)
24-37: Gate devtools mounting to development mode.
connectToServerBus: truetriggers network connection attempts when mounted; wrapping this in a dev-only guard avoids unnecessary production runtime overhead in this example.Suggested change
- <TanStackDevtools - eventBusConfig={{ - connectToServerBus: true - }} - config={{ position: 'bottom-right' }} - plugins={[ - { - id: 'server-events', - name: 'Server Events', - render: <ServerEventsPanel />, - }, - ]} - /> + {import.meta.env.DEV ? ( + <TanStackDevtools + eventBusConfig={{ + connectToServerBus: true, + }} + config={{ position: 'bottom-right' }} + plugins={[ + { + id: 'server-events', + name: 'Server Events', + render: <ServerEventsPanel />, + }, + ]} + /> + ) : null}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/react/start-nitro/src/routes/__root.tsx` around lines 24 - 37, The TanStackDevtools block mounts a network-connected event bus because eventBusConfig.connectToServerBus is true; guard its mounting to development only by conditionally rendering TanStackDevtools (or setting connectToServerBus: false in production) — wrap the TanStackDevtools usage that includes eventBusConfig and the ServerEventsPanel plugin in a dev-only check (e.g., process.env.NODE_ENV === 'development' or import.meta.env.DEV) so that ServerEventsPanel and connectToServerBus are not active in production.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@examples/react/start-nitro/src/routes/__root.tsx`:
- Around line 24-37: The TanStackDevtools block mounts a network-connected event
bus because eventBusConfig.connectToServerBus is true; guard its mounting to
development only by conditionally rendering TanStackDevtools (or setting
connectToServerBus: false in production) — wrap the TanStackDevtools usage that
includes eventBusConfig and the ServerEventsPanel plugin in a dev-only check
(e.g., process.env.NODE_ENV === 'development' or import.meta.env.DEV) so that
ServerEventsPanel and connectToServerBus are not active in production.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 9b89ae07-b4c2-4ac3-b75f-0273477dc2f3
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (4)
.gitignoreexamples/react/start-cloudflare/src/routes/__root.tsxexamples/react/start-nitro/src/routes/__root.tsxpackage.json
✅ Files skipped from review due to trivial changes (2)
- .gitignore
- package.json
🚧 Files skipped from review as they are similar to previous changes (1)
- examples/react/start-cloudflare/src/routes/__root.tsx
…dling-lark # Conflicts: # pnpm-lock.yaml
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
More templates
@tanstack/angular-devtools
@tanstack/devtools
@tanstack/devtools-a11y
@tanstack/devtools-client
@tanstack/devtools-ui
@tanstack/devtools-utils
@tanstack/devtools-vite
@tanstack/devtools-event-bus
@tanstack/devtools-event-client
@tanstack/preact-devtools
@tanstack/react-devtools
@tanstack/solid-devtools
@tanstack/vue-devtools
commit: |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/devtools-vite/src/plugin.ts`:
- Around line 240-243: The originalConsole object created at line 240 only
includes console methods from consolePipingLevels, but the fallback logic at
line 272 assumes originalConsole.log always exists. If users configure
consolePipingLevels without including 'log', the fallback will fail when
entry.level is unknown or malformed. Ensure that console.log is always available
as a fallback by adding it to originalConsole even if it is not in
consolePipingLevels, either by appending it to the map entries after
construction or by guaranteeing it is included in the initial mapping.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1d73483d-b5c2-4172-aed6-823fbd9a7479
📒 Files selected for processing (10)
.changeset/devtools-vite-runtime-bridge.mdexamples/react/start-cloudflare/src/routes/__root.tsxexamples/react/start-cloudflare/src/routes/index.tsxexamples/react/start-cloudflare/wrangler.jsoncexamples/react/start-nitro/src/routes/__root.tsxexamples/react/start-nitro/src/routes/index.tsxpackages/devtools-vite/src/plugin.tspackages/devtools-vite/src/runtime-bridge.test.tspackages/devtools-vite/src/runtime-bridge.tspackages/devtools-vite/tests/index.test.ts
✅ Files skipped from review due to trivial changes (2)
- .changeset/devtools-vite-runtime-bridge.md
- examples/react/start-cloudflare/wrangler.jsonc
🚧 Files skipped from review as they are similar to previous changes (4)
- examples/react/start-nitro/src/routes/__root.tsx
- examples/react/start-cloudflare/src/routes/__root.tsx
- examples/react/start-cloudflare/src/routes/index.tsx
- examples/react/start-nitro/src/routes/index.tsx
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/devtools-vite/src/plugin.ts`:
- Around line 240-243: The originalConsole object created at line 240 only
includes console methods from consolePipingLevels, but the fallback logic at
line 272 assumes originalConsole.log always exists. If users configure
consolePipingLevels without including 'log', the fallback will fail when
entry.level is unknown or malformed. Ensure that console.log is always available
as a fallback by adding it to originalConsole even if it is not in
consolePipingLevels, either by appending it to the map entries after
construction or by guaranteeing it is included in the initial mapping.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1d73483d-b5c2-4172-aed6-823fbd9a7479
📒 Files selected for processing (10)
.changeset/devtools-vite-runtime-bridge.mdexamples/react/start-cloudflare/src/routes/__root.tsxexamples/react/start-cloudflare/src/routes/index.tsxexamples/react/start-cloudflare/wrangler.jsoncexamples/react/start-nitro/src/routes/__root.tsxexamples/react/start-nitro/src/routes/index.tsxpackages/devtools-vite/src/plugin.tspackages/devtools-vite/src/runtime-bridge.test.tspackages/devtools-vite/src/runtime-bridge.tspackages/devtools-vite/tests/index.test.ts
✅ Files skipped from review due to trivial changes (2)
- .changeset/devtools-vite-runtime-bridge.md
- examples/react/start-cloudflare/wrangler.jsonc
🚧 Files skipped from review as they are similar to previous changes (4)
- examples/react/start-nitro/src/routes/__root.tsx
- examples/react/start-cloudflare/src/routes/__root.tsx
- examples/react/start-cloudflare/src/routes/index.tsx
- examples/react/start-nitro/src/routes/index.tsx
🛑 Comments failed to post (1)
packages/devtools-vite/src/plugin.ts (1)
240-243:
⚠️ Potential issue | 🟡 Minor | ⚡ Quick winEnsure fallback logging is always callable when custom levels omit
log.At Line 240,
originalConsoleis built only fromconsolePipingLevels; at Line 272, fallback assumesoriginalConsole.logalways exists. If users configure levels without'log', unknown/malformedentry.levelcan makelogMethodundefined and throw at Line 278.Suggested fix
- const originalConsole = Object.fromEntries( - consolePipingLevels.map((l) => [l, console[l].bind(console)]), - ) as Partial<Record<ConsoleLevel, typeof console.log>> + const originalConsole = { + log: console.log.bind(console), + ...Object.fromEntries( + consolePipingLevels + .map((l) => [l, console[l]] as const) + .filter(([, fn]) => typeof fn === 'function') + .map(([l, fn]) => [l, fn.bind(console)]), + ), + } as Partial<Record<ConsoleLevel, typeof console.log>> & { + log: typeof console.log + }Also applies to: 269-273
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/devtools-vite/src/plugin.ts` around lines 240 - 243, The originalConsole object created at line 240 only includes console methods from consolePipingLevels, but the fallback logic at line 272 assumes originalConsole.log always exists. If users configure consolePipingLevels without including 'log', the fallback will fail when entry.level is unknown or malformed. Ensure that console.log is always available as a fallback by adding it to originalConsole even if it is not in consolePipingLevels, either by appending it to the map entries after construction or by guaranteeing it is included in the initial mapping.
Summary
Fixes TanStack/ai#339
When TanStack Start uses Nitro v3's
nitro()Vite plugin (or any runtime that isolates server code in a separate thread/process), devtools events break becauseglobalThis.__TANSTACK_EVENT_TARGET__is not shared across isolation boundaries.This PR adds automatic network transport fallback:
ServerEventBusvia WebSocket?bridge=server) from browser clients and routes bridge messages through bothemitEventToClients()(browser devtools) andemitToServer()(in-process EventTarget)Changes
packages/event-bus/src/server/server.ts— bridge WebSocket support, POST source-based routingpackages/event-bus/src/client/client.ts— interface update (eventId, source fields)packages/event-bus-client/src/plugin.ts— network transport detection, WebSocket connection, emit/receive, reconnect, HTTP fallbackpackages/event-bus-client/src/ring-buffer.ts— new RingBuffer utility for deduppackages/event-bus-client/src/index.ts— exportcreateNetworkTransportClientTest plan
nitro()plugin inexamples/react/startSummary by CodeRabbit