diff --git a/src/app.ts b/src/app.ts index a215639..f271c0c 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,4 +1,4 @@ -import { AddOpts, QueryRouter, Route, RouteContext, RouteOpts } from './route.ts'; +import { AddOpts, QueryRouter, QueryRouterServer, Route, RouteContext, RouteOpts } from './route.ts'; import { ServerNode, ServerNodeOpts } from './server/server.ts'; import { HandleCtx } from './server/server-base.ts'; import { ServerType } from './server/server-type.ts'; @@ -10,7 +10,7 @@ import { randomId } from './utils/random.ts'; type RouterHandle = (msg: { path: string;[key: string]: any }) => { code: string; data?: any; message?: string;[key: string]: any }; type AppOptions = { - router?: QueryRouter; + router?: QueryRouterServer; server?: ServerType; /** handle msg 关联 */ routerHandle?: RouterHandle; @@ -25,12 +25,12 @@ export type AppRouteContext = HandleCtx & RouteContext & { app: App extends QueryRouter { +export class App extends QueryRouterServer { declare appId: string; - router: QueryRouter; + router: QueryRouterServer; server: ServerType; constructor(opts?: AppOptions) { - super(); + super({ initHandle: false, context: { needSerialize: true, ...opts?.routerContext } }); const router = this; let server = opts?.server; if (!server) { @@ -42,7 +42,6 @@ export class App extends QueryRouter { } } server.setHandle(router.getHandle(router, opts?.routerHandle, opts?.routerContext)); - router.setContext({ needSerialize: true, ...opts?.routerContext }); this.router = router; this.server = server; if (opts?.appId) { @@ -64,47 +63,15 @@ export class App extends QueryRouter { // @ts-ignore this.server.listen(...args); } - addRoute(route: Route, opts?: AddOpts) { - super.add(route, opts); - } - Route = Route; route(opts: RouteOpts>): Route>; route(path: string, key?: string): Route>; route(path: string, opts?: RouteOpts>): Route>; route(path: string, key?: string, opts?: RouteOpts>): Route>; route(...args: any[]) { - const [path, key, opts] = args; - if (typeof path === 'object') { - return new Route(path.path, path.key, path); - } - if (typeof path === 'string') { - if (opts) { - return new Route(path, key, opts); - } - if (key && typeof key === 'object') { - return new Route(path, key?.key || '', key); - } - return new Route(path, key); - } - return new Route(path, key, opts); - } - prompt(description: string): Route> - prompt(description: Function): Route> - prompt(...args: any[]) { - const [desc] = args; - let description = '' - if (typeof desc === 'string') { - description = desc; - } else if (typeof desc === 'function') { - description = desc() || ''; // 如果是Promise,需要addTo App之前就要获取应有的函数了。 - } - return new Route('', '', { description }); + return super.route(...args as any[]); } - async call(message: { id?: string, path?: string; key?: string; payload?: any }, ctx?: AppRouteContext & { [key: string]: any }) { - return await super.call(message, ctx); - } async run(msg: { id?: string, path?: string; key?: string; payload?: any }, ctx?: Partial> & { [key: string]: any }) { return await super.run(msg, ctx); } diff --git a/src/opencode.ts b/src/opencode.ts index ddc8a1c..562b324 100644 --- a/src/opencode.ts +++ b/src/opencode.ts @@ -59,9 +59,16 @@ export const createRouterAgentPluginFn = (opts?: { if (!router.hasRoute('call', '')) { addCallFn(router as App) } - if (!router.hasRoute('auth', '')) { - router.route({ path: 'auth', key: '', id: 'auth', description: '认证' }).define(async (ctx) => { }).addTo(router as App) + if (router) { + (router as any).route({ path: 'auth', key: '', id: 'auth', description: '认证' }).define(async (ctx) => { }).addTo(router as App, { + overwrite: false + }); + + (router as any).route({ path: 'auth-admin', key: '', id: 'auth-admin', description: '认证' }).define(async (ctx) => { }).addTo(router as App, { + overwrite: false + }) } + const _routes = filter(router.routes, opts?.query || '') const routes = _routes.filter(r => { const metadata = r.metadata as Skill diff --git a/src/route.ts b/src/route.ts index febfdb9..c8f609d 100644 --- a/src/route.ts +++ b/src/route.ts @@ -6,6 +6,23 @@ import { randomId } from './utils/random.ts'; import * as schema from './validator/schema.ts'; export type RouterContextT = { code?: number;[key: string]: any }; + +type ExtractArgs = A extends z.ZodTypeAny ? z.infer : A; + +type OptionalKeys = { + [K in keyof T]-?: {} extends Pick ? K : never; +}[keyof T]; + +type MakeOptional = Omit & Partial>; + +type BuildRouteContext = M extends { args?: infer A } + ? A extends z.ZodObject + ? RouteContext<{ args?: z.infer }, U> + : A extends Record + ? RouteContext<{ args?: { [K in keyof A]: z.infer } }, U> + : RouteContext + : RouteContext; + export type RouteContext = { /** * 本地自己调用的时候使用,可以标识为当前自调用,那么 auth 就不许重复的校验 @@ -23,7 +40,9 @@ export type RouteContext = { code?: number; /** return msg */ message?: string; - // 传递状态 + /** + * 传递状态 + */ state?: S; // transfer data /** @@ -127,7 +146,9 @@ export const createSkill = (skill: Skill): Skill => { export type RouteInfo = Pick; -export class Route implements throwError { +type ExtractMetadata = M extends { metadata?: infer Meta } ? Meta extends SimpleObject ? Meta : SimpleObject : SimpleObject; + +export class Route implements throwError { /** * 一级路径 */ @@ -137,10 +158,10 @@ export class Route>; nextRoute?: NextRoute; // route to run after this route description?: string; - metadata?: T; + metadata?: M; middleware?: RouteMiddleware[]; // middleware type? = 'route'; /** @@ -161,10 +182,10 @@ export class Route>; this.nextRoute = opts.nextRoute; this.description = opts.description; - this.metadata = opts.metadata as T; + this.metadata = opts.metadata as M; this.type = opts.type || 'route'; this.middleware = opts.middleware || []; this.key = opts.key || key; @@ -188,9 +209,9 @@ export class Route(opts: DefineRouteOpts): this; - define(fn: Run): this; - define(key: string, fn: Run): this; - define(path: string, key: string, fn: Run): this; + define(fn: Run>): this; + define(key: string, fn: Run>): this; + define(path: string, key: string, fn: Run>): this; define(...args: any[]) { const [path, key, opts] = args; // 全覆盖,所以opts需要准确,不能由idUsePath 需要check的变量 @@ -213,7 +234,7 @@ export class Route>; return this; } if (typeof path === 'string' && typeof key === 'function') { @@ -692,6 +713,7 @@ type QueryRouterServerOpts = { handleFn?: HandleFn; context?: RouteContext; appId?: string; + initHandle?: boolean; }; interface HandleFn { (msg: { path: string;[key: string]: any }, ctx?: any): { code: string; data?: any; message?: string;[key: string]: any }; @@ -706,7 +728,10 @@ export class QueryRouterServer extends QueryRouter { handle: any; constructor(opts?: QueryRouterServerOpts) { super(); - this.handle = this.getHandle(this, opts?.handleFn, opts?.context); + const initHandle = opts?.initHandle ?? true; + if (initHandle || opts?.handleFn) { + this.handle = this.getHandle(this, opts?.handleFn, opts?.context); + } this.setContext({ needSerialize: false, ...opts?.context }); if (opts?.appId) { this.appId = opts.appId; @@ -721,37 +746,25 @@ export class QueryRouterServer extends QueryRouter { this.add(route, opts); } Route = Route; - route(opts: RouteOpts): Route>; - route(path: string, key?: string): Route>; - route(path: string, opts?: RouteOpts): Route>; - route(path: string, key?: string, opts?: RouteOpts): Route>; - route(...args: any[]) { + route(opts: RouteOpts & { metadata?: M }): Route>; + route(path: string, opts?: RouteOpts & { metadata?: M }): Route>; + route(path: string, key?: string): Route>; + route(path: string, key?: string, opts?: RouteOpts & { metadata?: M }): Route>; + route(...args: any[]) { const [path, key, opts] = args; if (typeof path === 'object') { - return new Route(path.path, path.key, path); + return new Route>(path.path, path.key, path); } if (typeof path === 'string') { if (opts) { - return new Route(path, key, opts); + return new Route>(path, key, opts); } if (key && typeof key === 'object') { - return new Route(path, key?.key || '', key); + return new Route>(path, key?.key || '', key); } - return new Route(path, key); + return new Route>(path, key); } - return new Route(path, key, opts); - } - prompt(description: string): Route>; - prompt(description: Function): Route>; - prompt(...args: any[]) { - const [desc] = args; - let description = '' - if (typeof desc === 'string') { - description = desc; - } else if (typeof desc === 'function') { - description = desc() || ''; // 如果是Promise,需要addTo App之前就要获取应有的函数了。 - } - return new Route('', '', { description }); + return new Route>(path, key, opts); } /** diff --git a/src/test/route-ts.ts b/src/test/route-ts.ts new file mode 100644 index 0000000..1da0d8e --- /dev/null +++ b/src/test/route-ts.ts @@ -0,0 +1,15 @@ +import { QueryRouterServer } from "@/route.ts"; +import z from "zod"; + +const router = new QueryRouterServer() + +router.route({ + metadata: { + args: { + a: z.string(), + } + }, +}).define(async (ctx) => { + const argA: string = ctx.args.a; + ctx.body = '1'; +}) \ No newline at end of file