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
JavaScript
"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