@kevisual/router 0.0.83 → 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/agent/routes/index.ts +16 -10
- package/agent/routes/route-create.ts +3 -3
- package/dist/app.js +84 -58
- package/dist/opencode.d.ts +81 -81
- package/dist/opencode.js +10 -5
- package/dist/router-browser.d.ts +76 -50
- package/dist/router-browser.js +12 -13
- package/dist/router-define.d.ts +76 -50
- package/dist/router.d.ts +81 -81
- package/dist/router.js +14 -51
- package/dist/ws.d.ts +104 -68
- package/package.json +2 -2
- package/readme.md +78 -63
- package/src/app.ts +7 -50
- package/src/opencode.ts +12 -5
- package/src/route.ts +88 -60
- package/src/test/app-type.ts +66 -10
- package/src/test/chat.ts +1 -1
- package/src/test/route-ts.ts +15 -0
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 (
|
|
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,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
|
-
|
|
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
|
|
80
|
+
run?: (message: { path: string; key?: string; payload?: any }, ctx?: RouteContext) => Promise<any>;
|
|
58
81
|
index?: number;
|
|
59
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
|
|
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
|
-
|
|
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?:
|
|
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
|
|
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
|
|
188
|
-
define<T extends { [key: string]: any } = RouterContextT>(key: string, fn: Run<T & U
|
|
189
|
-
define<T extends { [key: string]: any } = RouterContextT>(path: string, key: string, fn: Run<T & U
|
|
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') {
|
|
@@ -262,11 +291,11 @@ export const fromJSONSchema = schema.fromJSONSchema;
|
|
|
262
291
|
* @parmas overwrite 是否覆盖已存在的route,默认true
|
|
263
292
|
*/
|
|
264
293
|
export type AddOpts = { overwrite?: boolean };
|
|
265
|
-
export class QueryRouter implements throwError {
|
|
294
|
+
export class QueryRouter<T extends SimpleObject = SimpleObject> implements throwError {
|
|
266
295
|
appId: string = '';
|
|
267
296
|
routes: Route[];
|
|
268
297
|
maxNextRoute = 40;
|
|
269
|
-
context?: RouteContext = {}
|
|
298
|
+
context?: RouteContext<T> = {} as RouteContext<T>; // default context for call
|
|
270
299
|
constructor() {
|
|
271
300
|
this.routes = [];
|
|
272
301
|
}
|
|
@@ -309,11 +338,12 @@ export class QueryRouter implements throwError {
|
|
|
309
338
|
* @param ctx
|
|
310
339
|
* @returns
|
|
311
340
|
*/
|
|
312
|
-
async runRoute(path: string, key: string, ctx?: RouteContext) {
|
|
341
|
+
async runRoute(path: string, key: string, ctx?: RouteContext<T>): Promise<RouteContext<T>> {
|
|
313
342
|
const route = this.routes.find((r) => r.path === path && r.key === key);
|
|
314
343
|
const maxNextRoute = this.maxNextRoute;
|
|
315
|
-
ctx = (ctx || {}) as RouteContext
|
|
344
|
+
ctx = (ctx || {}) as RouteContext<T>;
|
|
316
345
|
ctx.currentPath = path;
|
|
346
|
+
ctx.currentId = route?.id;
|
|
317
347
|
ctx.currentKey = key;
|
|
318
348
|
ctx.currentRoute = route;
|
|
319
349
|
ctx.index = (ctx.index || 0) + 1;
|
|
@@ -327,7 +357,7 @@ export class QueryRouter implements throwError {
|
|
|
327
357
|
ctx.code = 500;
|
|
328
358
|
ctx.message = 'Too many nextRoute';
|
|
329
359
|
ctx.body = null;
|
|
330
|
-
return;
|
|
360
|
+
return ctx;
|
|
331
361
|
}
|
|
332
362
|
// run middleware
|
|
333
363
|
if (route && route.middleware && route.middleware.length > 0) {
|
|
@@ -382,7 +412,7 @@ export class QueryRouter implements throwError {
|
|
|
382
412
|
const middleware = routeMiddleware[i];
|
|
383
413
|
if (middleware) {
|
|
384
414
|
try {
|
|
385
|
-
await middleware.run(ctx as Required<RouteContext
|
|
415
|
+
await middleware.run(ctx as Required<RouteContext<T>>);
|
|
386
416
|
} catch (e) {
|
|
387
417
|
if (route?.isDebug) {
|
|
388
418
|
console.error('=====debug====:middlerware error');
|
|
@@ -404,6 +434,7 @@ export class QueryRouter implements throwError {
|
|
|
404
434
|
return ctx;
|
|
405
435
|
}
|
|
406
436
|
if (ctx.end) {
|
|
437
|
+
return ctx;
|
|
407
438
|
}
|
|
408
439
|
}
|
|
409
440
|
}
|
|
@@ -412,7 +443,7 @@ export class QueryRouter implements throwError {
|
|
|
412
443
|
if (route) {
|
|
413
444
|
if (route.run) {
|
|
414
445
|
try {
|
|
415
|
-
await route.run(ctx as Required<RouteContext
|
|
446
|
+
await route.run(ctx as Required<RouteContext<T>>);
|
|
416
447
|
} catch (e) {
|
|
417
448
|
if (route?.isDebug) {
|
|
418
449
|
console.error('=====debug====:route error');
|
|
@@ -467,7 +498,7 @@ export class QueryRouter implements throwError {
|
|
|
467
498
|
}
|
|
468
499
|
}
|
|
469
500
|
// 如果没有找到route,返回404,这是因为出现了错误
|
|
470
|
-
return Promise.resolve({ code: 404, body: 'Not found' });
|
|
501
|
+
return Promise.resolve({ code: 404, body: 'Not found' } as RouteContext<T>);
|
|
471
502
|
}
|
|
472
503
|
/**
|
|
473
504
|
* 第一次执行
|
|
@@ -475,12 +506,12 @@ export class QueryRouter implements throwError {
|
|
|
475
506
|
* @param ctx
|
|
476
507
|
* @returns
|
|
477
508
|
*/
|
|
478
|
-
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 }) {
|
|
479
510
|
if (!message?.path) {
|
|
480
|
-
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>);
|
|
481
512
|
}
|
|
482
513
|
const { path, key = '', payload = {}, ...query } = message;
|
|
483
|
-
ctx = ctx || {}
|
|
514
|
+
ctx = ctx || {} as RouteContext<T>;
|
|
484
515
|
ctx.query = { ...ctx.query, ...query, ...payload };
|
|
485
516
|
ctx.args = ctx.query;
|
|
486
517
|
ctx.state = { ...ctx?.state };
|
|
@@ -514,7 +545,7 @@ export class QueryRouter implements throwError {
|
|
|
514
545
|
* @param ctx
|
|
515
546
|
* @returns
|
|
516
547
|
*/
|
|
517
|
-
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 }) {
|
|
518
549
|
let path = message.path;
|
|
519
550
|
let key = message.key;
|
|
520
551
|
// 优先 path + key
|
|
@@ -555,7 +586,7 @@ export class QueryRouter implements throwError {
|
|
|
555
586
|
* @param ctx
|
|
556
587
|
* @returns
|
|
557
588
|
*/
|
|
558
|
-
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 }) {
|
|
559
590
|
const res = await this.call(message, { ...this.context, ...ctx });
|
|
560
591
|
return {
|
|
561
592
|
code: res.code,
|
|
@@ -569,7 +600,7 @@ export class QueryRouter implements throwError {
|
|
|
569
600
|
* @param ctx
|
|
570
601
|
*/
|
|
571
602
|
setContext(ctx: RouteContext) {
|
|
572
|
-
this.context = ctx
|
|
603
|
+
this.context = ctx as RouteContext<T>;
|
|
573
604
|
}
|
|
574
605
|
getList(filter?: (route: Route) => boolean): RouteInfo[] {
|
|
575
606
|
return this.routes.filter(filter || (() => true)).map((r) => {
|
|
@@ -580,11 +611,11 @@ export class QueryRouter implements throwError {
|
|
|
580
611
|
/**
|
|
581
612
|
* 获取handle函数, 这里会去执行parse函数
|
|
582
613
|
*/
|
|
583
|
-
getHandle<T = any>(router: QueryRouter, wrapperFn?: HandleFn
|
|
584
|
-
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>) => {
|
|
585
616
|
try {
|
|
586
617
|
const context = { ...ctx, ...handleContext };
|
|
587
|
-
const res = await router.call(msg, context);
|
|
618
|
+
const res = await router.call(msg, context) as any;
|
|
588
619
|
if (wrapperFn) {
|
|
589
620
|
res.data = res.body;
|
|
590
621
|
return wrapperFn(res, context);
|
|
@@ -637,7 +668,7 @@ export class QueryRouter implements throwError {
|
|
|
637
668
|
description: '列出当前应用下的所有的路由信息',
|
|
638
669
|
middleware: opts?.middleware || [],
|
|
639
670
|
run: async (ctx: RouteContext) => {
|
|
640
|
-
const tokenUser = ctx.state
|
|
671
|
+
const tokenUser = ctx.state as unknown as { tokenUser?: any };
|
|
641
672
|
let isUser = !!tokenUser;
|
|
642
673
|
const list = this.getList(opts?.filter).filter((item) => {
|
|
643
674
|
if (item.id === 'auth' || item.id === 'auth-can' || item.id === 'check-auth-admin' || item.id === 'auth-admin') {
|
|
@@ -683,10 +714,11 @@ export class QueryRouter implements throwError {
|
|
|
683
714
|
fromJSONSchema = fromJSONSchema;
|
|
684
715
|
}
|
|
685
716
|
|
|
686
|
-
type QueryRouterServerOpts = {
|
|
717
|
+
type QueryRouterServerOpts<C extends SimpleObject = SimpleObject> = {
|
|
687
718
|
handleFn?: HandleFn;
|
|
688
|
-
context?: RouteContext
|
|
719
|
+
context?: RouteContext<C>;
|
|
689
720
|
appId?: string;
|
|
721
|
+
initHandle?: boolean;
|
|
690
722
|
};
|
|
691
723
|
interface HandleFn<T = any> {
|
|
692
724
|
(msg: { path: string;[key: string]: any }, ctx?: any): { code: string; data?: any; message?: string;[key: string]: any };
|
|
@@ -695,13 +727,18 @@ interface HandleFn<T = any> {
|
|
|
695
727
|
/**
|
|
696
728
|
* QueryRouterServer
|
|
697
729
|
* @description 移除server相关的功能,只保留router相关的功能,和http.createServer不相关,独立
|
|
730
|
+
* @template C 自定义 RouteContext 类型
|
|
698
731
|
*/
|
|
699
|
-
export class QueryRouterServer extends QueryRouter {
|
|
732
|
+
export class QueryRouterServer<C extends SimpleObject = SimpleObject> extends QueryRouter<C> {
|
|
700
733
|
declare appId: string;
|
|
701
734
|
handle: any;
|
|
702
|
-
|
|
735
|
+
declare context: RouteContext<C>;
|
|
736
|
+
constructor(opts?: QueryRouterServerOpts<C>) {
|
|
703
737
|
super();
|
|
704
|
-
|
|
738
|
+
const initHandle = opts?.initHandle ?? true;
|
|
739
|
+
if (initHandle || opts?.handleFn) {
|
|
740
|
+
this.handle = this.getHandle(this, opts?.handleFn, opts?.context);
|
|
741
|
+
}
|
|
705
742
|
this.setContext({ needSerialize: false, ...opts?.context });
|
|
706
743
|
if (opts?.appId) {
|
|
707
744
|
this.appId = opts.appId;
|
|
@@ -716,37 +753,28 @@ export class QueryRouterServer extends QueryRouter {
|
|
|
716
753
|
this.add(route, opts);
|
|
717
754
|
}
|
|
718
755
|
Route = Route;
|
|
719
|
-
route(opts: RouteOpts): Route<Required<RouteContext
|
|
720
|
-
route(path: string,
|
|
721
|
-
route(path: string,
|
|
722
|
-
route(path: string, key?: string, opts?: RouteOpts): Route<Required<RouteContext
|
|
723
|
-
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[]) {
|
|
724
761
|
const [path, key, opts] = args;
|
|
725
762
|
if (typeof path === 'object') {
|
|
726
|
-
return new Route(path.path, path.key, path);
|
|
763
|
+
return new Route<M, Required<RouteContext<C>>>(path.path, path.key, path);
|
|
727
764
|
}
|
|
728
765
|
if (typeof path === 'string') {
|
|
729
766
|
if (opts) {
|
|
730
|
-
return new Route(path, key, opts);
|
|
767
|
+
return new Route<M, Required<RouteContext<C>>>(path, key, opts);
|
|
731
768
|
}
|
|
732
769
|
if (key && typeof key === 'object') {
|
|
733
|
-
return new Route(path, key?.key || '', key);
|
|
770
|
+
return new Route<M, Required<RouteContext<C>>>(path, key?.key || '', key);
|
|
734
771
|
}
|
|
735
|
-
return new Route(path, key);
|
|
772
|
+
return new Route<M, Required<RouteContext<C>>>(path, key);
|
|
736
773
|
}
|
|
737
|
-
return new Route(path, key, opts);
|
|
774
|
+
return new Route<M, Required<RouteContext<C>>>(path, key, opts);
|
|
738
775
|
}
|
|
739
|
-
prompt(description: string)
|
|
740
|
-
|
|
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 });
|
|
776
|
+
prompt(description: string) {
|
|
777
|
+
return new Route(undefined, undefined, { description });
|
|
750
778
|
}
|
|
751
779
|
|
|
752
780
|
/**
|
|
@@ -754,12 +782,12 @@ export class QueryRouterServer extends QueryRouter {
|
|
|
754
782
|
* @param param0
|
|
755
783
|
* @returns
|
|
756
784
|
*/
|
|
757
|
-
async run(msg: { id?: string; path?: string; key?: string; payload?: any }, ctx?: RouteContext
|
|
785
|
+
async run(msg: { id?: string; path?: string; key?: string; payload?: any }, ctx?: Partial<RouteContext<C>>) {
|
|
758
786
|
const handle = this.handle;
|
|
759
787
|
if (handle) {
|
|
760
788
|
return handle(msg, ctx);
|
|
761
789
|
}
|
|
762
|
-
return super.run(msg, ctx);
|
|
790
|
+
return super.run(msg, ctx as RouteContext<C>);
|
|
763
791
|
}
|
|
764
792
|
}
|
|
765
793
|
|
package/src/test/app-type.ts
CHANGED
|
@@ -1,13 +1,69 @@
|
|
|
1
|
-
import { App } from
|
|
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
|
-
|
|
26
|
+
// ctx.req 和 ctx.res 来自 HandleCtx
|
|
27
|
+
const req = ctx.req;
|
|
28
|
+
const res = ctx.res;
|
|
4
29
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
13
|
-
|
|
65
|
+
const id: string = ctx.args.id;
|
|
66
|
+
|
|
67
|
+
ctx.body = id;
|
|
68
|
+
});
|
|
69
|
+
export { app, router, defaultRouter };
|
package/src/test/chat.ts
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { QueryRouterServer } from "@/route.ts";
|
|
2
|
+
import z from "zod";
|
|
3
|
+
|
|
4
|
+
const router = new QueryRouterServer()
|
|
5
|
+
|
|
6
|
+
router.route({
|
|
7
|
+
metadata: {
|
|
8
|
+
args: {
|
|
9
|
+
a: z.string(),
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
}).define(async (ctx) => {
|
|
13
|
+
const argA: string = ctx.args.a;
|
|
14
|
+
ctx.body = '1';
|
|
15
|
+
})
|