@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/agent/routes/index.ts +16 -10
- package/agent/routes/route-create.ts +3 -3
- package/dist/app.js +103 -64
- package/dist/opencode.d.ts +99 -82
- package/dist/opencode.js +10 -5
- package/dist/router-browser.d.ts +77 -51
- package/dist/router-browser.js +16 -17
- package/dist/router-define.d.ts +77 -51
- package/dist/router.d.ts +101 -82
- package/dist/router.js +33 -57
- package/dist/ws.d.ts +122 -69
- package/package.json +7 -7
- package/readme.md +78 -63
- package/src/app.ts +7 -50
- package/src/opencode.ts +12 -5
- package/src/route.ts +80 -62
- package/src/server/server-base.ts +4 -1
- package/src/server/server-bun.ts +6 -2
- package/src/server/server-type.ts +17 -0
- package/src/server/ws-server.ts +8 -1
- 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/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?:
|
|
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
|
|
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
|
|
28
|
+
export class App<U = {}> extends QueryRouterServer<AppRouteContext<U>> {
|
|
29
29
|
declare appId: string;
|
|
30
|
-
router:
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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?:
|
|
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
|
|
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
|
|
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
|
|
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 = {}
|
|
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
|
|
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
|
|
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
|
-
|
|
725
|
+
declare context: RouteContext<C>;
|
|
726
|
+
constructor(opts?: QueryRouterServerOpts<C>) {
|
|
703
727
|
super();
|
|
704
|
-
|
|
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,
|
|
721
|
-
route(path: string,
|
|
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)
|
|
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 });
|
|
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
|
|
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?.
|
|
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
|
}
|
package/src/server/server-bun.ts
CHANGED
|
@@ -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 (
|
|
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
|
* 鉴权后的获取的信息
|
package/src/server/ws-server.ts
CHANGED
|
@@ -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
|