From fbfc37573d896abe841b79d55f60eea650e5a41d Mon Sep 17 00:00:00 2001 From: 0xfe <2953427626@qq.com> Date: Mon, 12 Jan 2026 00:26:54 +0800 Subject: [PATCH 01/10] WIP --- packages/core/playground/vite.config.ts | 11 +++++++ packages/core/src/node/context.ts | 2 +- packages/core/src/node/rpc/index.ts | 6 ++-- .../src/node/rpc/internal/docks-on-launch.ts | 3 ++ packages/kit/package.json | 3 +- packages/kit/src/utils/define.ts | 33 ++++++++++++++++++- packages/vite/src/node/plugin.ts | 2 +- packages/vite/src/node/rpc/index.ts | 4 +-- pnpm-lock.yaml | 18 ++++++++++ pnpm-workspace.yaml | 1 + 10 files changed, 75 insertions(+), 8 deletions(-) diff --git a/packages/core/playground/vite.config.ts b/packages/core/playground/vite.config.ts index c956befd..4b3e091c 100644 --- a/packages/core/playground/vite.config.ts +++ b/packages/core/playground/vite.config.ts @@ -106,6 +106,17 @@ export default defineConfig({ action: ctx.utils.createSimpleClientScript(() => {}), }) + ctx.docks.register({ + id: 'test', + type: 'action', + icon: 'material-symbols:bug-report', + title: 'debug', + // TODO: HMR + action: ctx.utils.createSimpleClientScript(async (ctx) => { + console.log(await ctx.rpc.call('vite:internal:rpc:server:list')) + }), + }) + ctx.docks.register({ id: 'shared-state', type: 'iframe', diff --git a/packages/core/src/node/context.ts b/packages/core/src/node/context.ts index 46043764..dc46f1b7 100644 --- a/packages/core/src/node/context.ts +++ b/packages/core/src/node/context.ts @@ -41,7 +41,7 @@ export async function createDevToolsContext( // Build-in function to list all RPC functions for (const fn of builtinRpcDeclarations) { - rpcHost.register(fn) + rpcHost.register(fn.fn) } const docksSharedState = await rpcHost.sharedState.get('vite:internal:docks', { initialValue: [] }) diff --git a/packages/core/src/node/rpc/index.ts b/packages/core/src/node/rpc/index.ts index d663d90d..a33aa273 100644 --- a/packages/core/src/node/rpc/index.ts +++ b/packages/core/src/node/rpc/index.ts @@ -40,10 +40,12 @@ export const builtinRpcDeclarations = [ ...builtinInternalRpcDeclarations, ] as const -export type BuiltinServerFunctions = RpcDefinitionsToFunctions +type BuiltinRpcDecl = typeof builtinRpcDeclarations[number]['fn'][] + +export type BuiltinServerFunctions = RpcDefinitionsToFunctions export type BuiltinServerFunctionsStatic = RpcDefinitionsToFunctions< - RpcDefinitionsFilter + RpcDefinitionsFilter > export type BuiltinServerFunctionsDump = { diff --git a/packages/core/src/node/rpc/internal/docks-on-launch.ts b/packages/core/src/node/rpc/internal/docks-on-launch.ts index 7834a773..cb4792f3 100644 --- a/packages/core/src/node/rpc/internal/docks-on-launch.ts +++ b/packages/core/src/node/rpc/internal/docks-on-launch.ts @@ -1,8 +1,11 @@ import { defineRpcFunction } from '@vitejs/devtools-kit' +import * as v from 'valibot' export const docksOnLaunch = defineRpcFunction({ name: 'vite:internal:docks:on-launch', type: 'action', + args: v.string(), + returns: v.void(), setup: (context) => { const launchMap = new Map>() return { diff --git a/packages/kit/package.json b/packages/kit/package.json index cf615ea9..0ae7627d 100644 --- a/packages/kit/package.json +++ b/packages/kit/package.json @@ -42,7 +42,8 @@ "@vitejs/devtools-rpc": "workspace:*", "birpc": "catalog:deps", "birpc-x": "catalog:deps", - "immer": "catalog:deps" + "immer": "catalog:deps", + "valibot": "catalog:deps" }, "devDependencies": { "my-ua-parser": "catalog:frontend", diff --git a/packages/kit/src/utils/define.ts b/packages/kit/src/utils/define.ts index 34ac8361..fd688af4 100644 --- a/packages/kit/src/utils/define.ts +++ b/packages/kit/src/utils/define.ts @@ -1,4 +1,35 @@ +import type { GenericSchema, InferInput } from 'valibot' import type { DevToolsNodeContext } from '../types' + import { createDefineWrapperWithContext } from 'birpc-x' -export const defineRpcFunction = createDefineWrapperWithContext() +const _define = createDefineWrapperWithContext() + +export interface RpcOptions< + AS extends GenericSchema | undefined, + RS extends GenericSchema | undefined, +> extends Omit[0], 'setup'> { + args?: AS + returns?: RS + + setup: (ctx: DevToolsNodeContext) => { + handler: ( + params: AS extends GenericSchema ? InferInput : void, + ) => + | (RS extends GenericSchema ? InferInput : void) + | Promise : void> + } +} + +export function defineRpcFunction< + AS extends GenericSchema | undefined = undefined, + RS extends GenericSchema | undefined = undefined, +>(options: RpcOptions) { + const { args: argsSchema, returns: returnsSchema, ...rest } = options + + return { + fn: createDefineWrapperWithContext()({ ...rest }), + args: argsSchema, + returns: returnsSchema, + } +} diff --git a/packages/vite/src/node/plugin.ts b/packages/vite/src/node/plugin.ts index 892f6bc2..4ff682ed 100644 --- a/packages/vite/src/node/plugin.ts +++ b/packages/vite/src/node/plugin.ts @@ -8,7 +8,7 @@ export function DevToolsViteUI(): PluginWithDevTools { devtools: { setup(ctx) { for (const fn of rpcFunctions) { - ctx.rpc.register(fn) + ctx.rpc.register(fn.fn) } ctx.views.hostStatic( diff --git a/packages/vite/src/node/rpc/index.ts b/packages/vite/src/node/rpc/index.ts index 583fa4dd..c2cff3ad 100644 --- a/packages/vite/src/node/rpc/index.ts +++ b/packages/vite/src/node/rpc/index.ts @@ -32,10 +32,10 @@ export const rpcFunctions = [ rolldownGetPackageDetails, ] as const -export type ServerFunctions = RpcDefinitionsToFunctions +export type ServerFunctions = RpcDefinitionsToFunctions export type ServerFunctionsStatic = RpcDefinitionsToFunctions< - RpcDefinitionsFilter + RpcDefinitionsFilter > export type ServerFunctionsDump = { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 45228486..0ba1c2c6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -112,6 +112,9 @@ catalogs: unstorage: specifier: ^1.17.3 version: 1.17.3 + valibot: + specifier: ^1.2.0 + version: 1.2.0 webext-bridge: specifier: ^6.0.1 version: 6.0.1 @@ -623,6 +626,9 @@ importers: immer: specifier: catalog:deps version: 11.1.3 + valibot: + specifier: catalog:deps + version: 1.2.0(typescript@5.9.3) devDependencies: my-ua-parser: specifier: catalog:frontend @@ -6702,6 +6708,14 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + valibot@1.2.0: + resolution: {integrity: sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==} + peerDependencies: + typescript: '>=5' + peerDependenciesMeta: + typescript: + optional: true + vfile-message@4.0.3: resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} @@ -13749,6 +13763,10 @@ snapshots: util-deprecate@1.0.2: {} + valibot@1.2.0(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + vfile-message@4.0.3: dependencies: '@types/unist': 3.0.3 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index dcbc0657..7e75e071 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -54,6 +54,7 @@ catalogs: tinyglobby: ^0.2.15 unconfig: ^7.4.2 unstorage: ^1.17.3 + valibot: ^1.2.0 webext-bridge: ^6.0.1 ws: ^8.19.0 devtools: From fed5b208b1bcf2cbbbd672db89338a7bb2331c4c Mon Sep 17 00:00:00 2001 From: 0xfe <2953427626@qq.com> Date: Wed, 14 Jan 2026 00:56:24 +0800 Subject: [PATCH 02/10] chore: update args type in defineRpcFunction --- packages/kit/src/utils/define.ts | 46 ++++++++++++++++++++------------ packages/vite/src/modules/rpc.ts | 2 +- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/packages/kit/src/utils/define.ts b/packages/kit/src/utils/define.ts index fd688af4..c3654340 100644 --- a/packages/kit/src/utils/define.ts +++ b/packages/kit/src/utils/define.ts @@ -1,34 +1,46 @@ +import type { RpcFunctionDefinition, RpcFunctionSetupResult, RpcFunctionType, Thenable } from 'birpc-x' import type { GenericSchema, InferInput } from 'valibot' import type { DevToolsNodeContext } from '../types' - import { createDefineWrapperWithContext } from 'birpc-x' -const _define = createDefineWrapperWithContext() +type InferInputsTuple + = AS extends readonly GenericSchema[] + ? { -readonly [K in keyof AS]: AS[K] extends GenericSchema ? InferInput : any } + : any[] export interface RpcOptions< - AS extends GenericSchema | undefined, - RS extends GenericSchema | undefined, -> extends Omit[0], 'setup'> { + AS, + RS, + NAME extends string, + TYPE extends RpcFunctionType, +> extends Omit< + RpcFunctionDefinition< + NAME, + TYPE, + InferInputsTuple, + RS extends GenericSchema ? InferInput : any, + DevToolsNodeContext + >, + 'setup' + > { args?: AS returns?: RS - setup: (ctx: DevToolsNodeContext) => { - handler: ( - params: AS extends GenericSchema ? InferInput : void, - ) => - | (RS extends GenericSchema ? InferInput : void) - | Promise : void> - } + setup: (ctx: DevToolsNodeContext) => Thenable< + RpcFunctionSetupResult, RS extends GenericSchema ? InferInput : any> + > } -export function defineRpcFunction< - AS extends GenericSchema | undefined = undefined, - RS extends GenericSchema | undefined = undefined, ->(options: RpcOptions) { +export function defineRpcFunction( + options: RpcOptions, +) { const { args: argsSchema, returns: returnsSchema, ...rest } = options + const birpc = createDefineWrapperWithContext() return { - fn: createDefineWrapperWithContext()({ ...rest }), + fn: birpc, RS extends GenericSchema ? InferInput : any>({ + ...rest, + }), args: argsSchema, returns: returnsSchema, } diff --git a/packages/vite/src/modules/rpc.ts b/packages/vite/src/modules/rpc.ts index 3d44f550..faef7d96 100644 --- a/packages/vite/src/modules/rpc.ts +++ b/packages/vite/src/modules/rpc.ts @@ -15,7 +15,7 @@ export default defineNuxtModule({ devtools: { setup(ctx) { for (const fn of rpcFunctions) { - ctx.rpc.register(fn) + ctx.rpc.register(fn.fn) } }, }, From 31d18ddf319f1a246ab7cbfd4d920f2024049aa9 Mon Sep 17 00:00:00 2001 From: 0xfe <2953427626@qq.com> Date: Wed, 14 Jan 2026 15:40:41 +0800 Subject: [PATCH 03/10] restore changes --- packages/core/src/node/context.ts | 2 +- packages/core/src/node/rpc/index.ts | 6 ++---- packages/vite/src/node/plugin.ts | 2 +- packages/vite/src/node/rpc/index.ts | 4 ++-- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/core/src/node/context.ts b/packages/core/src/node/context.ts index dc46f1b7..46043764 100644 --- a/packages/core/src/node/context.ts +++ b/packages/core/src/node/context.ts @@ -41,7 +41,7 @@ export async function createDevToolsContext( // Build-in function to list all RPC functions for (const fn of builtinRpcDeclarations) { - rpcHost.register(fn.fn) + rpcHost.register(fn) } const docksSharedState = await rpcHost.sharedState.get('vite:internal:docks', { initialValue: [] }) diff --git a/packages/core/src/node/rpc/index.ts b/packages/core/src/node/rpc/index.ts index a33aa273..d663d90d 100644 --- a/packages/core/src/node/rpc/index.ts +++ b/packages/core/src/node/rpc/index.ts @@ -40,12 +40,10 @@ export const builtinRpcDeclarations = [ ...builtinInternalRpcDeclarations, ] as const -type BuiltinRpcDecl = typeof builtinRpcDeclarations[number]['fn'][] - -export type BuiltinServerFunctions = RpcDefinitionsToFunctions +export type BuiltinServerFunctions = RpcDefinitionsToFunctions export type BuiltinServerFunctionsStatic = RpcDefinitionsToFunctions< - RpcDefinitionsFilter + RpcDefinitionsFilter > export type BuiltinServerFunctionsDump = { diff --git a/packages/vite/src/node/plugin.ts b/packages/vite/src/node/plugin.ts index 4ff682ed..892f6bc2 100644 --- a/packages/vite/src/node/plugin.ts +++ b/packages/vite/src/node/plugin.ts @@ -8,7 +8,7 @@ export function DevToolsViteUI(): PluginWithDevTools { devtools: { setup(ctx) { for (const fn of rpcFunctions) { - ctx.rpc.register(fn.fn) + ctx.rpc.register(fn) } ctx.views.hostStatic( diff --git a/packages/vite/src/node/rpc/index.ts b/packages/vite/src/node/rpc/index.ts index c2cff3ad..583fa4dd 100644 --- a/packages/vite/src/node/rpc/index.ts +++ b/packages/vite/src/node/rpc/index.ts @@ -32,10 +32,10 @@ export const rpcFunctions = [ rolldownGetPackageDetails, ] as const -export type ServerFunctions = RpcDefinitionsToFunctions +export type ServerFunctions = RpcDefinitionsToFunctions export type ServerFunctionsStatic = RpcDefinitionsToFunctions< - RpcDefinitionsFilter + RpcDefinitionsFilter > export type ServerFunctionsDump = { From e015103a429daeeaa0a7bd5a3aa6c7d36b530af1 Mon Sep 17 00:00:00 2001 From: 0xfe <2953427626@qq.com> Date: Wed, 14 Jan 2026 16:37:06 +0800 Subject: [PATCH 04/10] fix: correct args and return properties in defineRpcFunction --- .../src/node/rpc/internal/docks-on-launch.ts | 4 +- packages/kit/src/utils/define.ts | 56 ++++++++----------- packages/vite/src/modules/rpc.ts | 2 +- 3 files changed, 25 insertions(+), 37 deletions(-) diff --git a/packages/core/src/node/rpc/internal/docks-on-launch.ts b/packages/core/src/node/rpc/internal/docks-on-launch.ts index cb4792f3..a10b54da 100644 --- a/packages/core/src/node/rpc/internal/docks-on-launch.ts +++ b/packages/core/src/node/rpc/internal/docks-on-launch.ts @@ -4,8 +4,8 @@ import * as v from 'valibot' export const docksOnLaunch = defineRpcFunction({ name: 'vite:internal:docks:on-launch', type: 'action', - args: v.string(), - returns: v.void(), + args: [v.string()], + return: v.void(), setup: (context) => { const launchMap = new Map>() return { diff --git a/packages/kit/src/utils/define.ts b/packages/kit/src/utils/define.ts index c3654340..8c941d65 100644 --- a/packages/kit/src/utils/define.ts +++ b/packages/kit/src/utils/define.ts @@ -1,47 +1,35 @@ -import type { RpcFunctionDefinition, RpcFunctionSetupResult, RpcFunctionType, Thenable } from 'birpc-x' -import type { GenericSchema, InferInput } from 'valibot' +import type { RpcFunctionDefinition, RpcFunctionType } from 'birpc-x' +import type { GenericSchema } from 'valibot' import type { DevToolsNodeContext } from '../types' import { createDefineWrapperWithContext } from 'birpc-x' -type InferInputsTuple - = AS extends readonly GenericSchema[] - ? { -readonly [K in keyof AS]: AS[K] extends GenericSchema ? InferInput : any } - : any[] - export interface RpcOptions< - AS, - RS, NAME extends string, TYPE extends RpcFunctionType, -> extends Omit< - RpcFunctionDefinition< - NAME, - TYPE, - InferInputsTuple, - RS extends GenericSchema ? InferInput : any, - DevToolsNodeContext - >, - 'setup' - > { + A extends any[], + R, + AS extends GenericSchema[] | undefined = undefined, + RS extends GenericSchema | undefined = undefined, +> + extends RpcFunctionDefinition { args?: AS - returns?: RS - - setup: (ctx: DevToolsNodeContext) => Thenable< - RpcFunctionSetupResult, RS extends GenericSchema ? InferInput : any> - > + return?: RS } -export function defineRpcFunction( - options: RpcOptions, +export function defineRpcFunction< + NAME extends string, + TYPE extends RpcFunctionType, + A extends any[], + R, + AS extends GenericSchema[] | undefined = undefined, + RS extends GenericSchema | undefined = undefined, +>( + options: RpcOptions, ) { - const { args: argsSchema, returns: returnsSchema, ...rest } = options + const { args, return: ret, ...rest } = options const birpc = createDefineWrapperWithContext() - return { - fn: birpc, RS extends GenericSchema ? InferInput : any>({ - ...rest, - }), - args: argsSchema, - returns: returnsSchema, - } + const fn = birpc(rest) + + return fn as typeof fn & { argsSchema?: AS, returnSchema?: RS } } diff --git a/packages/vite/src/modules/rpc.ts b/packages/vite/src/modules/rpc.ts index faef7d96..3d44f550 100644 --- a/packages/vite/src/modules/rpc.ts +++ b/packages/vite/src/modules/rpc.ts @@ -15,7 +15,7 @@ export default defineNuxtModule({ devtools: { setup(ctx) { for (const fn of rpcFunctions) { - ctx.rpc.register(fn.fn) + ctx.rpc.register(fn) } }, }, From 7ae96aa3589afb0051c7aa8d31297779761c2d71 Mon Sep 17 00:00:00 2001 From: 0xfe <2953427626@qq.com> Date: Wed, 14 Jan 2026 16:42:10 +0800 Subject: [PATCH 05/10] fix: enhance return type in defineRpcFunction to include args and return schemas --- packages/kit/src/utils/define.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/kit/src/utils/define.ts b/packages/kit/src/utils/define.ts index 8c941d65..3fea5a9f 100644 --- a/packages/kit/src/utils/define.ts +++ b/packages/kit/src/utils/define.ts @@ -31,5 +31,9 @@ export function defineRpcFunction< const fn = birpc(rest) - return fn as typeof fn & { argsSchema?: AS, returnSchema?: RS } + const augmentedFn = fn as typeof fn & { argsSchema?: AS, returnSchema?: RS } + augmentedFn.argsSchema = args + augmentedFn.returnSchema = ret + + return augmentedFn } From 13805f2c6acd48eb92163f9f6273d4941a7dc55a Mon Sep 17 00:00:00 2001 From: 0xfe <2953427626@qq.com> Date: Wed, 14 Jan 2026 19:13:44 +0800 Subject: [PATCH 06/10] fix: update RPC dump to use definitions instead of functions --- packages/core/src/node/cli-commands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/node/cli-commands.ts b/packages/core/src/node/cli-commands.ts index 74717c46..806e9cc5 100644 --- a/packages/core/src/node/cli-commands.ts +++ b/packages/core/src/node/cli-commands.ts @@ -99,7 +99,7 @@ export async function build(options: BuildOptions) { console.log(c.cyan`${MARK_NODE} Writing RPC dump to ${resolve(devToolsRoot, '.vdt-rpc-dump.json')}`) const dump: Record = {} - for (const [key, value] of Object.entries(devtools.context.rpc.functions)) { + for (const [key, value] of Object.entries(devtools.context.rpc.definitions)) { if (value.type === 'static') dump[key] = await value.handler?.() } From bf6906d3496e224c55725185deaeb6677491fe01 Mon Sep 17 00:00:00 2001 From: 0xfe <2953427626@qq.com> Date: Wed, 14 Jan 2026 22:15:53 +0800 Subject: [PATCH 07/10] feat: add RPC validation functions and schemas for argument and return value checks --- packages/core/src/index.ts | 1 + packages/core/src/node/rpc/index.ts | 9 +++- packages/kit/src/client/rpc.ts | 5 ++- packages/kit/src/utils/rpc-validation.ts | 57 ++++++++++++++++++++++++ packages/vite/src/node/index.ts | 1 + packages/vite/src/node/rpc/index.ts | 7 +++ 6 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 packages/kit/src/utils/rpc-validation.ts diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 85b777e6..d8d4ff0b 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,3 +1,4 @@ +export { builtinRpcSchemas } from '../../core/src/node/rpc' export { createDevToolsContext } from './node/context' export { DevTools } from './node/plugins' export { createDevToolsMiddleware } from './node/server' diff --git a/packages/core/src/node/rpc/index.ts b/packages/core/src/node/rpc/index.ts index d663d90d..839926dc 100644 --- a/packages/core/src/node/rpc/index.ts +++ b/packages/core/src/node/rpc/index.ts @@ -42,6 +42,13 @@ export const builtinRpcDeclarations = [ export type BuiltinServerFunctions = RpcDefinitionsToFunctions +export const builtinRpcSchemas = new Map( + builtinRpcDeclarations.map(d => [ + d.name, + { args: d.argsSchema, returns: d.returnSchema }, + ]), +) + export type BuiltinServerFunctionsStatic = RpcDefinitionsToFunctions< RpcDefinitionsFilter > @@ -51,7 +58,7 @@ export type BuiltinServerFunctionsDump = { } declare module '@vitejs/devtools-kit' { - export interface DevToolsRpcServerFunctions extends BuiltinServerFunctions {} + export interface DevToolsRpcServerFunctions extends BuiltinServerFunctions { } // @keep-sorted export interface DevToolsRpcClientFunctions { diff --git a/packages/kit/src/client/rpc.ts b/packages/kit/src/client/rpc.ts index cbd14222..9681d8e6 100644 --- a/packages/kit/src/client/rpc.ts +++ b/packages/kit/src/client/rpc.ts @@ -9,6 +9,7 @@ import { UAParser } from 'my-ua-parser' import { createEventEmitter } from '../utils/events' import { nanoid } from '../utils/nanoid' import { promiseWithResolver } from '../utils/promise' +import { validateRpcArgs, validateRpcReturn } from '../utils/rpc-validation' import { createRpcSharedStateClientHost } from './rpc-shared-state' const CONNECTION_META_KEY = '__VITE_DEVTOOLS_CONNECTION_META__' @@ -241,10 +242,12 @@ export async function getDevToolsRpcClient( ensureTrusted, requestTrust, call: (...args: any): any => { - return serverRpc.$call( + validateRpcArgs(args[0], args.slice(1)) + const ret = serverRpc.$call( // @ts-expect-error casting ...args, ) + validateRpcReturn(args[0], ret) }, callEvent: (...args: any): any => { return serverRpc.$callEvent( diff --git a/packages/kit/src/utils/rpc-validation.ts b/packages/kit/src/utils/rpc-validation.ts new file mode 100644 index 00000000..59185ebe --- /dev/null +++ b/packages/kit/src/utils/rpc-validation.ts @@ -0,0 +1,57 @@ +import type { GenericSchema } from 'valibot' +import { builtinRpcSchemas } from '@vitejs/devtools' +import { serverRpcSchemas } from '@vitejs/devtools-vite' +import { parse } from 'valibot' + +const rpcSchemas = new Map([ + ...builtinRpcSchemas, + ...serverRpcSchemas, +]) + +export function validateSchema(schema: GenericSchema, data: any, prefix: string) { + try { + parse(schema, data) + } + catch (e) { + throw new Error(`${prefix}: ${(e as Error).message}`) + } +} + +function getSchema(method: string) { + const schema = rpcSchemas.get(method as any) + if (!schema) + throw new Error(`RPC method "${method}" is not defined.`) + return schema +} + +export function validateRpcArgs(method: string, args: any[]) { + const schema = getSchema(method) + if (!schema) + throw new Error(`RPC method "${method}" is not defined.`) + + const { args: argsSchema } = schema + if (!argsSchema) + return + + if (argsSchema.length !== args.length) + throw new Error(`Invalid number of arguments for RPC method "${method}". Expected ${argsSchema.length}, got ${args.length}.`) + + for (let i = 0; i < argsSchema.length; i++) { + const s = argsSchema[i] + if (!s) + continue + validateSchema(s, args[i], `Invalid argument #${i + 1}`) + } +} + +export function validateRpcReturn(method: string, data: any) { + const schema = getSchema(method) + if (!schema) + throw new Error(`RPC method "${method}" is not defined.`) + + const { returns: returnSchema } = schema + if (!returnSchema) + return + + validateSchema(returnSchema, data, `Invalid return value for RPC method "${method}"`) +} diff --git a/packages/vite/src/node/index.ts b/packages/vite/src/node/index.ts index a0ed1384..51cf0eb9 100644 --- a/packages/vite/src/node/index.ts +++ b/packages/vite/src/node/index.ts @@ -1 +1,2 @@ export * from './plugin' +export { serverRpcSchemas } from './rpc/index' diff --git a/packages/vite/src/node/rpc/index.ts b/packages/vite/src/node/rpc/index.ts index 583fa4dd..412f1db4 100644 --- a/packages/vite/src/node/rpc/index.ts +++ b/packages/vite/src/node/rpc/index.ts @@ -34,6 +34,13 @@ export const rpcFunctions = [ export type ServerFunctions = RpcDefinitionsToFunctions +export const serverRpcSchemas = new Map( + rpcFunctions.map(d => [ + d.name, + { args: d.argsSchema, returns: d.returnSchema }, + ]), +) + export type ServerFunctionsStatic = RpcDefinitionsToFunctions< RpcDefinitionsFilter > From 85690ddd592019f2b1023dfafb5f40fd90c8c7df Mon Sep 17 00:00:00 2001 From: 0xfe <2953427626@qq.com> Date: Wed, 14 Jan 2026 23:56:21 +0800 Subject: [PATCH 08/10] fix: add 'lightningcss' to external dependencies in tsdown config --- packages/vite/tsdown.config.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/vite/tsdown.config.ts b/packages/vite/tsdown.config.ts index 3909fb00..1170f84a 100644 --- a/packages/vite/tsdown.config.ts +++ b/packages/vite/tsdown.config.ts @@ -5,6 +5,9 @@ export default defineConfig({ index: 'src/index.ts', dirs: 'src/dirs.ts', }, + external: [ + 'lightningcss', + ], tsconfig: '../../tsconfig.base.json', target: 'esnext', exports: true, From cb9c320b8fd5fe5b6718e01d80b0d17e4dc2b16f Mon Sep 17 00:00:00 2001 From: 0xfe <2953427626@qq.com> Date: Thu, 15 Jan 2026 00:16:48 +0800 Subject: [PATCH 09/10] fix: add 'lightningcss' as an external dependency in nuxt and tsdown configurations --- packages/vite/src/nuxt.config.ts | 2 ++ packages/vite/tsdown.config.ts | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/packages/vite/src/nuxt.config.ts b/packages/vite/src/nuxt.config.ts index 763b3099..2b06c574 100644 --- a/packages/vite/src/nuxt.config.ts +++ b/packages/vite/src/nuxt.config.ts @@ -91,6 +91,8 @@ export default defineNuxtConfig({ build: { rolldownOptions: { devtools: {}, + // https://github.com/parcel-bundler/lightningcss/issues/701 + external: ['lightningcss'], }, minify: NUXT_DEBUG_BUILD ? false : undefined, cssMinify: false, diff --git a/packages/vite/tsdown.config.ts b/packages/vite/tsdown.config.ts index 1170f84a..5c93afda 100644 --- a/packages/vite/tsdown.config.ts +++ b/packages/vite/tsdown.config.ts @@ -5,6 +5,11 @@ export default defineConfig({ index: 'src/index.ts', dirs: 'src/dirs.ts', }, + /* + * Since `lightningcss` is a dependency of vite, and devtools/vite -> devtools/kit -> devtools/core, lightningcss + * would be bundled into the final build output, which would cause issues when used in environments where + * lightningcss is expected to be an external dependency. https://github.com/parcel-bundler/lightningcss/issues/701 + */ external: [ 'lightningcss', ], From f898497617642de8cecf16d464b441fbad66f36d Mon Sep 17 00:00:00 2001 From: 0xfe <2953427626@qq.com> Date: Thu, 15 Jan 2026 00:20:08 +0800 Subject: [PATCH 10/10] feat: add serverRpcSchemas and builtinRpcSchemas to YAML configurations --- test/exports/@vitejs/devtools-vite.yaml | 1 + test/exports/@vitejs/devtools.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/test/exports/@vitejs/devtools-vite.yaml b/test/exports/@vitejs/devtools-vite.yaml index ec34cc10..1ab2b362 100644 --- a/test/exports/@vitejs/devtools-vite.yaml +++ b/test/exports/@vitejs/devtools-vite.yaml @@ -1,4 +1,5 @@ .: DevToolsViteUI: function + serverRpcSchemas: object ./dirs: clientPublicDir: string diff --git a/test/exports/@vitejs/devtools.yaml b/test/exports/@vitejs/devtools.yaml index 76311d2c..44a48a9c 100644 --- a/test/exports/@vitejs/devtools.yaml +++ b/test/exports/@vitejs/devtools.yaml @@ -1,4 +1,5 @@ .: + builtinRpcSchemas: object createDevToolsContext: function createDevToolsMiddleware: function DevTools: function