@kevisual/router 0.0.83 → 0.0.85

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,
package/src/route.ts CHANGED
@@ -6,7 +6,16 @@ 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 BuildRouteContext<M, U> = M extends { args?: infer A }
11
+ ? A extends z.ZodObject<any>
12
+ ? RouteContext<{ args?: z.infer<A> }, U>
13
+ : A extends Record<string, z.ZodTypeAny>
14
+ ? RouteContext<{ args?: { [K in keyof A]: z.infer<A[K]> } }, U>
15
+ : RouteContext<U>
16
+ : RouteContext<U>;
17
+
18
+ export type RouteContext<T = { code?: number }, U extends SimpleObject = {}, S = { [key: string]: any }> = {
10
19
  /**
11
20
  * 本地自己调用的时候使用,可以标识为当前自调用,那么 auth 就不许重复的校验
12
21
  * 或者不需要登录的,直接调用
@@ -23,9 +32,15 @@ export type RouteContext<T = { code?: number }, S = any> = {
23
32
  code?: number;
24
33
  /** return msg */
25
34
  message?: string;
26
- // 传递状态
35
+ /**
36
+ * 传递状态
37
+ */
27
38
  state?: S;
28
39
  // transfer data
40
+ /**
41
+ * 当前routerId
42
+ */
43
+ currentId?: string;
29
44
  /**
30
45
  * 当前路径
31
46
  */
@@ -54,19 +69,19 @@ export type RouteContext<T = { code?: number }, S = any> = {
54
69
  ctx?: RouteContext & { [key: string]: any },
55
70
  ) => Promise<any>;
56
71
  /** 请求 route的返回结果,解析了body为data,就类同于 query.post获取的数据*/
57
- run?: (message: { path: string; key?: string; payload?: any }, ctx?: RouteContext & { [key: string]: any }) => Promise<any>;
72
+ run?: (message: { path: string; key?: string; payload?: any }, ctx?: RouteContext) => Promise<any>;
58
73
  index?: number;
59
74
  throw?: throwError['throw'];
60
75
  /** 是否需要序列化, 使用JSON.stringify和JSON.parse */
61
76
  needSerialize?: boolean;
62
- } & T;
77
+ } & T & U;
63
78
  export type SimpleObject = Record<string, any>;
64
79
  export type Run<T extends SimpleObject = {}> = (ctx: Required<RouteContext<T>>) => Promise<typeof ctx | null | void>;
65
80
  export type RunMessage = { path?: string; key?: string; id?: string; payload?: any; };
66
81
  export type NextRoute = Pick<Route, 'id' | 'path' | 'key'>;
67
82
  export type RouteMiddleware =
68
83
  | {
69
- path: string;
84
+ path?: string;
70
85
  key?: string;
71
86
  id?: string;
72
87
  }
