@adalo/metrics 0.1.131 → 0.1.133

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.
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @param {string} url
3
+ * @returns {string} postgres | mysql
4
+ */
5
+ export function getDatabaseType(url: string): string;
6
+ /**
7
+ * @param {string} url
8
+ * @param {string} [type]
9
+ * @returns {{ host: string, port: number, user: string, password: string, database: string } | null}
10
+ */
11
+ export function parseConnectionUrl(url: string, type?: string | undefined): {
12
+ host: string;
13
+ port: number;
14
+ user: string;
15
+ password: string;
16
+ database: string;
17
+ } | null;
18
+ /**
19
+ * @param {string} env
20
+ * @param {string} url
21
+ * @param {number} connectionTimeoutMs
22
+ * @returns {{ pool: any, type: string }}
23
+ */
24
+ export function createDatabasePool(env: string, url: string, connectionTimeoutMs: number): {
25
+ pool: any;
26
+ type: string;
27
+ };
28
+ /**
29
+ * @param {any} pool
30
+ * @param {string} type
31
+ * @returns {Promise<void>}
32
+ */
33
+ export function runHealthCheck(pool: any, type: string): Promise<void>;
34
+ /**
35
+ * @param {any} pool
36
+ * @returns {Promise<void>}
37
+ */
38
+ export function closePool(pool: any): Promise<void>;
39
+ export const DB_TYPE_POSTGRES: "postgres";
40
+ export const DB_TYPE_MYSQL: "mysql";
41
+ //# sourceMappingURL=databaseChecker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"databaseChecker.d.ts","sourceRoot":"","sources":["../../src/health/databaseChecker.js"],"names":[],"mappings":"AAKA;;;GAGG;AACH,qCAHW,MAAM,GACJ,MAAM,CAYlB;AAED;;;;GAIG;AACH,wCAJW,MAAM,8BAEJ;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAiBnG;AAED;;;;;GAKG;AACH,wCALW,MAAM,OACN,MAAM,uBACN,MAAM;UACI,GAAG;UAAQ,MAAM;EA+BrC;AAED;;;;GAIG;AACH,qCAJW,GAAG,QACH,MAAM,GACJ,QAAQ,IAAI,CAAC,CAQzB;AAED;;;GAGG;AACH,gCAHW,GAAG,GACD,QAAQ,IAAI,CAAC,CAQzB;AArGD,0CAAmC;AACnC,oCAA6B"}
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+
3
+ const {
4
+ Pool
5
+ } = require('pg');
6
+ const DB_TYPE_POSTGRES = 'postgres';
7
+ const DB_TYPE_MYSQL = 'mysql';
8
+
9
+ /**
10
+ * @param {string} url
11
+ * @returns {string} postgres | mysql
12
+ */
13
+ function getDatabaseType(url) {
14
+ if (!url || typeof url !== 'string') return DB_TYPE_POSTGRES;
15
+ const lower = url.toLowerCase();
16
+ if (lower.startsWith('mysql://') || lower.startsWith('mysql2://')) {
17
+ return DB_TYPE_MYSQL;
18
+ }
19
+ if (lower.startsWith('mariadb://')) {
20
+ return DB_TYPE_MYSQL;
21
+ }
22
+ return DB_TYPE_POSTGRES;
23
+ }
24
+
25
+ /**
26
+ * @param {string} url
27
+ * @param {string} [type]
28
+ * @returns {{ host: string, port: number, user: string, password: string, database: string } | null}
29
+ */
30
+ function parseConnectionUrl(url, type) {
31
+ try {
32
+ const parsed = new URL(url);
33
+ const defaultPort = type === DB_TYPE_MYSQL ? 3306 : 5432;
34
+ const defaultDb = type === DB_TYPE_MYSQL ? 'mysql' : 'postgres';
35
+ return {
36
+ host: parsed.hostname || 'localhost',
37
+ port: parseInt(parsed.port || String(defaultPort), 10),
38
+ user: parsed.username || '',
39
+ password: parsed.password || '',
40
+ database: (parsed.pathname || '/').replace(/^\//, '') || defaultDb
41
+ };
42
+ } catch {
43
+ return null;
44
+ }
45
+ }
46
+
47
+ /**
48
+ * @param {string} env
49
+ * @param {string} url
50
+ * @param {number} connectionTimeoutMs
51
+ * @returns {{ pool: any, type: string }}
52
+ */
53
+ function createDatabasePool(env, url, connectionTimeoutMs) {
54
+ const type = getDatabaseType(url);
55
+ if (type === DB_TYPE_MYSQL) {
56
+ const mysql = require('mysql2/promise');
57
+ const config = parseConnectionUrl(url, DB_TYPE_MYSQL);
58
+ if (!config) {
59
+ throw new Error(`Invalid MySQL URL for ${env}`);
60
+ }
61
+ const pool = mysql.createPool({
62
+ host: config.host,
63
+ port: config.port,
64
+ user: config.user,
65
+ password: config.password,
66
+ database: config.database,
67
+ connectionLimit: 1,
68
+ connectTimeout: connectionTimeoutMs,
69
+ waitForConnections: false
70
+ });
71
+ return {
72
+ pool,
73
+ type: DB_TYPE_MYSQL
74
+ };
75
+ }
76
+ const pool = new Pool({
77
+ connectionString: url,
78
+ max: 1,
79
+ idleTimeoutMillis: 30000,
80
+ connectionTimeoutMillis: connectionTimeoutMs
81
+ });
82
+ return {
83
+ pool,
84
+ type: DB_TYPE_POSTGRES
85
+ };
86
+ }
87
+
88
+ /**
89
+ * @param {any} pool
90
+ * @param {string} type
91
+ * @returns {Promise<void>}
92
+ */
93
+ async function runHealthCheck(pool, type) {
94
+ if (type === DB_TYPE_MYSQL) {
95
+ const [rows] = await pool.execute('SELECT 1');
96
+ return;
97
+ }
98
+ await pool.query('SELECT 1');
99
+ }
100
+
101
+ /**
102
+ * @param {any} pool
103
+ * @returns {Promise<void>}
104
+ */
105
+ async function closePool(pool) {
106
+ try {
107
+ await pool.end();
108
+ } catch {
109
+ // ignore
110
+ }
111
+ }
112
+ module.exports = {
113
+ getDatabaseType,
114
+ parseConnectionUrl,
115
+ createDatabasePool,
116
+ runHealthCheck,
117
+ closePool,
118
+ DB_TYPE_POSTGRES,
119
+ DB_TYPE_MYSQL
120
+ };
121
+ //# sourceMappingURL=databaseChecker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"databaseChecker.js","names":["Pool","require","DB_TYPE_POSTGRES","DB_TYPE_MYSQL","getDatabaseType","url","lower","toLowerCase","startsWith","parseConnectionUrl","type","parsed","URL","defaultPort","defaultDb","host","hostname","port","parseInt","String","user","username","password","database","pathname","replace","createDatabasePool","env","connectionTimeoutMs","mysql","config","Error","pool","createPool","connectionLimit","connectTimeout","waitForConnections","connectionString","max","idleTimeoutMillis","connectionTimeoutMillis","runHealthCheck","rows","execute","query","closePool","end","module","exports"],"sources":["../../src/health/databaseChecker.js"],"sourcesContent":["const { Pool } = require('pg')\n\nconst DB_TYPE_POSTGRES = 'postgres'\nconst DB_TYPE_MYSQL = 'mysql'\n\n/**\n * @param {string} url\n * @returns {string} postgres | mysql\n */\nfunction getDatabaseType(url) {\n if (!url || typeof url !== 'string') return DB_TYPE_POSTGRES\n const lower = url.toLowerCase()\n if (lower.startsWith('mysql://') || lower.startsWith('mysql2://')) {\n return DB_TYPE_MYSQL\n }\n if (lower.startsWith('mariadb://')) {\n return DB_TYPE_MYSQL\n }\n return DB_TYPE_POSTGRES\n}\n\n/**\n * @param {string} url\n * @param {string} [type]\n * @returns {{ host: string, port: number, user: string, password: string, database: string } | null}\n */\nfunction parseConnectionUrl(url, type) {\n try {\n const parsed = new URL(url)\n const defaultPort = type === DB_TYPE_MYSQL ? 3306 : 5432\n const defaultDb = type === DB_TYPE_MYSQL ? 'mysql' : 'postgres'\n return {\n host: parsed.hostname || 'localhost',\n port: parseInt(parsed.port || String(defaultPort), 10),\n user: parsed.username || '',\n password: parsed.password || '',\n database: (parsed.pathname || '/').replace(/^\\//, '') || defaultDb,\n }\n } catch {\n return null\n }\n}\n\n/**\n * @param {string} env\n * @param {string} url\n * @param {number} connectionTimeoutMs\n * @returns {{ pool: any, type: string }}\n */\nfunction createDatabasePool(env, url, connectionTimeoutMs) {\n const type = getDatabaseType(url)\n\n if (type === DB_TYPE_MYSQL) {\n const mysql = require('mysql2/promise')\n const config = parseConnectionUrl(url, DB_TYPE_MYSQL)\n if (!config) {\n throw new Error(`Invalid MySQL URL for ${env}`)\n }\n const pool = mysql.createPool({\n host: config.host,\n port: config.port,\n user: config.user,\n password: config.password,\n database: config.database,\n connectionLimit: 1,\n connectTimeout: connectionTimeoutMs,\n waitForConnections: false,\n })\n return { pool, type: DB_TYPE_MYSQL }\n }\n\n const pool = new Pool({\n connectionString: url,\n max: 1,\n idleTimeoutMillis: 30000,\n connectionTimeoutMillis: connectionTimeoutMs,\n })\n return { pool, type: DB_TYPE_POSTGRES }\n}\n\n/**\n * @param {any} pool\n * @param {string} type\n * @returns {Promise<void>}\n */\nasync function runHealthCheck(pool, type) {\n if (type === DB_TYPE_MYSQL) {\n const [rows] = await pool.execute('SELECT 1')\n return\n }\n await pool.query('SELECT 1')\n}\n\n/**\n * @param {any} pool\n * @returns {Promise<void>}\n */\nasync function closePool(pool) {\n try {\n await pool.end()\n } catch {\n // ignore\n }\n}\n\nmodule.exports = {\n getDatabaseType,\n parseConnectionUrl,\n createDatabasePool,\n runHealthCheck,\n closePool,\n DB_TYPE_POSTGRES,\n DB_TYPE_MYSQL,\n}\n"],"mappings":"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","ignoreList":[]}
@@ -6,18 +6,20 @@
6
6
  export class HealthCheckCache {
7
7
  /**
8
8
  * @param {Object} options
9
- * @param {any} [options.redisClient] - Redis client instance (optional)
10
- * @param {string} [options.appName] - Application name for cache key
11
- * @param {number} [options.cacheTtlMs=60000] - Cache TTL in milliseconds
9
+ * @param {any} [options.redisClient]
10
+ * @param {string} [options.cacheKey] - Redis key (e.g. 'health:database:status')
11
+ * @param {string} [options.appName] - Used when cacheKey not set: healthcheck:${appName}
12
+ * @param {number} [options.staleThresholdMs]
12
13
  */
13
14
  constructor(options?: {
14
15
  redisClient?: any;
16
+ cacheKey?: string | undefined;
15
17
  appName?: string | undefined;
16
- cacheTtlMs?: number | undefined;
18
+ staleThresholdMs?: number | undefined;
17
19
  });
18
20
  redisClient: any;
19
21
  appName: string;
20
- cacheTtlMs: number;
22
+ staleThresholdMs: number;
21
23
  cacheKey: string;
22
24
  /** In-memory fallback cache */
23
25
  _memoryCache: any;
@@ -1 +1 @@
1
- {"version":3,"file":"healthCheckCache.d.ts","sourceRoot":"","sources":["../../src/health/healthCheckCache.js"],"names":[],"mappings":"AAOA;;;;GAIG;AACH;IACE;;;;;OAKG;IACH;QAJyB,WAAW,GAAzB,GAAG;QACc,OAAO;QACP,UAAU;OAsBrC;IAnBC,iBAA8C;IAC9C,gBACgE;IAChE,mBAA8C;IAC9C,iBAA6C;IAE7C,+BAA+B;IAC/B,kBAAwB;IACxB,2BAAiC;IAG/B,qCAA4D;IAC5D,yBAA2B;IAS/B;;;;OAIG;IACH,6BAgCC;IAED;;;;;OAKG;IACH,OAHa,QAAQ,MAAM,GAAG,IAAI,CAAC,CAsDlC;IAED;;;;OAIG;IACH,YAHW,MAAM,GACJ,QAAQ,IAAI,CAAC,CAqCzB;IAED;;;OAGG;IACH,SAFa,QAAQ,IAAI,CAAC,CAyBzB;IAED;;;OAGG;IACH,oBAFa,OAAO,CAInB;CACF"}
1
+ {"version":3,"file":"healthCheckCache.d.ts","sourceRoot":"","sources":["../../src/health/healthCheckCache.js"],"names":[],"mappings":"AAOA;;;;GAIG;AACH;IACE;;;;;;OAMG;IACH;QALyB,WAAW,GAAzB,GAAG;QACc,QAAQ;QACR,OAAO;QACP,gBAAgB;OAuB3C;IApBC,iBAA8C;IAC9C,gBACgE;IAChE,yBAA2D;IAC3D,iBACmD;IAEnD,+BAA+B;IAC/B,kBAAwB;IACxB,2BAAiC;IAG/B,qCAA4D;IAC5D,yBAA2B;IAS/B;;;;OAIG;IACH,6BAgCC;IAED;;;;;OAKG;IACH,OAHa,QAAQ,MAAM,GAAG,IAAI,CAAC,CAgDlC;IAED;;;;OAIG;IACH,YAHW,MAAM,GACJ,QAAQ,IAAI,CAAC,CAqCzB;IAED;;;OAGG;IACH,SAFa,QAAQ,IAAI,CAAC,CAyBzB;IAED;;;OAGG;IACH,oBAFa,OAAO,CAInB;CACF"}
@@ -15,15 +15,16 @@ const {
15
15
  class HealthCheckCache {
16
16
  /**
17
17
  * @param {Object} options
18
- * @param {any} [options.redisClient] - Redis client instance (optional)
19
- * @param {string} [options.appName] - Application name for cache key
20
- * @param {number} [options.cacheTtlMs=60000] - Cache TTL in milliseconds
18
+ * @param {any} [options.redisClient]
19
+ * @param {string} [options.cacheKey] - Redis key (e.g. 'health:database:status')
20
+ * @param {string} [options.appName] - Used when cacheKey not set: healthcheck:${appName}
21
+ * @param {number} [options.staleThresholdMs]
21
22
  */
22
23
  constructor(options = {}) {
23
24
  this.redisClient = options.redisClient || null;
24
25
  this.appName = options.appName || process.env.BUILD_APP_NAME || 'unknown-app';
25
- this.cacheTtlMs = options.cacheTtlMs ?? 60_000;
26
- this.cacheKey = `healthcheck:${this.appName}`;
26
+ this.staleThresholdMs = options.staleThresholdMs ?? 180_000;
27
+ this.cacheKey = options.cacheKey || `healthcheck:${this.appName}`;
27
28
 
28
29
  /** In-memory fallback cache */
29
30
  this._memoryCache = null;
@@ -92,12 +93,9 @@ class HealthCheckCache {
92
93
  try {
93
94
  const cached = JSON.parse(cachedStr);
94
95
  if (cached.result && cached.timestamp) {
95
- const age = Date.now() - cached.timestamp;
96
- if (age < this.cacheTtlMs) {
97
- this._memoryCache = cached.result;
98
- this._memoryCacheTimestamp = cached.timestamp;
99
- return cached.result;
100
- }
96
+ this._memoryCache = cached.result;
97
+ this._memoryCacheTimestamp = cached.timestamp;
98
+ return cached.result;
101
99
  }
102
100
  } catch (parseErr) {
103
101
  console.warn(`[HealthCheckCache] Failed to parse Redis cache:`, parseErr.message);
@@ -110,10 +108,7 @@ class HealthCheckCache {
110
108
  }
111
109
  }
112
110
  if (this._memoryCache && this._memoryCacheTimestamp) {
113
- const age = Date.now() - this._memoryCacheTimestamp;
114
- if (age < this.cacheTtlMs) {
115
- return this._memoryCache;
116
- }
111
+ return this._memoryCache;
117
112
  }
118
113
  return null;
119
114
  }
@@ -133,7 +128,7 @@ class HealthCheckCache {
133
128
  if (await this._checkRedisAvailable()) {
134
129
  try {
135
130
  const cacheStr = JSON.stringify(cacheData);
136
- const ttlSeconds = Math.ceil(this.cacheTtlMs / 1000) + 10;
131
+ const ttlSeconds = Math.ceil(this.staleThresholdMs / 1000) + 60;
137
132
  if (this._redisClientType === REDIS_V3) {
138
133
  await new Promise((resolve, reject) => {
139
134
  this.redisClient.setex(this.cacheKey, ttlSeconds, cacheStr, err => {
@@ -1 +1 @@
1
- {"version":3,"file":"healthCheckCache.js","names":["getRedisClientType","REDIS_V4","IOREDIS","REDIS_V3","require","HealthCheckCache","constructor","options","redisClient","appName","process","env","BUILD_APP_NAME","cacheTtlMs","cacheKey","_memoryCache","_memoryCacheTimestamp","_redisClientType","_redisAvailable","console","warn","_checkRedisAvailable","pong","Promise","resolve","reject","ping","err","result","message","get","cachedStr","cached","JSON","parse","timestamp","age","Date","now","parseErr","redisErr","Error","set","cacheData","cacheStr","stringify","ttlSeconds","Math","ceil","setex","clear","del","isRedisAvailable","module","exports"],"sources":["../../src/health/healthCheckCache.js"],"sourcesContent":["const {\n getRedisClientType,\n REDIS_V4,\n IOREDIS,\n REDIS_V3,\n} = require('../redisUtils')\n\n/**\n * HealthCheckCache provides a shared cache layer for health check results.\n * It uses Redis if available for cross-process sharing, with graceful fallback\n * to in-memory cache if Redis is not configured or unavailable.\n */\nclass HealthCheckCache {\n /**\n * @param {Object} options\n * @param {any} [options.redisClient] - Redis client instance (optional)\n * @param {string} [options.appName] - Application name for cache key\n * @param {number} [options.cacheTtlMs=60000] - Cache TTL in milliseconds\n */\n constructor(options = {}) {\n this.redisClient = options.redisClient || null\n this.appName =\n options.appName || process.env.BUILD_APP_NAME || 'unknown-app'\n this.cacheTtlMs = options.cacheTtlMs ?? 60_000\n this.cacheKey = `healthcheck:${this.appName}`\n\n /** In-memory fallback cache */\n this._memoryCache = null\n this._memoryCacheTimestamp = null\n\n if (this.redisClient) {\n this._redisClientType = getRedisClientType(this.redisClient)\n this._redisAvailable = true\n } else {\n this._redisAvailable = false\n console.warn(\n `[HealthCheckCache] Redis not configured for ${this.appName}, using in-memory cache only (not shared across processes)`\n )\n }\n }\n\n /**\n * Checks if Redis is available and working.\n * @returns {Promise<boolean>}\n * @private\n */\n async _checkRedisAvailable() {\n if (!this.redisClient || !this._redisAvailable) {\n return false\n }\n\n try {\n let pong\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 false\n }\n return pong === 'PONG'\n } catch (err) {\n if (this._redisAvailable) {\n console.warn(\n `[HealthCheckCache] Redis became unavailable: ${err.message}`\n )\n this._redisAvailable = false\n }\n return false\n }\n }\n\n /**\n * Gets cached health check result from Redis (if available) or in-memory cache.\n * Throws error if Redis is configured but read fails (so caller can return proper error format).\n * @returns {Promise<Object | null>} Cached result or null\n * @throws {Error} If Redis is configured but read fails\n */\n async get() {\n if (this.redisClient) {\n try {\n let cachedStr\n if (this._redisClientType === REDIS_V3) {\n cachedStr = await new Promise((resolve, reject) => {\n this.redisClient.get(this.cacheKey, (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 cachedStr = await this.redisClient.get(this.cacheKey)\n }\n\n if (cachedStr) {\n try {\n const cached = JSON.parse(cachedStr)\n if (cached.result && cached.timestamp) {\n const age = Date.now() - cached.timestamp\n if (age < this.cacheTtlMs) {\n this._memoryCache = cached.result\n this._memoryCacheTimestamp = cached.timestamp\n return cached.result\n }\n }\n } catch (parseErr) {\n console.warn(\n `[HealthCheckCache] Failed to parse Redis cache:`,\n parseErr.message\n )\n }\n }\n return null\n } catch (redisErr) {\n this._redisAvailable = false\n throw new Error(`Redis cache read failed: ${redisErr.message}`)\n }\n }\n\n if (this._memoryCache && this._memoryCacheTimestamp) {\n const age = Date.now() - this._memoryCacheTimestamp\n if (age < this.cacheTtlMs) {\n return this._memoryCache\n }\n }\n\n return null\n }\n\n /**\n * Sets cached health check result in Redis (if available) and in-memory.\n * @param {Object} result - Health check result to cache\n * @returns {Promise<void>}\n */\n async set(result) {\n const cacheData = {\n result,\n timestamp: Date.now(),\n }\n\n this._memoryCache = result\n this._memoryCacheTimestamp = cacheData.timestamp\n\n if (await this._checkRedisAvailable()) {\n try {\n const cacheStr = JSON.stringify(cacheData)\n const ttlSeconds = Math.ceil(this.cacheTtlMs / 1000) + 10\n\n if (this._redisClientType === REDIS_V3) {\n await new Promise((resolve, reject) => {\n this.redisClient.setex(this.cacheKey, ttlSeconds, cacheStr, err => {\n if (err) reject(err)\n else resolve()\n })\n })\n } else if (\n this._redisClientType === REDIS_V4 ||\n this._redisClientType === IOREDIS\n ) {\n await this.redisClient.setex(this.cacheKey, ttlSeconds, cacheStr)\n }\n } catch (redisErr) {\n console.warn(\n `[HealthCheckCache] Redis write failed (in-memory cache updated):`,\n redisErr.message\n )\n this._redisAvailable = false\n }\n }\n }\n\n /**\n * Clears the cache (both Redis and in-memory).\n * @returns {Promise<void>}\n */\n async clear() {\n this._memoryCache = null\n this._memoryCacheTimestamp = null\n\n if (await this._checkRedisAvailable()) {\n try {\n if (this._redisClientType === REDIS_V3) {\n await new Promise((resolve, reject) => {\n this.redisClient.del(this.cacheKey, err => {\n if (err) reject(err)\n else resolve()\n })\n })\n } else if (\n this._redisClientType === REDIS_V4 ||\n this._redisClientType === IOREDIS\n ) {\n await this.redisClient.del(this.cacheKey)\n }\n } catch (redisErr) {\n console.warn(`[HealthCheckCache] Redis clear failed:`, redisErr.message)\n }\n }\n }\n\n /**\n * Checks if Redis is configured and available.\n * @returns {boolean}\n */\n isRedisAvailable() {\n return this._redisAvailable && this.redisClient !== null\n }\n}\n\nmodule.exports = { HealthCheckCache }\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"healthCheckCache.js","names":["getRedisClientType","REDIS_V4","IOREDIS","REDIS_V3","require","HealthCheckCache","constructor","options","redisClient","appName","process","env","BUILD_APP_NAME","staleThresholdMs","cacheKey","_memoryCache","_memoryCacheTimestamp","_redisClientType","_redisAvailable","console","warn","_checkRedisAvailable","pong","Promise","resolve","reject","ping","err","result","message","get","cachedStr","cached","JSON","parse","timestamp","parseErr","redisErr","Error","set","cacheData","Date","now","cacheStr","stringify","ttlSeconds","Math","ceil","setex","clear","del","isRedisAvailable","module","exports"],"sources":["../../src/health/healthCheckCache.js"],"sourcesContent":["const {\n getRedisClientType,\n REDIS_V4,\n IOREDIS,\n REDIS_V3,\n} = require('../redisUtils')\n\n/**\n * HealthCheckCache provides a shared cache layer for health check results.\n * It uses Redis if available for cross-process sharing, with graceful fallback\n * to in-memory cache if Redis is not configured or unavailable.\n */\nclass HealthCheckCache {\n /**\n * @param {Object} options\n * @param {any} [options.redisClient]\n * @param {string} [options.cacheKey] - Redis key (e.g. 'health:database:status')\n * @param {string} [options.appName] - Used when cacheKey not set: healthcheck:${appName}\n * @param {number} [options.staleThresholdMs]\n */\n constructor(options = {}) {\n this.redisClient = options.redisClient || null\n this.appName =\n options.appName || process.env.BUILD_APP_NAME || 'unknown-app'\n this.staleThresholdMs = options.staleThresholdMs ?? 180_000\n this.cacheKey =\n options.cacheKey || `healthcheck:${this.appName}`\n\n /** In-memory fallback cache */\n this._memoryCache = null\n this._memoryCacheTimestamp = null\n\n if (this.redisClient) {\n this._redisClientType = getRedisClientType(this.redisClient)\n this._redisAvailable = true\n } else {\n this._redisAvailable = false\n console.warn(\n `[HealthCheckCache] Redis not configured for ${this.appName}, using in-memory cache only (not shared across processes)`\n )\n }\n }\n\n /**\n * Checks if Redis is available and working.\n * @returns {Promise<boolean>}\n * @private\n */\n async _checkRedisAvailable() {\n if (!this.redisClient || !this._redisAvailable) {\n return false\n }\n\n try {\n let pong\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 false\n }\n return pong === 'PONG'\n } catch (err) {\n if (this._redisAvailable) {\n console.warn(\n `[HealthCheckCache] Redis became unavailable: ${err.message}`\n )\n this._redisAvailable = false\n }\n return false\n }\n }\n\n /**\n * Gets cached health check result from Redis (if available) or in-memory cache.\n * Throws error if Redis is configured but read fails (so caller can return proper error format).\n * @returns {Promise<Object | null>} Cached result or null\n * @throws {Error} If Redis is configured but read fails\n */\n async get() {\n if (this.redisClient) {\n try {\n let cachedStr\n if (this._redisClientType === REDIS_V3) {\n cachedStr = await new Promise((resolve, reject) => {\n this.redisClient.get(this.cacheKey, (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 cachedStr = await this.redisClient.get(this.cacheKey)\n }\n\n if (cachedStr) {\n try {\n const cached = JSON.parse(cachedStr)\n if (cached.result && cached.timestamp) {\n this._memoryCache = cached.result\n this._memoryCacheTimestamp = cached.timestamp\n return cached.result\n }\n } catch (parseErr) {\n console.warn(\n `[HealthCheckCache] Failed to parse Redis cache:`,\n parseErr.message\n )\n }\n }\n return null\n } catch (redisErr) {\n this._redisAvailable = false\n throw new Error(`Redis cache read failed: ${redisErr.message}`)\n }\n }\n\n if (this._memoryCache && this._memoryCacheTimestamp) {\n return this._memoryCache\n }\n\n return null\n }\n\n /**\n * Sets cached health check result in Redis (if available) and in-memory.\n * @param {Object} result - Health check result to cache\n * @returns {Promise<void>}\n */\n async set(result) {\n const cacheData = {\n result,\n timestamp: Date.now(),\n }\n\n this._memoryCache = result\n this._memoryCacheTimestamp = cacheData.timestamp\n\n if (await this._checkRedisAvailable()) {\n try {\n const cacheStr = JSON.stringify(cacheData)\n const ttlSeconds = Math.ceil(this.staleThresholdMs / 1000) + 60\n\n if (this._redisClientType === REDIS_V3) {\n await new Promise((resolve, reject) => {\n this.redisClient.setex(this.cacheKey, ttlSeconds, cacheStr, err => {\n if (err) reject(err)\n else resolve()\n })\n })\n } else if (\n this._redisClientType === REDIS_V4 ||\n this._redisClientType === IOREDIS\n ) {\n await this.redisClient.setex(this.cacheKey, ttlSeconds, cacheStr)\n }\n } catch (redisErr) {\n console.warn(\n `[HealthCheckCache] Redis write failed (in-memory cache updated):`,\n redisErr.message\n )\n this._redisAvailable = false\n }\n }\n }\n\n /**\n * Clears the cache (both Redis and in-memory).\n * @returns {Promise<void>}\n */\n async clear() {\n this._memoryCache = null\n this._memoryCacheTimestamp = null\n\n if (await this._checkRedisAvailable()) {\n try {\n if (this._redisClientType === REDIS_V3) {\n await new Promise((resolve, reject) => {\n this.redisClient.del(this.cacheKey, err => {\n if (err) reject(err)\n else resolve()\n })\n })\n } else if (\n this._redisClientType === REDIS_V4 ||\n this._redisClientType === IOREDIS\n ) {\n await this.redisClient.del(this.cacheKey)\n }\n } catch (redisErr) {\n console.warn(`[HealthCheckCache] Redis clear failed:`, redisErr.message)\n }\n }\n }\n\n /**\n * Checks if Redis is configured and available.\n * @returns {boolean}\n */\n isRedisAvailable() {\n return this._redisAvailable && this.redisClient !== null\n }\n}\n\nmodule.exports = { HealthCheckCache }\n"],"mappings":"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","ignoreList":[]}
@@ -1,12 +1,12 @@
1
1
  export type HealthResource = {
2
- name: string;
2
+ env: string;
3
3
  url?: string;
4
4
  } | {
5
- name: string;
6
- client: any;
5
+ env: string;
6
+ client?: any;
7
7
  };
8
8
  /**
9
- * @typedef {{ name: string, url?: string } | { name: string, client: any }} HealthResource
9
+ * @typedef {{ env: string, url?: string } | { env: string, client?: any }} HealthResource
10
10
  */
11
11
  export class HealthCheckClient {
12
12
  /**
@@ -14,13 +14,15 @@ export class HealthCheckClient {
14
14
  * @param {HealthResource[]} options.resources - Must include Redis resource with client
15
15
  * @param {number} [options.cacheTtlMs]
16
16
  * @param {Object} [options.config]
17
- * @param {string} [options.appName]
17
+ * @param {string} [options.appName] - For cache key: healthcheck:${appName}
18
+ * @param {string} [options.cacheKey] - Redis key (overrides appName)
18
19
  */
19
20
  constructor(options?: {
20
21
  resources: HealthResource[];
21
22
  cacheTtlMs?: number | undefined;
22
23
  config?: Object | undefined;
23
24
  appName?: string | undefined;
25
+ cacheKey?: string | undefined;
24
26
  });
25
27
  healthConfig: {
26
28
  constructor?: Function | undefined;
@@ -61,13 +63,21 @@ export class HealthCheckClient {
61
63
  timestamp: number;
62
64
  } | null;
63
65
  _refreshPromise: any;
64
- _databasePools: Map<any, any>;
66
+ /** @type {Map<string, { pool: any, type: string }>} */
67
+ _databasePools: Map<string, {
68
+ pool: any;
69
+ type: string;
70
+ }>;
65
71
  /** @type {HealthResource[]} */
66
72
  _resources: HealthResource[];
67
73
  _redisClientType: string | undefined;
68
74
  _cache: HealthCheckCache;
75
+ _getEnv(resource: any): any;
69
76
  _getRedisClientForCache(): any;
70
- _getPool(name: any, url: any): any;
77
+ _getPool(env: any, url: any): {
78
+ pool: any;
79
+ type: string;
80
+ } | undefined;
71
81
  _isCacheValid(): boolean;
72
82
  _checkDatabase(resource: any): Promise<{
73
83
  status: string;
@@ -126,7 +136,6 @@ export class HealthCheckClient {
126
136
  }>;
127
137
  clearCache(): void;
128
138
  healthHandler(): (req: any, res: any) => Promise<void>;
129
- registerHealthEndpoint(app: any, path?: string): void;
130
139
  cleanup(): Promise<void>;
131
140
  }
132
141
  /** @type {{ cacheTtlMs: number, checkIntervalMs: number, staleThresholdMs: number, checkTimeoutMs: number }} */
@@ -1 +1 @@
1
- {"version":3,"file":"healthCheckClient.d.ts","sourceRoot":"","sources":["../../src/health/healthCheckClient.js"],"names":[],"mappings":"6BA6Da;IAAE,MAAM,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,MAAM,MAAM,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE;AAD3E;;GAEG;AACH;IACE;;;;;;OAMG;IACH;QALqC,SAAS,EAAnC,cAAc,EAAE;QACC,UAAU;QACV,MAAM;QACN,OAAO;OA8BlC;IA3BC;;;;;;;;;;;;MAAmE;IAInE,mBAA8C;IAC9C,gBACgE;IAEhE,mBAAmD;IAEnD;;;;;;;;;;;;;;;;;;;;;aAAyB;IACzB,qBAA2B;IAC3B,8BAA+B;IAE/B,+BAA+B;IAC/B,YADW,cAAc,EAAE,CACc;IAIvC,qCAAuD;IAGzD,yBAIE;IAGJ,+BAGC;IAED,mCAaC;IAED,yBAGC;IAED;;;;;;OAiBC;IAED;;;;;;OA0BC;IAED;;;;;;;;;;;;;;;;;;OAiCC;IAED,kDAWC;IAED,mCA2BC;IAED,gCAkBC;IAED;;;;;;;;;;;;;;;;;;OAIC;IAED,mBAGC;IAED,uDA0CC;IAED,sDAGC;IAED,yBASC;CACF;AA3UD,gHAAgH;AAChH,oCADW;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAM3G"}
1
+ {"version":3,"file":"healthCheckClient.d.ts","sourceRoot":"","sources":["../../src/health/healthCheckClient.js"],"names":[],"mappings":"6BAiEa;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,GAAG,CAAA;CAAE;AAD1E;;GAEG;AACH;IACE;;;;;;;OAOG;IACH;QANqC,SAAS,EAAnC,cAAc,EAAE;QACC,UAAU;QACV,MAAM;QACN,OAAO;QACP,QAAQ;OAgCnC;IA7BC;;;;;;;;;;;;MAAmE;IAInE,mBAA8C;IAC9C,gBACgE;IAEhE,mBAAmD;IAEnD;;;;;;;;;;;;;;;;;;;;;aAAyB;IACzB,qBAA2B;IAC3B,uDAAuD;IACvD;cAD+B,GAAG;cAAQ,MAAM;OACjB;IAE/B,+BAA+B;IAC/B,YADW,cAAc,EAAE,CACc;IAIvC,qCAAuD;IAGzD,yBAKE;IAGJ,4BAEC;IAED,+BAGC;IAED;cA5BiC,GAAG;cAAQ,MAAM;kBAsCjD;IAED,yBAGC;IAED;;;;;;OAiBC;IAED;;;;;;OA0BC;IAED;;;;;;;;;;;;;;;;;;OAiCC;IAED,kDAeC;IAED,mCA2BC;IAED,gCAiBC;IAED;;;;;;;;;;;;;;;;;;OAIC;IAED,mBAGC;IAED,uDAiCC;IAED,yBASC;CACF;AApUD,gHAAgH;AAChH,oCADW;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAM3G"}
@@ -1,8 +1,10 @@
1
1
  "use strict";
2
2
 
3
3
  const {
4
- Pool
5
- } = require('pg');
4
+ createDatabasePool,
5
+ runHealthCheck,
6
+ closePool
7
+ } = require('./databaseChecker');
6
8
  const {
7
9
  getRedisClientType,
8
10
  REDIS_V4,
@@ -57,7 +59,7 @@ function maskSensitiveData(text) {
57
59
  }
58
60
 
59
61
  /**
60
- * @typedef {{ name: string, url?: string } | { name: string, client: any }} HealthResource
62
+ * @typedef {{ env: string, url?: string } | { env: string, client?: any }} HealthResource
61
63
  */
62
64
  class HealthCheckClient {
63
65
  /**
@@ -65,7 +67,8 @@ class HealthCheckClient {
65
67
  * @param {HealthResource[]} options.resources - Must include Redis resource with client
66
68
  * @param {number} [options.cacheTtlMs]
67
69
  * @param {Object} [options.config]
68
- * @param {string} [options.appName]
70
+ * @param {string} [options.appName] - For cache key: healthcheck:${appName}
71
+ * @param {string} [options.cacheKey] - Redis key (overrides appName)
69
72
  */
70
73
  constructor(options = {}) {
71
74
  this.healthConfig = {
@@ -80,6 +83,7 @@ class HealthCheckClient {
80
83
  this.prefixLogs = `[${this.appName}] [HealthCheck]`;
81
84
  this._cachedResult = null;
82
85
  this._refreshPromise = null;
86
+ /** @type {Map<string, { pool: any, type: string }>} */
83
87
  this._databasePools = new Map();
84
88
 
85
89
  /** @type {HealthResource[]} */
@@ -90,43 +94,50 @@ class HealthCheckClient {
90
94
  }
91
95
  this._cache = new HealthCheckCache({
92
96
  redisClient: redisClient || null,
97
+ cacheKey: options.cacheKey,
93
98
  appName: this.appName,
94
- cacheTtlMs: this.cacheTtlMs
99
+ staleThresholdMs: this.healthConfig.staleThresholdMs
95
100
  });
96
101
  }
102
+ _getEnv(resource) {
103
+ return resource.env ?? resource.name;
104
+ }
97
105
  _getRedisClientForCache() {
98
106
  const redisResource = this._resources.find(r => 'client' in r && r.client);
99
107
  return redisResource?.client || null;
100
108
  }
101
- _getPool(name, url) {
102
- if (!this._databasePools.has(name)) {
103
- this._databasePools.set(name, new Pool({
104
- connectionString: url,
105
- max: 1,
106
- idleTimeoutMillis: 30000,
107
- connectionTimeoutMillis: this.healthConfig.checkTimeoutMs
108
- }));
109
+ _getPool(env, url) {
110
+ if (!this._databasePools.has(env)) {
111
+ const {
112
+ pool,
113
+ type
114
+ } = createDatabasePool(env, url, this.healthConfig.checkTimeoutMs);
115
+ this._databasePools.set(env, {
116
+ pool,
117
+ type
118
+ });
109
119
  }
110
- return this._databasePools.get(name);
120
+ return this._databasePools.get(env);
111
121
  }
112
122
  _isCacheValid() {
113
123
  if (!this._cachedResult) return false;
114
124
  return Date.now() - this._cachedResult.timestamp < this.cacheTtlMs;
115
125
  }
116
126
  async _checkDatabase(resource) {
117
- const {
118
- name
119
- } = resource;
120
- const url = 'url' in resource ? resource.url : process.env[name];
127
+ const env = this._getEnv(resource);
128
+ const url = 'url' in resource ? resource.url : process.env[env];
121
129
  if (!url) {
122
130
  return {
123
131
  status: 'error',
124
- error: `Env ${name} not set`
132
+ error: `Env ${env} not set`
125
133
  };
126
134
  }
127
135
  try {
128
- const pool = this._getPool(name, url);
129
- await pool.query('SELECT 1');
136
+ const {
137
+ pool,
138
+ type
139
+ } = this._getPool(env, url);
140
+ await runHealthCheck(pool, type);
130
141
  return {
131
142
  status: 'ok'
132
143
  };
@@ -176,13 +187,11 @@ class HealthCheckClient {
176
187
  async _performHealthCheckInternal() {
177
188
  const resources = {};
178
189
  for (const resource of this._resources) {
179
- const {
180
- name
181
- } = resource;
190
+ const env = this._getEnv(resource);
182
191
  if ('client' in resource && resource.client) {
183
- resources[name] = await this._checkRedis(resource);
192
+ resources[env] = await this._checkRedis(resource);
184
193
  } else {
185
- resources[name] = await this._checkDatabase(resource);
194
+ resources[env] = await this._checkDatabase(resource);
186
195
  }
187
196
  }
188
197
  const sortedResources = Object.keys(resources).sort().reduce((acc, key) => {
@@ -210,6 +219,9 @@ class HealthCheckClient {
210
219
  ...result,
211
220
  isStale,
212
221
  status: isStale ? 'stale' : result.status,
222
+ ...(isStale && {
223
+ error: 'Health check data is stale, health-check worker may not be running. Resource statuses are unknown.'
224
+ }),
213
225
  ...(cached && {
214
226
  cached: true
215
227
  })
@@ -244,19 +256,12 @@ class HealthCheckClient {
244
256
  return null;
245
257
  } catch (err) {
246
258
  console.error(`${this.prefixLogs} Failed to read from cache:`, err);
247
- const errorMessage = maskSensitiveData(err.message || 'Cache read failed');
248
- const redisResource = this._resources.find(r => 'client' in r);
249
- const redisName = redisResource?.name || 'REDIS_URL';
250
259
  return {
251
260
  status: 'error',
252
- lastCheckAt: Date.now(),
253
- resources: {
254
- [redisName]: {
255
- status: 'error',
256
- error: errorMessage
257
- }
258
- },
261
+ lastCheckAt: null,
262
+ resources: {},
259
263
  isStale: true,
264
+ error: 'Redis unavailable, unable to read health status of other resources',
260
265
  config: this.healthConfig
261
266
  };
262
267
  }
@@ -275,50 +280,38 @@ class HealthCheckClient {
275
280
  try {
276
281
  const result = await this.getCachedResult();
277
282
  if (!result) {
278
- const redisResource = this._resources.find(r => 'client' in r);
279
- const redisName = redisResource?.name || 'REDIS_URL';
280
283
  res.status(503).json({
281
284
  status: 'error',
282
285
  lastCheckAt: null,
283
- resources: {
284
- [redisName]: {
285
- status: 'error',
286
- error: 'Health check cache not available. Worker may not be running.'
287
- }
288
- },
286
+ resources: {},
289
287
  isStale: true,
288
+ error: 'No health check data yet, health-check worker may not be running',
290
289
  config: this.healthConfig
291
290
  });
292
291
  return;
293
292
  }
294
- res.status(result.status === 'ok' ? 200 : 503).json(result);
293
+ const statusCode = result.status === 'ok' ? 200 : 503;
294
+ res.status(statusCode).json(result);
295
295
  } catch (err) {
296
296
  console.error(`${this.prefixLogs} Health check failed:`, err);
297
- const redisResource = this._resources.find(r => 'client' in r);
298
- const redisName = redisResource?.name || 'REDIS_URL';
299
297
  res.status(503).json({
300
298
  status: 'error',
301
299
  lastCheckAt: null,
302
- resources: {
303
- [redisName]: {
304
- status: 'error',
305
- error: maskSensitiveData(err.message)
306
- }
307
- },
300
+ resources: {},
308
301
  isStale: true,
302
+ error: 'Redis unavailable, unable to read health status of other resources',
309
303
  config: this.healthConfig
310
304
  });
311
305
  }
312
306
  };
313
307
  }
314
- registerHealthEndpoint(app, path = '/health') {
315
- app.get(path, this.healthHandler());
316
- console.info(`${this.prefixLogs} Registered health endpoint at ${path}`);
317
- }
318
308
  async cleanup() {
319
- for (const [, pool] of this._databasePools) {
309
+ for (const [, {
310
+ pool,
311
+ type
312
+ }] of this._databasePools) {
320
313
  try {
321
- await pool.end();
314
+ await closePool(pool);
322
315
  } catch (err) {
323
316
  console.error(`${this.prefixLogs} Error closing database pool:`, err);
324
317
  }