UNPKG

@halsp/core

Version:

面向云的现代渐进式轻量 Node.js 框架

168 lines (152 loc) 4.57 kB
import { Context } from "../context"; import { HalspException } from "../exception"; import { isClass, isObject, ObjectConstructor } from "../utils"; import { HookType } from "../hook"; import { LambdaMiddleware } from "./lambda.middleware"; import { execConstructorHooks, execErrorHooks, execHooks, execUnhandledHooks, } from "../hook/hook.exec"; function isMiddlewareConstructor(md: any): md is MiddlewareConstructor { return !!md.prototype; } export type MiddlewareConstructor = { new (...args: any[]): Middleware; }; export type MiddlewareItem = | LambdaMiddleware | ((ctx: Context) => Middleware) | [(ctx: Context) => Middleware, MiddlewareConstructor] | ((ctx: Context) => Promise<Middleware>) | [(ctx: Context) => Promise<Middleware>, MiddlewareConstructor] | ((ctx: Context) => MiddlewareConstructor) | [(ctx: Context) => MiddlewareConstructor, MiddlewareConstructor] | ((ctx: Context) => Promise<MiddlewareConstructor>) | Middleware | MiddlewareConstructor; export async function createMiddleware( ctx: Context, middleware: MiddlewareItem, ): Promise<Middleware> { if (middleware instanceof Middleware) { return middleware; } else if (isMiddlewareConstructor(middleware)) { return await execConstructorHooks(ctx, middleware); } else if (Array.isArray(middleware)) { return createMiddleware(ctx, await middleware[0](ctx)); } else { return createMiddleware(ctx, await middleware(ctx)); } } export abstract class Middleware { #index!: number; #mds!: readonly MiddlewareItem[]; #ctx!: Context; public get ctx(): Context { return this.#ctx; } get req() { return this.#ctx.req; } get request() { return this.#ctx.req; } get res() { return this.#ctx.res; } get response() { return this.#ctx.response; } get logger() { return this.ctx.logger; } set logger(val) { this.ctx.logger = val; } public isPrevInstanceOf<T extends object = any>( target: ObjectConstructor<T>, ): target is ObjectConstructor<T> { const prevMd = this.#mds[this.#index - 1]; return this.#isInstanceOf(prevMd, target); } public isNextInstanceOf<T extends object = any>( target: ObjectConstructor<T>, ): target is ObjectConstructor<T> { const nextMd = this.#mds[this.#index + 1]; return this.#isInstanceOf(nextMd, target); } #isInstanceOf<T extends object = any>( md: MiddlewareItem | undefined, target: ObjectConstructor<T>, ) { if (!md) return false; if (md == target) return true; if (Array.isArray(md)) { return md[1] == target || md[1].prototype instanceof target; } else if (isClass(md)) { return md.prototype instanceof target; } else { return md instanceof target; } } abstract invoke(): void | Promise<void>; protected async next(): Promise<void> { let nextMd: Middleware | undefined = undefined; try { if (false == (await execHooks(this.ctx, HookType.BeforeNext, this))) { return; } if (this.#mds.length <= this.#index + 1) return; nextMd = await this.#createNextMiddleware(); nextMd.init(this.ctx, this.#index + 1, this.#mds); if (false == (await execHooks(this.ctx, HookType.BeforeInvoke, nextMd))) { return; } await nextMd.invoke(); await execHooks(this.ctx, HookType.AfterInvoke, nextMd); } catch (err) { const error = err as HalspException; if (isObject(error) && "breakthrough" in error && error.breakthrough) { throw error; } else { const md = nextMd ?? this; const hookResult = await execErrorHooks(this.ctx, md, error); if (!hookResult) { await execUnhandledHooks(this.ctx, md, error); } } } } #createNextMiddleware = async (): Promise<Middleware> => { const middleware = this.#mds[this.#index + 1]; return await createMiddleware(this.ctx, middleware); }; private init( ctx: Context, index: number, mds: readonly MiddlewareItem[], ): this { this.#mds = mds; this.#ctx = ctx; this.#index = index; return this; } } export async function invokeMiddlewares( ctx: Context, mds: MiddlewareItem[], isRoot = false, ) { const md = await createMiddleware(ctx, mds[0]); try { await (md as any).init(ctx, 0, mds).invoke(); } catch (err) { const error = err as HalspException; const hookResult = await execErrorHooks(ctx, md, error); if (isRoot && !hookResult) { await execUnhandledHooks(ctx, md, error); } } }