@@ -80,7 +95,7 @@ export type RouteOpts<U = {}, T = SimpleObject> = {
80
95
  description?: string;
81
96
  metadata?: T;
82
97
  middleware?: RouteMiddleware[]; // middleware
83
- type?: 'route' | 'middleware';
98
+ type?: 'route' | 'middleware' | 'compound'; // compound表示这个 route 作为一个聚合体,没有实际的 run,而是一个 router 的聚合列表
84
99
  /**
85
100
  * $#$ will be used to split path and key
86
101
  */
@@ -123,7 +138,11 @@ export const createSkill = <T = SimpleObject>(skill: Skill<T>): Skill<T> => {
123
138
 
124
139
  export type RouteInfo = Pick<Route, (typeof pickValue)[number]>;
125
140
 
126
- export class Route<U = { [key: string]: any }, T extends SimpleObject = SimpleObject> implements throwError {
141
+ /**
142
+ * @M 是 route的 metadate的类型,默认是 SimpleObject
143
+ * @U 是 RouteContext 里 state的类型
144
+ */
145
+ export class Route<M extends SimpleObject = SimpleObject, U extends SimpleObject = SimpleObject> implements throwError {
127
146
  /**
128
147
  * 一级路径
129
148
  */
@@ -133,10 +152,10 @@ export class Route<U = { [key: string]: any }, T extends SimpleObject = SimpleOb
133
152
  */
134
153
  key?: string;
135
154
  id?: string;
136
- run?: Run;
155
+ run?: Run<BuildRouteContext<M, U>>;
137
156
  nextRoute?: NextRoute; // route to run after this route
138
157
  description?: string;
139
- metadata?: T;
158
+ metadata?: M;
140
159
  middleware?: RouteMiddleware[]; // middleware
141
160
  type? = 'route';
142
161
  /**
@@ -154,13 +173,13 @@ export class Route<U = { [key: string]: any }, T extends SimpleObject = SimpleOb
154
173
  if (opts) {
155
174
  this.id = opts.id || randomId(12, 'rand-');
156
175
  if (!opts.id && opts.idUsePath) {
157
- const delimiter = opts.delimiter ?? '$#$';
176
+ const delimiter = opts.delimiter ?? '$$';
158
177
  this.id = path + delimiter + key;
159
178
  }
160
- this.run = opts.run;
179
+ this.run = opts.run as Run<BuildRouteContext<M, U>>;
161
180
  this.nextRoute = opts.nextRoute;
162
181
  this.description = opts.description;
163
- this.metadata = opts.metadata as T;
182
+ this.metadata = opts.metadata as M;
164
183
  this.type = opts.type || 'route';
165
184
  this.middleware = opts.middleware || [];
166
185
  this.key = opts.key || key;
@@ -184,9 +203,9 @@ export class Route<U = { [key: string]: any }, T extends SimpleObject = SimpleOb
184
203
  return this;
185
204
  }
186
205
  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;
206
+ define<T extends { [key: string]: any } = RouterContextT>(fn: Run<T & BuildRouteContext<M, U>>): this;
207
+ define<T extends { [key: string]: any } = RouterContextT>(key: string, fn: Run<T & BuildRouteContext<M, U>>): this;
208
+ define<T extends { [key: string]: any } = RouterContextT>(path: string, key: string, fn: Run<T & BuildRouteContext<M, U>>): this;
190
209
  define(...args: any[]) {
191
210
  const [path, key, opts] = args;
192
211
  // 全覆盖,所以opts需要准确,不能由idUsePath 需要check的变量
@@ -209,7 +228,7 @@ export class Route<U = { [key: string]: any }, T extends SimpleObject = SimpleOb
209
228
  return this;
210
229
  }
211
230
  if (typeof path === 'function') {
212
- this.run = path;
231
+ this.run = path as Run<BuildRouteContext<M, U>>;
213
232
  return this;
214
233
  }
215
234
  if (typeof path === 'string' && typeof key === 'function') {
@@ -262,11 +281,11 @@ export const fromJSONSchema = schema.fromJSONSchema;
262
281
  * @parmas overwrite 是否覆盖已存在的route,默认true
263
282
  */
264
283
  export type AddOpts = { overwrite?: boolean };
265
- export class QueryRouter implements throwError {
284
+ export class QueryRouter<T extends SimpleObject = SimpleObject> implements throwError {
266
285
  appId: string = '';
267
286
  routes: Route[];
268
287
  maxNextRoute = 40;
269
- context?: RouteContext = {}; // default context for call
288
+ context?: RouteContext<T> = {} as RouteContext<T>; // default context for call
270
289
  constructor() {
271
290
  this.routes = [];
272
291
  }
@@ -309,11 +328,12 @@ export class QueryRouter implements throwError {
309
328
  * @param ctx
310
329
  * @returns
311
330
  */
312
- async runRoute(path: string, key: string, ctx?: RouteContext) {
331
+ async runRoute(path: string, key: string, ctx?: RouteContext<T>): Promise<RouteContext<T>> {
313
332
  const route = this.routes.find((r) => r.path === path && r.key === key);
314
333
  const maxNextRoute = this.maxNextRoute;
315
- ctx = (ctx || {}) as RouteContext;
334
+ ctx = (ctx || {}) as RouteContext<T>;
316
335
  ctx.currentPath = path;
336
+ ctx.currentId = route?.id;
317
337
  ctx.currentKey = key;
318
338
  ctx.currentRoute = route;
319
339
  ctx.index = (ctx.index || 0) + 1;
@@ -327,7 +347,7 @@ export class QueryRouter implements throwError {
327
347
  ctx.code = 500;
328
348
  ctx.message = 'Too many nextRoute';
329
349
  ctx.body = null;
330
- return;
350
+ return ctx;
331
351
  }
332
352
  // run middleware
333
353
  if (route && route.middleware && route.middleware.length > 0) {
@@ -382,7 +402,7 @@ export class QueryRouter implements throwError {
382
402
  const middleware = routeMiddleware[i];
383
403
  if (middleware) {
384
404
  try {
385
- await middleware.run(ctx as Required<RouteContext>);
405
+ await middleware.run(ctx as Required<RouteContext<T>>);
386
406
  } catch (e) {
387
407
  if (route?.isDebug) {
388
408
  console.error('=====debug====:middlerware error');
@@ -404,6 +424,7 @@ export class QueryRouter implements throwError {
404
424
  return ctx;
405
425
  }
406
426
  if (ctx.end) {
427
+ return ctx;
407
428
  }
408
429
  }
409
430
  }
@@ -412,7 +433,7 @@ export class QueryRouter implements throwError {
412
433
  if (route) {
413
434
  if (route.run) {
414
435
  try {
415
- await route.run(ctx as Required<RouteContext>);
436
+ await route.run(ctx as Required<RouteContext<T>>);
416
437
  } catch (e) {
417
438
  if (route?.isDebug) {
418
439
  console.error('=====debug====:route error');
@@ -467,7 +488,7 @@ export class QueryRouter implements throwError {
467
488
  }
468
489
  }
469
490
  // 如果没有找到route,返回404,这是因为出现了错误
470
- return Promise.resolve({ code: 404, body: 'Not found' });
491
+ return Promise.resolve({ code: 404, body: 'Not found' } as RouteContext<T>);
471
492
  }
472
493
  /**
473
494
  * 第一次执行
@@ -475,12 +496,12 @@ export class QueryRouter implements throwError {
475
496
  * @param ctx
476
497
  * @returns
477
498
  */
478
- async parse(message: { path: string; key?: string; payload?: any }, ctx?: RouteContext & { [key: string]: any }) {
499
+ async parse(message: { path: string; key?: string; payload?: any }, ctx?: RouteContext<T> & { [key: string]: any }) {
479
500
  if (!message?.path) {
480
- return Promise.resolve({ code: 404, body: null, message: 'Not found path' });
501
+ return Promise.resolve({ code: 404, body: null, message: 'Not found path' } as RouteContext<T>);
481
502
  }
482
503
  const { path, key = '', payload = {}, ...query } = message;
483
- ctx = ctx || {};
504
+ ctx = ctx || {} as RouteContext<T>;
484
505
  ctx.query = { ...ctx.query, ...query, ...payload };
485
506
  ctx.args = ctx.query;
486
507
  ctx.state = { ...ctx?.state };
@@ -514,7 +535,7 @@ export class QueryRouter implements throwError {
514
535
  * @param ctx
515
536
  * @returns
516
537
  */
517
- async call(message: { id?: string; path?: string; key?: string; payload?: any }, ctx?: RouteContext & { [key: string]: any }) {
538
+ async call(message: { id?: string; path?: string; key?: string; payload?: any }, ctx?: RouteContext<T> & { [key: string]: any }) {
518
539
  let path = message.path;
519
540
  let key = message.key;
520
541
  // 优先 path + key
@@ -555,7 +576,7 @@ export class QueryRouter implements throwError {
555
576
  * @param ctx
556
577
  * @returns
557
578
  */
558
- async run(message: { id?: string; path?: string; key?: string; payload?: any }, ctx?: RouteContext & { [key: string]: any }) {
579
+ async run(message: { id?: string; path?: string; key?: string; payload?: any }, ctx?: RouteContext<T> & { [key: string]: any }) {
559
580
  const res = await this.call(message, { ...this.context, ...ctx });
560
581
  return {
561
582
  code: res.code,
@@ -569,7 +590,7 @@ export class QueryRouter implements throwError {
569
590
  * @param ctx
570
591
  */
571
592
  setContext(ctx: RouteContext) {
572
- this.context = ctx;
593
+ this.context = ctx as RouteContext<T>;
573
594
  }
574
595
  getList(filter?: (route: Route) => boolean): RouteInfo[] {
575
596
  return this.routes.filter(filter || (() => true)).map((r) => {
@@ -580,11 +601,11 @@ export class QueryRouter implements throwError {
580
601
  /**
581
602
  * 获取handle函数, 这里会去执行parse函数
582
603
  */
583
- getHandle<T = any>(router: QueryRouter, wrapperFn?: HandleFn<T>, ctx?: RouteContext) {
584
- return async (msg: { id?: string; path?: string; key?: string;[key: string]: any }, handleContext?: RouteContext) => {
604
+ getHandle<T = any>(router: QueryRouter, wrapperFn?: HandleFn, ctx?: RouteContext) {
605
+ return async (msg: { id?: string; path?: string; key?: string;[key: string]: any }, handleContext?: RouteContext<T>) => {
585
606
  try {
586
607
  const context = { ...ctx, ...handleContext };
587
- const res = await router.call(msg, context);
608
+ const res = await router.call(msg, context) as any;
588
609
  if (wrapperFn) {
589
610
  res.data = res.body;
590
611
  return wrapperFn(res, context);
@@ -637,7 +658,7 @@ export class QueryRouter implements throwError {
637
658
  description: '列出当前应用下的所有的路由信息',
638
659
  middleware: opts?.middleware || [],
639
660
  run: async (ctx: RouteContext) => {
640
- const tokenUser = ctx.state.tokenUser;
661
+ const tokenUser = ctx.state as unknown as { tokenUser?: any };
641
662
  let isUser = !!tokenUser;
642
663
  const list = this.getList(opts?.filter).filter((item) => {
643
664
  if (item.id === 'auth' || item.id === 'auth-can' || item.id === 'check-auth-admin' || item.id === 'auth-admin') {
@@ -683,10 +704,11 @@ export class QueryRouter implements throwError {
683
704
  fromJSONSchema = fromJSONSchema;
684
705
  }
685
706
 
686
- type QueryRouterServerOpts = {
707
+ type QueryRouterServerOpts<C extends SimpleObject = SimpleObject> = {
687
708
  handleFn?: HandleFn;
688
- context?: RouteContext;
709
+ context?: RouteContext<C>;
689
710
  appId?: string;
711
+ initHandle?: boolean;
690
712
  };
691
713
  interface HandleFn<T = any> {
692
714
  (msg: { path: string;[key: string]: any }, ctx?: any): { code: string; data?: any; message?: string;[key: string]: any };
@@ -695,13 +717,18 @@ interface HandleFn<T = any> {
695
717
  /**
696
718
  * QueryRouterServer
697
719
  * @description 移除server相关的功能,只保留router相关的功能,和http.createServer不相关,独立
720
+ * @template C 自定义 RouteContext 类型
698
721
  */
699
- export class QueryRouterServer extends QueryRouter {
722
+ export class QueryRouterServer<C extends SimpleObject = SimpleObject> extends QueryRouter<C> {
700
723
  declare appId: string;
701
724
  handle: any;
702
- constructor(opts?: QueryRouterServerOpts) {
725
+ declare context: RouteContext<C>;
726
+ constructor(opts?: QueryRouterServerOpts<C>) {
703
727
  super();
704
- this.handle = this.getHandle(this, opts?.handleFn, opts?.context);
728
+ const initHandle = opts?.initHandle ?? true;
729
+ if (initHandle || opts?.handleFn) {
730
+ this.handle = this.getHandle(this, opts?.handleFn, opts?.context);
731
+ }
705
732
  this.setContext({ needSerialize: false, ...opts?.context });
706
733
  if (opts?.appId) {
707
734
  this.appId = opts.appId;
@@ -716,37 +743,28 @@ export class QueryRouterServer extends QueryRouter {
716
743
  this.add(route, opts);
717
744
  }
718
745
  Route = Route;
719
- route(opts: RouteOpts): Route<Required<RouteContext>>;
720
- route(path: string, key?: string): Route<Required<RouteContext>>;
721
- route(path: string, opts?: RouteOpts): Route<Required<RouteContext>>;
722
- route(path: string, key?: string, opts?: RouteOpts): Route<Required<RouteContext>>;
723
- route(...args: any[]) {
746
+ route<M extends SimpleObject = SimpleObject>(opts: RouteOpts & { metadata?: M }): Route<M, Required<RouteContext<C>>>;
747
+ route<M extends SimpleObject = SimpleObject>(path: string, opts?: RouteOpts & { metadata?: M }): Route<M, Required<RouteContext<C>>>;
748
+ route<M extends SimpleObject = SimpleObject>(path: string, key?: string): Route<M, Required<RouteContext<C>>>;
749
+ route<M extends SimpleObject = SimpleObject>(path: string, key?: string, opts?: RouteOpts & { metadata?: M }): Route<M, Required<RouteContext<C>>>;
750
+ route<M extends SimpleObject = SimpleObject>(...args: any[]) {
724
751
  const [path, key, opts] = args;
725
752
  if (typeof path === 'object') {
726
- return new Route(path.path, path.key, path);
753
+ return new Route<M, Required<RouteContext<C>>>(path.path, path.key, path);
727
754
  }
728
755
  if (typeof path === 'string') {
729
756
  if (opts) {
730
- return new Route(path, key, opts);
757
+ return new Route<M, Required<RouteContext<C>>>(path, key, opts);
731
758
  }
732
759
  if (key && typeof key === 'object') {
733
- return new Route(path, key?.key || '', key);
760
+ return new Route<M, Required<RouteContext<C>>>(path, key?.key || '', key);
734
761
  }
735
- return new Route(path, key);
762
+ return new Route<M, Required<RouteContext<C>>>(path, key);
736
763
  }
737
- return new Route(path, key, opts);
764
+ return new Route<M, Required<RouteContext<C>>>(path, key, opts);
738
765
  }
739
- prompt(description: string): Route<Required<RouteContext>>;
740
- prompt(description: Function): Route<Required<RouteContext>>;
741
- prompt(...args: any[]) {
742
- const [desc] = args;
743
- let description = ''
744
- if (typeof desc === 'string') {
745
- description = desc;
746
- } else if (typeof desc === 'function') {
747
- description = desc() || ''; // 如果是Promise,需要addTo App之前就要获取应有的函数了。
748
- }
749
- return new Route('', '', { description });
766
+ prompt(description: string) {
767
+ return new Route(undefined, undefined, { description });
750
768
  }
751
769
 
752
770
  /**
@@ -754,12 +772,12 @@ export class QueryRouterServer extends QueryRouter {
754
772
  * @param param0
755
773
  * @returns
756
774
  */
757
- async run(msg: { id?: string; path?: string; key?: string; payload?: any }, ctx?: RouteContext & { [key: string]: any }) {
775
+ async run(msg: { id?: string; path?: string; key?: string; payload?: any }, ctx?: Partial<RouteContext<C>>) {
758
776
  const handle = this.handle;
759
777
  if (handle) {
760
778
  return handle(msg, ctx);
761
779
  }
762
- return super.run(msg, ctx);
780
+ return super.run(msg, ctx as RouteContext<C>);
763
781
  }
764
782
  }
765
783
 
@@ -284,7 +284,7 @@ export class ServerBase implements ServerType {
284
284
  * @param ws
285
285
  */
286
286
  async onWsClose(ws: WS) {
287
- const id = ws?.data?.id || '';
287
+ const id = ws?.wsId || '';
288
288
  if (id) {
289
289
  this.emitter.emit('close--' + id, { type: 'close', ws, id });
290
290
  setTimeout(() => {
@@ -298,4 +298,7 @@ export class ServerBase implements ServerType {
298
298
  if (this.showConnected)
299
299
  ws.send(JSON.stringify({ type: 'connected' }));
300
300
  }
301
+ createId() {
302
+ return Math.random().toString(36).substring(2, 15);
303
+ }
301
304
  }
@@ -4,7 +4,7 @@
4
4
  * @tags bun, server, websocket, http
5
5
  * @createdAt 2025-12-20
6
6
  */
7
- import { ServerType, type ServerOpts, type Cors, RouterRes, RouterReq } from './server-type.ts';
7
+ import { ServerType, type ServerOpts, type Cors, RouterRes, RouterReq, WS } from './server-type.ts';
8
8
  import { ServerBase } from './server-base.ts';
9
9
 
10
10
  export class BunServer extends ServerBase implements ServerType {
@@ -264,10 +264,14 @@ export class BunServer extends ServerBase implements ServerType {
264
264
  open: (ws: any) => {
265
265
  this.sendConnected(ws);
266
266
  },
267
- message: async (ws: any, message: string | Buffer) => {
267
+ message: async (bunWs: any, message: string | Buffer) => {
268
+ const ws = bunWs as WS;
268
269
  const pathname = ws.data.pathname || '';
269
270
  const token = ws.data.token || '';
270
271
  const id = ws.data.id || '';
272
+ if (!ws.wsId) {
273
+ ws.wsId = this.createId();
274
+ }
271
275
  await this.onWebSocket({ ws, message, pathname, token, id });
272
276
  },
273
277
  close: (ws: any) => {
@@ -49,16 +49,33 @@ export type OnWebSocketOptions<T = {}> = {
49
49
  message: string | Buffer;
50
50
  pathname: string,
51
51
  token?: string,
52
+ /** data 的id提取出来 */
52
53
  id?: string,
53
54
  }
54
55
  export type OnWebSocketFn = (options: OnWebSocketOptions) => Promise<void> | void;
55
56
  export type WS<T = {}> = {
56
57
  send: (data: any) => void;
57
58
  close: (code?: number, reason?: string) => void;
59
+ /**
60
+ * ws 自己生成的一个id,主要是为了区分不同的ws连接,方便在onWebSocket中使用
61
+ */
62
+ wsId?: string;
58
63
  data?: {
64
+ /**
65
+ * ws连接时的url,包含pathname和searchParams
66
+ */
59
67
  url: URL;
68
+ /**
69
+ * ws连接时的pathname
70
+ */
60
71
  pathname: string;
72
+ /**
73
+ * ws连接时的url中的token参数
74
+ */
61
75
  token?: string;
76
+ /**
77
+ * ws连接时的url中的id参数.
78
+ */
62
79
  id?: string;
63
80
  /**
64
81
  * 鉴权后的获取的信息
@@ -56,6 +56,11 @@ export class WsServerBase {
56
56
  token,
57
57
  id,
58
58
  }
59
+ // @ts-ignore
60
+ if (!ws.wsId) {
61
+ // @ts-ignore
62
+ ws.wsId = this.createId();
63
+ }
59
64
  ws.on('message', async (message: string | Buffer) => {
60
65
  await this.server.onWebSocket({ ws, message, pathname, token, id });
61
66
  });
@@ -66,7 +71,9 @@ export class WsServerBase {
66
71
  this.server.onWsClose(ws);
67
72
  });
68
73
  });
69
-
74
+ }
75
+ createId() {
76
+ return Math.random().toString(36).substring(2, 15);
70
77
  }
71
78
  }
72
79
  // TODO: ws handle and path and routerContext