UNPKG

@gati-framework/runtime

Version:

Gati runtime execution engine for running handler-based applications

184 lines 5.61 kB
/** * @module runtime/middleware * @description Middleware pipeline management for Gati framework */ import { logger } from './logger.js'; /** * Middleware manager for organizing and executing middleware chain */ export class MiddlewareManager { middlewares = []; errorMiddlewares = []; /** * Register a middleware function * * @param middleware - Middleware function to register * @param options - Middleware configuration options */ use(middleware, options = {}) { const entry = { middleware, options: { path: options.path || '*', methods: options.methods || ['*'], priority: options.priority || 0, }, }; this.middlewares.push(entry); this.sortMiddlewares(); } /** * Register an error handling middleware * * @param middleware - Error middleware function */ useError(middleware) { this.errorMiddlewares.push(middleware); } /** * Execute middleware chain for a request * * @param req - HTTP request * @param res - HTTP response * @param gctx - Global context * @param lctx - Local context * @param handler - Final handler to execute after middleware */ async execute(req, res, gctx, lctx, handler) { const applicableMiddlewares = this.getApplicableMiddlewares(req); let currentIndex = 0; const next = async () => { if (currentIndex >= applicableMiddlewares.length) { // All middleware executed, run the handler await handler(); return; } const entry = applicableMiddlewares[currentIndex++]; if (entry) { await entry.middleware(req, res, gctx, lctx, next); } }; try { await next(); } catch (error) { await this.handleError(error, req, res, gctx, lctx); } } /** * Handle errors through error middleware chain */ async handleError(error, req, res, gctx, lctx) { for (const errorMiddleware of this.errorMiddlewares) { try { await errorMiddleware(error, req, res, gctx, lctx); // If response was sent, stop processing if (res.headersSent) { return; } } catch (middlewareError) { logger.error({ middlewareError }, 'Error in error middleware'); } } // If no error middleware handled it, send generic error if (!res.headersSent) { res.status(500).json({ error: 'Internal Server Error', message: error.message, }); } } /** * Get middlewares applicable to the current request */ getApplicableMiddlewares(req) { return this.middlewares.filter((entry) => { const { path, methods } = entry.options; // Check path pattern if (path && path !== '*' && !this.matchPath(req.path || '/', path)) { return false; } // Check HTTP method if (!methods?.includes('*') && !methods?.includes(req.method)) { return false; } return true; }); } /** * Match request path against middleware path pattern * Supports: * - Exact match: '/api/users' * - Wildcard: '/api/*' matches '/api/anything' * - Parameters: '/api/:version/users' matches '/api/v1/users' */ matchPath(requestPath, pattern) { // Wildcard match - matches everything if (pattern === '*') { return true; } // Exact match if (pattern === requestPath) { return true; } // Prefix wildcard: /api/* matches /api/anything if (pattern.endsWith('/*')) { const base = pattern.slice(0, -2); return requestPath.startsWith(base); } // Convert pattern to regex for parameter matching const regex = this.patternToRegex(pattern); return regex.test(requestPath); } /** * Convert path pattern to regex * Supports :param syntax and wildcards */ patternToRegex(pattern) { const escaped = pattern // Escape special regex chars except * and : .replace(/[.+?^${}()|[\]\\]/g, '\\$&') // Convert * to match anything .replace(/\*/g, '.*') // Convert :param to named capture group .replace(/:(\w+)/g, '([^/]+)'); return new RegExp(`^${escaped}$`); } /** * Sort middlewares by priority (highest first) */ sortMiddlewares() { this.middlewares.sort((a, b) => { const priorityA = a.options.priority || 0; const priorityB = b.options.priority || 0; return priorityB - priorityA; }); } /** * Get all registered middlewares */ getMiddlewares() { return [...this.middlewares]; } /** * Clear all middlewares */ clear() { this.middlewares = []; this.errorMiddlewares = []; } /** * Get number of registered middlewares */ size() { return this.middlewares.length; } } /** * Create a new middleware manager instance */ export function createMiddlewareManager() { return new MiddlewareManager(); } //# sourceMappingURL=middleware.js.map