From ac5017a89f763d693397ef7585de78461c1d8da6 Mon Sep 17 00:00:00 2001 From: "Anthony Fu (via agent)" Date: Tue, 15 Sep 2026 05:24:03 +0000 Subject: [PATCH] feat: forward onTrusted from DevToolsConfig to the interactive auth handler --- .../src/node/__tests__/auth-handler.test.ts | 34 +++++++++++++++++++ packages/core/src/node/auth-handler.ts | 4 ++- packages/core/src/node/config.ts | 7 ++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/packages/core/src/node/__tests__/auth-handler.test.ts b/packages/core/src/node/__tests__/auth-handler.test.ts index 6ebd77f5..51503a33 100644 --- a/packages/core/src/node/__tests__/auth-handler.test.ts +++ b/packages/core/src/node/__tests__/auth-handler.test.ts @@ -1,11 +1,17 @@ import type { ResolvedConfig } from 'vite' import process from 'node:process' +import { createInteractiveAuth } from 'devframe/recipes/interactive-auth' import { describe, expect, it, vi } from 'vitest' import { getAuthHandler, getBuildCapabilityToken, isBuildCapabilityAuth, isClientAuthDisabled } from '../auth-handler' import { normalizeDevToolsConfig } from '../config' import { createDevToolsContext } from '../context' import '@vitejs/devtools-kit' +vi.mock('devframe/recipes/interactive-auth', async (importOriginal) => { + const actual = await importOriginal() + return { ...actual, createInteractiveAuth: vi.fn(actual.createInteractiveAuth) } +}) + function createConfig(command: 'serve' | 'build' = 'serve'): ResolvedConfig { return { root: process.cwd(), @@ -67,6 +73,34 @@ describe('getAuthHandler banner', () => { }) }) +describe('getAuthHandler onTrusted', () => { + it('forwards a configured onTrusted to the interactive auth handler', async () => { + const onTrusted = vi.fn() + const ctx = await createDevToolsContext( + createConfig(), + undefined, + normalizeDevToolsConfig({ onTrusted }, 'localhost'), + ) + + getAuthHandler(ctx) + + expect(vi.mocked(createInteractiveAuth).mock.lastCall?.[1]).toMatchObject({ onTrusted }) + }) + + it('skips onTrusted in implicit build mode (trust is token-based, no code exchange)', async () => { + const onTrusted = vi.fn() + const ctx = await createDevToolsContext( + createConfig('build'), + undefined, + normalizeDevToolsConfig({ onTrusted }, 'localhost'), + ) + + getAuthHandler(ctx) + + expect(vi.mocked(createInteractiveAuth).mock.lastCall?.[1]?.onTrusted).toBeUndefined() + }) +}) + describe('build-mode capability token', () => { it('flags implicit build mode as capability-token auth, not disabled', async () => { const ctx = await createDevToolsContext( diff --git a/packages/core/src/node/auth-handler.ts b/packages/core/src/node/auth-handler.ts index c7548ed2..98262cf1 100644 --- a/packages/core/src/node/auth-handler.ts +++ b/packages/core/src/node/auth-handler.ts @@ -52,8 +52,10 @@ export function getAuthHandler(context: ViteDevToolsNodeContext): DevToolsAuthHa handler = createInteractiveAuth(context, { clientAuthTokens, // Build mode trusts purely via the per-process capability token baked - // into the served connection meta, so silence the OTP console banner. + // into the served connection meta, so silence the OTP console banner + // and skip `onTrusted` — no code exchange ever happens to fire it. banner: buildCapability ? () => {} : config.banner, + onTrusted: buildCapability ? undefined : config.onTrusted, }) handlers.set(context, handler) } diff --git a/packages/core/src/node/config.ts b/packages/core/src/node/config.ts index 6deb033d..6fbe9328 100644 --- a/packages/core/src/node/config.ts +++ b/packages/core/src/node/config.ts @@ -1,3 +1,4 @@ +import type { CreateInteractiveAuthOptions } from 'devframe/recipes/interactive-auth' import type { McpSetting } from 'devframe/types' import type { StartOptions } from './cli-commands' import type { DevToolsUserOptions } from './plugin-options' @@ -44,6 +45,12 @@ export interface DevToolsConfig extends Partial, DevToolsUserOptio * Supply this to surface the code in the host's own chrome instead. */ banner?: (info: { code: string, url: string }) => void + /** + * Called once a code exchange succeeds, so a host rendering its own + * `banner` can retract it. Not called for connect-time trust from a + * static `clientAuthTokens` entry, since no code exchange happens. + */ + onTrusted?: CreateInteractiveAuthOptions['onTrusted'] /** * Origins allowed to open the DevTools WebSocket connection, in addition to the built-in * loopback allowlist (`localhost`, `127.0.0.1`, etc).