Skip to content

chore(deps): upgrade to Vite DevTools v0.4.0 and devframe v0.6.0#1010

Open
antfubot wants to merge 3 commits into
nuxt:mainfrom
antfubot:deps/vite-devtools-0.4
Open

chore(deps): upgrade to Vite DevTools v0.4.0 and devframe v0.6.0#1010
antfubot wants to merge 3 commits into
nuxt:mainfrom
antfubot:deps/vite-devtools-0.4

Conversation

@antfubot

@antfubot antfubot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Description

Upgrades the Vite DevTools integration to @vitejs/devtools / @vitejs/devtools-kit v0.4.0, which adopts the devframe 0.6 runtime — the official @devframes/plugin-terminals / plugin-messages / plugin-inspect built-ins, the crossws WebSocket transport bound to Vite's dev-server route, and the new OTP / magic-link auth handshake — and pulls vite-plugin-inspect up to v12.0.2 so the whole tree resolves a single devtools-kit / devframe runtime.

The plugin-author surface the Nuxt module builds on is unchanged upstream (Plugin.devtools.setup, DevTools(), the dock / rpc / panel context, and the kit client API), so no module wiring changes are needed beyond adapting to a couple of devframe 0.6 runtime behaviour changes.

Dependencies

  • Bump the @vitejs/devtools and @vitejs/devtools-kit catalogs to ^0.4.0, and vite-plugin-inspect to ^12.0.2 (which itself targets devtools-kit ^0.4.0). Refresh the minimumReleaseAgeExclude pins for the devframe 0.6 / devtools 0.4 / @devframes/plugin-* packages.
  • Align vue on 3.5.39 (required by devtools 0.4) to avoid mixed @vue/compiler-sfc versions producing duplicate-import SFC codegen during the UI-kit build.
  • vite-plugin-inspect@12.0.2 depends on devtools-kit ^0.4.0, so the temporary @vitejs/devtools-kit override is dropped — the tree resolves a single @vitejs/devtools-kit@0.4.0 / devframe@0.6.0 / @devframes/hub@0.6.0.

