UNPKG

@akala/core

Version:
189 lines (173 loc) 6.81 kB
import { each } from '../each.js'; import { process, convertToErrorMiddleware, convertToMiddleware, isErrorMiddleware, isStandardMiddleware } from './shared.js'; import type { AnySyncMiddleware, Middleware, MiddlewareResult, OptionsResponse, SpecialNextParam } from './shared.js'; import { type ExtendableCompositeMiddleware } from './composite-sync.js'; /** * A composite middleware class with priority for synchronous operations. * @template T - The type of the arguments. * @template TSpecialNextParam - The type of the special next parameter. */ export class MiddlewareCompositeWithPriority<T extends unknown[], TSpecialNextParam extends string | undefined = SpecialNextParam> implements Middleware<T, TSpecialNextParam>, ExtendableCompositeMiddleware<T> { public readonly name?: string; constructor(name?: string) { this.name = name; } private readonly stack: (readonly [number, AnySyncMiddleware<T, TSpecialNextParam>])[] = []; /** * Adds middleware with a specified priority. * @param {number} priority - The priority of the middleware (the lowest first). * @param {...AnySyncMiddleware<T, TSpecialNextParam>} middlewares - The middlewares to add. * @returns {this} The instance of the middleware composite. */ public useMiddleware(priority: number, ...middlewares: AnySyncMiddleware<T, TSpecialNextParam>[]): this { this.stack.push(...middlewares.map(m => [priority, m] as const)); this.stack.sort((a, b) => a[0] - b[0]); return this; } /** * Adds standard middleware with a specified priority. * @param {number} priority - The priority of the middleware (the lowest first). * @param {...Function} middlewares - The middlewares to add. * @returns {this} The instance of the middleware composite. */ public use(priority: number, ...middlewares: ((...args: T) => unknown)[]): this { return this.useMiddleware(priority, ...middlewares.map(m => convertToMiddleware<T, TSpecialNextParam>(m))); } /** * Adds error middleware with a specified priority. * @param {number} priority - The priority of the middleware (the lowest first). * @param {...Function} middlewares - The middlewares to add. * @returns {this} The instance of the middleware composite. */ public useError(priority: number, ...middlewares: ((error: Error | OptionsResponse, ...args: T) => unknown)[]): this { return this.useMiddleware(priority, ...middlewares.map(m => convertToErrorMiddleware<T, TSpecialNextParam>(m))); } /** * Processes the middleware stack. * @param {...T} req - The request parameters. * @returns {X} The result of the middleware processing. */ public process<X = unknown>(...req: T): X { return process<X, T, TSpecialNextParam>(this, ...req); } /** * Handles errors in the middleware stack. * @param {Error | OptionsResponse} error - The error to handle. * @param {...T} req - The request parameters. * @returns {MiddlewareResult<TSpecialNextParam>} The result of the error handling. */ public handleError(error: Error | OptionsResponse, ...req: T): MiddlewareResult<TSpecialNextParam> { let failed: boolean = !!error; // this.stack.sort((a, b) => a[0] - b[0]); try { if (this.stack.length === 0) return error; each(this.stack, (middleware) => { try { if (failed && isErrorMiddleware(middleware[1])) { const err = middleware[1].handleError(error as Error, ...req); if (err === 'break') throw err; if (typeof err != 'string' && typeof err != 'undefined') error = err; failed = true; } } catch (x) { throw { success: x }; } }); return error; } catch (err) { switch (typeof err) { case 'string': return; case 'undefined': return error; default: throw err.success; } } } /** * Handles the middleware stack. * @param {...T} req - The request parameters. * @returns {MiddlewareResult<TSpecialNextParam>} The result of the middleware handling. */ public handle(...req: T): MiddlewareResult<TSpecialNextParam> { let error: Error | OptionsResponse = undefined; let failed: boolean = undefined; // this.stack.sort((a, b) => a[0] - b[0]); try { if (this.stack.length === 0) return error; each(this.stack, (middleware) => { if (failed && isErrorMiddleware(middleware[1])) { let err: MiddlewareResult<TSpecialNextParam>; try { err = middleware[1].handleError(error as Error, ...req); } catch (e) { throw { success: e }; } if (err === 'break') throw err; if (typeof err != 'string' && typeof err != 'undefined') error = err; failed = true; } else if (!failed && isStandardMiddleware(middleware[1])) { let err: MiddlewareResult<TSpecialNextParam>; try { err = middleware[1].handle(...req); } catch (e) { throw { success: e }; } if (err === 'break') throw err; if (typeof err != 'string' && typeof err != 'undefined') { if (err['allow']) error['allow'].push(...err['allow']); else error = err; } failed = err instanceof Error; } else return; }); return error; } catch (err) { switch (typeof err) { case 'string': return; case 'undefined': return error; default: throw err.success; } } } }