UNPKG

nextrush

Version:

🎓 LEARNING PROJECT: My first attempt at building a Node.js web framework with Express-compatible API. Educational purposes only - NOT recommended for production use. Features TypeScript, zero dependencies, and plugin architecture.

1,632 lines (1,623 loc) • 459 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/errors/custom-errors.ts var custom_errors_exports = {}; __export(custom_errors_exports, { BadGatewayError: () => BadGatewayError, ConflictError: () => ConflictError, ConnectionRefusedError: () => ConnectionRefusedError, DatabaseConnectionError: () => DatabaseConnectionError, DatabaseError: () => DatabaseError, ErrorCategory: () => ErrorCategory, ErrorFactory: () => ErrorFactory, ErrorSeverity: () => ErrorSeverity, FileNotFoundError: () => FileNotFoundError, FilePermissionError: () => FilePermissionError, ForbiddenError: () => ForbiddenError, GatewayTimeoutError: () => GatewayTimeoutError, InternalServerError: () => InternalServerError, MethodNotAllowedError: () => MethodNotAllowedError, NetworkError: () => NetworkError, NextRushError: () => NextRushError, NotFoundError: () => NotFoundError, NotImplementedError: () => NotImplementedError, PayloadTooLargeError: () => PayloadTooLargeError, PluginError: () => PluginError, PluginNotFoundError: () => PluginNotFoundError, RequestTimeoutError: () => RequestTimeoutError, ServiceUnavailableError: () => ServiceUnavailableError, TooManyRequestsError: () => TooManyRequestsError, UnauthorizedError: () => UnauthorizedError, UnsupportedMediaTypeError: () => UnsupportedMediaTypeError, ValidationError: () => ValidationError }); var ErrorSeverity, ErrorCategory, NextRushError, ValidationError, NotFoundError, UnauthorizedError, ForbiddenError, MethodNotAllowedError, ConflictError, RequestTimeoutError, PayloadTooLargeError, UnsupportedMediaTypeError, TooManyRequestsError, InternalServerError, NotImplementedError, BadGatewayError, ServiceUnavailableError, GatewayTimeoutError, DatabaseError, DatabaseConnectionError, FileNotFoundError, FilePermissionError, NetworkError, ConnectionRefusedError, PluginError, PluginNotFoundError, ErrorFactory; var init_custom_errors = __esm({ "src/errors/custom-errors.ts"() { "use strict"; ErrorSeverity = /* @__PURE__ */ ((ErrorSeverity2) => { ErrorSeverity2["LOW"] = "low"; ErrorSeverity2["MEDIUM"] = "medium"; ErrorSeverity2["HIGH"] = "high"; ErrorSeverity2["CRITICAL"] = "critical"; return ErrorSeverity2; })(ErrorSeverity || {}); ErrorCategory = /* @__PURE__ */ ((ErrorCategory2) => { ErrorCategory2["VALIDATION"] = "validation"; ErrorCategory2["AUTHENTICATION"] = "authentication"; ErrorCategory2["AUTHORIZATION"] = "authorization"; ErrorCategory2["NETWORK"] = "network"; ErrorCategory2["DATABASE"] = "database"; ErrorCategory2["FILESYSTEM"] = "filesystem"; ErrorCategory2["BUSINESS_LOGIC"] = "business_logic"; ErrorCategory2["SYSTEM"] = "system"; ErrorCategory2["EXTERNAL_SERVICE"] = "external_service"; return ErrorCategory2; })(ErrorCategory || {}); NextRushError = class _NextRushError extends Error { static { __name(this, "NextRushError"); } code; statusCode; details; timestamp; // Use timestamp for performance severity; category; correlationId; retryable; // 🚀 Static error pool for memory optimization static errorPool = /* @__PURE__ */ new Map(); static maxPoolSize = 1e3; constructor(message, code, statusCode = 500, details, severity = "medium" /* MEDIUM */, category = "system" /* SYSTEM */, retryable = false, correlationId) { super(message); this.name = this.constructor.name; this.code = code; this.statusCode = statusCode; this.details = Object.freeze(details ?? {}); this.timestamp = Date.now(); this.severity = severity; this.category = category; this.retryable = retryable; this.correlationId = correlationId || this.generateCorrelationId(); if (this.shouldCaptureStack()) { Error.captureStackTrace?.(this, this.constructor); } } /** * 🚀 High-performance JSON serialization */ toJSON() { const result = { name: this.name, message: this.message, code: this.code, statusCode: this.statusCode, details: this.details, timestamp: this.timestamp, severity: this.severity, category: this.category, retryable: this.retryable, correlationId: this.correlationId }; if (this.shouldIncludeStack()) { result.stack = this.stack; } return result; } /** * 🚀 Smart stack capture decision */ shouldCaptureStack() { return process.env.NODE_ENV !== "production" || this.severity === "critical" /* CRITICAL */ || this.statusCode >= 500; } /** * 🚀 Smart stack inclusion decision */ shouldIncludeStack() { return process.env.NODE_ENV === "development" || this.severity === "critical" /* CRITICAL */; } /** * 🚀 Generate correlation ID for error tracking */ generateCorrelationId() { return `err_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } /** * 🚀 Static factory method with error pooling */ static create(...args) { const key = `${this.name}_${args.join("_")}`; if (_NextRushError.errorPool.has(key) && _NextRushError.errorPool.size < _NextRushError.maxPoolSize) { return _NextRushError.errorPool.get(key); } const error = new this(...args); if (_NextRushError.errorPool.size < _NextRushError.maxPoolSize) { _NextRushError.errorPool.set(key, error); } return error; } /** * 🚀 Check if error is retryable */ isRetryable() { return this.retryable; } /** * 🚀 Check if error is critical */ isCritical() { return this.severity === "critical" /* CRITICAL */; } /** * 🚀 Get user-friendly message (sanitized for production) */ getUserMessage() { if (process.env.NODE_ENV === "production" && this.statusCode >= 500) { return "An internal error occurred. Please try again later."; } return this.message; } }; ValidationError = class _ValidationError extends NextRushError { static { __name(this, "ValidationError"); } constructor(message, details, correlationId) { super( message, "VALIDATION_ERROR", 400, details, "low" /* LOW */, "validation" /* VALIDATION */, false, // Not retryable correlationId ); } static field(fieldName, value, expected, correlationId) { return new _ValidationError( `Invalid field '${fieldName}': expected ${expected}`, { field: fieldName, value, expected }, correlationId ); } static schema(errors, correlationId) { return new _ValidationError( "Schema validation failed", { errors, count: errors.length }, correlationId ); } }; NotFoundError = class extends NextRushError { static { __name(this, "NotFoundError"); } constructor(resource = "Resource", identifier, correlationId) { super( `${resource} not found${identifier ? ` (ID: ${identifier})` : ""}`, "NOT_FOUND", 404, { resource, identifier }, "low" /* LOW */, "business_logic" /* BUSINESS_LOGIC */, false, correlationId ); } }; UnauthorizedError = class extends NextRushError { static { __name(this, "UnauthorizedError"); } constructor(message = "Authentication required", correlationId) { super( message, "UNAUTHORIZED", 401, {}, "medium" /* MEDIUM */, "authentication" /* AUTHENTICATION */, false, correlationId ); } }; ForbiddenError = class extends NextRushError { static { __name(this, "ForbiddenError"); } constructor(resource, action, correlationId) { const message = resource && action ? `Forbidden: Cannot ${action} ${resource}` : "Access forbidden"; super( message, "FORBIDDEN", 403, { resource, action }, "medium" /* MEDIUM */, "authorization" /* AUTHORIZATION */, false, correlationId ); } }; MethodNotAllowedError = class extends NextRushError { static { __name(this, "MethodNotAllowedError"); } constructor(method, allowedMethods = [], correlationId) { super( `Method ${method} not allowed`, "METHOD_NOT_ALLOWED", 405, { method, allowedMethods }, "low" /* LOW */, "network" /* NETWORK */, false, correlationId ); } }; ConflictError = class extends NextRushError { static { __name(this, "ConflictError"); } constructor(message, resource, correlationId) { super( message, "CONFLICT", 409, { resource }, "medium" /* MEDIUM */, "business_logic" /* BUSINESS_LOGIC */, false, correlationId ); } }; RequestTimeoutError = class extends NextRushError { static { __name(this, "RequestTimeoutError"); } constructor(timeout, correlationId) { super( `Request timeout after ${timeout}ms`, "REQUEST_TIMEOUT", 408, { timeout }, "medium" /* MEDIUM */, "network" /* NETWORK */, true, // Retryable correlationId ); } }; PayloadTooLargeError = class extends NextRushError { static { __name(this, "PayloadTooLargeError"); } constructor(maxSize, actualSize, correlationId) { super( `Payload too large. Max: ${maxSize} bytes${actualSize ? `, received: ${actualSize} bytes` : ""}`, "PAYLOAD_TOO_LARGE", 413, { maxSize, actualSize }, "low" /* LOW */, "validation" /* VALIDATION */, false, correlationId ); } }; UnsupportedMediaTypeError = class extends NextRushError { static { __name(this, "UnsupportedMediaTypeError"); } constructor(contentType, supportedTypes = [], correlationId) { super( `Unsupported content type: ${contentType}`, "UNSUPPORTED_MEDIA_TYPE", 415, { contentType, supportedTypes }, "low" /* LOW */, "validation" /* VALIDATION */, false, correlationId ); } }; TooManyRequestsError = class extends NextRushError { static { __name(this, "TooManyRequestsError"); } constructor(retryAfter, correlationId) { super( "Too many requests", "TOO_MANY_REQUESTS", 429, { retryAfter }, "medium" /* MEDIUM */, "network" /* NETWORK */, true, // Retryable after delay correlationId ); } }; InternalServerError = class extends NextRushError { static { __name(this, "InternalServerError"); } constructor(message = "Internal server error", details, correlationId) { super( message, "INTERNAL_SERVER_ERROR", 500, details, "high" /* HIGH */, "system" /* SYSTEM */, true, // May be retryable correlationId ); } }; NotImplementedError = class extends NextRushError { static { __name(this, "NotImplementedError"); } constructor(feature, correlationId) { super( `Feature not implemented: ${feature}`, "NOT_IMPLEMENTED", 501, { feature }, "medium" /* MEDIUM */, "system" /* SYSTEM */, false, correlationId ); } }; BadGatewayError = class extends NextRushError { static { __name(this, "BadGatewayError"); } constructor(service, correlationId) { super( `Bad gateway${service ? ` from ${service}` : ""}`, "BAD_GATEWAY", 502, { service }, "high" /* HIGH */, "external_service" /* EXTERNAL_SERVICE */, true, // Retryable correlationId ); } }; ServiceUnavailableError = class extends NextRushError { static { __name(this, "ServiceUnavailableError"); } constructor(retryAfter, correlationId) { super( "Service temporarily unavailable", "SERVICE_UNAVAILABLE", 503, { retryAfter }, "high" /* HIGH */, "system" /* SYSTEM */, true, // Retryable correlationId ); } }; GatewayTimeoutError = class extends NextRushError { static { __name(this, "GatewayTimeoutError"); } constructor(timeout, service, correlationId) { super( `Gateway timeout after ${timeout}ms${service ? ` from ${service}` : ""}`, "GATEWAY_TIMEOUT", 504, { timeout, service }, "high" /* HIGH */, "external_service" /* EXTERNAL_SERVICE */, true, // Retryable correlationId ); } }; DatabaseError = class extends NextRushError { static { __name(this, "DatabaseError"); } constructor(message, operation, table, correlationId) { super( message, "DATABASE_ERROR", 500, { operation, table }, "high" /* HIGH */, "database" /* DATABASE */, true, // May be retryable correlationId ); } }; DatabaseConnectionError = class extends NextRushError { static { __name(this, "DatabaseConnectionError"); } constructor(database, host, correlationId) { super( `Failed to connect to database: ${database}`, "DATABASE_CONNECTION_ERROR", 503, { database, host }, "critical" /* CRITICAL */, "database" /* DATABASE */, true, // Retryable correlationId ); } }; FileNotFoundError = class extends NextRushError { static { __name(this, "FileNotFoundError"); } constructor(filePath, correlationId) { super( `File not found: ${filePath}`, "FILE_NOT_FOUND", 404, { filePath }, "medium" /* MEDIUM */, "filesystem" /* FILESYSTEM */, false, correlationId ); } }; FilePermissionError = class extends NextRushError { static { __name(this, "FilePermissionError"); } constructor(filePath, operation, correlationId) { super( `Permission denied: Cannot ${operation} file ${filePath}`, "FILE_PERMISSION_ERROR", 403, { filePath, operation }, "medium" /* MEDIUM */, "filesystem" /* FILESYSTEM */, false, correlationId ); } }; NetworkError = class extends NextRushError { static { __name(this, "NetworkError"); } constructor(message, host, port, correlationId) { super( message, "NETWORK_ERROR", 502, { host, port }, "high" /* HIGH */, "network" /* NETWORK */, true, // Retryable correlationId ); } }; ConnectionRefusedError = class extends NextRushError { static { __name(this, "ConnectionRefusedError"); } constructor(host, port, correlationId) { super( `Connection refused to ${host}:${port}`, "CONNECTION_REFUSED", 502, { host, port }, "high" /* HIGH */, "network" /* NETWORK */, true, // Retryable correlationId ); } }; PluginError = class extends NextRushError { static { __name(this, "PluginError"); } constructor(pluginName, message, details, correlationId) { super( `Plugin ${pluginName}: ${message}`, "PLUGIN_ERROR", 500, { plugin: pluginName, ...details }, "high" /* HIGH */, "system" /* SYSTEM */, false, correlationId ); } }; PluginNotFoundError = class extends NextRushError { static { __name(this, "PluginNotFoundError"); } constructor(pluginName, correlationId) { super( `Plugin not found: ${pluginName}`, "PLUGIN_NOT_FOUND", 404, { plugin: pluginName }, "medium" /* MEDIUM */, "system" /* SYSTEM */, false, correlationId ); } }; ErrorFactory = class { static { __name(this, "ErrorFactory"); } /** * Create error with automatic correlation ID */ static create(ErrorClass, ...args) { return new ErrorClass(...args); } /** * Convert unknown error to NextRushError */ static normalize(error, correlationId) { if (error instanceof NextRushError) { return error; } if (error instanceof Error) { return new InternalServerError( error.message, { originalError: error.name, originalStack: error.stack }, correlationId ); } return new InternalServerError( "Unknown error occurred", { originalError: String(error) }, correlationId ); } /** * Check if error is retryable */ static isRetryable(error) { return error instanceof NextRushError && error.isRetryable(); } /** * Get retry delay for retryable errors */ static getRetryDelay(error, attempt = 1) { if (!error.isRetryable()) return 0; const baseDelay = 1e3; const maxDelay = 3e4; const delay = Math.min(baseDelay * Math.pow(2, attempt - 1), maxDelay); const jitter = Math.random() * 0.1 * delay; return Math.round(delay + jitter); } }; } }); // src/core/event-system.ts var SimpleEventEmitter, ApplicationEventSystem, globalEventSystem; var init_event_system = __esm({ "src/core/event-system.ts"() { "use strict"; SimpleEventEmitter = class { static { __name(this, "SimpleEventEmitter"); } events = /* @__PURE__ */ new Map(); maxListeners = 10; /** * Add event listener */ on(event, callback) { if (!this.events.has(event)) { this.events.set(event, []); } const listeners = this.events.get(event); if (listeners.length >= this.maxListeners) { console.warn( `MaxListenersExceededWarning: Possible memory leak detected. ${listeners.length + 1} ${event} listeners added.` ); } listeners.push(callback); return this; } /** * Add one-time event listener */ once(event, callback) { const onceWrapper = /* @__PURE__ */ __name((data) => { this.off(event, onceWrapper); callback(data); }, "onceWrapper"); return this.on(event, onceWrapper); } /** * Remove event listener */ off(event, callback) { const listeners = this.events.get(event); if (!listeners) return this; const index = listeners.indexOf(callback); if (index !== -1) { listeners.splice(index, 1); } if (listeners.length === 0) { this.events.delete(event); } return this; } /** * Remove all listeners for event or all events */ removeAllListeners(event) { if (event) { this.events.delete(event); } else { this.events.clear(); } return this; } /** * Emit event */ emit(event, data) { const listeners = this.events.get(event); if (!listeners || listeners.length === 0) { return false; } for (const listener of listeners) { try { const result = listener(data); if (result && typeof result.then === "function") { result.catch((error) => { console.error( `Error in async event listener for '${event}':`, error ); }); } } catch (error) { console.error(`Error in event listener for '${event}':`, error); } } return true; } /** * Get listener count for event */ listenerCount(event) { const listeners = this.events.get(event); return listeners ? listeners.length : 0; } /** * Get all event names */ eventNames() { return Array.from(this.events.keys()); } /** * Get listeners for event */ listeners(event) { return this.events.get(event) ? [...this.events.get(event)] : []; } /** * Set max listeners */ setMaxListeners(max) { this.maxListeners = max; return this; } /** * Get max listeners */ getMaxListeners() { return this.maxListeners; } }; ApplicationEventSystem = class extends SimpleEventEmitter { static { __name(this, "ApplicationEventSystem"); } requestCounter = 0; constructor() { super(); this.setMaxListeners(50); } /** * Generate unique request ID */ generateRequestId() { return `req_${Date.now()}_${++this.requestCounter}`; } /** * Emit request start event */ emitRequestStart(data) { const requestId2 = this.generateRequestId(); const eventData = { ...data, id: requestId2, timestamp: Date.now() }; this.emit("request:start", eventData); this.emit("request", eventData); return requestId2; } /** * Emit request end event */ emitRequestEnd(data) { const eventData = { ...data, timestamp: Date.now() }; this.emit("request:end", eventData); this.emit("response", eventData); } /** * Emit error event */ emitError(error, requestId2, context) { const baseData = { error, id: `err_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, timestamp: Date.now() }; const eventData = requestId2 ? { ...baseData, requestId: requestId2, context } : { ...baseData, context }; this.emit("error", eventData); if (requestId2) { this.emit("request:error", eventData); } } /** * Emit application start event */ emitAppStart(data) { this.emit("app:start", { ...data, timestamp: Date.now(), id: `app_start_${Date.now()}` }); } /** * Emit application stop event */ emitAppStop() { this.emit("app:stop", { timestamp: Date.now(), id: `app_stop_${Date.now()}` }); } /** * Emit middleware event */ emitMiddleware(name, requestId2, duration) { this.emit("middleware", { name, requestId: requestId2, duration, timestamp: Date.now(), id: `mw_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` }); } /** * Emit route match event */ emitRouteMatch(route, method, requestId2) { this.emit("route:match", { route, method, requestId: requestId2, timestamp: Date.now(), id: `route_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` }); } }; globalEventSystem = new ApplicationEventSystem(); } }); // src/plugins/core/base-plugin.ts var BasePlugin; var init_base_plugin = __esm({ "src/plugins/core/base-plugin.ts"() { "use strict"; BasePlugin = class { static { __name(this, "BasePlugin"); } registry; constructor(registry) { this.registry = registry; } /** * Emit events to other plugins */ emit(event, ...args) { this.registry.emit(event, ...args); } /** * Listen to events from other plugins */ on(event, listener) { this.registry.on(event, listener); } /** * Remove event listener */ off(event, listener) { this.registry.off(event, listener); } }; } }); // src/plugins/metrics/formatter.ts var PrometheusFormatter; var init_formatter = __esm({ "src/plugins/metrics/formatter.ts"() { "use strict"; PrometheusFormatter = class { constructor(prefix = "nextrush_") { this.prefix = prefix; } static { __name(this, "PrometheusFormatter"); } /** * Format all metrics in Prometheus format */ format(storage, requestMetrics, systemMetrics) { const lines = []; this.addRequestMetrics(lines, requestMetrics); this.addSystemMetrics(lines, systemMetrics); this.addCustomMetrics(lines, storage); return lines.join("\n") + "\n"; } /** * Add HTTP request metrics */ addRequestMetrics(lines, metrics) { lines.push( `# HELP ${this.prefix}http_requests_total Total number of HTTP requests`, `# TYPE ${this.prefix}http_requests_total counter`, `${this.prefix}http_requests_total ${metrics.total}` ); lines.push( `# HELP ${this.prefix}http_requests_active Number of active HTTP requests`, `# TYPE ${this.prefix}http_requests_active gauge`, `${this.prefix}http_requests_active ${metrics.active}` ); lines.push( `# HELP ${this.prefix}http_requests_by_method_total HTTP requests by method`, `# TYPE ${this.prefix}http_requests_by_method_total counter` ); for (const [method, count] of Object.entries(metrics.byMethod)) { lines.push( `${this.prefix}http_requests_by_method_total{method="${method}"} ${count}` ); } lines.push( `# HELP ${this.prefix}http_requests_by_status_total HTTP requests by status code`, `# TYPE ${this.prefix}http_requests_by_status_total counter` ); for (const [status, count] of Object.entries(metrics.byStatus)) { lines.push( `${this.prefix}http_requests_by_status_total{status="${status}"} ${count}` ); } lines.push( `# HELP ${this.prefix}http_response_time_seconds HTTP response time percentiles`, `# TYPE ${this.prefix}http_response_time_seconds gauge`, `${this.prefix}http_response_time_seconds{quantile="0.95"} ${metrics.p95ResponseTime / 1e3}`, `${this.prefix}http_response_time_seconds{quantile="0.99"} ${metrics.p99ResponseTime / 1e3}` ); lines.push( `# HELP ${this.prefix}http_errors_total Total number of HTTP errors`, `# TYPE ${this.prefix}http_errors_total counter`, `${this.prefix}http_errors_total ${metrics.errors}` ); } /** * Add system metrics */ addSystemMetrics(lines, metrics) { lines.push( `# HELP ${this.prefix}uptime_seconds Application uptime in seconds`, `# TYPE ${this.prefix}uptime_seconds counter`, `${this.prefix}uptime_seconds ${Math.floor(metrics.uptime / 1e3)}` ); lines.push( `# HELP ${this.prefix}memory_usage_bytes Memory usage in bytes`, `# TYPE ${this.prefix}memory_usage_bytes gauge`, `${this.prefix}memory_usage_bytes{type="used"} ${metrics.memory.used}`, `${this.prefix}memory_usage_bytes{type="total"} ${metrics.memory.total}`, `${this.prefix}memory_usage_bytes{type="heap_used"} ${metrics.memory.heap.heapUsed}`, `${this.prefix}memory_usage_bytes{type="heap_total"} ${metrics.memory.heap.heapTotal}` ); lines.push( `# HELP ${this.prefix}cpu_usage_percent CPU usage percentage`, `# TYPE ${this.prefix}cpu_usage_percent gauge`, `${this.prefix}cpu_usage_percent ${metrics.cpu.usage}` ); if (metrics.cpu.load.length > 0) { lines.push( `# HELP ${this.prefix}load_average System load average`, `# TYPE ${this.prefix}load_average gauge` ); metrics.cpu.load.forEach((load, index) => { const period = index === 0 ? "1m" : index === 1 ? "5m" : "15m"; lines.push(`${this.prefix}load_average{period="${period}"} ${load}`); }); } lines.push( `# HELP ${this.prefix}process_uptime_seconds Process uptime in seconds`, `# TYPE ${this.prefix}process_uptime_seconds counter`, `${this.prefix}process_uptime_seconds ${metrics.process.uptime}` ); } /** * Add custom metrics */ addCustomMetrics(lines, storage) { const metricNames = storage.getMetricNames(); for (const name of metricNames) { if (name.startsWith("_internal_")) continue; const metrics = storage.getMetrics(name); if (metrics.length === 0) continue; lines.push( `# HELP ${this.prefix}${name} Custom metric: ${name}`, `# TYPE ${this.prefix}${name} gauge` ); for (const metric of metrics) { const labels = this.formatLabels(metric.labels); const labelStr = labels ? `{${labels}}` : ""; lines.push(`${this.prefix}${name}${labelStr} ${metric.value}`); } } } /** * Format labels for Prometheus */ formatLabels(labels) { if (!labels || Object.keys(labels).length === 0) { return ""; } return Object.entries(labels).sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => `${key}="${this.escapeValue(value)}"`).join(","); } /** * Escape label values for Prometheus */ escapeValue(value) { return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n"); } }; } }); // src/plugins/metrics/health.ts var HealthCheckManager; var init_health = __esm({ "src/plugins/metrics/health.ts"() { "use strict"; HealthCheckManager = class { static { __name(this, "HealthCheckManager"); } healthChecks = /* @__PURE__ */ new Map(); startTime = Date.now(); /** * Register a health check */ register(name, check) { this.healthChecks.set(name, check); } /** * Remove a health check */ remove(name) { this.healthChecks.delete(name); } /** * Run all health checks and get status */ async getStatus() { const checks = {}; let overallStatus = "healthy"; const checkPromises = Array.from(this.healthChecks.entries()).map( async ([name, check]) => { const startTime = Date.now(); try { const result = await Promise.race([ check(), this.timeout(5e3) // 5 second timeout ]); const checkResult = { status: result.status, duration: Date.now() - startTime, timestamp: Date.now(), ...result.message && { message: result.message } }; checks[name] = checkResult; if (result.status === "fail") { overallStatus = "unhealthy"; } else if (result.status === "warn" && overallStatus === "healthy") { overallStatus = "degraded"; } } catch (error) { checks[name] = { status: "fail", message: error instanceof Error ? error.message : "Health check failed", duration: Date.now() - startTime, timestamp: Date.now() }; overallStatus = "unhealthy"; } } ); await Promise.all(checkPromises); return { status: overallStatus, timestamp: Date.now(), uptime: Date.now() - this.startTime, checks }; } /** * Get registered health check names */ getCheckNames() { return Array.from(this.healthChecks.keys()); } /** * Check if a health check exists */ hasCheck(name) { return this.healthChecks.has(name); } /** * Clear all health checks */ clear() { this.healthChecks.clear(); } /** * Create timeout promise for health checks */ timeout(ms) { return new Promise((_, reject) => { setTimeout( () => reject(new Error(`Health check timed out after ${ms}ms`)), ms ); }); } }; } }); // src/plugins/metrics/monitor.ts var os, SystemMonitor; var init_monitor = __esm({ "src/plugins/metrics/monitor.ts"() { "use strict"; os = __toESM(require("os")); SystemMonitor = class { static { __name(this, "SystemMonitor"); } startTime = Date.now(); lastCpuUsage = process.cpuUsage(); lastCpuTime = Date.now(); /** * Get current system metrics */ getMetrics() { return { uptime: Date.now() - this.startTime, memory: this.getMemoryMetrics(), cpu: this.getCpuMetrics(), process: this.getProcessMetrics() }; } /** * Get memory metrics */ getMemoryMetrics() { const memUsage = process.memoryUsage(); const totalMem = os.totalmem(); const freeMem = os.freemem(); const usedMem = totalMem - freeMem; return { used: usedMem, total: totalMem, heap: memUsage }; } /** * Get CPU metrics */ getCpuMetrics() { const usage = this.calculateCpuUsage(); const load = os.loadavg(); return { usage, load }; } /** * Get process metrics */ getProcessMetrics() { return { pid: process.pid, uptime: process.uptime() }; } /** * Calculate CPU usage percentage */ calculateCpuUsage() { const currentUsage = process.cpuUsage(this.lastCpuUsage); const currentTime = Date.now(); const timeDiff = currentTime - this.lastCpuTime; if (timeDiff === 0) return 0; const totalCpuTime = (currentUsage.user + currentUsage.system) / 1e3; const usage = totalCpuTime / timeDiff * 100; this.lastCpuUsage = process.cpuUsage(); this.lastCpuTime = currentTime; return Math.min(Math.max(usage, 0), 100); } /** * Reset monitoring */ reset() { this.startTime = Date.now(); this.lastCpuUsage = process.cpuUsage(); this.lastCpuTime = Date.now(); } }; } }); // src/plugins/metrics/storage.ts var MetricsStorage; var init_storage = __esm({ "src/plugins/metrics/storage.ts"() { "use strict"; MetricsStorage = class { static { __name(this, "MetricsStorage"); } metrics = /* @__PURE__ */ new Map(); responseTimes = []; responseTimeIndex = 0; maxResponseTimes = 1e3; lastCleanup = Date.now(); /** * Store a metric value */ setMetric(name, value, labels) { const key = this.createMetricKey(name, labels); const metricMap = this.metrics.get(name) || /* @__PURE__ */ new Map(); metricMap.set(key, { value, labels, timestamp: Date.now() }); this.metrics.set(name, metricMap); } /** * Increment a counter metric */ incrementCounter(name, labels, value = 1) { const key = this.createMetricKey(name, labels); const metricMap = this.metrics.get(name) || /* @__PURE__ */ new Map(); const existing = metricMap.get(key) || { value: 0, labels, timestamp: Date.now() }; existing.value += value; existing.timestamp = Date.now(); metricMap.set(key, existing); this.metrics.set(name, metricMap); } /** * Get metric value */ getMetric(name, labels) { const key = this.createMetricKey(name, labels); const metricMap = this.metrics.get(name); return metricMap?.get(key); } /** * Get all metrics for a name */ getMetrics(name) { const metricMap = this.metrics.get(name); return metricMap ? Array.from(metricMap.values()) : []; } /** * Get all metric names */ getMetricNames() { return Array.from(this.metrics.keys()); } /** * Add response time for percentile calculation */ addResponseTime(time) { this.responseTimes[this.responseTimeIndex] = time; this.responseTimeIndex = (this.responseTimeIndex + 1) % this.maxResponseTimes; } /** * Calculate response time percentiles */ getPercentiles() { const times = this.responseTimes.filter((t) => t > 0).sort((a, b) => a - b); if (times.length === 0) return { p95: 0, p99: 0 }; const p95Index = Math.floor(times.length * 0.95); const p99Index = Math.floor(times.length * 0.99); return { p95: times[p95Index] || 0, p99: times[p99Index] || 0 }; } /** * Clean up old metrics */ cleanup(maxAge = 5 * 60 * 1e3) { const now = Date.now(); if (now - this.lastCleanup < 6e4) return; for (const [metricName, metricMap] of this.metrics.entries()) { for (const [key, metric] of metricMap.entries()) { if (now - metric.timestamp > maxAge) { metricMap.delete(key); } } if (metricMap.size === 0) { this.metrics.delete(metricName); } } this.lastCleanup = now; } /** * Get storage statistics */ getStats() { let metricCount = 0; for (const metricMap of this.metrics.values()) { metricCount += metricMap.size; } const memoryUsage = metricCount * 200; return { metricCount, memoryUsage }; } /** * Clear all metrics */ clear() { this.metrics.clear(); this.responseTimes.fill(0); this.responseTimeIndex = 0; } /** * Create unique metric key from name and labels */ createMetricKey(name, labels) { if (!labels || Object.keys(labels).length === 0) { return name; } const labelStr = Object.entries(labels).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}="${v}"`).join(","); return `${name}{${labelStr}}`; } }; } }); // src/plugins/metrics/metrics.plugin.ts var metrics_plugin_exports = {}; __export(metrics_plugin_exports, { MetricsPlugin: () => MetricsPlugin }); var MetricsPlugin; var init_metrics_plugin = __esm({ "src/plugins/metrics/metrics.plugin.ts"() { "use strict"; init_base_plugin(); init_formatter(); init_health(); init_monitor(); init_storage(); MetricsPlugin = class extends BasePlugin { static { __name(this, "MetricsPlugin"); } name = "Metrics"; // Core components storage = new MetricsStorage(); healthManager = new HealthCheckManager(); formatter; monitor = new SystemMonitor(); // Simple state tracking requestMetrics = { total: 0, active: 0, byMethod: {}, byStatus: {}, averageResponseTime: 0, errors: 0, p95ResponseTime: 0, p99ResponseTime: 0 }; // Options with defaults options = { endpoint: "/metrics", enableHealthCheck: true, collectDefaultMetrics: true, prefix: "nextrush_", defaultLabels: {}, customMetrics: [], authentication: void 0 }; constructor(registry) { super(registry); this.formatter = new PrometheusFormatter(this.options.prefix); } /** * Install metrics capabilities on the app */ install(app) { app.enableMetrics = (options = {}) => { this.configureMetrics(app, options); return app; }; app.incrementCounter = (name, labels, value) => { this.storage.incrementCounter(name, labels, value); }; app.setGauge = (name, value, labels) => { this.storage.setMetric(name, value, labels); }; app.observeHistogram = (name, value, labels) => { this.storage.setMetric(name, value, labels); }; app.addHealthCheck = (name, check) => { this.healthManager.register(name, check); }; app.removeHealthCheck = (name) => { this.healthManager.remove(name); }; app.getMetrics = () => { return this.formatter.format( this.storage, this.requestMetrics, this.monitor.getMetrics() ); }; app.getHealth = () => { return this.healthManager.getStatus(); }; this.emit("metrics:installed"); } /** * Configure metrics with user options */ configureMetrics(app, options) { this.options = { ...this.options, ...options }; this.formatter = new PrometheusFormatter(this.options.prefix); for (const metric of this.options.customMetrics) { this.storage.setMetric(metric.name, 0, {}); } this.setupEndpoints(app); if (this.options.collectDefaultMetrics) { this.setupRequestTracking(app); } this.setupBasicHealthChecks(); } /** * Set up metrics and health endpoints */ setupEndpoints(app) { app.get( this.options.endpoint, (req, res) => { if (this.options.authentication && !this.options.authentication(req)) { res.status(401).json({ error: "Unauthorized" }); return; } const metrics = this.formatter.format( this.storage, this.requestMetrics, this.monitor.getMetrics() ); res.set("Content-Type", "text/plain; version=0.0.4; charset=utf-8"); res.send(metrics); } ); if (this.options.enableHealthCheck) { app.get( "/health", async (req, res) => { try { const health = await this.healthManager.getStatus(); const statusCode = health.status === "healthy" ? 200 : health.status === "degraded" ? 200 : 503; res.status(statusCode).json(health); } catch (error) { res.status(503).json({ status: "unhealthy", error: error instanceof Error ? error.message : "Health check failed", timestamp: Date.now() }); } } ); } } /** * Set up request tracking middleware */ setupRequestTracking(app) { const self = this; app.use( (req, res, next) => { const startTime = Date.now(); self.requestMetrics.active++; self.requestMetrics.total++; const method = req.method || "UNKNOWN"; self.requestMetrics.byMethod[method] = (self.requestMetrics.byMethod[method] || 0) + 1; const originalEnd = res.end; res.end = function(...args) { const duration = Date.now() - startTime; const statusCode = res.statusCode; self.requestMetrics.active--; self.requestMetrics.byStatus[statusCode] = (self.requestMetrics.byStatus[statusCode] || 0) + 1; if (statusCode >= 400) { self.requestMetrics.errors++; } self.storage.addResponseTime(duration); const percentiles = self.storage.getPercentiles(); self.requestMetrics.p95ResponseTime = percentiles.p95; self.requestMetrics.p99ResponseTime = percentiles.p99; const totalRequests = self.requestMetrics.total; self.requestMetrics.averageResponseTime = (self.requestMetrics.averageResponseTime * (totalRequests - 1) + duration) / totalRequests; return originalEnd.apply(this, args); }; next(); } ); } /** * Set up basic health checks */ setupBasicHealthChecks() { this.healthManager.register("memory", async () => { const usage = process.memoryUsage(); const heapUsedMB = usage.heapUsed / 1024 / 1024; const heapTotalMB = usage.heapTotal / 1024 / 1024; const usagePercent = heapUsedMB / heapTotalMB * 100; if (usagePercent > 90) { return { status: "fail", message: `High memory usage: ${usagePercent.toFixed(1)}%` }; } else if (usagePercent > 75) { return { status: "warn", message: `Memory usage: ${usagePercent.toFixed(1)}%` }; } return { status: "pass", message: `Memory usage: ${usagePercent.toFixed(1)}%` }; }); this.healthManager.register("process", async () => { const uptime = process.uptime(); return { status: "pass", message: `Process healthy, uptime: ${Math.floor(uptime)}s` }; }); } /** * Start the plugin */ start() { setInterval(() => { this.storage.cleanup(); }, 6e4); this.emit("metrics:started"); } /** * Stop the plugin */ stop() { this.storage.clear(); this.healthManager.clear(); this.monitor.reset(); this.emit("metrics:stopped"); } }; } }); // src/plugins/middleware/validation.plugin.ts var validation_plugin_exports = {}; __export(validation_plugin_exports, { ValidationPlugin: () => ValidationPlugin }); var ValidationPlugin; var init_validation_plugin = __esm({ "src/plugins/middleware/validation.plugin.ts"() { "use strict"; init_base_plugin(); ValidationPlugin = class extends BasePlugin { static { __name(this, "ValidationPlugin"); } name = "Validation"; constructor(registry) { super(registry); } /** * Install validation capabilities */ ins