devframe 0.6 adaptations

  • Client RPC registration. devframe 0.6 split the collector into register() (throws DF0021 if the name exists) and update() (throws DF0022 if it does not). The Nuxt client's callback registration now goes through a has()-guarded upsert helper.
  • e2e trust handshake. The devframe:docks shared state is only fetched once the client is trusted (rpc:is-trusted:updated). VITE_DEVTOOLS_DISABLE_CLIENT_AUTH trusts the server peer (RPC works) but never flips the client flag, so the dock list stayed empty and the e2e fixture timed out; the fixture now nudges the client trust event to start the docks subscription (a no-op under built/preview's static RPC).

Vue DevTools bridge fix

  • The in-panel Vue DevTools applets (Pinia, component inspector) were stuck on "Connecting...". @vue/devtools-kit's iframe messaging channel only answers the iframe registered via setIframeServerContext(), and that call lived in the legacy getIframe() path — dead now that the panel renders as an iframe inside the Vite DevTools dock. The host Vue DevTools backend therefore never learned which iframe to talk to. The host client now binds the dock iframe via setIframeServerContext() as soon as Vite DevTools mounts it. (This also fixes the tab-pinia e2e spec, which was flaky/failing on main before this PR.)

Verification

  • pnpm lint, pnpm build, pnpm typecheck all pass.
  • pnpm test:e2e (dev + built) passes locally and in CI — dev 44 passed, built 3 passed, 0 failures (the tab-pinia spec that failed on main now passes reliably, ~6s vs the previous 30s timeout).

This PR was created with the help of an agent.

Bump `@vitejs/devtools` and `@vitejs/devtools-kit` to 0.4.0, which pulls
in the devframe 0.6 runtime (official terminals/messages/inspect plugins,
crossws transport, OTP/magic-link auth). The plugin-author surface the
Nuxt module builds on (`Plugin.devtools.setup`, `DevTools()`, the dock/rpc
context, and the kit client) is unchanged, so no module code changes are
needed.

Dedupe the tree onto a single devtools runtime: force `@vitejs/devtools-kit`
to 0.4.0 (vite-plugin-inspect still pins ^0.3.1 but only uses the
version-agnostic `defineRpcFunction` passthrough) and align vue on 3.5.39
(required by devtools 0.4) to avoid mixed `@vue/compiler-sfc` versions
producing duplicate-import SFC codegen.
@socket-security

socket-security Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatedvue@​3.5.35 ⏵ 3.5.39100 +110091 +196 -1100

View full report

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Updates client RPC registration to use an idempotent helper that chooses between updating existing definitions and registering new handlers. The DevTools E2E fixture initializes client RPC trust before waiting for docks. Vue DevTools messaging now binds to the active dock iframe and rebinds when it is recreated. Workspace dependency exclusions, catalogs, and Vue resolution overrides are updated for newer toolchain releases.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main dependency upgrade to Vite DevTools v0.4.0 and devframe v0.6.0.
Description check ✅ Passed The description is directly related to the dependency upgrades and runtime adaptations in the changeset.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…evframe 0.6

vite-plugin-inspect 12.0.2 depends on `@vitejs/devtools-kit@^0.4.0`, so the
temporary override that forced the kit onto 0.4.0 (to dedupe away the
beta.3 → devframe 0.5 runtime) is no longer needed and is removed; the tree
now resolves a single devtools-kit / devframe naturally.

Also adapt to two devframe 0.6 behaviour changes that broke the e2e run:

- Client RPC registration split into `register()` (throws DF0021 if the name
  exists) and `update()` (throws DF0022 if it does not). Register the Nuxt
  client's callback functions through a small `has()`-guarded upsert so
  re-registration no longer throws.
- The `devframe:docks` shared state is only fetched once the client is marked
  trusted. `VITE_DEVTOOLS_DISABLE_CLIENT_AUTH` trusts the server peer (RPC
  works) but never flips the client-side trust flag, leaving the dock list
  empty forever, so the e2e fixture now nudges the client trust event to kick
  off the docks subscription (a no-op in built/preview's static-RPC mode).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/devtools/client/composables/rpc.ts (1)

40-43: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider extracting the shared clientFunctions iteration.

The loop body at lines 40–43 and 67–70 is identical. A small helper would eliminate the duplication and keep both registration paths in sync if the iteration logic ever changes.

♻️ Optional DRY refactor
+function registerAllClientFunctions(client: DevToolsRpcClient) {
+  for (const [name, handler] of Object.entries(clientFunctions)) {
+    if (typeof handler === 'function')
+      upsertClientFunction(client, name, handler)
+  }
+}
+
 async function connectDevToolsRpc(): Promise<DevToolsRpcClient> {
   try {
     const client = await getDevToolsRpcClient()

     rpcClient.value = client

-    // Register client functions so the server can call them.
-    for (const [name, handler] of Object.entries(clientFunctions)) {
-      if (typeof handler === 'function')
-        upsertClientFunction(client, name, handler)
-    }
+    // Register client functions so the server can call them.
+    registerAllClientFunctions(client)

     // eslint-disable-next-line no-console
     console.log('[nuxt-devtools] Connected to Vite DevTools RPC')

And in registerClientFunctions:

 export async function registerClientFunctions() {
   const client = rpcClient.value || await connectPromise
-  for (const [name, handler] of Object.entries(clientFunctions)) {
-    if (typeof handler === 'function')
-      upsertClientFunction(client, name, handler)
-  }
+  registerAllClientFunctions(client)
 }

Also applies to: 65-71

🤖 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/client/composables/rpc.ts` around lines 40 - 43, Extract
the duplicated clientFunctions iteration into a shared helper near the existing
registration logic, using the same function-type check and upsertClientFunction
call. Replace both loops in the client registration paths, including
registerClientFunctions, with this helper so both paths remain synchronized.
🤖 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.

Nitpick comments:
In `@packages/devtools/client/composables/rpc.ts`:
- Around line 40-43: Extract the duplicated clientFunctions iteration into a
shared helper near the existing registration logic, using the same function-type
check and upsertClientFunction call. Replace both loops in the client
registration paths, including registerClientFunctions, with this helper so both
paths remain synchronized.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 282632c6-d519-40ed-a833-4648a73192a1

📥 Commits

Reviewing files that changed from the base of the PR and between 30671dc and 66703b8.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (4)
  • packages/devtools/client/composables/client.ts
  • packages/devtools/client/composables/rpc.ts
  • pnpm-workspace.yaml
  • tests/e2e/fixtures/devtools.ts

… iframe

The Pinia / component-inspector panels were stuck on "Connecting..." because
`@vue/devtools-kit`'s iframe messaging channel only answers the iframe passed
to `setIframeServerContext()`. That call previously lived in the legacy
`getIframe()` path, which is dead now that the panel is rendered as an iframe
inside Vite DevTools' dock — so the host Vue DevTools backend never learned
which iframe to talk to and the applet's RPC heartbeat timed out.

Bind the dock iframe (from the Vite DevTools client context) via
`setIframeServerContext()` as soon as it mounts, re-pointing if the element is
recreated. Fixes the flaky `tab-pinia` e2e spec (previously failing on `main`).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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/src/runtime/plugins/view/client.ts`:
- Around line 298-313: Update bindVueDevToolsIframe to retain and clear the
polling interval once the dock iframe is found and bound, preventing indefinite
polling and duplicate intervals across repeated setup calls. Preserve rebinding
when the iframe is later recreated by using an appropriate lifecycle mechanism,
such as a MutationObserver or controlled slower retry, while ensuring only one
active polling mechanism exists.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 88a02e95-c6ea-47aa-9b84-6ee57c7e2a0f

📥 Commits

Reviewing files that changed from the base of the PR and between 66703b8 and 974b03e.

📒 Files selected for processing (1)
  • packages/devtools/src/runtime/plugins/view/client.ts

Comment on lines +298 to +313
function bindVueDevToolsIframe() {
let bound: HTMLIFrameElement | undefined
const bind = () => {
const ctx = getViteDevToolsContext()
const dockIframe = ctx?.docks?.getStateById?.('nuxt:devtools')?.domElements?.iframe as HTMLIFrameElement | null | undefined
if (!dockIframe || dockIframe === bound)
return
bound = dockIframe
iframe = dockIframe
// The channel reads `.contentWindow` lazily on every message, so binding
// the element once is enough even across in-iframe navigations/reloads.
setIframeServerContext(dockIframe)
}
bind()
setInterval(bind, 500)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

setInterval is never cleared — resource leak and duplicate-interval risk.

The interval created at line 312 runs indefinitely with no cleanup path. If setupDevToolsClient is invoked more than once (e.g., during HMR or module re-initialization), each call spawns a new 500ms interval that stacks on top of previous ones, causing unbounded polling and redundant setIframeServerContext calls.

Even in the single-invocation case, the interval persists forever after binding succeeds, continually traversing getViteDevToolsContext() and the dock state tree every 500ms for the lifetime of the page.

Consider capturing the interval handle and clearing it when the dock iframe is found, then re-establishing polling only if the element is later recreated (e.g., via a MutationObserver or a slower heartbeat). At minimum, guard against duplicate intervals:

🔒 Proposed fix: clear interval after binding, use MutationObserver for recreation
 function bindVueDevToolsIframe() {
     let bound: HTMLIFrameElement | undefined
+    let timer: ReturnType<typeof setInterval> | undefined
+
     const bind = () => {
       const ctx = getViteDevToolsContext()
       const dockIframe = ctx?.docks?.getStateById?.('nuxt:devtools')?.domElements?.iframe as HTMLIFrameElement | null | undefined
       if (!dockIframe || dockIframe === bound)
         return
       bound = dockIframe
       iframe = dockIframe
       setIframeServerContext(dockIframe)
+      // Once bound, stop the initial fast-poll interval and watch for
+      // future iframe recreation via MutationObserver instead.
+      if (timer) {
+        clearInterval(timer)
+         timer = undefined
+      }
+      const observer = new MutationObserver(() => bind())
+      observer.observe(document.body, { childList: true, subtree: true })
     }
-    bind()
-    setInterval(bind, 500)
+    bind()
+    timer = setInterval(bind, 500)
 }
🤖 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/src/runtime/plugins/view/client.ts` around lines 298 - 313,
Update bindVueDevToolsIframe to retain and clear the polling interval once the
dock iframe is found and bound, preventing indefinite polling and duplicate
intervals across repeated setup calls. Preserve rebinding when the iframe is
later recreated by using an appropriate lifecycle mechanism, such as a
MutationObserver or controlled slower retry, while ensuring only one active
polling mechanism exists.

Sign up for free to 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.

1 participant