@adalo/metrics 0.1.124 → 0.1.125

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -252,7 +252,7 @@ export class HealthCheckClient {
252
252
  * Register health check endpoint on an Express app.
253
253
  *
254
254
  * @param {import('express').Application} app - Express application
255
- * @param {string} [path='/health-status'] - Path for the health endpoint
255
+ * @param {string} [path='/health'] - Path for the health endpoint
256
256
  */
257
257
  registerHealthEndpoint(app: any, path?: string | undefined): void;
258
258
  /**
@@ -600,9 +600,9 @@ class HealthCheckClient {
600
600
  * Register health check endpoint on an Express app.
601
601
  *
602
602
  * @param {import('express').Application} app - Express application
603
- * @param {string} [path='/health-status'] - Path for the health endpoint
603
+ * @param {string} [path='/health'] - Path for the health endpoint
604
604
  */
605
- registerHealthEndpoint(app, path = '/health-status') {
605
+ registerHealthEndpoint(app, path = '/health') {
606
606
  app.get(path, this.healthHandler());
607
607
  console.info(`${this.prefixLogs} Registered health endpoint at ${path}`);
608
608
  }
@@ -1 +1 @@
1
- {"version":3,"file":"healthCheckClient.js","names":["Pool","require","getRedisClientType","REDIS_V4","IOREDIS","REDIS_V3","HealthCheckCache","HEALTH_CHECK_CACHE_TTL_MS","SENSITIVE_PATTERNS","pattern","replacement","maskSensitiveData","text","masked","replace","HealthCheckClient","constructor","options","redisClient","includeRedisCheck","cacheTtlMs","appName","process","env","BUILD_APP_NAME","prefixLogs","_cachedResult","_refreshPromise","_databasePools","Map","_mainDatabaseConfig","_clusterConfigs","_initDatabases","_redisClientType","_cache","mainUrl","databaseUrl","DATABASE_URL","mainName","databaseName","name","url","additionalUrls","additionalDatabaseUrls","Object","entries","push","_getPool","config","has","set","connectionString","max","idleTimeoutMillis","connectionTimeoutMillis","get","_isCacheValid","Date","now","timestamp","_checkSingleDatabase","start","pool","query","status","latencyMs","err","error","message","_checkAllDatabases","length","mainHealth","clusters","clusterResults","Promise","all","map","health","allStatuses","values","c","overallStatus","some","s","result","_checkRedis","pong","resolve","reject","ping","performHealthCheck","cached","_performHealthCheckInternal","then","catch","dbHealth","redisHealth","checks","database","redis","statuses","toISOString","getCachedResult","console","errorMessage","errors","refreshCache","clearCache","_getErrorMessages","db","dbName","errorMsg","healthHandler","req","res","json","statusCode","response","registerHealthEndpoint","app","path","info","cleanup","end","clear","module","exports"],"sources":["../../src/health/healthCheckClient.js"],"sourcesContent":["const { Pool } = require('pg')\nconst {\n getRedisClientType,\n REDIS_V4,\n IOREDIS,\n REDIS_V3,\n} = require('../redisUtils')\nconst { HealthCheckCache } = require('./healthCheckCache')\n\nconst HEALTH_CHECK_CACHE_TTL_MS = 60 * 1000\n\n/**\n * Patterns to detect and mask sensitive information in error messages\n */\nconst SENSITIVE_PATTERNS = [\n // Database connection strings: postgres://user:password@host:port/database\n {\n pattern: /(postgres(?:ql)?|mysql|mongodb|redis|amqp):\\/\\/([^:]+):([^@]+)@([^:/]+)(:\\d+)?\\/([^\\s?]+)/gi,\n replacement: '$1://***:***@***$5/***',\n },\n // Generic URLs with credentials: protocol://user:password@host\n {\n pattern: /(\\w+):\\/\\/([^:]+):([^@]+)@([^\\s/]+)/gi,\n replacement: '$1://***:***@***',\n },\n // Password fields in JSON or key=value format\n {\n pattern: /(password|passwd|pwd|secret|token|api[_-]?key|auth[_-]?token)[\"\\s]*[:=][\"\\s]*([^\\s,}\"]+)/gi,\n replacement: '$1=***',\n },\n // Database/table/schema/role/user names in error messages: database \"name\", table \"name\", etc.\n {\n pattern: /(database|table|schema|role|user|relation|column|index)\\s*[\"']([^\"']+)[\"']/gi,\n replacement: '$1 \"***\"',\n },\n // IP addresses (to hide internal network structure)\n {\n pattern: /\\b(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})(:\\d+)?\\b/g,\n replacement: '***$2',\n },\n // Host names that might reveal internal infrastructure\n {\n pattern: /\\b(host|hostname|server)[\"\\s]*[:=][\"\\s]*([^\\s,}\"]+)/gi,\n replacement: '$1=***',\n },\n]\n\n/**\n * Masks sensitive information in a string\n * @param {string} text - Text that might contain sensitive data\n * @returns {string} - Text with sensitive data masked\n */\nfunction maskSensitiveData(text) {\n if (!text || typeof text !== 'string') {\n return text\n }\n\n let masked = text\n for (const { pattern, replacement } of SENSITIVE_PATTERNS) {\n masked = masked.replace(pattern, replacement)\n }\n return masked\n}\n\n/**\n * @typedef {'healthy' | 'unhealthy' | 'degraded'} HealthStatus\n */\n\n/**\n * @typedef {Object} ComponentHealth\n * @property {HealthStatus} status - Component health status\n * @property {string} [error] - Error message if status is unhealthy\n * @property {string} [message] - Optional status message (deprecated, use error)\n * @property {number} [latencyMs] - Connection latency in milliseconds\n */\n\n/**\n * @typedef {Object} DatabaseClusterHealth\n * @property {HealthStatus} status - Overall databases status\n * @property {Object<string, ComponentHealth>} clusters - Individual cluster health\n */\n\n/**\n * @typedef {Object} HealthCheckResult\n * @property {HealthStatus} status - Overall health status\n * @property {string} timestamp - ISO timestamp of the check\n * @property {Object<string, ComponentHealth | DatabaseClusterHealth>} checks - Individual service health checks\n * @property {string[]} [errors] - Top-level error messages (not related to specific services)\n */\n\n/**\n * @typedef {Object} CachedHealthResult\n * @property {HealthCheckResult} result - The cached health check result\n * @property {number} timestamp - Unix timestamp when cached\n */\n\n/**\n * @typedef {Object} DatabaseConfig\n * @property {string} name - Database/cluster name\n * @property {string} url - Connection URL\n */\n\n/**\n * HealthCheckClient provides a health check middleware for external monitoring services\n * like BetterStack. It validates database and Redis connections with rate limiting\n * to prevent excessive load on backend services.\n *\n * Features:\n * - Multi-cluster DB validation (PostgreSQL)\n * - Redis connection validation (supports ioredis, node-redis v3/v4)\n * - Result caching (default: 60 seconds) to prevent overloading services\n * - Express middleware support\n * - BetterStack-compatible JSON response format\n */\nclass HealthCheckClient {\n /**\n * @param {Object} options\n * @param {string} [options.databaseUrl] - Main PostgreSQL connection URL\n * @param {string} [options.databaseName='main'] - Name for the main database\n * @param {Object<string, string>} [options.additionalDatabaseUrls] - Additional DB clusters (name -> URL)\n * @param {any} [options.redisClient] - Redis client instance (ioredis or node-redis) - used for cache\n * @param {boolean} [options.includeRedisCheck=false] - Include Redis health check in results (for backend)\n * @param {number} [options.cacheTtlMs=60000] - Cache TTL in milliseconds (default: 60s)\n * @param {string} [options.appName] - Application name for logging\n */\n constructor(options = {}) {\n this.redisClient = options.redisClient || null\n this.includeRedisCheck = options.includeRedisCheck || false\n this.cacheTtlMs = options.cacheTtlMs ?? HEALTH_CHECK_CACHE_TTL_MS\n this.appName =\n options.appName || process.env.BUILD_APP_NAME || 'unknown-app'\n\n this.prefixLogs = `[${this.appName}] [HealthCheck]`\n\n /** @type {CachedHealthResult | null} */\n this._cachedResult = null\n\n /** @type {Promise<HealthCheckResult> | null} */\n this._refreshPromise = null\n\n /** @type {Map<string, Pool>} */\n this._databasePools = new Map()\n\n /** @type {DatabaseConfig | null} */\n this._mainDatabaseConfig = null\n\n /** @type {DatabaseConfig[]} */\n this._clusterConfigs = []\n\n this._initDatabases(options)\n\n if (this.redisClient) {\n this._redisClientType = getRedisClientType(this.redisClient)\n }\n\n this._cache = new HealthCheckCache({\n redisClient: this.redisClient,\n appName: this.appName,\n cacheTtlMs: this.cacheTtlMs,\n })\n }\n\n /**\n * Initialize database configurations from options.\n * @param {Object} options - Constructor options\n * @private\n */\n _initDatabases(options) {\n const mainUrl = options.databaseUrl || process.env.DATABASE_URL || ''\n const mainName = options.databaseName || `${this.appName}_db`\n\n if (mainUrl) {\n this._mainDatabaseConfig = { name: mainName, url: mainUrl }\n }\n\n const additionalUrls = options.additionalDatabaseUrls || {}\n for (const [name, url] of Object.entries(additionalUrls)) {\n if (url) {\n this._clusterConfigs.push({ name, url })\n }\n }\n }\n\n /**\n * Get or create a database pool for a given config.\n * @param {DatabaseConfig} config - Database configuration\n * @returns {Pool}\n * @private\n */\n _getPool(config) {\n if (!this._databasePools.has(config.name)) {\n this._databasePools.set(\n config.name,\n new Pool({\n connectionString: config.url,\n max: 1,\n idleTimeoutMillis: 30000,\n connectionTimeoutMillis: 5000,\n })\n )\n }\n return this._databasePools.get(config.name)\n }\n\n /**\n * Checks if cached result is still valid based on TTL.\n * @returns {boolean}\n * @private\n */\n _isCacheValid() {\n if (!this._cachedResult) return false\n return Date.now() - this._cachedResult.timestamp < this.cacheTtlMs\n }\n\n /**\n * Tests a single database cluster connectivity.\n * @param {DatabaseConfig} config - Database configuration\n * @returns {Promise<ComponentHealth>}\n * @private\n */\n async _checkSingleDatabase(config) {\n const start = Date.now()\n\n try {\n const pool = this._getPool(config)\n await pool.query('SELECT 1')\n return {\n status: 'healthy',\n latencyMs: Date.now() - start,\n }\n } catch (err) {\n return {\n status: 'unhealthy',\n error: maskSensitiveData(err.message),\n latencyMs: Date.now() - start,\n }\n }\n }\n\n /**\n * Tests all PostgreSQL databases (main + clusters) in parallel.\n * @returns {Promise<Object | null>} Database health with optional clusters\n * @private\n */\n async _checkAllDatabases() {\n if (!this._mainDatabaseConfig && this._clusterConfigs.length === 0) {\n return null\n }\n\n // Check main database\n let mainHealth = null\n if (this._mainDatabaseConfig) {\n mainHealth = await this._checkSingleDatabase(this._mainDatabaseConfig)\n }\n\n // Check clusters in parallel\n let clusters = null\n if (this._clusterConfigs.length > 0) {\n const clusterResults = await Promise.all(\n this._clusterConfigs.map(async config => ({\n name: config.name,\n health: await this._checkSingleDatabase(config),\n }))\n )\n\n clusters = {}\n for (const { name, health } of clusterResults) {\n clusters[name] = health\n }\n }\n\n // Calculate overall status\n const allStatuses = []\n if (mainHealth) allStatuses.push(mainHealth.status)\n if (clusters) {\n allStatuses.push(...Object.values(clusters).map(c => c.status))\n }\n\n let overallStatus = 'healthy'\n if (allStatuses.some(s => s === 'unhealthy')) {\n overallStatus = 'unhealthy'\n } else if (allStatuses.some(s => s === 'degraded')) {\n overallStatus = 'degraded'\n }\n\n // Build result\n const result = { status: overallStatus }\n if (mainHealth) {\n result.latencyMs = mainHealth.latencyMs\n if (mainHealth.error) result.error = mainHealth.error\n // Keep message for backward compatibility\n if (mainHealth.message) result.message = mainHealth.message\n }\n if (clusters) {\n result.clusters = clusters\n }\n\n return result\n }\n\n /**\n * Tests Redis connectivity using PING command.\n * @returns {Promise<ComponentHealth>}\n * @private\n */\n async _checkRedis() {\n if (!this.redisClient) {\n return { status: 'healthy', message: 'Not configured' }\n }\n\n const start = Date.now()\n\n try {\n let pong\n\n if (this._redisClientType === REDIS_V3) {\n pong = await new Promise((resolve, reject) => {\n this.redisClient.ping((err, result) => {\n if (err) reject(err)\n else resolve(result)\n })\n })\n } else if (\n this._redisClientType === REDIS_V4 ||\n this._redisClientType === IOREDIS\n ) {\n pong = await this.redisClient.ping()\n } else {\n return { status: 'unhealthy', message: 'Unknown Redis client type' }\n }\n\n if (pong === 'PONG') {\n return {\n status: 'healthy',\n latencyMs: Date.now() - start,\n }\n }\n\n return {\n status: 'unhealthy',\n message: maskSensitiveData(`Unexpected PING response: ${pong}`),\n latencyMs: Date.now() - start,\n }\n } catch (err) {\n return {\n status: 'unhealthy',\n message: maskSensitiveData(err.message),\n latencyMs: Date.now() - start,\n }\n }\n }\n\n /**\n * Performs a full health check on all configured components.\n * Results are cached for the configured TTL to prevent excessive load.\n * Uses a mutex pattern to prevent concurrent health checks when cache expires.\n * If cache is expired but a refresh is in progress, returns stale cache (if available).\n *\n * @returns {Promise<HealthCheckResult>}\n */\n async performHealthCheck() {\n // If cache is valid, return immediately\n if (this._isCacheValid()) {\n return { ...this._cachedResult.result, cached: true }\n }\n\n // If a refresh is already in progress, return stale cache (if available) or wait for refresh\n if (this._refreshPromise) {\n // Return stale cache immediately if available, otherwise wait for refresh\n if (this._cachedResult) {\n return { ...this._cachedResult.result, cached: true }\n }\n // Wait for the in-progress refresh\n return this._refreshPromise\n }\n\n // Start a new refresh\n this._refreshPromise = this._performHealthCheckInternal()\n .then(result => {\n this._refreshPromise = null\n return result\n })\n .catch(err => {\n this._refreshPromise = null\n throw err\n })\n\n // Return stale cache if available while refresh is happening, otherwise wait\n if (this._cachedResult) {\n // Return stale cache immediately, refresh will happen in background\n return { ...this._cachedResult.result, cached: true }\n }\n\n // No stale cache available, wait for refresh\n return this._refreshPromise\n }\n\n /**\n * Internal method that actually performs the health check.\n * This is separated to allow the mutex pattern in performHealthCheck.\n * Only checks database - Redis is used only for cache, not health check.\n *\n * @returns {Promise<HealthCheckResult>}\n * @private\n */\n async _performHealthCheckInternal() {\n // Check database\n const dbHealth = await this._checkAllDatabases()\n\n // Optionally check Redis (for backend)\n let redisHealth = null\n if (this.includeRedisCheck && this.redisClient) {\n redisHealth = await this._checkRedis()\n }\n\n const checks = {}\n if (dbHealth) checks.database = dbHealth\n if (redisHealth) checks.redis = redisHealth\n\n const statuses = Object.values(checks).map(c => c.status)\n let overallStatus = 'healthy'\n\n if (statuses.some(s => s === 'unhealthy')) {\n overallStatus = 'unhealthy'\n } else if (statuses.some(s => s === 'degraded')) {\n overallStatus = 'degraded'\n }\n\n /** @type {HealthCheckResult} */\n const result = {\n status: overallStatus,\n timestamp: new Date().toISOString(),\n checks,\n }\n\n this._cachedResult = {\n result,\n timestamp: Date.now(),\n }\n\n return result\n }\n\n /**\n * Gets cached result from shared cache (Redis if available, otherwise in-memory).\n * Returns null if no cache is available. This is used by endpoints to read-only access.\n * If Redis fails, returns error result with proper format.\n *\n * @returns {Promise<HealthCheckResult | null>} Cached result, error result if Redis fails, or null if not available\n */\n async getCachedResult() {\n try {\n const cached = await this._cache.get()\n if (cached) {\n return cached\n }\n // No cache available - worker may not have run yet\n return null\n } catch (err) {\n // Redis read failed - return error result with proper format\n console.error(`${this.prefixLogs} Failed to read from cache:`, err)\n const errorMessage = maskSensitiveData(err.message || 'Cache read failed')\n return {\n status: 'unhealthy',\n timestamp: new Date().toISOString(),\n checks: {\n redis: {\n status: 'unhealthy',\n error: errorMessage,\n },\n },\n errors: [errorMessage],\n }\n }\n }\n\n /**\n * Forces a refresh of the health check cache.\n * This is used by background workers to periodically update the cache.\n * Updates shared cache (Redis if available, otherwise in-memory).\n *\n * @returns {Promise<HealthCheckResult>}\n */\n async refreshCache() {\n const result = await this._performHealthCheckInternal()\n await this._cache.set(result)\n\n return result\n }\n\n /**\n * Clears the cached health check result, forcing the next check to be fresh.\n */\n clearCache() {\n this._cachedResult = null\n this._refreshPromise = null\n }\n\n /**\n * Builds a list of error messages from health check result.\n * All error messages are sanitized to remove sensitive information.\n * @param {HealthCheckResult} result - Health check result\n * @returns {string[]} Array of sanitized error messages\n * @private\n */\n _getErrorMessages(result) {\n const errors = []\n\n if (result.checks.database) {\n const db = result.checks.database\n\n // Check main database status\n if (db.status === 'unhealthy' && (db.message || db.error)) {\n const dbName = this._mainDatabaseConfig?.name || 'main'\n const errorMsg = db.error || db.message\n errors.push(`DB ${dbName}: ${maskSensitiveData(errorMsg)}`)\n }\n\n // Check clusters\n if (db.clusters) {\n for (const [name, health] of Object.entries(db.clusters)) {\n if (health.status === 'unhealthy') {\n const message = health.error || health.message || 'connection failed'\n errors.push(`DB ${name}: ${maskSensitiveData(message)}`)\n }\n }\n }\n }\n\n if (result.checks.redis && result.checks.redis.status === 'unhealthy') {\n const message = result.checks.redis.error || result.checks.redis.message || 'connection failed'\n errors.push(`Redis: ${maskSensitiveData(message)}`)\n }\n\n return errors\n }\n\n /**\n * Express middleware handler for health check endpoint.\n * Returns 200 for healthy/degraded, 503 for unhealthy.\n * Response includes errors array when status is not healthy.\n * All sensitive data (passwords, connection strings, etc.) is masked.\n *\n * This handler only reads from cache and never triggers database queries.\n * Use a background worker to periodically refresh the cache.\n *\n * @returns {(req: any, res: any) => Promise<void>} Express request handler\n */\n healthHandler() {\n return async (req, res) => {\n try {\n // Only read from cache, never trigger DB queries\n const result = await this.getCachedResult()\n\n if (!result) {\n // No cache available - return unhealthy status with proper format\n const errorMsg = 'Health check cache not available. Worker may not be running.'\n res.status(503).json({\n status: 'unhealthy',\n timestamp: new Date().toISOString(),\n checks: {\n redis: {\n status: 'unhealthy',\n error: errorMsg,\n },\n },\n errors: [errorMsg],\n })\n return\n }\n\n const statusCode = result.status === 'unhealthy' ? 503 : 200\n\n // Build response - errors are already in result if present\n const response = { ...result }\n \n // Add top-level errors array if not healthy and errors not already present\n if (result.status !== 'healthy' && !result.errors) {\n const errors = this._getErrorMessages(result)\n if (errors.length > 0) {\n response.errors = errors\n }\n }\n\n res.status(statusCode).json(response)\n } catch (err) {\n console.error(`${this.prefixLogs} Health check failed:`, err)\n const errorMsg = maskSensitiveData(err.message)\n res.status(503).json({\n status: 'unhealthy',\n timestamp: new Date().toISOString(),\n checks: {\n redis: {\n status: 'unhealthy',\n error: errorMsg,\n },\n },\n errors: [errorMsg],\n })\n }\n }\n }\n\n /**\n * Register health check endpoint on an Express app.\n *\n * @param {import('express').Application} app - Express application\n * @param {string} [path='/health-status'] - Path for the health endpoint\n */\n registerHealthEndpoint(app, path = '/health-status') {\n app.get(path, this.healthHandler())\n console.info(`${this.prefixLogs} Registered health endpoint at ${path}`)\n }\n\n /**\n * Cleanup resources (database pools).\n * @returns {Promise<void>}\n */\n async cleanup() {\n for (const [name, pool] of this._databasePools) {\n try {\n await pool.end()\n } catch (err) {\n console.error(`${this.prefixLogs} Error closing database pool ${name}:`, err)\n }\n }\n this._databasePools.clear()\n }\n}\n\nmodule.exports = { HealthCheckClient }\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"healthCheckClient.js","names":["Pool","require","getRedisClientType","REDIS_V4","IOREDIS","REDIS_V3","HealthCheckCache","HEALTH_CHECK_CACHE_TTL_MS","SENSITIVE_PATTERNS","pattern","replacement","maskSensitiveData","text","masked","replace","HealthCheckClient","constructor","options","redisClient","includeRedisCheck","cacheTtlMs","appName","process","env","BUILD_APP_NAME","prefixLogs","_cachedResult","_refreshPromise","_databasePools","Map","_mainDatabaseConfig","_clusterConfigs","_initDatabases","_redisClientType","_cache","mainUrl","databaseUrl","DATABASE_URL","mainName","databaseName","name","url","additionalUrls","additionalDatabaseUrls","Object","entries","push","_getPool","config","has","set","connectionString","max","idleTimeoutMillis","connectionTimeoutMillis","get","_isCacheValid","Date","now","timestamp","_checkSingleDatabase","start","pool","query","status","latencyMs","err","error","message","_checkAllDatabases","length","mainHealth","clusters","clusterResults","Promise","all","map","health","allStatuses","values","c","overallStatus","some","s","result","_checkRedis","pong","resolve","reject","ping","performHealthCheck","cached","_performHealthCheckInternal","then","catch","dbHealth","redisHealth","checks","database","redis","statuses","toISOString","getCachedResult","console","errorMessage","errors","refreshCache","clearCache","_getErrorMessages","db","dbName","errorMsg","healthHandler","req","res","json","statusCode","response","registerHealthEndpoint","app","path","info","cleanup","end","clear","module","exports"],"sources":["../../src/health/healthCheckClient.js"],"sourcesContent":["const { Pool } = require('pg')\nconst {\n getRedisClientType,\n REDIS_V4,\n IOREDIS,\n REDIS_V3,\n} = require('../redisUtils')\nconst { HealthCheckCache } = require('./healthCheckCache')\n\nconst HEALTH_CHECK_CACHE_TTL_MS = 60 * 1000\n\n/**\n * Patterns to detect and mask sensitive information in error messages\n */\nconst SENSITIVE_PATTERNS = [\n // Database connection strings: postgres://user:password@host:port/database\n {\n pattern: /(postgres(?:ql)?|mysql|mongodb|redis|amqp):\\/\\/([^:]+):([^@]+)@([^:/]+)(:\\d+)?\\/([^\\s?]+)/gi,\n replacement: '$1://***:***@***$5/***',\n },\n // Generic URLs with credentials: protocol://user:password@host\n {\n pattern: /(\\w+):\\/\\/([^:]+):([^@]+)@([^\\s/]+)/gi,\n replacement: '$1://***:***@***',\n },\n // Password fields in JSON or key=value format\n {\n pattern: /(password|passwd|pwd|secret|token|api[_-]?key|auth[_-]?token)[\"\\s]*[:=][\"\\s]*([^\\s,}\"]+)/gi,\n replacement: '$1=***',\n },\n // Database/table/schema/role/user names in error messages: database \"name\", table \"name\", etc.\n {\n pattern: /(database|table|schema|role|user|relation|column|index)\\s*[\"']([^\"']+)[\"']/gi,\n replacement: '$1 \"***\"',\n },\n // IP addresses (to hide internal network structure)\n {\n pattern: /\\b(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})(:\\d+)?\\b/g,\n replacement: '***$2',\n },\n // Host names that might reveal internal infrastructure\n {\n pattern: /\\b(host|hostname|server)[\"\\s]*[:=][\"\\s]*([^\\s,}\"]+)/gi,\n replacement: '$1=***',\n },\n]\n\n/**\n * Masks sensitive information in a string\n * @param {string} text - Text that might contain sensitive data\n * @returns {string} - Text with sensitive data masked\n */\nfunction maskSensitiveData(text) {\n if (!text || typeof text !== 'string') {\n return text\n }\n\n let masked = text\n for (const { pattern, replacement } of SENSITIVE_PATTERNS) {\n masked = masked.replace(pattern, replacement)\n }\n return masked\n}\n\n/**\n * @typedef {'healthy' | 'unhealthy' | 'degraded'} HealthStatus\n */\n\n/**\n * @typedef {Object} ComponentHealth\n * @property {HealthStatus} status - Component health status\n * @property {string} [error] - Error message if status is unhealthy\n * @property {string} [message] - Optional status message (deprecated, use error)\n * @property {number} [latencyMs] - Connection latency in milliseconds\n */\n\n/**\n * @typedef {Object} DatabaseClusterHealth\n * @property {HealthStatus} status - Overall databases status\n * @property {Object<string, ComponentHealth>} clusters - Individual cluster health\n */\n\n/**\n * @typedef {Object} HealthCheckResult\n * @property {HealthStatus} status - Overall health status\n * @property {string} timestamp - ISO timestamp of the check\n * @property {Object<string, ComponentHealth | DatabaseClusterHealth>} checks - Individual service health checks\n * @property {string[]} [errors] - Top-level error messages (not related to specific services)\n */\n\n/**\n * @typedef {Object} CachedHealthResult\n * @property {HealthCheckResult} result - The cached health check result\n * @property {number} timestamp - Unix timestamp when cached\n */\n\n/**\n * @typedef {Object} DatabaseConfig\n * @property {string} name - Database/cluster name\n * @property {string} url - Connection URL\n */\n\n/**\n * HealthCheckClient provides a health check middleware for external monitoring services\n * like BetterStack. It validates database and Redis connections with rate limiting\n * to prevent excessive load on backend services.\n *\n * Features:\n * - Multi-cluster DB validation (PostgreSQL)\n * - Redis connection validation (supports ioredis, node-redis v3/v4)\n * - Result caching (default: 60 seconds) to prevent overloading services\n * - Express middleware support\n * - BetterStack-compatible JSON response format\n */\nclass HealthCheckClient {\n /**\n * @param {Object} options\n * @param {string} [options.databaseUrl] - Main PostgreSQL connection URL\n * @param {string} [options.databaseName='main'] - Name for the main database\n * @param {Object<string, string>} [options.additionalDatabaseUrls] - Additional DB clusters (name -> URL)\n * @param {any} [options.redisClient] - Redis client instance (ioredis or node-redis) - used for cache\n * @param {boolean} [options.includeRedisCheck=false] - Include Redis health check in results (for backend)\n * @param {number} [options.cacheTtlMs=60000] - Cache TTL in milliseconds (default: 60s)\n * @param {string} [options.appName] - Application name for logging\n */\n constructor(options = {}) {\n this.redisClient = options.redisClient || null\n this.includeRedisCheck = options.includeRedisCheck || false\n this.cacheTtlMs = options.cacheTtlMs ?? HEALTH_CHECK_CACHE_TTL_MS\n this.appName =\n options.appName || process.env.BUILD_APP_NAME || 'unknown-app'\n\n this.prefixLogs = `[${this.appName}] [HealthCheck]`\n\n /** @type {CachedHealthResult | null} */\n this._cachedResult = null\n\n /** @type {Promise<HealthCheckResult> | null} */\n this._refreshPromise = null\n\n /** @type {Map<string, Pool>} */\n this._databasePools = new Map()\n\n /** @type {DatabaseConfig | null} */\n this._mainDatabaseConfig = null\n\n /** @type {DatabaseConfig[]} */\n this._clusterConfigs = []\n\n this._initDatabases(options)\n\n if (this.redisClient) {\n this._redisClientType = getRedisClientType(this.redisClient)\n }\n\n this._cache = new HealthCheckCache({\n redisClient: this.redisClient,\n appName: this.appName,\n cacheTtlMs: this.cacheTtlMs,\n })\n }\n\n /**\n * Initialize database configurations from options.\n * @param {Object} options - Constructor options\n * @private\n */\n _initDatabases(options) {\n const mainUrl = options.databaseUrl || process.env.DATABASE_URL || ''\n const mainName = options.databaseName || `${this.appName}_db`\n\n if (mainUrl) {\n this._mainDatabaseConfig = { name: mainName, url: mainUrl }\n }\n\n const additionalUrls = options.additionalDatabaseUrls || {}\n for (const [name, url] of Object.entries(additionalUrls)) {\n if (url) {\n this._clusterConfigs.push({ name, url })\n }\n }\n }\n\n /**\n * Get or create a database pool for a given config.\n * @param {DatabaseConfig} config - Database configuration\n * @returns {Pool}\n * @private\n */\n _getPool(config) {\n if (!this._databasePools.has(config.name)) {\n this._databasePools.set(\n config.name,\n new Pool({\n connectionString: config.url,\n max: 1,\n idleTimeoutMillis: 30000,\n connectionTimeoutMillis: 5000,\n })\n )\n }\n return this._databasePools.get(config.name)\n }\n\n /**\n * Checks if cached result is still valid based on TTL.\n * @returns {boolean}\n * @private\n */\n _isCacheValid() {\n if (!this._cachedResult) return false\n return Date.now() - this._cachedResult.timestamp < this.cacheTtlMs\n }\n\n /**\n * Tests a single database cluster connectivity.\n * @param {DatabaseConfig} config - Database configuration\n * @returns {Promise<ComponentHealth>}\n * @private\n */\n async _checkSingleDatabase(config) {\n const start = Date.now()\n\n try {\n const pool = this._getPool(config)\n await pool.query('SELECT 1')\n return {\n status: 'healthy',\n latencyMs: Date.now() - start,\n }\n } catch (err) {\n return {\n status: 'unhealthy',\n error: maskSensitiveData(err.message),\n latencyMs: Date.now() - start,\n }\n }\n }\n\n /**\n * Tests all PostgreSQL databases (main + clusters) in parallel.\n * @returns {Promise<Object | null>} Database health with optional clusters\n * @private\n */\n async _checkAllDatabases() {\n if (!this._mainDatabaseConfig && this._clusterConfigs.length === 0) {\n return null\n }\n\n // Check main database\n let mainHealth = null\n if (this._mainDatabaseConfig) {\n mainHealth = await this._checkSingleDatabase(this._mainDatabaseConfig)\n }\n\n // Check clusters in parallel\n let clusters = null\n if (this._clusterConfigs.length > 0) {\n const clusterResults = await Promise.all(\n this._clusterConfigs.map(async config => ({\n name: config.name,\n health: await this._checkSingleDatabase(config),\n }))\n )\n\n clusters = {}\n for (const { name, health } of clusterResults) {\n clusters[name] = health\n }\n }\n\n // Calculate overall status\n const allStatuses = []\n if (mainHealth) allStatuses.push(mainHealth.status)\n if (clusters) {\n allStatuses.push(...Object.values(clusters).map(c => c.status))\n }\n\n let overallStatus = 'healthy'\n if (allStatuses.some(s => s === 'unhealthy')) {\n overallStatus = 'unhealthy'\n } else if (allStatuses.some(s => s === 'degraded')) {\n overallStatus = 'degraded'\n }\n\n // Build result\n const result = { status: overallStatus }\n if (mainHealth) {\n result.latencyMs = mainHealth.latencyMs\n if (mainHealth.error) result.error = mainHealth.error\n // Keep message for backward compatibility\n if (mainHealth.message) result.message = mainHealth.message\n }\n if (clusters) {\n result.clusters = clusters\n }\n\n return result\n }\n\n /**\n * Tests Redis connectivity using PING command.\n * @returns {Promise<ComponentHealth>}\n * @private\n */\n async _checkRedis() {\n if (!this.redisClient) {\n return { status: 'healthy', message: 'Not configured' }\n }\n\n const start = Date.now()\n\n try {\n let pong\n\n if (this._redisClientType === REDIS_V3) {\n pong = await new Promise((resolve, reject) => {\n this.redisClient.ping((err, result) => {\n if (err) reject(err)\n else resolve(result)\n })\n })\n } else if (\n this._redisClientType === REDIS_V4 ||\n this._redisClientType === IOREDIS\n ) {\n pong = await this.redisClient.ping()\n } else {\n return { status: 'unhealthy', message: 'Unknown Redis client type' }\n }\n\n if (pong === 'PONG') {\n return {\n status: 'healthy',\n latencyMs: Date.now() - start,\n }\n }\n\n return {\n status: 'unhealthy',\n message: maskSensitiveData(`Unexpected PING response: ${pong}`),\n latencyMs: Date.now() - start,\n }\n } catch (err) {\n return {\n status: 'unhealthy',\n message: maskSensitiveData(err.message),\n latencyMs: Date.now() - start,\n }\n }\n }\n\n /**\n * Performs a full health check on all configured components.\n * Results are cached for the configured TTL to prevent excessive load.\n * Uses a mutex pattern to prevent concurrent health checks when cache expires.\n * If cache is expired but a refresh is in progress, returns stale cache (if available).\n *\n * @returns {Promise<HealthCheckResult>}\n */\n async performHealthCheck() {\n // If cache is valid, return immediately\n if (this._isCacheValid()) {\n return { ...this._cachedResult.result, cached: true }\n }\n\n // If a refresh is already in progress, return stale cache (if available) or wait for refresh\n if (this._refreshPromise) {\n // Return stale cache immediately if available, otherwise wait for refresh\n if (this._cachedResult) {\n return { ...this._cachedResult.result, cached: true }\n }\n // Wait for the in-progress refresh\n return this._refreshPromise\n }\n\n // Start a new refresh\n this._refreshPromise = this._performHealthCheckInternal()\n .then(result => {\n this._refreshPromise = null\n return result\n })\n .catch(err => {\n this._refreshPromise = null\n throw err\n })\n\n // Return stale cache if available while refresh is happening, otherwise wait\n if (this._cachedResult) {\n // Return stale cache immediately, refresh will happen in background\n return { ...this._cachedResult.result, cached: true }\n }\n\n // No stale cache available, wait for refresh\n return this._refreshPromise\n }\n\n /**\n * Internal method that actually performs the health check.\n * This is separated to allow the mutex pattern in performHealthCheck.\n * Only checks database - Redis is used only for cache, not health check.\n *\n * @returns {Promise<HealthCheckResult>}\n * @private\n */\n async _performHealthCheckInternal() {\n // Check database\n const dbHealth = await this._checkAllDatabases()\n\n // Optionally check Redis (for backend)\n let redisHealth = null\n if (this.includeRedisCheck && this.redisClient) {\n redisHealth = await this._checkRedis()\n }\n\n const checks = {}\n if (dbHealth) checks.database = dbHealth\n if (redisHealth) checks.redis = redisHealth\n\n const statuses = Object.values(checks).map(c => c.status)\n let overallStatus = 'healthy'\n\n if (statuses.some(s => s === 'unhealthy')) {\n overallStatus = 'unhealthy'\n } else if (statuses.some(s => s === 'degraded')) {\n overallStatus = 'degraded'\n }\n\n /** @type {HealthCheckResult} */\n const result = {\n status: overallStatus,\n timestamp: new Date().toISOString(),\n checks,\n }\n\n this._cachedResult = {\n result,\n timestamp: Date.now(),\n }\n\n return result\n }\n\n /**\n * Gets cached result from shared cache (Redis if available, otherwise in-memory).\n * Returns null if no cache is available. This is used by endpoints to read-only access.\n * If Redis fails, returns error result with proper format.\n *\n * @returns {Promise<HealthCheckResult | null>} Cached result, error result if Redis fails, or null if not available\n */\n async getCachedResult() {\n try {\n const cached = await this._cache.get()\n if (cached) {\n return cached\n }\n // No cache available - worker may not have run yet\n return null\n } catch (err) {\n // Redis read failed - return error result with proper format\n console.error(`${this.prefixLogs} Failed to read from cache:`, err)\n const errorMessage = maskSensitiveData(err.message || 'Cache read failed')\n return {\n status: 'unhealthy',\n timestamp: new Date().toISOString(),\n checks: {\n redis: {\n status: 'unhealthy',\n error: errorMessage,\n },\n },\n errors: [errorMessage],\n }\n }\n }\n\n /**\n * Forces a refresh of the health check cache.\n * This is used by background workers to periodically update the cache.\n * Updates shared cache (Redis if available, otherwise in-memory).\n *\n * @returns {Promise<HealthCheckResult>}\n */\n async refreshCache() {\n const result = await this._performHealthCheckInternal()\n await this._cache.set(result)\n\n return result\n }\n\n /**\n * Clears the cached health check result, forcing the next check to be fresh.\n */\n clearCache() {\n this._cachedResult = null\n this._refreshPromise = null\n }\n\n /**\n * Builds a list of error messages from health check result.\n * All error messages are sanitized to remove sensitive information.\n * @param {HealthCheckResult} result - Health check result\n * @returns {string[]} Array of sanitized error messages\n * @private\n */\n _getErrorMessages(result) {\n const errors = []\n\n if (result.checks.database) {\n const db = result.checks.database\n\n // Check main database status\n if (db.status === 'unhealthy' && (db.message || db.error)) {\n const dbName = this._mainDatabaseConfig?.name || 'main'\n const errorMsg = db.error || db.message\n errors.push(`DB ${dbName}: ${maskSensitiveData(errorMsg)}`)\n }\n\n // Check clusters\n if (db.clusters) {\n for (const [name, health] of Object.entries(db.clusters)) {\n if (health.status === 'unhealthy') {\n const message = health.error || health.message || 'connection failed'\n errors.push(`DB ${name}: ${maskSensitiveData(message)}`)\n }\n }\n }\n }\n\n if (result.checks.redis && result.checks.redis.status === 'unhealthy') {\n const message = result.checks.redis.error || result.checks.redis.message || 'connection failed'\n errors.push(`Redis: ${maskSensitiveData(message)}`)\n }\n\n return errors\n }\n\n /**\n * Express middleware handler for health check endpoint.\n * Returns 200 for healthy/degraded, 503 for unhealthy.\n * Response includes errors array when status is not healthy.\n * All sensitive data (passwords, connection strings, etc.) is masked.\n *\n * This handler only reads from cache and never triggers database queries.\n * Use a background worker to periodically refresh the cache.\n *\n * @returns {(req: any, res: any) => Promise<void>} Express request handler\n */\n healthHandler() {\n return async (req, res) => {\n try {\n // Only read from cache, never trigger DB queries\n const result = await this.getCachedResult()\n\n if (!result) {\n // No cache available - return unhealthy status with proper format\n const errorMsg = 'Health check cache not available. Worker may not be running.'\n res.status(503).json({\n status: 'unhealthy',\n timestamp: new Date().toISOString(),\n checks: {\n redis: {\n status: 'unhealthy',\n error: errorMsg,\n },\n },\n errors: [errorMsg],\n })\n return\n }\n\n const statusCode = result.status === 'unhealthy' ? 503 : 200\n\n // Build response - errors are already in result if present\n const response = { ...result }\n \n // Add top-level errors array if not healthy and errors not already present\n if (result.status !== 'healthy' && !result.errors) {\n const errors = this._getErrorMessages(result)\n if (errors.length > 0) {\n response.errors = errors\n }\n }\n\n res.status(statusCode).json(response)\n } catch (err) {\n console.error(`${this.prefixLogs} Health check failed:`, err)\n const errorMsg = maskSensitiveData(err.message)\n res.status(503).json({\n status: 'unhealthy',\n timestamp: new Date().toISOString(),\n checks: {\n redis: {\n status: 'unhealthy',\n error: errorMsg,\n },\n },\n errors: [errorMsg],\n })\n }\n }\n }\n\n /**\n * Register health check endpoint on an Express app.\n *\n * @param {import('express').Application} app - Express application\n * @param {string} [path='/health'] - Path for the health endpoint\n */\n registerHealthEndpoint(app, path = '/health') {\n app.get(path, this.healthHandler())\n console.info(`${this.prefixLogs} Registered health endpoint at ${path}`)\n }\n\n /**\n * Cleanup resources (database pools).\n * @returns {Promise<void>}\n */\n async cleanup() {\n for (const [name, pool] of this._databasePools) {\n try {\n await pool.end()\n } catch (err) {\n console.error(`${this.prefixLogs} Error closing database pool ${name}:`, err)\n }\n }\n this._databasePools.clear()\n }\n}\n\nmodule.exports = { HealthCheckClient }\n"],"mappings":";;AAAA,MAAM;EAAEA;AAAK,CAAC,GAAGC,OAAO,CAAC,IAAI,CAAC;AAC9B,MAAM;EACJC,kBAAkB;EAClBC,QAAQ;EACRC,OAAO;EACPC;AACF,CAAC,GAAGJ,OAAO,CAAC,eAAe,CAAC;AAC5B,MAAM;EAAEK;AAAiB,CAAC,GAAGL,OAAO,CAAC,oBAAoB,CAAC;AAE1D,MAAMM,yBAAyB,GAAG,EAAE,GAAG,IAAI;;AAE3C;AACA;AACA;AACA,MAAMC,kBAAkB,GAAG;AACzB;AACA;EACEC,OAAO,EAAE,6FAA6F;EACtGC,WAAW,EAAE;AACf,CAAC;AACD;AACA;EACED,OAAO,EAAE,uCAAuC;EAChDC,WAAW,EAAE;AACf,CAAC;AACD;AACA;EACED,OAAO,EAAE,4FAA4F;EACrGC,WAAW,EAAE;AACf,CAAC;AACD;AACA;EACED,OAAO,EAAE,8EAA8E;EACvFC,WAAW,EAAE;AACf,CAAC;AACD;AACA;EACED,OAAO,EAAE,kDAAkD;EAC3DC,WAAW,EAAE;AACf,CAAC;AACD;AACA;EACED,OAAO,EAAE,uDAAuD;EAChEC,WAAW,EAAE;AACf,CAAC,CACF;;AAED;AACA;AACA;AACA;AACA;AACA,SAASC,iBAAiBA,CAACC,IAAI,EAAE;EAC/B,IAAI,CAACA,IAAI,IAAI,OAAOA,IAAI,KAAK,QAAQ,EAAE;IACrC,OAAOA,IAAI;EACb;EAEA,IAAIC,MAAM,GAAGD,IAAI;EACjB,KAAK,MAAM;IAAEH,OAAO;IAAEC;EAAY,CAAC,IAAIF,kBAAkB,EAAE;IACzDK,MAAM,GAAGA,MAAM,CAACC,OAAO,CAACL,OAAO,EAAEC,WAAW,CAAC;EAC/C;EACA,OAAOG,MAAM;AACf;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAME,iBAAiB,CAAC;EACtB;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACEC,WAAWA,CAACC,OAAO,GAAG,CAAC,CAAC,EAAE;IACxB,IAAI,CAACC,WAAW,GAAGD,OAAO,CAACC,WAAW,IAAI,IAAI;IAC9C,IAAI,CAACC,iBAAiB,GAAGF,OAAO,CAACE,iBAAiB,IAAI,KAAK;IAC3D,IAAI,CAACC,UAAU,GAAGH,OAAO,CAACG,UAAU,IAAIb,yBAAyB;IACjE,IAAI,CAACc,OAAO,GACVJ,OAAO,CAACI,OAAO,IAAIC,OAAO,CAACC,GAAG,CAACC,cAAc,IAAI,aAAa;IAEhE,IAAI,CAACC,UAAU,GAAG,IAAI,IAAI,CAACJ,OAAO,iBAAiB;;IAEnD;IACA,IAAI,CAACK,aAAa,GAAG,IAAI;;IAEzB;IACA,IAAI,CAACC,eAAe,GAAG,IAAI;;IAE3B;IACA,IAAI,CAACC,cAAc,GAAG,IAAIC,GAAG,CAAC,CAAC;;IAE/B;IACA,IAAI,CAACC,mBAAmB,GAAG,IAAI;;IAE/B;IACA,IAAI,CAACC,eAAe,GAAG,EAAE;IAEzB,IAAI,CAACC,cAAc,CAACf,OAAO,CAAC;IAE5B,IAAI,IAAI,CAACC,WAAW,EAAE;MACpB,IAAI,CAACe,gBAAgB,GAAG/B,kBAAkB,CAAC,IAAI,CAACgB,WAAW,CAAC;IAC9D;IAEA,IAAI,CAACgB,MAAM,GAAG,IAAI5B,gBAAgB,CAAC;MACjCY,WAAW,EAAE,IAAI,CAACA,WAAW;MAC7BG,OAAO,EAAE,IAAI,CAACA,OAAO;MACrBD,UAAU,EAAE,IAAI,CAACA;IACnB,CAAC,CAAC;EACJ;;EAEA;AACF;AACA;AACA;AACA;EACEY,cAAcA,CAACf,OAAO,EAAE;IACtB,MAAMkB,OAAO,GAAGlB,OAAO,CAACmB,WAAW,IAAId,OAAO,CAACC,GAAG,CAACc,YAAY,IAAI,EAAE;IACrE,MAAMC,QAAQ,GAAGrB,OAAO,CAACsB,YAAY,IAAI,GAAG,IAAI,CAAClB,OAAO,KAAK;IAE7D,IAAIc,OAAO,EAAE;MACX,IAAI,CAACL,mBAAmB,GAAG;QAAEU,IAAI,EAAEF,QAAQ;QAAEG,GAAG,EAAEN;MAAQ,CAAC;IAC7D;IAEA,MAAMO,cAAc,GAAGzB,OAAO,CAAC0B,sBAAsB,IAAI,CAAC,CAAC;IAC3D,KAAK,MAAM,CAACH,IAAI,EAAEC,GAAG,CAAC,IAAIG,MAAM,CAACC,OAAO,CAACH,cAAc,CAAC,EAAE;MACxD,IAAID,GAAG,EAAE;QACP,IAAI,CAACV,eAAe,CAACe,IAAI,CAAC;UAAEN,IAAI;UAAEC;QAAI,CAAC,CAAC;MAC1C;IACF;EACF;;EAEA;AACF;AACA;AACA;AACA;AACA;EACEM,QAAQA,CAACC,MAAM,EAAE;IACf,IAAI,CAAC,IAAI,CAACpB,cAAc,CAACqB,GAAG,CAACD,MAAM,CAACR,IAAI,CAAC,EAAE;MACzC,IAAI,CAACZ,cAAc,CAACsB,GAAG,CACrBF,MAAM,CAACR,IAAI,EACX,IAAIxC,IAAI,CAAC;QACPmD,gBAAgB,EAAEH,MAAM,CAACP,GAAG;QAC5BW,GAAG,EAAE,CAAC;QACNC,iBAAiB,EAAE,KAAK;QACxBC,uBAAuB,EAAE;MAC3B,CAAC,CACH,CAAC;IACH;IACA,OAAO,IAAI,CAAC1B,cAAc,CAAC2B,GAAG,CAACP,MAAM,CAACR,IAAI,CAAC;EAC7C;;EAEA;AACF;AACA;AACA;AACA;EACEgB,aAAaA,CAAA,EAAG;IACd,IAAI,CAAC,IAAI,CAAC9B,aAAa,EAAE,OAAO,KAAK;IACrC,OAAO+B,IAAI,CAACC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAChC,aAAa,CAACiC,SAAS,GAAG,IAAI,CAACvC,UAAU;EACpE;;EAEA;AACF;AACA;AACA;AACA;AACA;EACE,MAAMwC,oBAAoBA,CAACZ,MAAM,EAAE;IACjC,MAAMa,KAAK,GAAGJ,IAAI,CAACC,GAAG,CAAC,CAAC;IAExB,IAAI;MACF,MAAMI,IAAI,GAAG,IAAI,CAACf,QAAQ,CAACC,MAAM,CAAC;MAClC,MAAMc,IAAI,CAACC,KAAK,CAAC,UAAU,CAAC;MAC5B,OAAO;QACLC,MAAM,EAAE,SAAS;QACjBC,SAAS,EAAER,IAAI,CAACC,GAAG,CAAC,CAAC,GAAGG;MAC1B,CAAC;IACH,CAAC,CAAC,OAAOK,GAAG,EAAE;MACZ,OAAO;QACLF,MAAM,EAAE,WAAW;QACnBG,KAAK,EAAExD,iBAAiB,CAACuD,GAAG,CAACE,OAAO,CAAC;QACrCH,SAAS,EAAER,IAAI,CAACC,GAAG,CAAC,CAAC,GAAGG;MAC1B,CAAC;IACH;EACF;;EAEA;AACF;AACA;AACA;AACA;EACE,MAAMQ,kBAAkBA,CAAA,EAAG;IACzB,IAAI,CAAC,IAAI,CAACvC,mBAAmB,IAAI,IAAI,CAACC,eAAe,CAACuC,MAAM,KAAK,CAAC,EAAE;MAClE,OAAO,IAAI;IACb;;IAEA;IACA,IAAIC,UAAU,GAAG,IAAI;IACrB,IAAI,IAAI,CAACzC,mBAAmB,EAAE;MAC5ByC,UAAU,GAAG,MAAM,IAAI,CAACX,oBAAoB,CAAC,IAAI,CAAC9B,mBAAmB,CAAC;IACxE;;IAEA;IACA,IAAI0C,QAAQ,GAAG,IAAI;IACnB,IAAI,IAAI,CAACzC,eAAe,CAACuC,MAAM,GAAG,CAAC,EAAE;MACnC,MAAMG,cAAc,GAAG,MAAMC,OAAO,CAACC,GAAG,CACtC,IAAI,CAAC5C,eAAe,CAAC6C,GAAG,CAAC,MAAM5B,MAAM,KAAK;QACxCR,IAAI,EAAEQ,MAAM,CAACR,IAAI;QACjBqC,MAAM,EAAE,MAAM,IAAI,CAACjB,oBAAoB,CAACZ,MAAM;MAChD,CAAC,CAAC,CACJ,CAAC;MAEDwB,QAAQ,GAAG,CAAC,CAAC;MACb,KAAK,MAAM;QAAEhC,IAAI;QAAEqC;MAAO,CAAC,IAAIJ,cAAc,EAAE;QAC7CD,QAAQ,CAAChC,IAAI,CAAC,GAAGqC,MAAM;MACzB;IACF;;IAEA;IACA,MAAMC,WAAW,GAAG,EAAE;IACtB,IAAIP,UAAU,EAAEO,WAAW,CAAChC,IAAI,CAACyB,UAAU,CAACP,MAAM,CAAC;IACnD,IAAIQ,QAAQ,EAAE;MACZM,WAAW,CAAChC,IAAI,CAAC,GAAGF,MAAM,CAACmC,MAAM,CAACP,QAAQ,CAAC,CAACI,GAAG,CAACI,CAAC,IAAIA,CAAC,CAAChB,MAAM,CAAC,CAAC;IACjE;IAEA,IAAIiB,aAAa,GAAG,SAAS;IAC7B,IAAIH,WAAW,CAACI,IAAI,CAACC,CAAC,IAAIA,CAAC,KAAK,WAAW,CAAC,EAAE;MAC5CF,aAAa,GAAG,WAAW;IAC7B,CAAC,MAAM,IAAIH,WAAW,CAACI,IAAI,CAACC,CAAC,IAAIA,CAAC,KAAK,UAAU,CAAC,EAAE;MAClDF,aAAa,GAAG,UAAU;IAC5B;;IAEA;IACA,MAAMG,MAAM,GAAG;MAAEpB,MAAM,EAAEiB;IAAc,CAAC;IACxC,IAAIV,UAAU,EAAE;MACda,MAAM,CAACnB,SAAS,GAAGM,UAAU,CAACN,SAAS;MACvC,IAAIM,UAAU,CAACJ,KAAK,EAAEiB,MAAM,CAACjB,KAAK,GAAGI,UAAU,CAACJ,KAAK;MACrD;MACA,IAAII,UAAU,CAACH,OAAO,EAAEgB,MAAM,CAAChB,OAAO,GAAGG,UAAU,CAACH,OAAO;IAC7D;IACA,IAAII,QAAQ,EAAE;MACZY,MAAM,CAACZ,QAAQ,GAAGA,QAAQ;IAC5B;IAEA,OAAOY,MAAM;EACf;;EAEA;AACF;AACA;AACA;AACA;EACE,MAAMC,WAAWA,CAAA,EAAG;IAClB,IAAI,CAAC,IAAI,CAACnE,WAAW,EAAE;MACrB,OAAO;QAAE8C,MAAM,EAAE,SAAS;QAAEI,OAAO,EAAE;MAAiB,CAAC;IACzD;IAEA,MAAMP,KAAK,GAAGJ,IAAI,CAACC,GAAG,CAAC,CAAC;IAExB,IAAI;MACF,IAAI4B,IAAI;MAER,IAAI,IAAI,CAACrD,gBAAgB,KAAK5B,QAAQ,EAAE;QACtCiF,IAAI,GAAG,MAAM,IAAIZ,OAAO,CAAC,CAACa,OAAO,EAAEC,MAAM,KAAK;UAC5C,IAAI,CAACtE,WAAW,CAACuE,IAAI,CAAC,CAACvB,GAAG,EAAEkB,MAAM,KAAK;YACrC,IAAIlB,GAAG,EAAEsB,MAAM,CAACtB,GAAG,CAAC,MACfqB,OAAO,CAACH,MAAM,CAAC;UACtB,CAAC,CAAC;QACJ,CAAC,CAAC;MACJ,CAAC,MAAM,IACL,IAAI,CAACnD,gBAAgB,KAAK9B,QAAQ,IAClC,IAAI,CAAC8B,gBAAgB,KAAK7B,OAAO,EACjC;QACAkF,IAAI,GAAG,MAAM,IAAI,CAACpE,WAAW,CAACuE,IAAI,CAAC,CAAC;MACtC,CAAC,MAAM;QACL,OAAO;UAAEzB,MAAM,EAAE,WAAW;UAAEI,OAAO,EAAE;QAA4B,CAAC;MACtE;MAEA,IAAIkB,IAAI,KAAK,MAAM,EAAE;QACnB,OAAO;UACLtB,MAAM,EAAE,SAAS;UACjBC,SAAS,EAAER,IAAI,CAACC,GAAG,CAAC,CAAC,GAAGG;QAC1B,CAAC;MACH;MAEA,OAAO;QACLG,MAAM,EAAE,WAAW;QACnBI,OAAO,EAAEzD,iBAAiB,CAAC,6BAA6B2E,IAAI,EAAE,CAAC;QAC/DrB,SAAS,EAAER,IAAI,CAACC,GAAG,CAAC,CAAC,GAAGG;MAC1B,CAAC;IACH,CAAC,CAAC,OAAOK,GAAG,EAAE;MACZ,OAAO;QACLF,MAAM,EAAE,WAAW;QACnBI,OAAO,EAAEzD,iBAAiB,CAACuD,GAAG,CAACE,OAAO,CAAC;QACvCH,SAAS,EAAER,IAAI,CAACC,GAAG,CAAC,CAAC,GAAGG;MAC1B,CAAC;IACH;EACF;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACE,MAAM6B,kBAAkBA,CAAA,EAAG;IACzB;IACA,IAAI,IAAI,CAAClC,aAAa,CAAC,CAAC,EAAE;MACxB,OAAO;QAAE,GAAG,IAAI,CAAC9B,aAAa,CAAC0D,MAAM;QAAEO,MAAM,EAAE;MAAK,CAAC;IACvD;;IAEA;IACA,IAAI,IAAI,CAAChE,eAAe,EAAE;MACxB;MACA,IAAI,IAAI,CAACD,aAAa,EAAE;QACtB,OAAO;UAAE,GAAG,IAAI,CAACA,aAAa,CAAC0D,MAAM;UAAEO,MAAM,EAAE;QAAK,CAAC;MACvD;MACA;MACA,OAAO,IAAI,CAAChE,eAAe;IAC7B;;IAEA;IACA,IAAI,CAACA,eAAe,GAAG,IAAI,CAACiE,2BAA2B,CAAC,CAAC,CACtDC,IAAI,CAACT,MAAM,IAAI;MACd,IAAI,CAACzD,eAAe,GAAG,IAAI;MAC3B,OAAOyD,MAAM;IACf,CAAC,CAAC,CACDU,KAAK,CAAC5B,GAAG,IAAI;MACZ,IAAI,CAACvC,eAAe,GAAG,IAAI;MAC3B,MAAMuC,GAAG;IACX,CAAC,CAAC;;IAEJ;IACA,IAAI,IAAI,CAACxC,aAAa,EAAE;MACtB;MACA,OAAO;QAAE,GAAG,IAAI,CAACA,aAAa,CAAC0D,MAAM;QAAEO,MAAM,EAAE;MAAK,CAAC;IACvD;;IAEA;IACA,OAAO,IAAI,CAAChE,eAAe;EAC7B;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACE,MAAMiE,2BAA2BA,CAAA,EAAG;IAClC;IACA,MAAMG,QAAQ,GAAG,MAAM,IAAI,CAAC1B,kBAAkB,CAAC,CAAC;;IAEhD;IACA,IAAI2B,WAAW,GAAG,IAAI;IACtB,IAAI,IAAI,CAAC7E,iBAAiB,IAAI,IAAI,CAACD,WAAW,EAAE;MAC9C8E,WAAW,GAAG,MAAM,IAAI,CAACX,WAAW,CAAC,CAAC;IACxC;IAEA,MAAMY,MAAM,GAAG,CAAC,CAAC;IACjB,IAAIF,QAAQ,EAAEE,MAAM,CAACC,QAAQ,GAAGH,QAAQ;IACxC,IAAIC,WAAW,EAAEC,MAAM,CAACE,KAAK,GAAGH,WAAW;IAE3C,MAAMI,QAAQ,GAAGxD,MAAM,CAACmC,MAAM,CAACkB,MAAM,CAAC,CAACrB,GAAG,CAACI,CAAC,IAAIA,CAAC,CAAChB,MAAM,CAAC;IACzD,IAAIiB,aAAa,GAAG,SAAS;IAE7B,IAAImB,QAAQ,CAAClB,IAAI,CAACC,CAAC,IAAIA,CAAC,KAAK,WAAW,CAAC,EAAE;MACzCF,aAAa,GAAG,WAAW;IAC7B,CAAC,MAAM,IAAImB,QAAQ,CAAClB,IAAI,CAACC,CAAC,IAAIA,CAAC,KAAK,UAAU,CAAC,EAAE;MAC/CF,aAAa,GAAG,UAAU;IAC5B;;IAEA;IACA,MAAMG,MAAM,GAAG;MACbpB,MAAM,EAAEiB,aAAa;MACrBtB,SAAS,EAAE,IAAIF,IAAI,CAAC,CAAC,CAAC4C,WAAW,CAAC,CAAC;MACnCJ;IACF,CAAC;IAED,IAAI,CAACvE,aAAa,GAAG;MACnB0D,MAAM;MACNzB,SAAS,EAAEF,IAAI,CAACC,GAAG,CAAC;IACtB,CAAC;IAED,OAAO0B,MAAM;EACf;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;EACE,MAAMkB,eAAeA,CAAA,EAAG;IACtB,IAAI;MACF,MAAMX,MAAM,GAAG,MAAM,IAAI,CAACzD,MAAM,CAACqB,GAAG,CAAC,CAAC;MACtC,IAAIoC,MAAM,EAAE;QACV,OAAOA,MAAM;MACf;MACA;MACA,OAAO,IAAI;IACb,CAAC,CAAC,OAAOzB,GAAG,EAAE;MACZ;MACAqC,OAAO,CAACpC,KAAK,CAAC,GAAG,IAAI,CAAC1C,UAAU,6BAA6B,EAAEyC,GAAG,CAAC;MACnE,MAAMsC,YAAY,GAAG7F,iBAAiB,CAACuD,GAAG,CAACE,OAAO,IAAI,mBAAmB,CAAC;MAC1E,OAAO;QACLJ,MAAM,EAAE,WAAW;QACnBL,SAAS,EAAE,IAAIF,IAAI,CAAC,CAAC,CAAC4C,WAAW,CAAC,CAAC;QACnCJ,MAAM,EAAE;UACNE,KAAK,EAAE;YACLnC,MAAM,EAAE,WAAW;YACnBG,KAAK,EAAEqC;UACT;QACF,CAAC;QACDC,MAAM,EAAE,CAACD,YAAY;MACvB,CAAC;IACH;EACF;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;EACE,MAAME,YAAYA,CAAA,EAAG;IACnB,MAAMtB,MAAM,GAAG,MAAM,IAAI,CAACQ,2BAA2B,CAAC,CAAC;IACvD,MAAM,IAAI,CAAC1D,MAAM,CAACgB,GAAG,CAACkC,MAAM,CAAC;IAE7B,OAAOA,MAAM;EACf;;EAEA;AACF;AACA;EACEuB,UAAUA,CAAA,EAAG;IACX,IAAI,CAACjF,aAAa,GAAG,IAAI;IACzB,IAAI,CAACC,eAAe,GAAG,IAAI;EAC7B;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;EACEiF,iBAAiBA,CAACxB,MAAM,EAAE;IACxB,MAAMqB,MAAM,GAAG,EAAE;IAEjB,IAAIrB,MAAM,CAACa,MAAM,CAACC,QAAQ,EAAE;MAC1B,MAAMW,EAAE,GAAGzB,MAAM,CAACa,MAAM,CAACC,QAAQ;;MAEjC;MACA,IAAIW,EAAE,CAAC7C,MAAM,KAAK,WAAW,KAAK6C,EAAE,CAACzC,OAAO,IAAIyC,EAAE,CAAC1C,KAAK,CAAC,EAAE;QACzD,MAAM2C,MAAM,GAAG,IAAI,CAAChF,mBAAmB,EAAEU,IAAI,IAAI,MAAM;QACvD,MAAMuE,QAAQ,GAAGF,EAAE,CAAC1C,KAAK,IAAI0C,EAAE,CAACzC,OAAO;QACvCqC,MAAM,CAAC3D,IAAI,CAAC,MAAMgE,MAAM,KAAKnG,iBAAiB,CAACoG,QAAQ,CAAC,EAAE,CAAC;MAC7D;;MAEA;MACA,IAAIF,EAAE,CAACrC,QAAQ,EAAE;QACf,KAAK,MAAM,CAAChC,IAAI,EAAEqC,MAAM,CAAC,IAAIjC,MAAM,CAACC,OAAO,CAACgE,EAAE,CAACrC,QAAQ,CAAC,EAAE;UACxD,IAAIK,MAAM,CAACb,MAAM,KAAK,WAAW,EAAE;YACjC,MAAMI,OAAO,GAAGS,MAAM,CAACV,KAAK,IAAIU,MAAM,CAACT,OAAO,IAAI,mBAAmB;YACrEqC,MAAM,CAAC3D,IAAI,CAAC,MAAMN,IAAI,KAAK7B,iBAAiB,CAACyD,OAAO,CAAC,EAAE,CAAC;UAC1D;QACF;MACF;IACF;IAEA,IAAIgB,MAAM,CAACa,MAAM,CAACE,KAAK,IAAIf,MAAM,CAACa,MAAM,CAACE,KAAK,CAACnC,MAAM,KAAK,WAAW,EAAE;MACrE,MAAMI,OAAO,GAAGgB,MAAM,CAACa,MAAM,CAACE,KAAK,CAAChC,KAAK,IAAIiB,MAAM,CAACa,MAAM,CAACE,KAAK,CAAC/B,OAAO,IAAI,mBAAmB;MAC/FqC,MAAM,CAAC3D,IAAI,CAAC,UAAUnC,iBAAiB,CAACyD,OAAO,CAAC,EAAE,CAAC;IACrD;IAEA,OAAOqC,MAAM;EACf;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACEO,aAAaA,CAAA,EAAG;IACd,OAAO,OAAOC,GAAG,EAAEC,GAAG,KAAK;MACzB,IAAI;QACF;QACA,MAAM9B,MAAM,GAAG,MAAM,IAAI,CAACkB,eAAe,CAAC,CAAC;QAE3C,IAAI,CAAClB,MAAM,EAAE;UACX;UACA,MAAM2B,QAAQ,GAAG,8DAA8D;UAC/EG,GAAG,CAAClD,MAAM,CAAC,GAAG,CAAC,CAACmD,IAAI,CAAC;YACnBnD,MAAM,EAAE,WAAW;YACnBL,SAAS,EAAE,IAAIF,IAAI,CAAC,CAAC,CAAC4C,WAAW,CAAC,CAAC;YACnCJ,MAAM,EAAE;cACNE,KAAK,EAAE;gBACLnC,MAAM,EAAE,WAAW;gBACnBG,KAAK,EAAE4C;cACT;YACF,CAAC;YACDN,MAAM,EAAE,CAACM,QAAQ;UACnB,CAAC,CAAC;UACF;QACF;QAEA,MAAMK,UAAU,GAAGhC,MAAM,CAACpB,MAAM,KAAK,WAAW,GAAG,GAAG,GAAG,GAAG;;QAE5D;QACA,MAAMqD,QAAQ,GAAG;UAAE,GAAGjC;QAAO,CAAC;;QAE9B;QACA,IAAIA,MAAM,CAACpB,MAAM,KAAK,SAAS,IAAI,CAACoB,MAAM,CAACqB,MAAM,EAAE;UACjD,MAAMA,MAAM,GAAG,IAAI,CAACG,iBAAiB,CAACxB,MAAM,CAAC;UAC7C,IAAIqB,MAAM,CAACnC,MAAM,GAAG,CAAC,EAAE;YACrB+C,QAAQ,CAACZ,MAAM,GAAGA,MAAM;UAC1B;QACF;QAEAS,GAAG,CAAClD,MAAM,CAACoD,UAAU,CAAC,CAACD,IAAI,CAACE,QAAQ,CAAC;MACvC,CAAC,CAAC,OAAOnD,GAAG,EAAE;QACZqC,OAAO,CAACpC,KAAK,CAAC,GAAG,IAAI,CAAC1C,UAAU,uBAAuB,EAAEyC,GAAG,CAAC;QAC7D,MAAM6C,QAAQ,GAAGpG,iBAAiB,CAACuD,GAAG,CAACE,OAAO,CAAC;QAC/C8C,GAAG,CAAClD,MAAM,CAAC,GAAG,CAAC,CAACmD,IAAI,CAAC;UACnBnD,MAAM,EAAE,WAAW;UACnBL,SAAS,EAAE,IAAIF,IAAI,CAAC,CAAC,CAAC4C,WAAW,CAAC,CAAC;UACnCJ,MAAM,EAAE;YACNE,KAAK,EAAE;cACLnC,MAAM,EAAE,WAAW;cACnBG,KAAK,EAAE4C;YACT;UACF,CAAC;UACDN,MAAM,EAAE,CAACM,QAAQ;QACnB,CAAC,CAAC;MACJ;IACF,CAAC;EACH;;EAEA;AACF;AACA;AACA;AACA;AACA;EACEO,sBAAsBA,CAACC,GAAG,EAAEC,IAAI,GAAG,SAAS,EAAE;IAC5CD,GAAG,CAAChE,GAAG,CAACiE,IAAI,EAAE,IAAI,CAACR,aAAa,CAAC,CAAC,CAAC;IACnCT,OAAO,CAACkB,IAAI,CAAC,GAAG,IAAI,CAAChG,UAAU,kCAAkC+F,IAAI,EAAE,CAAC;EAC1E;;EAEA;AACF;AACA;AACA;EACE,MAAME,OAAOA,CAAA,EAAG;IACd,KAAK,MAAM,CAAClF,IAAI,EAAEsB,IAAI,CAAC,IAAI,IAAI,CAAClC,cAAc,EAAE;MAC9C,IAAI;QACF,MAAMkC,IAAI,CAAC6D,GAAG,CAAC,CAAC;MAClB,CAAC,CAAC,OAAOzD,GAAG,EAAE;QACZqC,OAAO,CAACpC,KAAK,CAAC,GAAG,IAAI,CAAC1C,UAAU,gCAAgCe,IAAI,GAAG,EAAE0B,GAAG,CAAC;MAC/E;IACF;IACA,IAAI,CAACtC,cAAc,CAACgG,KAAK,CAAC,CAAC;EAC7B;AACF;AAEAC,MAAM,CAACC,OAAO,GAAG;EAAE/G;AAAkB,CAAC","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adalo/metrics",
3
- "version": "0.1.124",
3
+ "version": "0.1.125",
4
4
  "description": "Reusable metrics utilities for Node.js and Laravel apps",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -605,9 +605,9 @@ class HealthCheckClient {
605
605
  * Register health check endpoint on an Express app.
606
606
  *
607
607
  * @param {import('express').Application} app - Express application
608
- * @param {string} [path='/health-status'] - Path for the health endpoint
608
+ * @param {string} [path='/health'] - Path for the health endpoint
609
609
  */
610
- registerHealthEndpoint(app, path = '/health-status') {
610
+ registerHealthEndpoint(app, path = '/health') {
611
611
  app.get(path, this.healthHandler())
612
612
  console.info(`${this.prefixLogs} Registered health endpoint at ${path}`)
613
613
  }