@kevisual/router 0.0.82 → 0.0.84

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/app.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AddOpts, QueryRouter, Route, RouteContext, RouteOpts } from './route.ts';
1
+ import { AddOpts, QueryRouter, QueryRouterServer, Route, RouteContext, RouteOpts } from './route.ts';
2
2
  import { ServerNode, ServerNodeOpts } from './server/server.ts';
3
3
  import { HandleCtx } from './server/server-base.ts';
4
4
  import { ServerType } from './server/server-type.ts';
@@ -10,7 +10,7 @@ import { randomId } from './utils/random.ts';
10
10
 
11
11
  type RouterHandle = (msg: { path: string;[key: string]: any }) => { code: string; data?: any; message?: string;[key: string]: any };
12
12
  type AppOptions<T = {}> = {
13
- router?: QueryRouter;
13
+ router?: QueryRouterServer;
14
14
  server?: ServerType;
15
15
  /** handle msg 关联 */
16
16
  routerHandle?: RouterHandle;
@@ -19,18 +19,19 @@ type AppOptions<T = {}> = {
19
19
  appId?: string;
20
20
  };
21
21
 
22
- export type AppRouteContext<T = {}> = HandleCtx & RouteContext<T> & { app: App<T> };
22
+ export type AppRouteContext<T> = HandleCtx & RouteContext<T> & { app: App<T> };
23
23
 
24
24
  /**
25
25
  * 封装了 Router 和 Server 的 App 模块,处理http的请求和响应,内置了 Cookie 和 Token 和 res 的处理
26
26
  * U - Route Context的扩展类型
27
27
  */
28
- export class App<U = {}> extends QueryRouter {
28
+ export class App<U = {}> extends QueryRouterServer<AppRouteContext<U>> {
29
29
  declare appId: string;
30
- router: QueryRouter;
30
+ router: QueryRouterServer;
31
31
  server: ServerType;
32
+ declare context: AppRouteContext<U>;
32
33
  constructor(opts?: AppOptions<U>) {
33
- super();
34
+ super({ initHandle: false, context: { needSerialize: true, ...opts?.routerContext } as any });
34
35
  const router = this;
35
36
  let server = opts?.server;
36
37
  if (!server) {
@@ -42,7 +43,6 @@ export class App<U = {}> extends QueryRouter {
42
43
  }
43
44
  }
44
45
  server.setHandle(router.getHandle(router, opts?.routerHandle, opts?.routerContext));
45
- router.setContext({ needSerialize: true, ...opts?.routerContext });
46
46
  this.router = router;
47
47
  this.server = server;
48
48
  if (opts?.appId) {
@@ -64,50 +64,7 @@ export class App<U = {}> extends QueryRouter {
64
64
  // @ts-ignore
65
65
  this.server.listen(...args);
66
66
  }
67
- addRoute(route: Route, opts?: AddOpts) {
68
- super.add(route, opts);
69
- }
70
-
71
67
  Route = Route;
72
- route(opts: RouteOpts<AppRouteContext<U>>): Route<AppRouteContext<U>>;
73
- route(path: string, key?: string): Route<AppRouteContext<U>>;
74
- route(path: string, opts?: RouteOpts<AppRouteContext<U>>): Route<AppRouteContext<U>>;
75
- route(path: string, key?: string, opts?: RouteOpts<AppRouteContext<U>>): Route<AppRouteContext<U>>;
76
- route(...args: any[]) {
77
- const [path, key, opts] = args;
78
- if (typeof path === 'object') {
79
- return new Route(path.path, path.key, path);
80
- }
81
- if (typeof path === 'string') {
82
- if (opts) {
83
- return new Route(path, key, opts);
84
- }
85
- if (key && typeof key === 'object') {
86
- return new Route(path, key?.key || '', key);
87
- }
88
- return new Route(path, key);
89
- }
90
- return new Route(path, key, opts);
91
- }
92
- prompt(description: string): Route<AppRouteContext<U>>
93
- prompt(description: Function): Route<AppRouteContext<U>>
94
- prompt(...args: any[]) {
95
- const [desc] = args;
96
- let description = ''
97
- if (typeof desc === 'string') {
98
- description = desc;
99
- } else if (typeof desc === 'function') {
100
- description = desc() || ''; // 如果是Promise,需要addTo App之前就要获取应有的函数了。
101
- }
102
- return new Route('', '', { description });
103
- }
104
-
105
- async call(message: { id?: string, path?: string; key?: string; payload?: any }, ctx?: AppRouteContext<U> & { [key: string]: any }) {
106
- return await super.call(message, ctx);
107
- }
108
- async run(msg: { id?: string, path?: string; key?: string; payload?: any }, ctx?: Partial<AppRouteContext<U>> & { [key: string]: any }) {
109
- return await super.run(msg, ctx);
110
- }
111
68
  static handleRequest(req: IncomingMessage, res: ServerResponse) {
112
69
  return handleServer(req, res);
113
70
  }
package/src/opencode.ts CHANGED
@@ -10,7 +10,7 @@ export const addCallFn = (app: App) => {
10
10
  path: 'call',
11
11
  key: '',
12
12
  description: '调用',
13
- middleware: ['auth'],
13
+ middleware: ['auth-admin'],
14
14
  metadata: {
15
15
  tags: ['opencode'],
16
16
  ...createSkill({
@@ -24,7 +24,7 @@ export const addCallFn = (app: App) => {
24
24
  args: {
25
25
  path: tool.schema.string().describe('应用路径,例如 cnb'),
26
26
  key: tool.schema.string().optional().describe('应用key,例如 list-repos'),
27
- payload: tool.schema.object({}).optional().describe('调用参数'),
27
+ payload: tool.schema.object({}).optional().describe('调用参数, 为对象, 例如 { "query": "javascript" }'),
28
28
  }
29
29
  })
30
30
  },
@@ -59,9 +59,16 @@ export const createRouterAgentPluginFn = (opts?: {
59
59
  if (!router.hasRoute('call', '')) {
60
60
  addCallFn(router as App)
61
61
  }
62
- if (!router.hasRoute('auth', '')) {
63
- router.route({ path: 'auth', key: '', id: 'auth', description: '认证' }).define(async (ctx) => { }).addTo(router as App)
62
+ if (router) {
63
+ (router as any).route({ path: 'auth', key: '', id: 'auth', description: '认证' }).define(async (ctx) => { }).addTo(router as App, {
64
+ overwrite: false
65
+ });
66
+
67
+ (router as any).route({ path: 'auth-admin', key: '', id: 'auth-admin', description: '认证' }).define(async (ctx) => { }).addTo(router as App, {
68
+ overwrite: false
69
+ })
64
70
  }
71
+
65
72
  const _routes = filter(router.routes, opts?.query || '')
66
73
  const routes = _routes.filter(r => {
67
74
  const metadata = r.metadata as Skill
@@ -75,7 +82,7 @@ export const createRouterAgentPluginFn = (opts?: {
75
82
  });
76
83
  // opencode run "使用技能查看系统信息"
77
84
  const AgentPlugin: Plugin = async (pluginInput) => {
78
- useContextKey<PluginInput>('plugin-input', () => pluginInput, true)
85
+ useContextKey<PluginInput>('plugin-input', () => pluginInput, { isNew: true })
79
86
  const hooks = opts?.hooks ? await opts.hooks(pluginInput) : {}
80
87
  return {
81
88
  ...hooks,
@@ -47,6 +47,22 @@ export class CustomError extends Error {
47
47
  static isError(error: unknown): error is CustomError {
48
48
  return error instanceof CustomError || (typeof error === 'object' && error !== null && 'code' in error);
49
49
  }
50
+ static throw(code?: number | string, message?: string): void;
51
+ static throw(code?: number | string, opts?: CustomErrorOptions): void;
52
+ static throw(opts?: CustomErrorOptions): void;
53
+ static throw(...args: any[]) {
54
+ const [args0, args1] = args;
55
+ if (args0 && typeof args0 === 'object') {
56
+ throw new CustomError(args0);
57
+ }
58
+ if (args1 && typeof args1 === 'object') {
59
+ throw new CustomError(args0, args1);
60
+ } else if (args1) {
61
+ throw new CustomError(args0, { message: args1 });
62
+ }
63
+ // args1 不存在;
64
+ throw new CustomError(args0);
65
+ }
50
66
  parse(e?: CustomError) {
51
67
  if (e) {
52
68
  return CustomError.parseError(e);
@@ -61,6 +77,12 @@ export class CustomError extends Error {
61
77
  }
62
78
  }
63
79
 
80
+ export interface throwError {
81
+ throw(code?: number | string, message?: string): void;
82
+ throw(code?: number | string, opts?: CustomErrorOptions): void;
83
+ throw(opts?: CustomErrorOptions): void;
84
+ }
85
+
64
86
  /*
65
87
  try {
66
88
  //
package/src/route.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { CustomError, CustomErrorOptions } from './result/error.ts';
1
+ import { CustomError, throwError, CustomErrorOptions } from './result/error.ts';
2
2
  import { pick } from './utils/pick.ts';
3
3
  import { listenProcess, MockProcess } from './utils/listen-process.ts';
4
4
  import { z } from 'zod';
@@ -6,7 +6,24 @@ import { randomId } from './utils/random.ts';
6
6
  import * as schema from './validator/schema.ts';
7
7
 
8
8
  export type RouterContextT = { code?: number;[key: string]: any };
9
- export type RouteContext<T = { code?: number }, S = any> = {
9
+
10
+ type ExtractArgs<A> = A extends z.ZodTypeAny ? z.infer<A> : A;
11
+
12
+ type OptionalKeys<T> = {
13
+ [K in keyof T]-?: {} extends Pick<T, K> ? K : never;
14
+ }[keyof T];
15
+
16
+ type MakeOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
17
+
18
+ type BuildRouteContext<M, U> = M extends { args?: infer A }
19
+ ? A extends z.ZodObject<any>
20
+ ? RouteContext<{ args?: z.infer<A> }, U>
21
+ : A extends Record<string, z.ZodTypeAny>
22
+ ? RouteContext<{ args?: { [K in keyof A]: z.infer<A[K]> } }, U>
23
+ : RouteContext<U>
24
+ : RouteContext<U>;
25
+
26
+ export type RouteContext<T = { code?: number }, U extends SimpleObject = {}, S = { [key: string]: any }> = {
10
27
  /**
11
28
  * 本地自己调用的时候使用,可以标识为当前自调用,那么 auth 就不许重复的校验
12
29
  * 或者不需要登录的,直接调用
@@ -23,9 +40,15 @@ export type RouteContext<T = { code?: number }, S = any> = {
23
40
  code?: number;
24
41
  /** return msg */
25
42
  message?: string;
26
- // 传递状态
43
+ /**
44
+ * 传递状态
45
+ */
27
46
  state?: S;
28
47
  // transfer data
48
+ /**
49
+ * 当前routerId
50
+ */
51
+ currentId?: string;
29
52
  /**
30
53
  * 当前路径
31
54
  */
@@ -54,19 +77,19 @@ export type RouteContext<T = { code?: number }, S = any> = {
54
77
  ctx?: RouteContext & { [key: string]: any },
55
78
  ) => Promise<any>;
56
79
  /** 请求 route的返回结果,解析了body为data,就类同于 query.post获取的数据*/
57
- run?: (message: { path: string; key?: string; payload?: any }, ctx?: RouteContext & { [key: string]: any }) => Promise<any>;
80
+ run?: (message: { path: string; key?: string; payload?: any }, ctx?: RouteContext) => Promise<any>;
58
81
  index?: number;
59
- throw?: (code?: number | string, message?: string, tips?: string) => void;
82
+ throw?: throwError['throw'];
60
83
  /** 是否需要序列化, 使用JSON.stringify和JSON.parse */
61
84
  needSerialize?: boolean;
62
- } & T;
85
+ } & T & U;
63
86
  export type SimpleObject = Record<string, any>;
64
87
  export type Run<T extends SimpleObject = {}> = (ctx: Required<RouteContext<T>>) => Promise<typeof ctx | null | void>;
65
88
  export type RunMessage = { path?: string; key?: string; id?: string; payload?: any; };
66
89
  export type NextRoute = Pick<Route, 'id' | 'path' | 'key'>;
67
90
  export type RouteMiddleware =
68
91
  | {
69
- path: string;
92
+ path?: string;
70
93
  key?: string;
71
94
  id?: string;
72
95
  }
@@ -123,7 +146,13 @@ export const createSkill = <T = SimpleObject>(skill: Skill<T>): Skill<T> => {
123
146
 
124
147
  export type RouteInfo = Pick<Route, (typeof pickValue)[number]>;
125
148
 
126
- export class Route<U = { [key: string]: any }, T extends SimpleObject = SimpleObject> {
149
+ type ExtractMetadata<M> = M extends { metadata?: infer Meta } ? Meta extends SimpleObject ? Meta : SimpleObject : SimpleObject;
150
+
151
+ /**
152
+ * @M 是 route的 metadate的类型,默认是 SimpleObject
153
+ * @U 是 RouteContext 里 state的类型
154
+ */
155
+ export class Route<M extends SimpleObject = SimpleObject, U extends SimpleObject = SimpleObject> implements throwError {
127
156
  /**
128
157
  * 一级路径
129
158
  */
@@ -133,10 +162,10 @@ export class Route<U = { [key: string]: any }, T extends SimpleObject = SimpleOb
133
162
  */
134
163
  key?: string;
135
164
  id?: string;
136
- run?: Run;
165
+ run?: Run<BuildRouteContext<M, U>>;
137
166
  nextRoute?: NextRoute; // route to run after this route
138
167
  description?: string;
139
- metadata?: T;
168
+ metadata?: M;
140
169
  middleware?: RouteMiddleware[]; // middleware
141
170
  type? = 'route';
142
171
  /**
@@ -157,10 +186,10 @@ export class Route<U = { [key: string]: any }, T extends SimpleObject = SimpleOb
157
186
  const delimiter = opts.delimiter ?? '$#$';
158
187
  this.id = path + delimiter + key;
159
188
  }
160
- this.run = opts.run;
189
+ this.run = opts.run as Run<BuildRouteContext<M, U>>;
161
190
  this.nextRoute = opts.nextRoute;
162
191
  this.description = opts.description;
163
- this.metadata = opts.metadata as T;
192
+ this.metadata = opts.metadata as M;
164
193
  this.type = opts.type || 'route';
165
194
  this.middleware = opts.middleware || [];
166
195
  this.key = opts.key || key;
@@ -184,9 +213,9 @@ export class Route<U = { [key: string]: any }, T extends SimpleObject = SimpleOb
184
213
  return this;
185
214
  }
186
215
  define<T extends { [key: string]: any } = RouterContextT>(opts: DefineRouteOpts): this;
187
- define<T extends { [key: string]: any } = RouterContextT>(fn: Run<T & U>): this;
188
- define<T extends { [key: string]: any } = RouterContextT>(key: string, fn: Run<T & U>): this;
189
- define<T extends { [key: string]: any } = RouterContextT>(path: string, key: string, fn: Run<T & U>): this;
216
+ define<T extends { [key: string]: any } = RouterContextT>(fn: Run<T & BuildRouteContext<M, U>>): this;
217
+ define<T extends { [key: string]: any } = RouterContextT>(key: string, fn: Run<T & BuildRouteContext<M, U>>): this;
218
+ define<T extends { [key: string]: any } = RouterContextT>(path: string, key: string, fn: Run<T & BuildRouteContext<M, U>>): this;
190
219
  define(...args: any[]) {
191
220
  const [path, key, opts] = args;
192
221
  // 全覆盖,所以opts需要准确,不能由idUsePath 需要check的变量
@@ -209,7 +238,7 @@ export class Route<U = { [key: string]: any }, T extends SimpleObject = SimpleOb
209
238
  return this;
210
239
  }
211
240
  if (typeof path === 'function') {
212
- this.run = path;
241
+ this.run = path as Run<BuildRouteContext<M, U>>;
213
242
  return this;
214
243
  }
215
244
  if (typeof path === 'string' && typeof key === 'function') {
@@ -242,9 +271,8 @@ export class Route<U = { [key: string]: any }, T extends SimpleObject = SimpleOb
242
271
  addTo(router: QueryRouter | { add: (route: Route) => void;[key: string]: any }, opts?: AddOpts) {
243
272
  router.add(this, opts);
244
273
  }
245
- throw(code?: number | string, message?: string, tips?: string): void;
246
274
  throw(...args: any[]) {
247
- throw new CustomError(...args);
275
+ CustomError.throw(...args);
248
276
  }
249
277
  }
250
278
 
@@ -263,11 +291,11 @@ export const fromJSONSchema = schema.fromJSONSchema;
263
291
  * @parmas overwrite 是否覆盖已存在的route,默认true
264
292
  */
265
293
  export type AddOpts = { overwrite?: boolean };
266
- export class QueryRouter {
294
+ export class QueryRouter<T extends SimpleObject = SimpleObject> implements throwError {
267
295
  appId: string = '';
268
296
  routes: Route[];
269
297
  maxNextRoute = 40;
270
- context?: RouteContext = {}; // default context for call
298
+ context?: RouteContext<T> = {} as RouteContext<T>; // default context for call
271
299
  constructor() {
272
300
  this.routes = [];
273
301
  }
@@ -310,11 +338,12 @@ export class QueryRouter {
310
338
  * @param ctx
311
339
  * @returns
312
340
  */
313
- async runRoute(path: string, key: string, ctx?: RouteContext) {
341
+ async runRoute(path: string, key: string, ctx?: RouteContext<T>): Promise<RouteContext<T>> {
314
342
  const route = this.routes.find((r) => r.path === path && r.key === key);
315
343
  const maxNextRoute = this.maxNextRoute;
316
- ctx = (ctx || {}) as RouteContext;
344
+ ctx = (ctx || {}) as RouteContext<T>;
317
345
  ctx.currentPath = path;
346
+ ctx.currentId = route?.id;
318
347
  ctx.currentKey = key;
319
348
  ctx.currentRoute = route;
320
349
  ctx.index = (ctx.index || 0) + 1;
@@ -328,7 +357,7 @@ export class QueryRouter {
328
357
  ctx.code = 500;
329
358
  ctx.message = 'Too many nextRoute';
330
359
  ctx.body = null;
331
- return;
360
+ return ctx;
332
361
  }
333
362
  // run middleware
334
363
  if (route && route.middleware && route.middleware.length > 0) {
@@ -383,7 +412,7 @@ export class QueryRouter {
383
412
  const middleware = routeMiddleware[i];
384
413
  if (middleware) {
385
414
  try {
386
- await middleware.run(ctx as Required<RouteContext>);
415
+ await middleware.run(ctx as Required<RouteContext<T>>);
387
416
  } catch (e) {
388
417
  if (route?.isDebug) {
389
418
  console.error('=====debug====:middlerware error');
@@ -405,6 +434,7 @@ export class QueryRouter {
405
434
  return ctx;
406
435
  }
407
436
  if (ctx.end) {
437
+ return ctx;
408
438
  }
409
439
  }
410
440
  }
@@ -413,7 +443,7 @@ export class QueryRouter {
413
443
  if (route) {
414
444
  if (route.run) {
415
445
  try {
416
- await route.run(ctx as Required<RouteContext>);
446
+ await route.run(ctx as Required<RouteContext<T>>);
417
447
  } catch (e) {
418
448
  if (route?.isDebug) {
419
449
  console.error('=====debug====:route error');
@@ -468,7 +498,7 @@ export class QueryRouter {
468
498
  }
469
499
  }
470
500
  // 如果没有找到route,返回404,这是因为出现了错误
471
- return Promise.resolve({ code: 404, body: 'Not found' });
501
+ return Promise.resolve({ code: 404, body: 'Not found' } as RouteContext<T>);
472
502
  }
473
503
  /**
474
504
  * 第一次执行
@@ -476,12 +506,12 @@ export class QueryRouter {
476
506
  * @param ctx
477
507
  * @returns
478
508
  */
479
- async parse(message: { path: string; key?: string; payload?: any }, ctx?: RouteContext & { [key: string]: any }) {
509
+ async parse(message: { path: string; key?: string; payload?: any }, ctx?: RouteContext<T> & { [key: string]: any }) {
480
510
  if (!message?.path) {
481
- return Promise.resolve({ code: 404, body: null, message: 'Not found path' });
511
+ return Promise.resolve({ code: 404, body: null, message: 'Not found path' } as RouteContext<T>);
482
512
  }
483
513
  const { path, key = '', payload = {}, ...query } = message;
484
- ctx = ctx || {};
514
+ ctx = ctx || {} as RouteContext<T>;
485
515
  ctx.query = { ...ctx.query, ...query, ...payload };
486
516
  ctx.args = ctx.query;
487
517
  ctx.state = { ...ctx?.state };
@@ -515,7 +545,7 @@ export class QueryRouter {
515
545
  * @param ctx
516
546
  * @returns
517
547
  */
518
- async call(message: { id?: string; path?: string; key?: string; payload?: any }, ctx?: RouteContext & { [key: string]: any }) {
548
+ async call(message: { id?: string; path?: string; key?: string; payload?: any }, ctx?: RouteContext<T> & { [key: string]: any }) {
519
549
  let path = message.path;
520
550
  let key = message.key;
521
551
  // 优先 path + key
@@ -556,7 +586,7 @@ export class QueryRouter {
556
586
  * @param ctx
557
587
  * @returns
558
588
  */
559
- async run(message: { id?: string; path?: string; key?: string; payload?: any }, ctx?: RouteContext & { [key: string]: any }) {
589
+ async run(message: { id?: string; path?: string; key?: string; payload?: any }, ctx?: RouteContext<T> & { [key: string]: any }) {
560
590
  const res = await this.call(message, { ...this.context, ...ctx });
561
591
  return {
562
592
  code: res.code,
@@ -570,7 +600,7 @@ export class QueryRouter {
570
600
  * @param ctx
571
601
  */
572
602
  setContext(ctx: RouteContext) {
573
- this.context = ctx;
603
+ this.context = ctx as RouteContext<T>;
574
604
  }
575
605
  getList(filter?: (route: Route) => boolean): RouteInfo[] {
576
606
  return this.routes.filter(filter || (() => true)).map((r) => {
@@ -581,11 +611,11 @@ export class QueryRouter {
581
611
  /**
582
612
  * 获取handle函数, 这里会去执行parse函数
583
613
  */
584
- getHandle<T = any>(router: QueryRouter, wrapperFn?: HandleFn<T>, ctx?: RouteContext) {
585
- return async (msg: { id?: string; path?: string; key?: string;[key: string]: any }, handleContext?: RouteContext) => {
614
+ getHandle<T = any>(router: QueryRouter, wrapperFn?: HandleFn, ctx?: RouteContext) {
615
+ return async (msg: { id?: string; path?: string; key?: string;[key: string]: any }, handleContext?: RouteContext<T>) => {
586
616
  try {
587
617
  const context = { ...ctx, ...handleContext };
588
- const res = await router.call(msg, context);
618
+ const res = await router.call(msg, context) as any;
589
619
  if (wrapperFn) {
590
620
  res.data = res.body;
591
621
  return wrapperFn(res, context);
@@ -610,21 +640,8 @@ export class QueryRouter {
610
640
  importRouter(router: QueryRouter) {
611
641
  this.importRoutes(router.routes);
612
642
  }
613
- throw(code?: number | string, message?: string): void;
614
- throw(code?: number | string, opts?: CustomErrorOptions): void;
615
- throw(opts?: CustomErrorOptions): void;
616
643
  throw(...args: any[]) {
617
- const [args0, args1] = args;
618
- if (args0 && typeof args0 === 'object') {
619
- throw new CustomError(args0);
620
- }
621
- if (args1 && typeof args1 === 'object') {
622
- throw new CustomError(args0, args1);
623
- } else if (args1) {
624
- throw new CustomError(args0, { message: args1 });
625
- }
626
- // args1 不存在;
627
- throw new CustomError(args0);
644
+ CustomError.throw(...args);
628
645
  }
629
646
  hasRoute(path: string, key: string = '') {
630
647
  return this.routes.find((r) => r.path === path && r.key === key);
@@ -651,7 +668,7 @@ export class QueryRouter {
651
668
  description: '列出当前应用下的所有的路由信息',
652
669
  middleware: opts?.middleware || [],
653
670
  run: async (ctx: RouteContext) => {
654
- const tokenUser = ctx.state.tokenUser;
671
+ const tokenUser = ctx.state as unknown as { tokenUser?: any };
655
672
  let isUser = !!tokenUser;
656
673
  const list = this.getList(opts?.filter).filter((item) => {
657
674
  if (item.id === 'auth' || item.id === 'auth-can' || item.id === 'check-auth-admin' || item.id === 'auth-admin') {
@@ -697,10 +714,11 @@ export class QueryRouter {
697
714
  fromJSONSchema = fromJSONSchema;
698
715
  }
699
716
 
700
- type QueryRouterServerOpts = {
717
+ type QueryRouterServerOpts<C extends SimpleObject = SimpleObject> = {
701
718
  handleFn?: HandleFn;
702
- context?: RouteContext;
719
+ context?: RouteContext<C>;
703
720
  appId?: string;
721
+ initHandle?: boolean;
704
722
  };
705
723
  interface HandleFn<T = any> {
706
724
  (msg: { path: string;[key: string]: any }, ctx?: any): { code: string; data?: any; message?: string;[key: string]: any };
@@ -709,13 +727,18 @@ interface HandleFn<T = any> {
709
727
  /**
710
728
  * QueryRouterServer
711
729
  * @description 移除server相关的功能,只保留router相关的功能,和http.createServer不相关,独立
730
+ * @template C 自定义 RouteContext 类型
712
731
  */
713
- export class QueryRouterServer extends QueryRouter {
732
+ export class QueryRouterServer<C extends SimpleObject = SimpleObject> extends QueryRouter<C> {
714
733
  declare appId: string;
715
734
  handle: any;
716
- constructor(opts?: QueryRouterServerOpts) {
735
+ declare context: RouteContext<C>;
736
+ constructor(opts?: QueryRouterServerOpts<C>) {
717
737
  super();
718
- this.handle = this.getHandle(this, opts?.handleFn, opts?.context);
738
+ const initHandle = opts?.initHandle ?? true;
739
+ if (initHandle || opts?.handleFn) {
740
+ this.handle = this.getHandle(this, opts?.handleFn, opts?.context);
741
+ }
719
742
  this.setContext({ needSerialize: false, ...opts?.context });
720
743
  if (opts?.appId) {
721
744
  this.appId = opts.appId;
@@ -730,37 +753,28 @@ export class QueryRouterServer extends QueryRouter {
730
753
  this.add(route, opts);
731
754
  }
732
755
  Route = Route;
733
- route(opts: RouteOpts): Route<Required<RouteContext>>;
734
- route(path: string, key?: string): Route<Required<RouteContext>>;
735
- route(path: string, opts?: RouteOpts): Route<Required<RouteContext>>;
736
- route(path: string, key?: string, opts?: RouteOpts): Route<Required<RouteContext>>;
737
- route(...args: any[]) {
756
+ route<M extends SimpleObject = SimpleObject>(opts: RouteOpts & { metadata?: M }): Route<M, Required<RouteContext<C>>>;
757
+ route<M extends SimpleObject = SimpleObject>(path: string, opts?: RouteOpts & { metadata?: M }): Route<M, Required<RouteContext<C>>>;
758
+ route<M extends SimpleObject = SimpleObject>(path: string, key?: string): Route<M, Required<RouteContext<C>>>;
759
+ route<M extends SimpleObject = SimpleObject>(path: string, key?: string, opts?: RouteOpts & { metadata?: M }): Route<M, Required<RouteContext<C>>>;
760
+ route<M extends SimpleObject = SimpleObject>(...args: any[]) {
738
761
  const [path, key, opts] = args;
739
762
  if (typeof path === 'object') {
740
- return new Route(path.path, path.key, path);
763
+ return new Route<M, Required<RouteContext<C>>>(path.path, path.key, path);
741
764
  }
742
765
  if (typeof path === 'string') {
743
766
  if (opts) {
744
- return new Route(path, key, opts);
767
+ return new Route<M, Required<RouteContext<C>>>(path, key, opts);
745
768
  }
746
769
  if (key && typeof key === 'object') {
747
- return new Route(path, key?.key || '', key);
770
+ return new Route<M, Required<RouteContext<C>>>(path, key?.key || '', key);
748
771
  }
749
- return new Route(path, key);
772
+ return new Route<M, Required<RouteContext<C>>>(path, key);
750
773
  }
751
- return new Route(path, key, opts);
774
+ return new Route<M, Required<RouteContext<C>>>(path, key, opts);
752
775
  }
753
- prompt(description: string): Route<Required<RouteContext>>;
754
- prompt(description: Function): Route<Required<RouteContext>>;
755
- prompt(...args: any[]) {
756
- const [desc] = args;
757
- let description = ''
758
- if (typeof desc === 'string') {
759
- description = desc;
760
- } else if (typeof desc === 'function') {
761
- description = desc() || ''; // 如果是Promise,需要addTo App之前就要获取应有的函数了。
762
- }
763
- return new Route('', '', { description });
776
+ prompt(description: string) {
777
+ return new Route(undefined, undefined, { description });
764
778
  }
765
779
 
766
780
  /**
@@ -768,12 +782,12 @@ export class QueryRouterServer extends QueryRouter {
768
782
  * @param param0
769
783
  * @returns
770
784
  */
771
- async run(msg: { id?: string; path?: string; key?: string; payload?: any }, ctx?: RouteContext & { [key: string]: any }) {
785
+ async run(msg: { id?: string; path?: string; key?: string; payload?: any }, ctx?: Partial<RouteContext<C>>) {
772
786
  const handle = this.handle;
773
787
  if (handle) {
774
788
  return handle(msg, ctx);
775
789
  }
776
- return super.run(msg, ctx);
790
+ return super.run(msg, ctx as RouteContext<C>);
777
791
  }
778
792
  }
779
793
 
@@ -1,13 +1,69 @@
1
- import { App } from '../app.ts'
1
+ import { App, AppRouteContext } from "@/app.ts";
2
+ import { QueryRouterServer, RouteContext } from "@/app.ts";
3
+ import z from "zod";
4
+ const route: RouteContext<{ customField: string }> = {} as any;
5
+ route.customField
6
+ const appRoute: AppRouteContext<{ customField: string }> = {} as any;
7
+ appRoute.customField
8
+ // 示例 1: 使用 App,它会自动使用 AppRouteContext<U> 作为 ctx 类型
9
+ const app = new App<{
10
+ customField: string;
11
+ }>();
12
+ app.context.customField = "customValue"; // 可以在 app.context 中添加自定义字段,这些字段会在 ctx 中可用
13
+ app.route({
14
+ path: 'test1',
15
+ metadata: {
16
+ args: {
17
+ name: z.string(),
18
+ }
19
+ },
20
+ }).define(async (ctx) => {
21
+ // ctx.app 是 App 类型
22
+ const appName = ctx.app.appId;
23
+ // ctx.customField 来自自定义泛型参数
24
+ const customField: string | undefined = ctx.customField;
2
25
 
3
- const app = new App<{ f: string }>();
26
+ // ctx.req ctx.res 来自 HandleCtx
27
+ const req = ctx.req;
28
+ const res = ctx.res;
4
29
 
5
- app.route({
6
- path: 't',
7
- run: async (ctx) => {
8
- // ctx.r
9
- ctx.app;
10
- }
30
+ // ctx.args 从 metadata.args 推断
31
+ const name: string = ctx.args.name;
32
+ const name2: string = ctx.query.name;
33
+
34
+
35
+ ctx.body = `Hello ${name}!`;
36
+ });
37
+ // 示例 2: 使用 QueryRouterServer,它可以传递自定义的 Context 类型
38
+ const router = new QueryRouterServer<{
39
+ routerContextField: number;
40
+ }>();
41
+ router.context.routerContextField
42
+ router.route({
43
+ path: 'router-test',
44
+ metadata: {
45
+ args: {
46
+ value: z.number(),
47
+ }
48
+ },
49
+ }).define(async (ctx) => {
50
+ const value: number = ctx.args.value;
51
+ const field: number | undefined = ctx.routerContextField;
52
+
53
+ ctx.body = value;
54
+ });
55
+ // 示例 3: 不带泛型参数的 QueryRouterServer,使用默认的 RouteContext
56
+ const defaultRouter = new QueryRouterServer();
57
+ defaultRouter.route({
58
+ path: 'default-test',
59
+ metadata: {
60
+ args: {
61
+ id: z.string(),
62
+ }
63
+ },
11
64
  }).define(async (ctx) => {
12
- ctx.f = 'hello';
13
- }).addTo(app);
65
+ const id: string = ctx.args.id;
66
+
67
+ ctx.body = id;
68
+ });
69
+ export { app, router, defaultRouter };
package/src/test/chat.ts CHANGED
@@ -14,4 +14,4 @@ app.prompt('获取天气的工具。\n参数是 city 为对应的城市').define
14
14
 
15
15
  export const chat = new RouterChat({ router: app.router });
16
16
 
17
- console.log(chat.chat());
17
+ console.log(chat.getChatPrompt());