refactor router system and update version
- Simplified Route class by removing validator functionality - Added AppRouteContext type for better type safety - Added forward method to RouteContext for response handling - Replaced queryRoute with run method for consistency - Improved Server class with proper cleanup methods - Updated Mini class to extend QueryRouterServer properly - Removed lodash-es dependency and nanoid random import - Added deprecation warnings for older methods - Enhanced route handling and middleware execution 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package",
|
"$schema": "https://json.schemastore.org/package",
|
||||||
"name": "@kevisual/router",
|
"name": "@kevisual/router",
|
||||||
"version": "0.0.34",
|
"version": "0.0.36",
|
||||||
"description": "",
|
"description": "",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/router.js",
|
"main": "./dist/router.js",
|
||||||
|
|||||||
30
src/app.ts
30
src/app.ts
@@ -16,16 +16,18 @@ type AppOptions<T = {}> = {
|
|||||||
io?: boolean;
|
io?: boolean;
|
||||||
ioOpts?: { routerHandle?: RouterHandle; routerContext?: RouteContext<T>; path?: string };
|
ioOpts?: { routerHandle?: RouterHandle; routerContext?: RouteContext<T>; path?: string };
|
||||||
};
|
};
|
||||||
export type AppReqRes = HandleCtx;
|
|
||||||
|
export type AppRouteContext<T = {}> = HandleCtx & RouteContext<T> & { app: App<T> };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 封装了 Router 和 Server 的 App 模块,处理http的请求和响应,内置了 Cookie 和 Token 和 res 的处理
|
* 封装了 Router 和 Server 的 App 模块,处理http的请求和响应,内置了 Cookie 和 Token 和 res 的处理
|
||||||
|
* U - Route Context的扩展类型
|
||||||
*/
|
*/
|
||||||
export class App<T = {}, U = AppReqRes> {
|
export class App<U = {}> {
|
||||||
router: QueryRouter;
|
router: QueryRouter;
|
||||||
server: Server;
|
server: Server;
|
||||||
io: WsServer;
|
io: WsServer;
|
||||||
constructor(opts?: AppOptions<T>) {
|
constructor(opts?: AppOptions<U>) {
|
||||||
const router = opts?.router || new QueryRouter();
|
const router = opts?.router || new QueryRouter();
|
||||||
const server = opts?.server || new Server(opts?.serverOptions || {});
|
const server = opts?.server || new Server(opts?.serverOptions || {});
|
||||||
server.setHandle(router.getHandle(router, opts?.routerHandle, opts?.routerContext));
|
server.setHandle(router.getHandle(router, opts?.routerHandle, opts?.routerContext));
|
||||||
@@ -62,10 +64,10 @@ export class App<T = {}, U = AppReqRes> {
|
|||||||
add = this.addRoute;
|
add = this.addRoute;
|
||||||
|
|
||||||
Route = Route;
|
Route = Route;
|
||||||
route(opts: RouteOpts): Route<U>;
|
route(opts: RouteOpts<AppRouteContext<U>>): Route<AppRouteContext<U>>;
|
||||||
route(path: string, key?: string): Route<U>;
|
route(path: string, key?: string): Route<AppRouteContext<U>>;
|
||||||
route(path: string, opts?: RouteOpts): Route<U>;
|
route(path: string, opts?: RouteOpts<AppRouteContext<U>>): Route<AppRouteContext<U>>;
|
||||||
route(path: string, key?: string, opts?: RouteOpts): Route<U>;
|
route(path: string, key?: string, opts?: RouteOpts<AppRouteContext<U>>): Route<AppRouteContext<U>>;
|
||||||
route(...args: any[]) {
|
route(...args: any[]) {
|
||||||
const [path, key, opts] = args;
|
const [path, key, opts] = args;
|
||||||
if (typeof path === 'object') {
|
if (typeof path === 'object') {
|
||||||
@@ -82,8 +84,8 @@ export class App<T = {}, U = AppReqRes> {
|
|||||||
}
|
}
|
||||||
return new Route(path, key, opts);
|
return new Route(path, key, opts);
|
||||||
}
|
}
|
||||||
prompt(description: string): Route<Required<RouteContext>>;
|
prompt(description: string): Route<AppRouteContext<U>>
|
||||||
prompt(description: Function): Route<Required<RouteContext>>;
|
prompt(description: Function): Route<AppRouteContext<U>>
|
||||||
prompt(...args: any[]) {
|
prompt(...args: any[]) {
|
||||||
const [desc] = args;
|
const [desc] = args;
|
||||||
let description = ''
|
let description = ''
|
||||||
@@ -95,13 +97,19 @@ export class App<T = {}, U = AppReqRes> {
|
|||||||
return new Route('', '', { description });
|
return new Route('', '', { description });
|
||||||
}
|
}
|
||||||
|
|
||||||
async call(message: { id?: string, path?: string; key?: string; payload?: any }, ctx?: RouteContext & { [key: string]: any }) {
|
async call(message: { id?: string, path?: string; key?: string; payload?: any }, ctx?: AppRouteContext<U> & { [key: string]: any }) {
|
||||||
const router = this.router;
|
const router = this.router;
|
||||||
return await router.call(message, ctx);
|
return await router.call(message, ctx);
|
||||||
}
|
}
|
||||||
async queryRoute(path: string, key?: string, payload?: any, ctx?: RouteContext & { [key: string]: any }) {
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
async queryRoute(path: string, key?: string, payload?: any, ctx?: AppRouteContext<U> & { [key: string]: any }) {
|
||||||
return await this.router.queryRoute({ path, key, payload }, ctx);
|
return await this.router.queryRoute({ path, key, payload }, ctx);
|
||||||
}
|
}
|
||||||
|
async run(path: string, key?: string, payload?: any, ctx?: AppRouteContext<U> & { [key: string]: any }) {
|
||||||
|
return await this.router.run({ path, key, payload }, ctx);
|
||||||
|
}
|
||||||
exportRoutes() {
|
exportRoutes() {
|
||||||
return this.router.exportRoutes();
|
return this.router.exportRoutes();
|
||||||
}
|
}
|
||||||
|
|||||||
260
src/route.ts
260
src/route.ts
@@ -1,8 +1,6 @@
|
|||||||
import { nanoid, random } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import { CustomError } from './result/error.ts';
|
import { CustomError } from './result/error.ts';
|
||||||
import { Schema, Rule, createSchema } from './validator/index.ts';
|
|
||||||
import { pick } from './utils/pick.ts';
|
import { pick } from './utils/pick.ts';
|
||||||
import { get } from 'lodash-es';
|
|
||||||
import { listenProcess } from './utils/listen-process.ts';
|
import { listenProcess } from './utils/listen-process.ts';
|
||||||
|
|
||||||
export type RouterContextT = { code?: number;[key: string]: any };
|
export type RouterContextT = { code?: number;[key: string]: any };
|
||||||
@@ -12,6 +10,7 @@ export type RouteContext<T = { code?: number }, S = any> = {
|
|||||||
// response body
|
// response body
|
||||||
/** return body */
|
/** return body */
|
||||||
body?: number | string | Object;
|
body?: number | string | Object;
|
||||||
|
forward?: (response: { code: number, data?: any, message?: any }) => void;
|
||||||
/** return code */
|
/** return code */
|
||||||
code?: number;
|
code?: number;
|
||||||
/** return msg */
|
/** return msg */
|
||||||
@@ -39,20 +38,15 @@ export type RouteContext<T = { code?: number }, S = any> = {
|
|||||||
nextQuery?: { [key: string]: any };
|
nextQuery?: { [key: string]: any };
|
||||||
// end
|
// end
|
||||||
end?: boolean;
|
end?: boolean;
|
||||||
// 处理router manager
|
app?: QueryRouter;
|
||||||
// TODO:
|
|
||||||
/**
|
|
||||||
* 请求 route的返回结果,包函ctx
|
|
||||||
*/
|
|
||||||
queryRouter?: QueryRouter;
|
|
||||||
error?: any;
|
error?: any;
|
||||||
/** 请求 route的返回结果,包函ctx */
|
/** 请求 route的返回结果,不解析body为data */
|
||||||
call?: (
|
call?: (
|
||||||
message: { path: string; key?: string; payload?: any;[key: string]: any } | { id: string; apyload?: any;[key: string]: any },
|
message: { path: string; key?: string; payload?: any;[key: string]: any } | { id: string; apyload?: any;[key: string]: any },
|
||||||
ctx?: RouteContext & { [key: string]: any },
|
ctx?: RouteContext & { [key: string]: any },
|
||||||
) => Promise<any>;
|
) => Promise<any>;
|
||||||
/** 请求 route的返回结果,不包函ctx */
|
/** 请求 route的返回结果,解析了body为data,就类同于 query.post获取的数据*/
|
||||||
queryRoute?: (message: { path: string; key?: string; payload?: any }, ctx?: RouteContext & { [key: string]: any }) => Promise<any>;
|
run?: (message: { path: string; key?: string; payload?: any }, ctx?: RouteContext & { [key: string]: any }) => Promise<any>;
|
||||||
index?: number;
|
index?: number;
|
||||||
throw?: (code?: number | string, message?: string, tips?: string) => void;
|
throw?: (code?: number | string, message?: string, tips?: string) => void;
|
||||||
/** 是否需要序列化, 使用JSON.stringify和JSON.parse */
|
/** 是否需要序列化, 使用JSON.stringify和JSON.parse */
|
||||||
@@ -69,29 +63,16 @@ export type RouteMiddleware =
|
|||||||
id?: string;
|
id?: string;
|
||||||
}
|
}
|
||||||
| string;
|
| string;
|
||||||
export type RouteOpts = {
|
export type RouteOpts<T = {}> = {
|
||||||
path?: string;
|
path?: string;
|
||||||
key?: string;
|
key?: string;
|
||||||
id?: string;
|
id?: string;
|
||||||
run?: Run;
|
run?: Run<T>;
|
||||||
nextRoute?: NextRoute; // route to run after this route
|
nextRoute?: NextRoute; // route to run after this route
|
||||||
description?: string;
|
description?: string;
|
||||||
metadata?: { [key: string]: any };
|
metadata?: { [key: string]: any };
|
||||||
middleware?: RouteMiddleware[]; // middleware
|
middleware?: RouteMiddleware[]; // middleware
|
||||||
type?: 'route' | 'middleware';
|
type?: 'route' | 'middleware';
|
||||||
/**
|
|
||||||
* validator: {
|
|
||||||
* packageName: {
|
|
||||||
* type: 'string',
|
|
||||||
* required: true,
|
|
||||||
* },
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
validator?: { [key: string]: Rule };
|
|
||||||
schema?: { [key: string]: any };
|
|
||||||
isVerify?: boolean;
|
|
||||||
verify?: (ctx?: RouteContext, dev?: boolean) => boolean;
|
|
||||||
verifyKey?: (key: string, ctx?: RouteContext, dev?: boolean) => boolean;
|
|
||||||
/**
|
/**
|
||||||
* $#$ will be used to split path and key
|
* $#$ will be used to split path and key
|
||||||
*/
|
*/
|
||||||
@@ -102,8 +83,8 @@ export type RouteOpts = {
|
|||||||
delimiter?: string;
|
delimiter?: string;
|
||||||
isDebug?: boolean;
|
isDebug?: boolean;
|
||||||
};
|
};
|
||||||
export type DefineRouteOpts = Omit<RouteOpts, 'idUsePath' | 'verify' | 'verifyKey' | 'nextRoute'>;
|
export type DefineRouteOpts = Omit<RouteOpts, 'idUsePath' | 'nextRoute'>;
|
||||||
const pickValue = ['path', 'key', 'id', 'description', 'type', 'validator', 'middleware', 'metadata'] as const;
|
const pickValue = ['path', 'key', 'id', 'description', 'type', 'middleware', 'metadata'] as const;
|
||||||
export type RouteInfo = Pick<Route, (typeof pickValue)[number]>;
|
export type RouteInfo = Pick<Route, (typeof pickValue)[number]>;
|
||||||
export class Route<U = { [key: string]: any }> {
|
export class Route<U = { [key: string]: any }> {
|
||||||
/**
|
/**
|
||||||
@@ -121,13 +102,7 @@ export class Route<U = { [key: string]: any }> {
|
|||||||
metadata?: { [key: string]: any };
|
metadata?: { [key: string]: any };
|
||||||
middleware?: RouteMiddleware[]; // middleware
|
middleware?: RouteMiddleware[]; // middleware
|
||||||
type? = 'route';
|
type? = 'route';
|
||||||
private _validator?: { [key: string]: Rule };
|
|
||||||
schema?: { [key: string]: any };
|
|
||||||
data?: any;
|
data?: any;
|
||||||
/**
|
|
||||||
* 是否需要验证
|
|
||||||
*/
|
|
||||||
isVerify?: boolean;
|
|
||||||
/**
|
/**
|
||||||
* 是否开启debug,开启后会打印错误信息
|
* 是否开启debug,开启后会打印错误信息
|
||||||
*/
|
*/
|
||||||
@@ -151,118 +126,16 @@ export class Route<U = { [key: string]: any }> {
|
|||||||
this.description = opts.description;
|
this.description = opts.description;
|
||||||
this.metadata = opts.metadata;
|
this.metadata = opts.metadata;
|
||||||
this.type = opts.type || 'route';
|
this.type = opts.type || 'route';
|
||||||
this.validator = opts.validator;
|
|
||||||
this.middleware = opts.middleware || [];
|
this.middleware = opts.middleware || [];
|
||||||
this.key = opts.key || key;
|
this.key = opts.key || key;
|
||||||
this.path = opts.path || path;
|
this.path = opts.path || path;
|
||||||
this.isVerify = opts.isVerify ?? true;
|
|
||||||
this.createSchema();
|
|
||||||
} else {
|
} else {
|
||||||
this.isVerify = true;
|
|
||||||
this.middleware = [];
|
this.middleware = [];
|
||||||
this.id = nanoid();
|
this.id = nanoid();
|
||||||
}
|
}
|
||||||
this.isDebug = opts?.isDebug ?? false;
|
this.isDebug = opts?.isDebug ?? false;
|
||||||
}
|
}
|
||||||
private createSchema() {
|
|
||||||
try {
|
|
||||||
const validator = this.validator;
|
|
||||||
const keys = Object.keys(validator || {});
|
|
||||||
const schemaList = keys.map((key) => {
|
|
||||||
return { [key]: createSchema(validator[key]) };
|
|
||||||
});
|
|
||||||
const schema = schemaList.reduce((prev, current) => {
|
|
||||||
return { ...prev, ...current };
|
|
||||||
}, {});
|
|
||||||
this.schema = schema;
|
|
||||||
} catch (e) {
|
|
||||||
console.error('createSchema error:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set validator and create schema
|
|
||||||
* @param validator
|
|
||||||
*/
|
|
||||||
set validator(validator: { [key: string]: Rule }) {
|
|
||||||
this._validator = validator;
|
|
||||||
this.createSchema();
|
|
||||||
}
|
|
||||||
get validator() {
|
|
||||||
return this._validator || {};
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* has code, body, message in ctx, return ctx if has error
|
|
||||||
* @param ctx
|
|
||||||
* @param dev
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
verify(ctx: RouteContext, dev = false) {
|
|
||||||
const query = ctx.query || {};
|
|
||||||
const schema = this.schema || {};
|
|
||||||
const validator = this.validator;
|
|
||||||
const check = () => {
|
|
||||||
const queryKeys = Object.keys(validator);
|
|
||||||
for (let i = 0; i < queryKeys.length; i++) {
|
|
||||||
const key = queryKeys[i];
|
|
||||||
const value = query[key];
|
|
||||||
if (schema[key]) {
|
|
||||||
const result = schema[key].safeParse(value);
|
|
||||||
if (!result.success) {
|
|
||||||
const path = result.error.errors[0]?.path?.join?.('.properties.');
|
|
||||||
let message = 'Invalid params';
|
|
||||||
if (path) {
|
|
||||||
const keyS = `${key}.properties.${path}.message`;
|
|
||||||
message = get(validator, keyS, 'Invalid params') as any;
|
|
||||||
}
|
|
||||||
throw new CustomError(500, message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
check();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Need to manully call return ctx fn and configure body, code, message
|
|
||||||
* @param key
|
|
||||||
* @param ctx
|
|
||||||
* @param dev
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
verifyKey(key: string, ctx: RouteContext, dev = false) {
|
|
||||||
const query = ctx.query || {};
|
|
||||||
const schema = this.schema || {};
|
|
||||||
const validator = this.validator;
|
|
||||||
const check = () => {
|
|
||||||
const value = query[key];
|
|
||||||
if (schema[key]) {
|
|
||||||
try {
|
|
||||||
schema[key].parse(value);
|
|
||||||
} catch (e) {
|
|
||||||
if (dev) {
|
|
||||||
return {
|
|
||||||
message: validator[key].message || 'Invalid params',
|
|
||||||
path: this.path,
|
|
||||||
key: this.key,
|
|
||||||
error: e.message.toString(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
message: validator[key].message || 'Invalid params',
|
|
||||||
path: this.path,
|
|
||||||
key: this.key,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const checkRes = check();
|
|
||||||
return checkRes;
|
|
||||||
}
|
|
||||||
setValidator(validator: { [key: string]: Rule }) {
|
|
||||||
this.validator = validator;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
prompt(description: string): this;
|
prompt(description: string): this;
|
||||||
prompt(description: Function): this;
|
prompt(description: Function): this;
|
||||||
prompt(...args: any[]) {
|
prompt(...args: any[]) {
|
||||||
@@ -283,15 +156,11 @@ export class Route<U = { [key: string]: any }> {
|
|||||||
// 全覆盖,所以opts需要准确,不能由idUsePath 需要check的变量
|
// 全覆盖,所以opts需要准确,不能由idUsePath 需要check的变量
|
||||||
const setOpts = (opts: DefineRouteOpts) => {
|
const setOpts = (opts: DefineRouteOpts) => {
|
||||||
const keys = Object.keys(opts);
|
const keys = Object.keys(opts);
|
||||||
const checkList = ['path', 'key', 'run', 'nextRoute', 'description', 'metadata', 'middleware', 'type', 'validator', 'isVerify', 'isDebug'];
|
const checkList = ['path', 'key', 'run', 'nextRoute', 'description', 'metadata', 'middleware', 'type', 'isDebug'];
|
||||||
for (let item of keys) {
|
for (let item of keys) {
|
||||||
if (!checkList.includes(item)) {
|
if (!checkList.includes(item)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (item === 'validator') {
|
|
||||||
this.validator = opts[item];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (item === 'middleware') {
|
if (item === 'middleware') {
|
||||||
this.middleware = this.middleware.concat(opts[item]);
|
this.middleware = this.middleware.concat(opts[item]);
|
||||||
continue;
|
continue;
|
||||||
@@ -320,16 +189,12 @@ export class Route<U = { [key: string]: any }> {
|
|||||||
|
|
||||||
update(opts: DefineRouteOpts, checkList?: string[]): this {
|
update(opts: DefineRouteOpts, checkList?: string[]): this {
|
||||||
const keys = Object.keys(opts);
|
const keys = Object.keys(opts);
|
||||||
const defaultCheckList = ['path', 'key', 'run', 'nextRoute', 'description', 'metadata', 'middleware', 'type', 'validator', 'isVerify', 'isDebug'];
|
const defaultCheckList = ['path', 'key', 'run', 'nextRoute', 'description', 'metadata', 'middleware', 'type', 'isDebug'];
|
||||||
checkList = checkList || defaultCheckList;
|
checkList = checkList || defaultCheckList;
|
||||||
for (let item of keys) {
|
for (let item of keys) {
|
||||||
if (!checkList.includes(item)) {
|
if (!checkList.includes(item)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (item === 'validator') {
|
|
||||||
this.validator = opts[item];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (item === 'middleware') {
|
if (item === 'middleware') {
|
||||||
this.middleware = this.middleware.concat(opts[item]);
|
this.middleware = this.middleware.concat(opts[item]);
|
||||||
continue;
|
continue;
|
||||||
@@ -360,10 +225,10 @@ export class QueryRouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
add(route: Route) {
|
add(route: Route) {
|
||||||
const has = this.routes.find((r) => r.path === route.path && r.key === route.key);
|
const has = this.routes.findIndex((r) => r.path === route.path && r.key === route.key);
|
||||||
if (has) {
|
if (has !== -1) {
|
||||||
// remove the old route
|
// remove the old route
|
||||||
this.routes = this.routes.filter((r) => r.id !== has.id);
|
this.routes.splice(has, 1);
|
||||||
}
|
}
|
||||||
this.routes.push(route);
|
this.routes.push(route);
|
||||||
}
|
}
|
||||||
@@ -460,19 +325,6 @@ export class QueryRouter {
|
|||||||
for (let i = 0; i < routeMiddleware.length; i++) {
|
for (let i = 0; i < routeMiddleware.length; i++) {
|
||||||
const middleware = routeMiddleware[i];
|
const middleware = routeMiddleware[i];
|
||||||
if (middleware) {
|
if (middleware) {
|
||||||
if (middleware?.isVerify) {
|
|
||||||
try {
|
|
||||||
middleware.verify(ctx);
|
|
||||||
} catch (e) {
|
|
||||||
if (middleware?.isDebug) {
|
|
||||||
console.error('=====debug====:', 'middleware verify error:', e.message);
|
|
||||||
}
|
|
||||||
ctx.message = e.message;
|
|
||||||
ctx.code = 500;
|
|
||||||
ctx.body = null;
|
|
||||||
return ctx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
await middleware.run(ctx);
|
await middleware.run(ctx);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -503,19 +355,6 @@ export class QueryRouter {
|
|||||||
// run route
|
// run route
|
||||||
if (route) {
|
if (route) {
|
||||||
if (route.run) {
|
if (route.run) {
|
||||||
if (route?.isVerify) {
|
|
||||||
try {
|
|
||||||
route.verify(ctx);
|
|
||||||
} catch (e) {
|
|
||||||
if (route?.isDebug) {
|
|
||||||
console.error('=====debug====:', 'verify error:', e.message);
|
|
||||||
}
|
|
||||||
ctx.message = e.message;
|
|
||||||
ctx.code = 500;
|
|
||||||
ctx.body = null;
|
|
||||||
return ctx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
await route.run(ctx);
|
await route.run(ctx);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -588,9 +427,20 @@ export class QueryRouter {
|
|||||||
ctx.throw = this.throw;
|
ctx.throw = this.throw;
|
||||||
ctx.app = this;
|
ctx.app = this;
|
||||||
ctx.call = this.call.bind(this);
|
ctx.call = this.call.bind(this);
|
||||||
ctx.queryRoute = this.queryRoute.bind(this);
|
ctx.run = this.run.bind(this);
|
||||||
ctx.index = 0;
|
ctx.index = 0;
|
||||||
ctx.progress = ctx.progress || [];
|
ctx.progress = ctx.progress || [];
|
||||||
|
ctx.forward = (response: { code: number; data?: any; message?: any }) => {
|
||||||
|
if (response.code) {
|
||||||
|
ctx.code = response.code;
|
||||||
|
}
|
||||||
|
if (response.data !== undefined) {
|
||||||
|
ctx.body = response.data;
|
||||||
|
}
|
||||||
|
if (response.message !== undefined) {
|
||||||
|
ctx.message = response.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
const res = await this.runRoute(path, key, ctx);
|
const res = await this.runRoute(path, key, ctx);
|
||||||
const serialize = ctx.needSerialize ?? true; // 是否需要序列化
|
const serialize = ctx.needSerialize ?? true; // 是否需要序列化
|
||||||
if (serialize) {
|
if (serialize) {
|
||||||
@@ -628,6 +478,7 @@ export class QueryRouter {
|
|||||||
* 请求 result 的数据
|
* 请求 result 的数据
|
||||||
* @param message
|
* @param message
|
||||||
* @param ctx
|
* @param ctx
|
||||||
|
* @deprecated use run or call instead
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async queryRoute(message: { id?: string; path: string; key?: string; payload?: any }, ctx?: RouteContext & { [key: string]: any }) {
|
async queryRoute(message: { id?: string; path: string; key?: string; payload?: any }, ctx?: RouteContext & { [key: string]: any }) {
|
||||||
@@ -638,6 +489,20 @@ export class QueryRouter {
|
|||||||
message: res.message,
|
message: res.message,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Router Run获取数据
|
||||||
|
* @param message
|
||||||
|
* @param ctx
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async run(message: { id?: string; path?: string; key?: string; payload?: any }, ctx?: RouteContext & { [key: string]: any }) {
|
||||||
|
const res = await this.call(message, { ...this.context, ...ctx });
|
||||||
|
return {
|
||||||
|
code: res.code,
|
||||||
|
data: res.body,
|
||||||
|
message: res.message,
|
||||||
|
};
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 设置上下文
|
* 设置上下文
|
||||||
* @description 这里的上下文是为了在handle函数中使用
|
* @description 这里的上下文是为了在handle函数中使用
|
||||||
@@ -658,17 +523,7 @@ export class QueryRouter {
|
|||||||
return async (msg: { id?: string; path?: string; key?: string;[key: string]: any }, handleContext?: RouteContext) => {
|
return async (msg: { id?: string; path?: string; key?: string;[key: string]: any }, handleContext?: RouteContext) => {
|
||||||
try {
|
try {
|
||||||
const context = { ...ctx, ...handleContext };
|
const context = { ...ctx, ...handleContext };
|
||||||
if (msg.id) {
|
const res = await router.call(msg, context);
|
||||||
const route = router.routes.find((r) => r.id === msg.id);
|
|
||||||
if (route) {
|
|
||||||
msg.path = route.path;
|
|
||||||
msg.key = route.key;
|
|
||||||
} else {
|
|
||||||
return { code: 404, message: 'Not found route' };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// @ts-ignore
|
|
||||||
const res = await router.parse(msg, context);
|
|
||||||
if (wrapperFn) {
|
if (wrapperFn) {
|
||||||
res.data = res.body;
|
res.data = res.body;
|
||||||
return wrapperFn(res, context);
|
return wrapperFn(res, context);
|
||||||
@@ -796,34 +651,19 @@ export class QueryRouterServer extends QueryRouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 等于queryRoute,但是调用了handle
|
* 调用了handle
|
||||||
* @param param0
|
* @param param0
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async run({ path, key, payload }: { path: string; key?: string; payload?: any }) {
|
async run({ path, key, payload }: { path: string; key?: string; payload?: any }, ctx?: RouteContext & { [key: string]: any }) {
|
||||||
const handle = this.handle;
|
const handle = this.handle;
|
||||||
const resultError = (error: string, code = 500) => {
|
if (handle) {
|
||||||
const r = {
|
const result = await this.call({ path, key, payload }, ctx);
|
||||||
code: code,
|
return handle(result);
|
||||||
message: error,
|
|
||||||
};
|
|
||||||
return r;
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
const end = handle({ path, key, ...payload });
|
|
||||||
return end;
|
|
||||||
} catch (e) {
|
|
||||||
if (e.code && typeof e.code === 'number') {
|
|
||||||
return {
|
|
||||||
code: e.code,
|
|
||||||
message: e.message,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return resultError('Router Server error');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return super.run({ path, key, payload }, ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const Mini = QueryRouterServer
|
export class Mini extends QueryRouterServer { }
|
||||||
@@ -11,8 +11,8 @@ type SimpleObject = Record<string, any>;
|
|||||||
export function define<T extends Record<string, RouteOpts>>(
|
export function define<T extends Record<string, RouteOpts>>(
|
||||||
value: T,
|
value: T,
|
||||||
): {
|
): {
|
||||||
[K in keyof T]: T[K] & RouteOpts;
|
[K in keyof T]: T[K] & RouteOpts;
|
||||||
} {
|
} {
|
||||||
return value as { [K in keyof T]: T[K] & RouteOpts };
|
return value as { [K in keyof T]: T[K] & RouteOpts };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ class QueryChain {
|
|||||||
* @param queryData
|
* @param queryData
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
getKey(queryData?: SimpleObject): Pick<RouteOpts, 'path' | 'key' | 'metadata' | 'description' | 'validator'> {
|
getKey(queryData?: SimpleObject): Pick<RouteOpts, 'path' | 'key' | 'metadata' | 'description'> {
|
||||||
const obj = this.omit(this.obj, this.omitKeys);
|
const obj = this.omit(this.obj, this.omitKeys);
|
||||||
return {
|
return {
|
||||||
...obj,
|
...obj,
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export type ServerOpts = {
|
|||||||
/**path default `/api/router` */
|
/**path default `/api/router` */
|
||||||
path?: string;
|
path?: string;
|
||||||
/**handle Fn */
|
/**handle Fn */
|
||||||
handle?: (msg?: { path: string; key?: string; [key: string]: any }, ctx?: { req: http.IncomingMessage; res: http.ServerResponse }) => any;
|
handle?: (msg?: { path: string; key?: string;[key: string]: any }, ctx?: { req: http.IncomingMessage; res: http.ServerResponse }) => any;
|
||||||
cors?: Cors;
|
cors?: Cors;
|
||||||
httpType?: 'http' | 'https' | 'http2';
|
httpType?: 'http' | 'https' | 'http2';
|
||||||
httpsKey?: string;
|
httpsKey?: string;
|
||||||
@@ -222,7 +222,17 @@ export class Server {
|
|||||||
} else {
|
} else {
|
||||||
this._server.on('request', listener);
|
this._server.on('request', listener);
|
||||||
}
|
}
|
||||||
this._server.on('request', this._callback || this.createCallback());
|
const callbackListener = this._callback || this.createCallback();
|
||||||
|
this._server.on('request', callbackListener);
|
||||||
|
return () => {
|
||||||
|
if (Array.isArray(listener)) {
|
||||||
|
listener.forEach((l) => this._server.removeListener('request', l as Listener));
|
||||||
|
} else {
|
||||||
|
this._server.removeListener('request', listener as Listener);
|
||||||
|
}
|
||||||
|
this.hasOn = false;
|
||||||
|
this._server.removeListener('request', callbackListener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
get callback() {
|
get callback() {
|
||||||
return this._callback || this.createCallback();
|
return this._callback || this.createCallback();
|
||||||
|
|||||||
13
src/test/app-type.ts
Normal file
13
src/test/app-type.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { App } from '../app.ts'
|
||||||
|
|
||||||
|
const app = new App<{ f: string }>();
|
||||||
|
|
||||||
|
app.route({
|
||||||
|
path: 't',
|
||||||
|
run: async (ctx) => {
|
||||||
|
// ctx.r
|
||||||
|
ctx.app;
|
||||||
|
}
|
||||||
|
}).define(async (ctx) => {
|
||||||
|
ctx.f = 'hello';
|
||||||
|
}).addTo(app);
|
||||||
Reference in New Issue
Block a user