@gati-framework/runtime
Version:
Gati runtime execution engine for running handler-based applications
184 lines • 5.61 kB
JavaScript
/**
* @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