@kevisual/router 0.0.34 → 0.0.36

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package",
3
3
  "name": "@kevisual/router",
4
- "version": "0.0.34",
4
+ "version": "0.0.36",
5
5
  "description": "",
6
6
  "type": "module",
7
7
  "main": "./dist/router.js",
package/src/app.ts CHANGED
@@ -16,16 +16,18 @@ type AppOptions<T = {}> = {
16
16
  io?: boolean;
17
17
  ioOpts?: { routerHandle?: RouterHandle; routerContext?: RouteContext<T>; path?: string };
18
18
  };
19
- export type AppReqRes = HandleCtx;
19
+
20
+ export type AppRouteContext<T = {}> = HandleCtx & RouteContext<T> & { app: App<T> };
20
21
 
21
22
  /**
22
23
  * 封装了 Router 和 Server 的 App 模块,处理http的请求和响应,内置了 Cookie 和 Token 和 res 的处理
24
+ * U - Route Context的扩展类型
23
25
  */
24
- export class App<T = {}, U = AppReqRes> {
26
+ export class App<U = {}> {
25
27
  router: QueryRouter;
26
28
  server: Server;
27
29
  io: WsServer;
28
- constructor(opts?: AppOptions<T>) {
30
+ constructor(opts?: AppOptions<U>) {
29
31
  const router = opts?.router || new QueryRouter();
30
32
  const server = opts?.server || new Server(opts?.serverOptions || {});
31
33
  server.setHandle(router.getHandle(router, opts?.routerHandle, opts?.routerContext));
@@ -62,10 +64,10 @@ export class App<T = {}, U = AppReqRes> {
62
64
  add = this.addRoute;
63
65
 
64
66
  Route = Route;
65
- route(opts: RouteOpts): Route<U>;
66
- route(path: string, key?: string): Route<U>;
67
- route(path: string, opts?: RouteOpts): Route<U>;
68
- route(path: string, key?: string, opts?: RouteOpts): Route<U>;
67
+ route(opts: RouteOpts<AppRouteContext<U>>): Route<AppRouteContext<U>>;
68
+ route(path: string, key?: string): Route<AppRouteContext<U>>;
69
+ route(path: string, opts?: RouteOpts<AppRouteContext<U>>): Route<AppRouteContext<U>>;
70
+ route(path: string, key?: string, opts?: RouteOpts<AppRouteContext<U>>): Route<AppRouteContext<U>>;
69
71
  route(...args: any[]) {
70
72
  const [path, key, opts] = args;
71
73
  if (typeof path === 'object') {
@@ -82,8 +84,8 @@ export class App<T = {}, U = AppReqRes> {
82
84
  }
83
85
  return new Route(path, key, opts);
84
86
  }
85
- prompt(description: string): Route<Required<RouteContext>>;
86
- prompt(description: Function): Route<Required<RouteContext>>;
87
+ prompt(description: string): Route<AppRouteContext<U>>
88
+ prompt(description: Function): Route<AppRouteContext<U>>
87
89
  prompt(...args: any[]) {
88
90
  const [desc] = args;
89
91
  let description = ''
@@ -94,14 +96,20 @@ export class App<T = {}, U = AppReqRes> {
94
96
  }
95
97
  return new Route('', '', { description });
96
98
  }
97
-
98
- async call(message: { id?: string, path?: string; key?: string; payload?: any }, ctx?: RouteContext & { [key: string]: any }) {
99
+
100
+ async call(message: { id?: string, path?: string; key?: string; payload?: any }, ctx?: AppRouteContext<U> & { [key: string]: any }) {
99
101
  const router = this.router;
100
102
  return await router.call(message, ctx);
101
103
  }
102
- async queryRoute(path: string, key?: string, payload?: any, ctx?: RouteContext & { [key: string]: any }) {
104
+ /**
105
+ * @deprecated
106
+ */
107
+ async queryRoute(path: string, key?: string, payload?: any, ctx?: AppRouteContext<U> & { [key: string]: any }) {
103
108
  return await this.router.queryRoute({ path, key, payload }, ctx);
104
109
  }
110
+ async run(path: string, key?: string, payload?: any, ctx?: AppRouteContext<U> & { [key: string]: any }) {
111
+ return await this.router.run({ path, key, payload }, ctx);
112
+ }
105
113
  exportRoutes() {
106
114
  return this.router.exportRoutes();
107
115
  }
package/src/route.ts CHANGED
@@ -1,8 +1,6 @@
1
- import { nanoid, random } from 'nanoid';
1
+ import { nanoid } from 'nanoid';
2
2
  import { CustomError } from './result/error.ts';
3
- import { Schema, Rule, createSchema } from './validator/index.ts';
4
3
  import { pick } from './utils/pick.ts';
5
- import { get } from 'lodash-es';
6
4
  import { listenProcess } from './utils/listen-process.ts';
7
5
 
8
6
  export type RouterContextT = { code?: number;[key: string]: any };
@@ -12,6 +10,7 @@ export type RouteContext<T = { code?: number }, S = any> = {
12
10
  // response body
13
11
  /** return body */
14
12
  body?: number | string | Object;
13
+ forward?: (response: { code: number, data?: any, message?: any }) => void;
15
14
  /** return code */
16
15
  code?: number;
17
16
  /** return msg */
@@ -39,20 +38,15 @@ export type RouteContext<T = { code?: number }, S = any> = {
39
38
  nextQuery?: { [key: string]: any };
40
39
  // end
41
40
  end?: boolean;
42
- // 处理router manager
43
- // TODO:
44
- /**
45
- * 请求 route的返回结果,包函ctx
46
- */
47
- queryRouter?: QueryRouter;
41
+ app?: QueryRouter;
48
42
  error?: any;
49
- /** 请求 route的返回结果,包函ctx */
43
+ /** 请求 route的返回结果,不解析body为data */
50
44
  call?: (
51
45
  message: { path: string; key?: string; payload?: any;[key: string]: any } | { id: string; apyload?: any;[key: string]: any },
52
46
  ctx?: RouteContext & { [key: string]: any },
53
47
  ) => Promise<any>;
54
- /** 请求 route的返回结果,不包函ctx */
55
- queryRoute?: (message: { path: string; key?: string; payload?: any }, ctx?: RouteContext & { [key: string]: any }) => Promise<any>;
48
+ /** 请求 route的返回结果,解析了body为data,就类同于 query.post获取的数据*/
49
+ run?: (message: { path: string; key?: string; payload?: any }, ctx?: RouteContext & { [key: string]: any }) => Promise<any>;
56
50
  index?: number;
57
51
  throw?: (code?: number | string, message?: string, tips?: string) => void;
58
52
  /** 是否需要序列化, 使用JSON.stringify和JSON.parse */
@@ -69,29 +63,16 @@ export type RouteMiddleware =
69
63
  id?: string;
70
64
  }
71
65
  | string;
72
- export type RouteOpts = {
66
+ export type RouteOpts<T = {}> = {
73
67
  path?: string;
74
68
  key?: string;
75
69
  id?: string;
76
- run?: Run;
70
+ run?: Run<T>;
77
71
  nextRoute?: NextRoute; // route to run after this route
78
72
  description?: string;
79
73
  metadata?: { [key: string]: any };
80
74
  middleware?: RouteMiddleware[]; // middleware
81
75
  type?: 'route' | 'middleware';
82
- /**
83
- * validator: {
84
- * packageName: {
85
- * type: 'string',
86
- * required: true,
87
- * },
88
- * }
89
- */
90
- validator?: { [key: string]: Rule };
91
- schema?: { [key: string]: any };
92
- isVerify?: boolean;
93
- verify?: (ctx?: RouteContext, dev?: boolean) => boolean;
94
- verifyKey?: (key: string, ctx?: RouteContext, dev?: boolean) => boolean;
95
76
  /**
96
77
  * $#$ will be used to split path and key
97
78
  */
@@ -102,8 +83,8 @@ export type RouteOpts = {
102
83
  delimiter?: string;
103
84
  isDebug?: boolean;
104
85
  };
105
- export type DefineRouteOpts = Omit<RouteOpts, 'idUsePath' | 'verify' | 'verifyKey' | 'nextRoute'>;
106
- const pickValue = ['path', 'key', 'id', 'description', 'type', 'validator', 'middleware', 'metadata'] as const;
86
+ export type DefineRouteOpts = Omit<RouteOpts, 'idUsePath' | 'nextRoute'>;
87
+ const pickValue = ['path', 'key', 'id', 'description', 'type', 'middleware', 'metadata'] as const;
107
88
  export type RouteInfo = Pick<Route, (typeof pickValue)[number]>;
108
89
  export class Route<U = { [key: string]: any }> {
109
90
  /**
@@ -121,13 +102,7 @@ export class Route<U = { [key: string]: any }> {
121
102
  metadata?: { [key: string]: any };
122
103
  middleware?: RouteMiddleware[]; // middleware
123
104
  type? = 'route';
124
- private _validator?: { [key: string]: Rule };
125
- schema?: { [key: string]: any };
126
105
  data?: any;
127
- /**
128
- * 是否需要验证
129
- */
130
- isVerify?: boolean;
131
106
  /**
132
107
  * 是否开启debug,开启后会打印错误信息
133
108
  */
@@ -151,118 +126,16 @@ export class Route<U = { [key: string]: any }> {
151
126
  this.description = opts.description;
152
127
  this.metadata = opts.metadata;
153
128
  this.type = opts.type || 'route';
154
- this.validator = opts.validator;
155
129
  this.middleware = opts.middleware || [];
156
130
  this.key = opts.key || key;
157
131
  this.path = opts.path || path;
158
- this.isVerify = opts.isVerify ?? true;
159
- this.createSchema();
160
132
  } else {
161
- this.isVerify = true;
162
133
  this.middleware = [];
163
134
  this.id = nanoid();
164
135
  }
165
136
  this.isDebug = opts?.isDebug ?? false;
166
137
  }
167
- private createSchema() {
168
- try {
169
- const validator = this.validator;
170
- const keys = Object.keys(validator || {});
171
- const schemaList = keys.map((key) => {
172
- return { [key]: createSchema(validator[key]) };
173
- });
174
- const schema = schemaList.reduce((prev, current) => {
175
- return { ...prev, ...current };
176
- }, {});
177
- this.schema = schema;
178
- } catch (e) {
179
- console.error('createSchema error:', e);
180
- }
181
- }
182
-
183
- /**
184
- * set validator and create schema
185
- * @param validator
186
- */
187
- set validator(validator: { [key: string]: Rule }) {
188
- this._validator = validator;
189
- this.createSchema();
190
- }
191
- get validator() {
192
- return this._validator || {};
193
- }
194
- /**
195
- * has code, body, message in ctx, return ctx if has error
196
- * @param ctx
197
- * @param dev
198
- * @returns
199
- */
200
- verify(ctx: RouteContext, dev = false) {
201
- const query = ctx.query || {};
202
- const schema = this.schema || {};
203
- const validator = this.validator;
204
- const check = () => {
205
- const queryKeys = Object.keys(validator);
206
- for (let i = 0; i < queryKeys.length; i++) {
207
- const key = queryKeys[i];
208
- const value = query[key];
209
- if (schema[key]) {
210
- const result = schema[key].safeParse(value);
211
- if (!result.success) {
212
- const path = result.error.errors[0]?.path?.join?.('.properties.');
213
- let message = 'Invalid params';
214
- if (path) {
215
- const keyS = `${key}.properties.${path}.message`;
216
- message = get(validator, keyS, 'Invalid params') as any;
217
- }
218
- throw new CustomError(500, message);
219
- }
220
- }
221
- }
222
- };
223
- check();
224
- }
225
138
 
226
- /**
227
- * Need to manully call return ctx fn and configure body, code, message
228
- * @param key
229
- * @param ctx
230
- * @param dev
231
- * @returns
232
- */
233
- verifyKey(key: string, ctx: RouteContext, dev = false) {
234
- const query = ctx.query || {};
235
- const schema = this.schema || {};
236
- const validator = this.validator;
237
- const check = () => {
238
- const value = query[key];
239
- if (schema[key]) {
240
- try {
241
- schema[key].parse(value);
242
- } catch (e) {
243
- if (dev) {
244
- return {
245
- message: validator[key].message || 'Invalid params',
246
- path: this.path,
247
- key: this.key,
248
- error: e.message.toString(),
249
- };
250
- }
251
- return {
252
- message: validator[key].message || 'Invalid params',
253
- path: this.path,
254
- key: this.key,
255
- };
256
- }
257
- }
258
- };
259
- const checkRes = check();
260
- return checkRes;
261
- }
262
- setValidator(validator: { [key: string]: Rule }) {
263
- this.validator = validator;
264
- return this;
265
- }
266
139
  prompt(description: string): this;
267
140
  prompt(description: Function): this;
268
141
  prompt(...args: any[]) {
@@ -283,15 +156,11 @@ export class Route<U = { [key: string]: any }> {
283
156
  // 全覆盖,所以opts需要准确,不能由idUsePath 需要check的变量
284
157
  const setOpts = (opts: DefineRouteOpts) => {
285
158
  const keys = Object.keys(opts);
286
- const checkList = ['path', 'key', 'run', 'nextRoute', 'description', 'metadata', 'middleware', 'type', 'validator', 'isVerify', 'isDebug'];
159
+ const checkList = ['path', 'key', 'run', 'nextRoute', 'description', 'metadata', 'middleware', 'type', 'isDebug'];
287
160
  for (let item of keys) {
288
161
  if (!checkList.includes(item)) {
289
162
  continue;
290
163
  }
291
- if (item === 'validator') {
292
- this.validator = opts[item];
293
- continue;
294
- }
295
164
  if (item === 'middleware') {
296
165
  this.middleware = this.middleware.concat(opts[item]);
297
166
  continue;
@@ -320,16 +189,12 @@ export class Route<U = { [key: string]: any }> {
320
189
 
321
190
  update(opts: DefineRouteOpts, checkList?: string[]): this {
322
191
  const keys = Object.keys(opts);
323
- const defaultCheckList = ['path', 'key', 'run', 'nextRoute', 'description', 'metadata', 'middleware', 'type', 'validator', 'isVerify', 'isDebug'];
192
+ const defaultCheckList = ['path', 'key', 'run', 'nextRoute', 'description', 'metadata', 'middleware', 'type', 'isDebug'];
324
193
  checkList = checkList || defaultCheckList;
325
194
  for (let item of keys) {
326
195
  if (!checkList.includes(item)) {
327
196
  continue;
328
197
  }
329
- if (item === 'validator') {
330
- this.validator = opts[item];
331
- continue;
332
- }
333
198
  if (item === 'middleware') {
334
199
  this.middleware = this.middleware.concat(opts[item]);
335
200
  continue;
@@ -360,10 +225,10 @@ export class QueryRouter {
360
225
  }
361
226
 
362
227
  add(route: Route) {
363
- const has = this.routes.find((r) => r.path === route.path && r.key === route.key);
364
- if (has) {
228
+ const has = this.routes.findIndex((r) => r.path === route.path && r.key === route.key);
229
+ if (has !== -1) {
365
230
  // remove the old route
366
- this.routes = this.routes.filter((r) => r.id !== has.id);
231
+ this.routes.splice(has, 1);
367
232
  }
368
233
  this.routes.push(route);
369
234
  }
@@ -460,19 +325,6 @@ export class QueryRouter {
460
325
  for (let i = 0; i < routeMiddleware.length; i++) {
461
326
  const middleware = routeMiddleware[i];
462
327
  if (middleware) {
463
- if (middleware?.isVerify) {
464
- try {
465
- middleware.verify(ctx);
466
- } catch (e) {
467
- if (middleware?.isDebug) {
468
- console.error('=====debug====:', 'middleware verify error:', e.message);
469
- }
470
- ctx.message = e.message;
471
- ctx.code = 500;
472
- ctx.body = null;
473
- return ctx;
474
- }
475
- }
476
328
  try {
477
329
  await middleware.run(ctx);
478
330
  } catch (e) {
@@ -503,19 +355,6 @@ export class QueryRouter {
503
355
  // run route
504
356
  if (route) {
505
357
  if (route.run) {
506
- if (route?.isVerify) {
507
- try {
508
- route.verify(ctx);
509
- } catch (e) {
510
- if (route?.isDebug) {
511
- console.error('=====debug====:', 'verify error:', e.message);
512
- }
513
- ctx.message = e.message;
514
- ctx.code = 500;
515
- ctx.body = null;
516
- return ctx;
517
- }
518
- }
519
358
  try {
520
359
  await route.run(ctx);
521
360
  } catch (e) {
@@ -588,9 +427,20 @@ export class QueryRouter {
588
427
  ctx.throw = this.throw;
589
428
  ctx.app = this;
590
429
  ctx.call = this.call.bind(this);
591
- ctx.queryRoute = this.queryRoute.bind(this);
430
+ ctx.run = this.run.bind(this);
592
431
  ctx.index = 0;
593
432
  ctx.progress = ctx.progress || [];
433
+ ctx.forward = (response: { code: number; data?: any; message?: any }) => {
434
+ if (response.code) {
435
+ ctx.code = response.code;
436
+ }
437
+ if (response.data !== undefined) {
438
+ ctx.body = response.data;
439
+ }
440
+ if (response.message !== undefined) {
441
+ ctx.message = response.message;
442
+ }
443
+ }
594
444
  const res = await this.runRoute(path, key, ctx);
595
445
  const serialize = ctx.needSerialize ?? true; // 是否需要序列化
596
446
  if (serialize) {
@@ -628,6 +478,7 @@ export class QueryRouter {
628
478
  * 请求 result 的数据
629
479
  * @param message
630
480
  * @param ctx
481
+ * @deprecated use run or call instead
631
482
  * @returns
632
483
  */
633
484
  async queryRoute(message: { id?: string; path: string; key?: string; payload?: any }, ctx?: RouteContext & { [key: string]: any }) {
@@ -638,6 +489,20 @@ export class QueryRouter {
638
489
  message: res.message,
639
490
  };
640
491
  }
492
+ /**
493
+ * Router Run获取数据
494
+ * @param message
495
+ * @param ctx
496
+ * @returns
497
+ */
498
+ async run(message: { id?: string; path?: string; key?: string; payload?: any }, ctx?: RouteContext & { [key: string]: any }) {
499
+ const res = await this.call(message, { ...this.context, ...ctx });
500
+ return {
501
+ code: res.code,
502
+ data: res.body,
503
+ message: res.message,
504
+ };
505
+ }
641
506
  /**
642
507
  * 设置上下文
643
508
  * @description 这里的上下文是为了在handle函数中使用
@@ -658,17 +523,7 @@ export class QueryRouter {
658
523
  return async (msg: { id?: string; path?: string; key?: string;[key: string]: any }, handleContext?: RouteContext) => {
659
524
  try {
660
525
  const context = { ...ctx, ...handleContext };
661
- if (msg.id) {
662
- const route = router.routes.find((r) => r.id === msg.id);
663
- if (route) {
664
- msg.path = route.path;
665
- msg.key = route.key;
666
- } else {
667
- return { code: 404, message: 'Not found route' };
668
- }
669
- }
670
- // @ts-ignore
671
- const res = await router.parse(msg, context);
526
+ const res = await router.call(msg, context);
672
527
  if (wrapperFn) {
673
528
  res.data = res.body;
674
529
  return wrapperFn(res, context);
@@ -796,34 +651,19 @@ export class QueryRouterServer extends QueryRouter {
796
651
  }
797
652
 
798
653
  /**
799
- * 等于queryRoute,但是调用了handle
654
+ * 调用了handle
800
655
  * @param param0
801
656
  * @returns
802
657
  */
803
- async run({ path, key, payload }: { path: string; key?: string; payload?: any }) {
658
+ async run({ path, key, payload }: { path: string; key?: string; payload?: any }, ctx?: RouteContext & { [key: string]: any }) {
804
659
  const handle = this.handle;
805
- const resultError = (error: string, code = 500) => {
806
- const r = {
807
- code: code,
808
- message: error,
809
- };
810
- return r;
811
- };
812
- try {
813
- const end = handle({ path, key, ...payload });
814
- return end;
815
- } catch (e) {
816
- if (e.code && typeof e.code === 'number') {
817
- return {
818
- code: e.code,
819
- message: e.message,
820
- };
821
- } else {
822
- return resultError('Router Server error');
823
- }
660
+ if (handle) {
661
+ const result = await this.call({ path, key, payload }, ctx);
662
+ return handle(result);
824
663
  }
664
+ return super.run({ path, key, payload }, ctx);
825
665
  }
826
666
  }
827
667
 
828
668
 
829
- export const Mini = QueryRouterServer
669
+ export class Mini extends QueryRouterServer { }
@@ -11,8 +11,8 @@ type SimpleObject = Record<string, any>;
11
11
  export function define<T extends Record<string, RouteOpts>>(
12
12
  value: T,
13
13
  ): {
14
- [K in keyof T]: T[K] & RouteOpts;
15
- } {
14
+ [K in keyof T]: T[K] & RouteOpts;
15
+ } {
16
16
  return value as { [K in keyof T]: T[K] & RouteOpts };
17
17
  }
18
18
 
@@ -95,7 +95,7 @@ class QueryChain {
95
95
  * @param queryData
96
96
  * @returns
97
97
  */
98
- getKey(queryData?: SimpleObject): Pick<RouteOpts, 'path' | 'key' | 'metadata' | 'description' | 'validator'> {
98
+ getKey(queryData?: SimpleObject): Pick<RouteOpts, 'path' | 'key' | 'metadata' | 'description'> {
99
99
  const obj = this.omit(this.obj, this.omitKeys);
100
100
  return {
101
101
  ...obj,
@@ -53,7 +53,7 @@ export type ServerOpts = {
53
53
  /**path default `/api/router` */
54
54
  path?: string;
55
55
  /**handle Fn */
56
- handle?: (msg?: { path: string; key?: string; [key: string]: any }, ctx?: { req: http.IncomingMessage; res: http.ServerResponse }) => any;
56
+ handle?: (msg?: { path: string; key?: string;[key: string]: any }, ctx?: { req: http.IncomingMessage; res: http.ServerResponse }) => any;
57
57
  cors?: Cors;
58
58
  httpType?: 'http' | 'https' | 'http2';
59
59
  httpsKey?: string;
@@ -222,7 +222,17 @@ export class Server {
222
222
  } else {
223
223
  this._server.on('request', listener);
224
224
  }
225
- this._server.on('request', this._callback || this.createCallback());
225
+ const callbackListener = this._callback || this.createCallback();
226
+ this._server.on('request', callbackListener);
227
+ return () => {
228
+ if (Array.isArray(listener)) {
229
+ listener.forEach((l) => this._server.removeListener('request', l as Listener));
230
+ } else {
231
+ this._server.removeListener('request', listener as Listener);
232
+ }
233
+ this.hasOn = false;
234
+ this._server.removeListener('request', callbackListener);
235
+ }
226
236
  }
227
237
  get callback() {
228
238
  return this._callback || this.createCallback();
@@ -0,0 +1,13 @@
1
+ import { App } from '../app.ts'
2
+
3
+ const app = new App<{ f: string }>();
4
+
5
+ app.route({
6
+ path: 't',
7
+ run: async (ctx) => {
8
+ // ctx.r
9
+ ctx.app;
10
+ }
11
+ }).define(async (ctx) => {
12
+ ctx.f = 'hello';
13
+ }).addTo(app);