feat: add JSON schema conversion methods and update route handling
This commit is contained in:
@@ -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 } });
|
const app = new App({ serverOptions: { io: true } });
|
||||||
app.listen(4002);
|
app.listen(4002);
|
||||||
const route01 = new Route('demo', '01');
|
const route01 = new Route('demo', '01');
|
||||||
@@ -7,14 +9,6 @@ route01.run = async (ctx) => {
|
|||||||
ctx.body = '01';
|
ctx.body = '01';
|
||||||
return ctx;
|
return ctx;
|
||||||
};
|
};
|
||||||
app.use(
|
|
||||||
'demo',
|
|
||||||
async (ctx) => {
|
|
||||||
ctx.body = '01';
|
|
||||||
return ctx;
|
|
||||||
},
|
|
||||||
{ key: '01' },
|
|
||||||
);
|
|
||||||
|
|
||||||
const route02 = new Route('demo', '02');
|
const route02 = new Route('demo', '02');
|
||||||
route02.run = async (ctx) => {
|
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=02`);
|
||||||
console.log(`http://localhost:4002/api/router?path=demo&key=01`);
|
console.log(`http://localhost:4002/api/router?path=demo&key=01`);
|
||||||
|
|
||||||
app.server.on({
|
app.route({
|
||||||
id: 'abc',
|
path: 'demo',
|
||||||
path: '/ws',
|
key: '03',
|
||||||
io: true,
|
metadata: {
|
||||||
fun: async ({ data }, { end }) => {
|
info: 'This is route 03',
|
||||||
console.log('Custom middleware for /ws');
|
args: {
|
||||||
console.log('Data received:', data);
|
test: tool.schema.string().describe('defaultTest'),
|
||||||
end({ message: 'Hello from /ws middleware' });
|
}
|
||||||
|
},
|
||||||
|
}).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<any>;
|
||||||
|
// 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);
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package",
|
"$schema": "https://json.schemastore.org/package",
|
||||||
"name": "@kevisual/router",
|
"name": "@kevisual/router",
|
||||||
"version": "0.0.67",
|
"version": "0.0.68",
|
||||||
"description": "",
|
"description": "",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/router.js",
|
"main": "./dist/router.js",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export type { RouteContext, RouteOpts, RouteMiddleware } from './route.ts';
|
|||||||
|
|
||||||
export type { Run, Skill } 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';
|
export { CustomError } from './result/error.ts';
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export type { RouteContext, RouteOpts, RouteMiddleware } from './route.ts';
|
|||||||
|
|
||||||
export type { Run, Skill } 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';
|
export { CustomError } from './result/error.ts';
|
||||||
|
|
||||||
|
|||||||
66
src/route.ts
66
src/route.ts
@@ -1,7 +1,7 @@
|
|||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import { CustomError } from './result/error.ts';
|
import { CustomError } from './result/error.ts';
|
||||||
import { pick } from './utils/pick.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 { z } from 'zod';
|
||||||
import { filter } from '@kevisual/js-filter'
|
import { filter } from '@kevisual/js-filter'
|
||||||
|
|
||||||
@@ -243,11 +243,42 @@ export class Route<U = { [key: string]: any }, T extends SimpleObject = SimpleOb
|
|||||||
throw new CustomError(...args);
|
throw new CustomError(...args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export const toJSONSchema = (route: RouteInfo) => {
|
||||||
|
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 {
|
export class QueryRouter {
|
||||||
appId: string = '';
|
appId: string = '';
|
||||||
routes: Route[];
|
routes: Route[];
|
||||||
@@ -262,14 +293,14 @@ export class QueryRouter {
|
|||||||
* @param opts
|
* @param opts
|
||||||
*/
|
*/
|
||||||
add(route: Route, opts?: AddOpts) {
|
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);
|
const has = this.routes.findIndex((r) => r.path === route.path && r.key === route.key);
|
||||||
|
|
||||||
if (has !== -1) {
|
if (has !== -1) {
|
||||||
if (!override) {
|
if (!overwrite) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 如果存在,且override为true,则覆盖
|
// 如果存在,且overwrite为true,则覆盖
|
||||||
this.routes.splice(has, 1);
|
this.routes.splice(has, 1);
|
||||||
}
|
}
|
||||||
this.routes.push(route);
|
this.routes.push(route);
|
||||||
@@ -555,7 +586,23 @@ export class QueryRouter {
|
|||||||
}
|
}
|
||||||
getList(filter?: (route: Route) => boolean): RouteInfo[] {
|
getList(filter?: (route: Route) => boolean): RouteInfo[] {
|
||||||
return this.routes.filter(filter || (() => true)).map((r) => {
|
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
|
* -- .send
|
||||||
*/
|
*/
|
||||||
wait(params?: { path?: string; key?: string; payload?: any }, opts?: {
|
wait(params?: { path?: string; key?: string; payload?: any }, opts?: {
|
||||||
emitter?: any,
|
mockProcess?: MockProcess,
|
||||||
timeout?: number,
|
timeout?: number,
|
||||||
getList?: boolean
|
getList?: boolean
|
||||||
force?: boolean
|
force?: boolean
|
||||||
@@ -646,6 +693,8 @@ export class QueryRouter {
|
|||||||
}
|
}
|
||||||
return listenProcess({ app: this as any, params, ...opts });
|
return listenProcess({ app: this as any, params, ...opts });
|
||||||
}
|
}
|
||||||
|
static toJSONSchema = toJSONSchema;
|
||||||
|
static fromJSONSchema = fromJSONSchema;
|
||||||
}
|
}
|
||||||
|
|
||||||
type QueryRouterServerOpts = {
|
type QueryRouterServerOpts = {
|
||||||
@@ -730,3 +779,4 @@ export class QueryRouterServer extends QueryRouter {
|
|||||||
|
|
||||||
|
|
||||||
export class Mini extends QueryRouterServer { }
|
export class Mini extends QueryRouterServer { }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user