UNPKG

honion

Version:
131 lines (119 loc) 3.42 kB
import { Context } from "../context"; import { Middleware, MiddlewareConstructor } from "./middleware"; const MIDDLEWARE_HOOK_BAG = "@hal-wang/honion/middlewareHooksBag"; export type MdHook< TC extends Context, TM extends Middleware<TC>, U extends TM | MiddlewareConstructor<TC> | Error = any > = ( ctx: TC, md: U, error?: Error ) => | void | Promise<void | boolean> | TM | undefined | Promise<TM | undefined> | boolean | Promise<boolean>; export enum HookType { BeforeInvoke, AfterInvoke, BeforeNext, Constructor, Error, } export interface HookItem<TC extends Context, TM extends Middleware<TC>> { hook: MdHook<TC, TM>; type: HookType; } export class HookMiddleware< TC extends Context, TM extends Middleware<TC> > extends Middleware<TC> { constructor(mh: MdHook<TC, TM>, type: HookType) { super(); this.#mh = mh; this.#type = type; } #mh: MdHook<TC, TM>; #type: HookType; async invoke(): Promise<void> { const hooks = this.ctx.get<HookItem<TC, TM>[]>(MIDDLEWARE_HOOK_BAG) ?? []; hooks.push({ hook: this.#mh, type: this.#type }); this.ctx.set(MIDDLEWARE_HOOK_BAG, hooks); await this.next(); } } export async function execHooks( ctx: Context, middleware: Middleware, type: HookType.Error, error: Error ): Promise<boolean>; export async function execHooks( ctx: Context, middleware: MiddlewareConstructor, type: HookType.Constructor ): Promise<Middleware>; export async function execHooks( ctx: Context, middleware: Middleware, type: HookType.BeforeInvoke | HookType.BeforeNext ): Promise<boolean | void>; export async function execHooks( ctx: Context, middleware: Middleware, type: HookType.AfterInvoke ): Promise<void>; export async function execHooks( ctx: Context, middleware: Middleware | MiddlewareConstructor, type: HookType, error?: Error ): Promise<Middleware | void | boolean> { if (type == HookType.Constructor) { return await execConstructorHooks(ctx, middleware as MiddlewareConstructor); } else if (type == HookType.Error) { return await execErrorHooks(ctx, middleware as Middleware, error as Error); } const hooks = ctx.get<HookItem<Context, Middleware>[]>(MIDDLEWARE_HOOK_BAG) ?? []; for (const hookItem of hooks.filter((h) => h.type == type)) { const hookResult = await hookItem.hook(ctx, middleware); if (typeof hookResult == "boolean" && !hookResult) { return false; } } } async function execErrorHooks( ctx: Context, middleware: Middleware, error: Error ): Promise<boolean> { const hooks = ctx.get<HookItem<Context, Middleware>[]>(MIDDLEWARE_HOOK_BAG) ?? []; let result = false; for (const hookItem of hooks.filter((h) => h.type == HookType.Error)) { result = (await hookItem.hook(ctx, middleware, error)) as boolean; if (result) break; } return result; } async function execConstructorHooks( ctx: Context, middleware: MiddlewareConstructor ): Promise<Middleware | undefined> { const hooks = ctx.get<HookItem<Context, Middleware>[]>(MIDDLEWARE_HOOK_BAG) ?? []; let result: Middleware | undefined; for (const hookItem of hooks.filter((h) => h.type == HookType.Constructor)) { if (!(middleware instanceof Middleware)) { result = (await hookItem.hook(ctx, middleware)) as Middleware; if (result) break; } } if (!result) result = new middleware(); return result; }