diff --git a/demo/simple/src/app-ws.ts b/demo/simple/src/app-ws.ts index bc40309..5b00af1 100644 --- a/demo/simple/src/app-ws.ts +++ b/demo/simple/src/app-ws.ts @@ -1,5 +1,7 @@ -import { Route, App } from '@kevisual/router/src/app.ts'; - +import { Route, App, tool } from '@kevisual/router/src/app.ts'; +import util from 'node:util'; +import z from 'zod'; +const showMore = (obj) => util.inspect(obj, { showHidden: false, depth: null, colors: true }); const app = new App({ serverOptions: { io: true } }); app.listen(4002); const route01 = new Route('demo', '01'); @@ -7,14 +9,6 @@ route01.run = async (ctx) => { ctx.body = '01'; return ctx; }; -app.use( - 'demo', - async (ctx) => { - ctx.body = '01'; - return ctx; - }, - { key: '01' }, -); const route02 = new Route('demo', '02'); route02.run = async (ctx) => { @@ -26,13 +20,54 @@ app.addRoute(route02); console.log(`http://localhost:4002/api/router?path=demo&key=02`); console.log(`http://localhost:4002/api/router?path=demo&key=01`); -app.server.on({ - id: 'abc', - path: '/ws', - io: true, - fun: async ({ data }, { end }) => { - console.log('Custom middleware for /ws'); - console.log('Data received:', data); - end({ message: 'Hello from /ws middleware' }); +app.route({ + path: 'demo', + key: '03', + metadata: { + info: 'This is route 03', + args: { + test: tool.schema.string().describe('defaultTest'), + } + }, +}).define(async (ctx) => { + ctx.body = '03'; + return ctx; +}).addTo(app); +// app.server.on({ +// id: 'abc', +// path: '/ws', +// io: true, +// func: async (req,res) => { +// console.log('Custom middleware for /ws'); +// // console.log('Data received:', data); +// // end({ message: 'Hello from /ws middleware' }); +// } +// }) +await app.createRouteList() +const res = await app.run({ path: 'router', key: 'list' }) + +console.log('Route List:', showMore(res.data)); + +const list = res.data.list; + +for (const item of list) { + const args = item.metadata?.args || {} + const keys = Object.keys(args) + if (keys.length > 0) { + // console.log(`Route ${item.key} has args:`, showMore(args)); + // for (const k of keys) { + // const argSchema = args[k]; + // const v = z.fromJSONSchema(argSchema) + // console.log(` Arg ${k}:`, v.description, v.toJSONSchema()); + // } + // const v = z.fromJSONSchema(args) as z.ZodObject; + // if (v instanceof z.ZodObject) { + // const testZod = v.shape['test']; + // console.log('testZod:', testZod.description); + // } + // console.log(`Route ${item.key} args schema:`, v.description, v.toJSONSchema()); + // // console.log('v.', v.) + // const test = v.parse({ test: 'hello' }) + // console.log('Parsed args:', test); } -}) \ No newline at end of file +} \ No newline at end of file diff --git a/package.json b/package.json index 9b54397..5ac7b2e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package", "name": "@kevisual/router", - "version": "0.0.67", + "version": "0.0.68", "description": "", "type": "module", "main": "./dist/router.js", diff --git a/src/browser.ts b/src/browser.ts index 585b809..cbbce03 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -8,7 +8,7 @@ export type { RouteContext, RouteOpts, RouteMiddleware } from './route.ts'; export type { Run, Skill } from './route.ts'; -export { createSkill, tool } from './route.ts'; +export { createSkill, tool, fromJSONSchema, toJSONSchema } from './route.ts'; export { CustomError } from './result/error.ts'; diff --git a/src/index.ts b/src/index.ts index 7fe8590..8a355f4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,7 +8,7 @@ export type { RouteContext, RouteOpts, RouteMiddleware } from './route.ts'; export type { Run, Skill } from './route.ts'; -export { createSkill, tool } from './route.ts'; +export { createSkill, tool, fromJSONSchema, toJSONSchema } from './route.ts'; export { CustomError } from './result/error.ts'; diff --git a/src/route.ts b/src/route.ts index 555a2e8..6ca8db0 100644 --- a/src/route.ts +++ b/src/route.ts @@ -1,7 +1,7 @@ import { nanoid } from 'nanoid'; import { CustomError } from './result/error.ts'; import { pick } from './utils/pick.ts'; -import { listenProcess } from './utils/listen-process.ts'; +import { listenProcess, MockProcess } from './utils/listen-process.ts'; import { z } from 'zod'; import { filter } from '@kevisual/js-filter' @@ -243,11 +243,42 @@ export class Route { + const pickValues = pick(route, pickValue as any); + if (pickValues?.metadata?.args) { + const args = pickValues.metadata.args; + const keys = Object.keys(args); + const newArgs: { [key: string]: any } = {}; + for (let key of keys) { + const item = args[key] as z.ZodAny; + if (item && typeof item === 'object' && typeof item.toJSONSchema === 'function') { + newArgs[key] = item.toJSONSchema(); + } else { + newArgs[key] = args[key]; // 可能不是schema + } + } + pickValues.metadata.args = newArgs; + } + return pickValues; +} + +export const fromJSONSchema = (route: RouteInfo): { + [key: string]: z.ZodTypeAny +} => { + const args = route?.metadata?.args || {}; + const keys = Object.keys(args); + const newArgs: { [key: string]: any } = {}; + for (let key of keys) { + const item = args[key]; + newArgs[key] = z.fromJSONSchema(item); + } + return newArgs; +} /** - * @parmas override 是否覆盖已存在的route,默认true + * @parmas overwrite 是否覆盖已存在的route,默认true */ -export type AddOpts = { override?: boolean }; +export type AddOpts = { overwrite?: boolean }; export class QueryRouter { appId: string = ''; routes: Route[]; @@ -262,14 +293,14 @@ export class QueryRouter { * @param opts */ add(route: Route, opts?: AddOpts) { - const override = opts?.override ?? true; + const overwrite = opts?.overwrite ?? true; const has = this.routes.findIndex((r) => r.path === route.path && r.key === route.key); if (has !== -1) { - if (!override) { + if (!overwrite) { return; } - // 如果存在,且override为true,则覆盖 + // 如果存在,且overwrite为true,则覆盖 this.routes.splice(has, 1); } this.routes.push(route); @@ -555,7 +586,23 @@ export class QueryRouter { } getList(filter?: (route: Route) => boolean): RouteInfo[] { return this.routes.filter(filter || (() => true)).map((r) => { - return pick(r, pickValue as any); + const pickValues = pick(r, pickValue as any); + if (pickValues?.metadata?.args) { + // const demoArgs = { k: tool.schema.string().describe('示例参数') }; + const args = pickValues.metadata.args; + const keys = Object.keys(args); + const newArgs: { [key: string]: any } = {}; + for (let key of keys) { + const item = args[key] as z.ZodAny; + if (item && typeof item === 'object' && typeof item.toJSONSchema === 'function') { + newArgs[key] = item.toJSONSchema(); + } else { + newArgs[key] = args[key]; // 可能不是schema + } + } + pickValues.metadata.args = newArgs; + } + return pickValues; }); } /** @@ -634,7 +681,7 @@ export class QueryRouter { * -- .send */ wait(params?: { path?: string; key?: string; payload?: any }, opts?: { - emitter?: any, + mockProcess?: MockProcess, timeout?: number, getList?: boolean force?: boolean @@ -646,6 +693,8 @@ export class QueryRouter { } return listenProcess({ app: this as any, params, ...opts }); } + static toJSONSchema = toJSONSchema; + static fromJSONSchema = fromJSONSchema; } type QueryRouterServerOpts = { @@ -729,4 +778,5 @@ export class QueryRouterServer extends QueryRouter { } -export class Mini extends QueryRouterServer { } \ No newline at end of file +export class Mini extends QueryRouterServer { } +