@adalo/metrics 0.1.122 → 0.1.124

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.
Files changed (63) hide show
  1. package/lib/health/healthCheckCache.d.ts +57 -0
  2. package/lib/health/healthCheckCache.d.ts.map +1 -0
  3. package/lib/health/healthCheckCache.js +200 -0
  4. package/lib/health/healthCheckCache.js.map +1 -0
  5. package/lib/{healthCheckClient.d.ts → health/healthCheckClient.d.ts} +59 -15
  6. package/lib/health/healthCheckClient.d.ts.map +1 -0
  7. package/lib/{healthCheckClient.js → health/healthCheckClient.js} +232 -49
  8. package/lib/health/healthCheckClient.js.map +1 -0
  9. package/lib/health/healthCheckUtils.d.ts +54 -0
  10. package/lib/health/healthCheckUtils.d.ts.map +1 -0
  11. package/lib/health/healthCheckUtils.js +142 -0
  12. package/lib/health/healthCheckUtils.js.map +1 -0
  13. package/lib/health/healthCheckWorker.d.ts +2 -0
  14. package/lib/health/healthCheckWorker.d.ts.map +1 -0
  15. package/lib/health/healthCheckWorker.js +64 -0
  16. package/lib/health/healthCheckWorker.js.map +1 -0
  17. package/lib/index.d.ts +8 -6
  18. package/lib/index.d.ts.map +1 -1
  19. package/lib/index.js +28 -6
  20. package/lib/index.js.map +1 -1
  21. package/lib/metrics/baseMetricsClient.d.ts.map +1 -0
  22. package/lib/metrics/baseMetricsClient.js.map +1 -0
  23. package/lib/metrics/metricsClient.d.ts.map +1 -0
  24. package/lib/metrics/metricsClient.js.map +1 -0
  25. package/lib/metrics/metricsDatabaseClient.d.ts.map +1 -0
  26. package/lib/metrics/metricsDatabaseClient.js.map +1 -0
  27. package/lib/metrics/metricsQueueRedisClient.d.ts.map +1 -0
  28. package/lib/{metricsQueueRedisClient.js → metrics/metricsQueueRedisClient.js} +2 -2
  29. package/lib/metrics/metricsQueueRedisClient.js.map +1 -0
  30. package/lib/metrics/metricsRedisClient.d.ts.map +1 -0
  31. package/lib/{metricsRedisClient.js → metrics/metricsRedisClient.js} +1 -1
  32. package/lib/metrics/metricsRedisClient.js.map +1 -0
  33. package/package.json +1 -1
  34. package/src/health/healthCheckCache.js +237 -0
  35. package/src/{healthCheckClient.js → health/healthCheckClient.js} +226 -49
  36. package/src/health/healthCheckUtils.js +143 -0
  37. package/src/health/healthCheckWorker.js +61 -0
  38. package/src/index.ts +8 -6
  39. package/src/{metricsQueueRedisClient.js → metrics/metricsQueueRedisClient.js} +2 -2
  40. package/src/{metricsRedisClient.js → metrics/metricsRedisClient.js} +1 -1
  41. package/lib/baseMetricsClient.d.ts.map +0 -1
  42. package/lib/baseMetricsClient.js.map +0 -1
  43. package/lib/healthCheckClient.d.ts.map +0 -1
  44. package/lib/healthCheckClient.js.map +0 -1
  45. package/lib/metricsClient.d.ts.map +0 -1
  46. package/lib/metricsClient.js.map +0 -1
  47. package/lib/metricsDatabaseClient.d.ts.map +0 -1
  48. package/lib/metricsDatabaseClient.js.map +0 -1
  49. package/lib/metricsQueueRedisClient.d.ts.map +0 -1
  50. package/lib/metricsQueueRedisClient.js.map +0 -1
  51. package/lib/metricsRedisClient.d.ts.map +0 -1
  52. package/lib/metricsRedisClient.js.map +0 -1
  53. /package/lib/{baseMetricsClient.d.ts → metrics/baseMetricsClient.d.ts} +0 -0
  54. /package/lib/{baseMetricsClient.js → metrics/baseMetricsClient.js} +0 -0
  55. /package/lib/{metricsClient.d.ts → metrics/metricsClient.d.ts} +0 -0
  56. /package/lib/{metricsClient.js → metrics/metricsClient.js} +0 -0
  57. /package/lib/{metricsDatabaseClient.d.ts → metrics/metricsDatabaseClient.d.ts} +0 -0
  58. /package/lib/{metricsDatabaseClient.js → metrics/metricsDatabaseClient.js} +0 -0
  59. /package/lib/{metricsQueueRedisClient.d.ts → metrics/metricsQueueRedisClient.d.ts} +0 -0
  60. /package/lib/{metricsRedisClient.d.ts → metrics/metricsRedisClient.d.ts} +0 -0
  61. /package/src/{baseMetricsClient.js → metrics/baseMetricsClient.js} +0 -0
  62. /package/src/{metricsClient.js → metrics/metricsClient.js} +0 -0
  63. /package/src/{metricsDatabaseClient.js → metrics/metricsDatabaseClient.js} +0 -0
@@ -0,0 +1,57 @@
1
+ /**
2
+ * HealthCheckCache provides a shared cache layer for health check results.
3
+ * It uses Redis if available for cross-process sharing, with graceful fallback
4
+ * to in-memory cache if Redis is not configured or unavailable.
5
+ */
6
+ export class HealthCheckCache {
7
+ /**
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
12
+ */
13
+ constructor(options?: {
14
+ redisClient?: any;
15
+ appName?: string | undefined;
16
+ cacheTtlMs?: number | undefined;
17
+ });
18
+ redisClient: any;
19
+ appName: string;
20
+ cacheTtlMs: number;
21
+ cacheKey: string;
22
+ /** In-memory fallback cache */
23
+ _memoryCache: any;
24
+ _memoryCacheTimestamp: any;
25
+ _redisClientType: string | undefined;
26
+ _redisAvailable: boolean;
27
+ /**
28
+ * Checks if Redis is available and working.
29
+ * @returns {Promise<boolean>}
30
+ * @private
31
+ */
32
+ private _checkRedisAvailable;
33
+ /**
34
+ * Gets cached health check result from Redis (if available) or in-memory cache.
35
+ * Throws error if Redis is configured but read fails (so caller can return proper error format).
36
+ * @returns {Promise<Object | null>} Cached result or null
37
+ * @throws {Error} If Redis is configured but read fails
38
+ */
39
+ get(): Promise<Object | null>;
40
+ /**
41
+ * Sets cached health check result in Redis (if available) and in-memory.
42
+ * @param {Object} result - Health check result to cache
43
+ * @returns {Promise<void>}
44
+ */
45
+ set(result: Object): Promise<void>;
46
+ /**
47
+ * Clears the cache (both Redis and in-memory).
48
+ * @returns {Promise<void>}
49
+ */
50
+ clear(): Promise<void>;
51
+ /**
52
+ * Checks if Redis is configured and available.
53
+ * @returns {boolean}
54
+ */
55
+ isRedisAvailable(): boolean;
56
+ }
57
+ //# sourceMappingURL=healthCheckCache.d.ts.map
@@ -0,0 +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;OAqBrC;IAlBC,iBAA8C;IAC9C,gBAA6E;IAC7E,mBAAiD;IACjD,iBAA6C;IAE7C,+BAA+B;IAC/B,kBAAwB;IACxB,2BAAiC;IAG/B,qCAA4D;IAC5D,yBAA2B;IAS/B;;;;OAIG;IACH,6BAiCC;IAED;;;;;OAKG;IACH,OAHa,QAAQ,MAAM,GAAG,IAAI,CAAC,CA2DlC;IAED;;;;OAIG;IACH,YAHW,MAAM,GACJ,QAAQ,IAAI,CAAC,CA6CzB;IAED;;;OAGG;IACH,SAFa,QAAQ,IAAI,CAAC,CA4BzB;IAED;;;OAGG;IACH,oBAFa,OAAO,CAInB;CACF"}
@@ -0,0 +1,200 @@
1
+ "use strict";
2
+
3
+ const {
4
+ getRedisClientType,
5
+ REDIS_V4,
6
+ IOREDIS,
7
+ REDIS_V3
8
+ } = require('../redisUtils');
9
+
10
+ /**
11
+ * HealthCheckCache provides a shared cache layer for health check results.
12
+ * It uses Redis if available for cross-process sharing, with graceful fallback
13
+ * to in-memory cache if Redis is not configured or unavailable.
14
+ */
15
+ class HealthCheckCache {
16
+ /**
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
21
+ */
22
+ constructor(options = {}) {
23
+ this.redisClient = options.redisClient || null;
24
+ this.appName = options.appName || process.env.BUILD_APP_NAME || 'unknown-app';
25
+ this.cacheTtlMs = options.cacheTtlMs ?? 60 * 1000;
26
+ this.cacheKey = `healthcheck:${this.appName}`;
27
+
28
+ /** In-memory fallback cache */
29
+ this._memoryCache = null;
30
+ this._memoryCacheTimestamp = null;
31
+ if (this.redisClient) {
32
+ this._redisClientType = getRedisClientType(this.redisClient);
33
+ this._redisAvailable = true;
34
+ } else {
35
+ this._redisAvailable = false;
36
+ console.warn(`[HealthCheckCache] Redis not configured for ${this.appName}, using in-memory cache only (not shared across processes)`);
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Checks if Redis is available and working.
42
+ * @returns {Promise<boolean>}
43
+ * @private
44
+ */
45
+ async _checkRedisAvailable() {
46
+ if (!this.redisClient || !this._redisAvailable) {
47
+ return false;
48
+ }
49
+ try {
50
+ let pong;
51
+ if (this._redisClientType === REDIS_V3) {
52
+ pong = await new Promise((resolve, reject) => {
53
+ this.redisClient.ping((err, result) => {
54
+ if (err) reject(err);else resolve(result);
55
+ });
56
+ });
57
+ } else if (this._redisClientType === REDIS_V4 || this._redisClientType === IOREDIS) {
58
+ pong = await this.redisClient.ping();
59
+ } else {
60
+ return false;
61
+ }
62
+ return pong === 'PONG';
63
+ } catch (err) {
64
+ // Redis not available
65
+ if (this._redisAvailable) {
66
+ console.warn(`[HealthCheckCache] Redis became unavailable: ${err.message}`);
67
+ this._redisAvailable = false;
68
+ }
69
+ return false;
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Gets cached health check result from Redis (if available) or in-memory cache.
75
+ * Throws error if Redis is configured but read fails (so caller can return proper error format).
76
+ * @returns {Promise<Object | null>} Cached result or null
77
+ * @throws {Error} If Redis is configured but read fails
78
+ */
79
+ async get() {
80
+ // If Redis is configured, we MUST read from it (don't fall back to memory)
81
+ if (this.redisClient) {
82
+ try {
83
+ let cachedStr;
84
+ if (this._redisClientType === REDIS_V3) {
85
+ cachedStr = await new Promise((resolve, reject) => {
86
+ this.redisClient.get(this.cacheKey, (err, result) => {
87
+ if (err) reject(err);else resolve(result);
88
+ });
89
+ });
90
+ } else if (this._redisClientType === REDIS_V4 || this._redisClientType === IOREDIS) {
91
+ cachedStr = await this.redisClient.get(this.cacheKey);
92
+ }
93
+ if (cachedStr) {
94
+ try {
95
+ const cached = JSON.parse(cachedStr);
96
+ if (cached.result && cached.timestamp) {
97
+ const age = Date.now() - cached.timestamp;
98
+ if (age < this.cacheTtlMs) {
99
+ // Also update in-memory cache as backup
100
+ this._memoryCache = cached.result;
101
+ this._memoryCacheTimestamp = cached.timestamp;
102
+ return cached.result;
103
+ }
104
+ }
105
+ } catch (parseErr) {
106
+ console.warn(`[HealthCheckCache] Failed to parse Redis cache:`, parseErr.message);
107
+ }
108
+ }
109
+ // No cache in Redis - return null (worker may not have run yet)
110
+ return null;
111
+ } catch (redisErr) {
112
+ // Redis read failed - throw error so caller can return proper error format
113
+ this._redisAvailable = false;
114
+ throw new Error(`Redis cache read failed: ${redisErr.message}`);
115
+ }
116
+ }
117
+
118
+ // No Redis configured - fall back to in-memory cache
119
+ if (this._memoryCache && this._memoryCacheTimestamp) {
120
+ const age = Date.now() - this._memoryCacheTimestamp;
121
+ if (age < this.cacheTtlMs) {
122
+ return this._memoryCache;
123
+ }
124
+ }
125
+ return null;
126
+ }
127
+
128
+ /**
129
+ * Sets cached health check result in Redis (if available) and in-memory.
130
+ * @param {Object} result - Health check result to cache
131
+ * @returns {Promise<void>}
132
+ */
133
+ async set(result) {
134
+ const cacheData = {
135
+ result,
136
+ timestamp: Date.now()
137
+ };
138
+
139
+ // Update in-memory cache
140
+ this._memoryCache = result;
141
+ this._memoryCacheTimestamp = cacheData.timestamp;
142
+
143
+ // Try to update Redis if available
144
+ if (await this._checkRedisAvailable()) {
145
+ try {
146
+ const cacheStr = JSON.stringify(cacheData);
147
+ const ttlSeconds = Math.ceil(this.cacheTtlMs / 1000) + 10;
148
+ if (this._redisClientType === REDIS_V3) {
149
+ await new Promise((resolve, reject) => {
150
+ this.redisClient.setex(this.cacheKey, ttlSeconds, cacheStr, err => {
151
+ if (err) reject(err);else resolve();
152
+ });
153
+ });
154
+ } else if (this._redisClientType === REDIS_V4 || this._redisClientType === IOREDIS) {
155
+ await this.redisClient.setex(this.cacheKey, ttlSeconds, cacheStr);
156
+ }
157
+ } catch (redisErr) {
158
+ // Redis write failed, but in-memory cache is updated
159
+ console.warn(`[HealthCheckCache] Redis write failed (in-memory cache updated):`, redisErr.message);
160
+ this._redisAvailable = false;
161
+ }
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Clears the cache (both Redis and in-memory).
167
+ * @returns {Promise<void>}
168
+ */
169
+ async clear() {
170
+ this._memoryCache = null;
171
+ this._memoryCacheTimestamp = null;
172
+ if (await this._checkRedisAvailable()) {
173
+ try {
174
+ if (this._redisClientType === REDIS_V3) {
175
+ await new Promise((resolve, reject) => {
176
+ this.redisClient.del(this.cacheKey, err => {
177
+ if (err) reject(err);else resolve();
178
+ });
179
+ });
180
+ } else if (this._redisClientType === REDIS_V4 || this._redisClientType === IOREDIS) {
181
+ await this.redisClient.del(this.cacheKey);
182
+ }
183
+ } catch (redisErr) {
184
+ console.warn(`[HealthCheckCache] Redis clear failed:`, redisErr.message);
185
+ }
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Checks if Redis is configured and available.
191
+ * @returns {boolean}
192
+ */
193
+ isRedisAvailable() {
194
+ return this._redisAvailable && this.redisClient !== null;
195
+ }
196
+ }
197
+ module.exports = {
198
+ HealthCheckCache
199
+ };
200
+ //# sourceMappingURL=healthCheckCache.js.map
@@ -0,0 +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 = options.appName || process.env.BUILD_APP_NAME || 'unknown-app'\n this.cacheTtlMs = options.cacheTtlMs ?? 60 * 1000\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 // Redis not available\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 Redis is configured, we MUST read from it (don't fall back to memory)\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 // Also update in-memory cache as backup\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 // No cache in Redis - return null (worker may not have run yet)\n return null\n } catch (redisErr) {\n // Redis read failed - throw error so caller can return proper error format\n this._redisAvailable = false\n throw new Error(`Redis cache read failed: ${redisErr.message}`)\n }\n }\n\n // No Redis configured - fall back to in-memory cache\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 // Update in-memory cache\n this._memoryCache = result\n this._memoryCacheTimestamp = cacheData.timestamp\n\n // Try to update Redis if available\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(\n this.cacheKey,\n ttlSeconds,\n cacheStr,\n (err) => {\n if (err) reject(err)\n else resolve()\n }\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 // Redis write failed, but in-memory cache is updated\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(\n `[HealthCheckCache] Redis clear failed:`,\n redisErr.message\n )\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\n"],"mappings":";;AAAA,MAAM;EACJA,kBAAkB;EAClBC,QAAQ;EACRC,OAAO;EACPC;AACF,CAAC,GAAGC,OAAO,CAAC,eAAe,CAAC;;AAE5B;AACA;AACA;AACA;AACA;AACA,MAAMC,gBAAgB,CAAC;EACrB;AACF;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,OAAO,GAAGF,OAAO,CAACE,OAAO,IAAIC,OAAO,CAACC,GAAG,CAACC,cAAc,IAAI,aAAa;IAC7E,IAAI,CAACC,UAAU,GAAGN,OAAO,CAACM,UAAU,IAAI,EAAE,GAAG,IAAI;IACjD,IAAI,CAACC,QAAQ,GAAG,eAAe,IAAI,CAACL,OAAO,EAAE;;IAE7C;IACA,IAAI,CAACM,YAAY,GAAG,IAAI;IACxB,IAAI,CAACC,qBAAqB,GAAG,IAAI;IAEjC,IAAI,IAAI,CAACR,WAAW,EAAE;MACpB,IAAI,CAACS,gBAAgB,GAAGjB,kBAAkB,CAAC,IAAI,CAACQ,WAAW,CAAC;MAC5D,IAAI,CAACU,eAAe,GAAG,IAAI;IAC7B,CAAC,MAAM;MACL,IAAI,CAACA,eAAe,GAAG,KAAK;MAC5BC,OAAO,CAACC,IAAI,CACV,+CAA+C,IAAI,CAACX,OAAO,4DAC7D,CAAC;IACH;EACF;;EAEA;AACF;AACA;AACA;AACA;EACE,MAAMY,oBAAoBA,CAAA,EAAG;IAC3B,IAAI,CAAC,IAAI,CAACb,WAAW,IAAI,CAAC,IAAI,CAACU,eAAe,EAAE;MAC9C,OAAO,KAAK;IACd;IAEA,IAAI;MACF,IAAII,IAAI;MACR,IAAI,IAAI,CAACL,gBAAgB,KAAKd,QAAQ,EAAE;QACtCmB,IAAI,GAAG,MAAM,IAAIC,OAAO,CAAC,CAACC,OAAO,EAAEC,MAAM,KAAK;UAC5C,IAAI,CAACjB,WAAW,CAACkB,IAAI,CAAC,CAACC,GAAG,EAAEC,MAAM,KAAK;YACrC,IAAID,GAAG,EAAEF,MAAM,CAACE,GAAG,CAAC,MACfH,OAAO,CAACI,MAAM,CAAC;UACtB,CAAC,CAAC;QACJ,CAAC,CAAC;MACJ,CAAC,MAAM,IACL,IAAI,CAACX,gBAAgB,KAAKhB,QAAQ,IAClC,IAAI,CAACgB,gBAAgB,KAAKf,OAAO,EACjC;QACAoB,IAAI,GAAG,MAAM,IAAI,CAACd,WAAW,CAACkB,IAAI,CAAC,CAAC;MACtC,CAAC,MAAM;QACL,OAAO,KAAK;MACd;MACA,OAAOJ,IAAI,KAAK,MAAM;IACxB,CAAC,CAAC,OAAOK,GAAG,EAAE;MACZ;MACA,IAAI,IAAI,CAACT,eAAe,EAAE;QACxBC,OAAO,CAACC,IAAI,CACV,gDAAgDO,GAAG,CAACE,OAAO,EAC7D,CAAC;QACD,IAAI,CAACX,eAAe,GAAG,KAAK;MAC9B;MACA,OAAO,KAAK;IACd;EACF;;EAEA;AACF;AACA;AACA;AACA;AACA;EACE,MAAMY,GAAGA,CAAA,EAAG;IACV;IACA,IAAI,IAAI,CAACtB,WAAW,EAAE;MACpB,IAAI;QACF,IAAIuB,SAAS;QACb,IAAI,IAAI,CAACd,gBAAgB,KAAKd,QAAQ,EAAE;UACtC4B,SAAS,GAAG,MAAM,IAAIR,OAAO,CAAC,CAACC,OAAO,EAAEC,MAAM,KAAK;YACjD,IAAI,CAACjB,WAAW,CAACsB,GAAG,CAAC,IAAI,CAAChB,QAAQ,EAAE,CAACa,GAAG,EAAEC,MAAM,KAAK;cACnD,IAAID,GAAG,EAAEF,MAAM,CAACE,GAAG,CAAC,MACfH,OAAO,CAACI,MAAM,CAAC;YACtB,CAAC,CAAC;UACJ,CAAC,CAAC;QACJ,CAAC,MAAM,IACL,IAAI,CAACX,gBAAgB,KAAKhB,QAAQ,IAClC,IAAI,CAACgB,gBAAgB,KAAKf,OAAO,EACjC;UACA6B,SAAS,GAAG,MAAM,IAAI,CAACvB,WAAW,CAACsB,GAAG,CAAC,IAAI,CAAChB,QAAQ,CAAC;QACvD;QAEA,IAAIiB,SAAS,EAAE;UACb,IAAI;YACF,MAAMC,MAAM,GAAGC,IAAI,CAACC,KAAK,CAACH,SAAS,CAAC;YACpC,IAAIC,MAAM,CAACJ,MAAM,IAAII,MAAM,CAACG,SAAS,EAAE;cACrC,MAAMC,GAAG,GAAGC,IAAI,CAACC,GAAG,CAAC,CAAC,GAAGN,MAAM,CAACG,SAAS;cACzC,IAAIC,GAAG,GAAG,IAAI,CAACvB,UAAU,EAAE;gBACzB;gBACA,IAAI,CAACE,YAAY,GAAGiB,MAAM,CAACJ,MAAM;gBACjC,IAAI,CAACZ,qBAAqB,GAAGgB,MAAM,CAACG,SAAS;gBAC7C,OAAOH,MAAM,CAACJ,MAAM;cACtB;YACF;UACF,CAAC,CAAC,OAAOW,QAAQ,EAAE;YACjBpB,OAAO,CAACC,IAAI,CACV,iDAAiD,EACjDmB,QAAQ,CAACV,OACX,CAAC;UACH;QACF;QACA;QACA,OAAO,IAAI;MACb,CAAC,CAAC,OAAOW,QAAQ,EAAE;QACjB;QACA,IAAI,CAACtB,eAAe,GAAG,KAAK;QAC5B,MAAM,IAAIuB,KAAK,CAAC,4BAA4BD,QAAQ,CAACX,OAAO,EAAE,CAAC;MACjE;IACF;;IAEA;IACA,IAAI,IAAI,CAACd,YAAY,IAAI,IAAI,CAACC,qBAAqB,EAAE;MACnD,MAAMoB,GAAG,GAAGC,IAAI,CAACC,GAAG,CAAC,CAAC,GAAG,IAAI,CAACtB,qBAAqB;MACnD,IAAIoB,GAAG,GAAG,IAAI,CAACvB,UAAU,EAAE;QACzB,OAAO,IAAI,CAACE,YAAY;MAC1B;IACF;IAEA,OAAO,IAAI;EACb;;EAEA;AACF;AACA;AACA;AACA;EACE,MAAM2B,GAAGA,CAACd,MAAM,EAAE;IAChB,MAAMe,SAAS,GAAG;MAChBf,MAAM;MACNO,SAAS,EAAEE,IAAI,CAACC,GAAG,CAAC;IACtB,CAAC;;IAED;IACA,IAAI,CAACvB,YAAY,GAAGa,MAAM;IAC1B,IAAI,CAACZ,qBAAqB,GAAG2B,SAAS,CAACR,SAAS;;IAEhD;IACA,IAAI,MAAM,IAAI,CAACd,oBAAoB,CAAC,CAAC,EAAE;MACrC,IAAI;QACF,MAAMuB,QAAQ,GAAGX,IAAI,CAACY,SAAS,CAACF,SAAS,CAAC;QAC1C,MAAMG,UAAU,GAAGC,IAAI,CAACC,IAAI,CAAC,IAAI,CAACnC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE;QAEzD,IAAI,IAAI,CAACI,gBAAgB,KAAKd,QAAQ,EAAE;UACtC,MAAM,IAAIoB,OAAO,CAAC,CAACC,OAAO,EAAEC,MAAM,KAAK;YACrC,IAAI,CAACjB,WAAW,CAACyC,KAAK,CACpB,IAAI,CAACnC,QAAQ,EACbgC,UAAU,EACVF,QAAQ,EACPjB,GAAG,IAAK;cACP,IAAIA,GAAG,EAAEF,MAAM,CAACE,GAAG,CAAC,MACfH,OAAO,CAAC,CAAC;YAChB,CACF,CAAC;UACH,CAAC,CAAC;QACJ,CAAC,MAAM,IACL,IAAI,CAACP,gBAAgB,KAAKhB,QAAQ,IAClC,IAAI,CAACgB,gBAAgB,KAAKf,OAAO,EACjC;UACA,MAAM,IAAI,CAACM,WAAW,CAACyC,KAAK,CAAC,IAAI,CAACnC,QAAQ,EAAEgC,UAAU,EAAEF,QAAQ,CAAC;QACnE;MACF,CAAC,CAAC,OAAOJ,QAAQ,EAAE;QACjB;QACArB,OAAO,CAACC,IAAI,CACV,kEAAkE,EAClEoB,QAAQ,CAACX,OACX,CAAC;QACD,IAAI,CAACX,eAAe,GAAG,KAAK;MAC9B;IACF;EACF;;EAEA;AACF;AACA;AACA;EACE,MAAMgC,KAAKA,CAAA,EAAG;IACZ,IAAI,CAACnC,YAAY,GAAG,IAAI;IACxB,IAAI,CAACC,qBAAqB,GAAG,IAAI;IAEjC,IAAI,MAAM,IAAI,CAACK,oBAAoB,CAAC,CAAC,EAAE;MACrC,IAAI;QACF,IAAI,IAAI,CAACJ,gBAAgB,KAAKd,QAAQ,EAAE;UACtC,MAAM,IAAIoB,OAAO,CAAC,CAACC,OAAO,EAAEC,MAAM,KAAK;YACrC,IAAI,CAACjB,WAAW,CAAC2C,GAAG,CAAC,IAAI,CAACrC,QAAQ,EAAGa,GAAG,IAAK;cAC3C,IAAIA,GAAG,EAAEF,MAAM,CAACE,GAAG,CAAC,MACfH,OAAO,CAAC,CAAC;YAChB,CAAC,CAAC;UACJ,CAAC,CAAC;QACJ,CAAC,MAAM,IACL,IAAI,CAACP,gBAAgB,KAAKhB,QAAQ,IAClC,IAAI,CAACgB,gBAAgB,KAAKf,OAAO,EACjC;UACA,MAAM,IAAI,CAACM,WAAW,CAAC2C,GAAG,CAAC,IAAI,CAACrC,QAAQ,CAAC;QAC3C;MACF,CAAC,CAAC,OAAO0B,QAAQ,EAAE;QACjBrB,OAAO,CAACC,IAAI,CACV,wCAAwC,EACxCoB,QAAQ,CAACX,OACX,CAAC;MACH;IACF;EACF;;EAEA;AACF;AACA;AACA;EACEuB,gBAAgBA,CAAA,EAAG;IACjB,OAAO,IAAI,CAAClC,eAAe,IAAI,IAAI,CAACV,WAAW,KAAK,IAAI;EAC1D;AACF;AAEA6C,MAAM,CAACC,OAAO,GAAG;EAAEjD;AAAiB,CAAC","ignoreList":[]}
@@ -5,7 +5,11 @@ export type ComponentHealth = {
5
5
  */
6
6
  status: HealthStatus;
7
7
  /**
8
- * - Optional status message
8
+ * - Error message if status is unhealthy
9
+ */
10
+ error?: string | undefined;
11
+ /**
12
+ * - Optional status message (deprecated, use error)
9
13
  */
10
14
  message?: string | undefined;
11
15
  /**
@@ -35,15 +39,15 @@ export type HealthCheckResult = {
35
39
  */
36
40
  timestamp: string;
37
41
  /**
38
- * - Whether this result is from cache
42
+ * - Individual service health checks
39
43
  */
40
- cached: boolean;
41
- /**
42
- * - Individual component health
43
- */
44
- components: {
44
+ checks: {
45
45
  [x: string]: ComponentHealth | DatabaseClusterHealth;
46
46
  };
47
+ /**
48
+ * - Top-level error messages (not related to specific services)
49
+ */
50
+ errors?: string[] | undefined;
47
51
  };
48
52
  export type CachedHealthResult = {
49
53
  /**
@@ -71,7 +75,8 @@ export type DatabaseConfig = {
71
75
  /**
72
76
  * @typedef {Object} ComponentHealth
73
77
  * @property {HealthStatus} status - Component health status
74
- * @property {string} [message] - Optional status message
78
+ * @property {string} [error] - Error message if status is unhealthy
79
+ * @property {string} [message] - Optional status message (deprecated, use error)
75
80
  * @property {number} [latencyMs] - Connection latency in milliseconds
76
81
  */
77
82
  /**
@@ -83,8 +88,8 @@ export type DatabaseConfig = {
83
88
  * @typedef {Object} HealthCheckResult
84
89
  * @property {HealthStatus} status - Overall health status
85
90
  * @property {string} timestamp - ISO timestamp of the check
86
- * @property {boolean} cached - Whether this result is from cache
87
- * @property {Object<string, ComponentHealth | DatabaseClusterHealth>} components - Individual component health
91
+ * @property {Object<string, ComponentHealth | DatabaseClusterHealth>} checks - Individual service health checks
92
+ * @property {string[]} [errors] - Top-level error messages (not related to specific services)
88
93
  */
89
94
  /**
90
95
  * @typedef {Object} CachedHealthResult
@@ -114,7 +119,8 @@ export class HealthCheckClient {
114
119
  * @param {string} [options.databaseUrl] - Main PostgreSQL connection URL
115
120
  * @param {string} [options.databaseName='main'] - Name for the main database
116
121
  * @param {Object<string, string>} [options.additionalDatabaseUrls] - Additional DB clusters (name -> URL)
117
- * @param {any} [options.redisClient] - Redis client instance (ioredis or node-redis)
122
+ * @param {any} [options.redisClient] - Redis client instance (ioredis or node-redis) - used for cache
123
+ * @param {boolean} [options.includeRedisCheck=false] - Include Redis health check in results (for backend)
118
124
  * @param {number} [options.cacheTtlMs=60000] - Cache TTL in milliseconds (default: 60s)
119
125
  * @param {string} [options.appName] - Application name for logging
120
126
  */
@@ -125,20 +131,27 @@ export class HealthCheckClient {
125
131
  [x: string]: string;
126
132
  } | undefined;
127
133
  redisClient?: any;
134
+ includeRedisCheck?: boolean | undefined;
128
135
  cacheTtlMs?: number | undefined;
129
136
  appName?: string | undefined;
130
137
  });
131
138
  redisClient: any;
139
+ includeRedisCheck: boolean;
132
140
  cacheTtlMs: number;
133
141
  appName: string;
134
142
  prefixLogs: string;
135
143
  /** @type {CachedHealthResult | null} */
136
144
  _cachedResult: CachedHealthResult | null;
145
+ /** @type {Promise<HealthCheckResult> | null} */
146
+ _refreshPromise: Promise<HealthCheckResult> | null;
137
147
  /** @type {Map<string, Pool>} */
138
148
  _databasePools: Map<string, Pool>;
149
+ /** @type {DatabaseConfig | null} */
150
+ _mainDatabaseConfig: DatabaseConfig | null;
139
151
  /** @type {DatabaseConfig[]} */
140
- _databaseConfigs: DatabaseConfig[];
152
+ _clusterConfigs: DatabaseConfig[];
141
153
  _redisClientType: string | undefined;
154
+ _cache: HealthCheckCache;
142
155
  /**
143
156
  * Initialize database configurations from options.
144
157
  * @param {Object} options - Constructor options
@@ -166,8 +179,8 @@ export class HealthCheckClient {
166
179
  */
167
180
  private _checkSingleDatabase;
168
181
  /**
169
- * Tests all PostgreSQL database clusters in parallel.
170
- * @returns {Promise<DatabaseClusterHealth | null>}
182
+ * Tests all PostgreSQL databases (main + clusters) in parallel.
183
+ * @returns {Promise<Object | null>} Database health with optional clusters
171
184
  * @private
172
185
  */
173
186
  private _checkAllDatabases;
@@ -180,10 +193,37 @@ export class HealthCheckClient {
180
193
  /**
181
194
  * Performs a full health check on all configured components.
182
195
  * Results are cached for the configured TTL to prevent excessive load.
196
+ * Uses a mutex pattern to prevent concurrent health checks when cache expires.
197
+ * If cache is expired but a refresh is in progress, returns stale cache (if available).
183
198
  *
184
199
  * @returns {Promise<HealthCheckResult>}
185
200
  */
186
201
  performHealthCheck(): Promise<HealthCheckResult>;
202
+ /**
203
+ * Internal method that actually performs the health check.
204
+ * This is separated to allow the mutex pattern in performHealthCheck.
205
+ * Only checks database - Redis is used only for cache, not health check.
206
+ *
207
+ * @returns {Promise<HealthCheckResult>}
208
+ * @private
209
+ */
210
+ private _performHealthCheckInternal;
211
+ /**
212
+ * Gets cached result from shared cache (Redis if available, otherwise in-memory).
213
+ * Returns null if no cache is available. This is used by endpoints to read-only access.
214
+ * If Redis fails, returns error result with proper format.
215
+ *
216
+ * @returns {Promise<HealthCheckResult | null>} Cached result, error result if Redis fails, or null if not available
217
+ */
218
+ getCachedResult(): Promise<HealthCheckResult | null>;
219
+ /**
220
+ * Forces a refresh of the health check cache.
221
+ * This is used by background workers to periodically update the cache.
222
+ * Updates shared cache (Redis if available, otherwise in-memory).
223
+ *
224
+ * @returns {Promise<HealthCheckResult>}
225
+ */
226
+ refreshCache(): Promise<HealthCheckResult>;
187
227
  /**
188
228
  * Clears the cached health check result, forcing the next check to be fresh.
189
229
  */
@@ -202,6 +242,9 @@ export class HealthCheckClient {
202
242
  * Response includes errors array when status is not healthy.
203
243
  * All sensitive data (passwords, connection strings, etc.) is masked.
204
244
  *
245
+ * This handler only reads from cache and never triggers database queries.
246
+ * Use a background worker to periodically refresh the cache.
247
+ *
205
248
  * @returns {(req: any, res: any) => Promise<void>} Express request handler
206
249
  */
207
250
  healthHandler(): (req: any, res: any) => Promise<void>;
@@ -209,7 +252,7 @@ export class HealthCheckClient {
209
252
  * Register health check endpoint on an Express app.
210
253
  *
211
254
  * @param {import('express').Application} app - Express application
212
- * @param {string} [path='/health'] - Path for the health endpoint
255
+ * @param {string} [path='/health-status'] - Path for the health endpoint
213
256
  */
214
257
  registerHealthEndpoint(app: any, path?: string | undefined): void;
215
258
  /**
@@ -219,4 +262,5 @@ export class HealthCheckClient {
219
262
  cleanup(): Promise<void>;
220
263
  }
221
264
  import { Pool } from "pg";
265
+ import { HealthCheckCache } from "./healthCheckCache";
222
266
  //# sourceMappingURL=healthCheckClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"healthCheckClient.d.ts","sourceRoot":"","sources":["../../src/health/healthCheckClient.js"],"names":[],"mappings":"2BAiEa,SAAS,GAAG,WAAW,GAAG,UAAU;;;;;YAKnC,YAAY;;;;;;;;;;;;;;;;;;YAQZ,YAAY;;;;;YACL,MAAM,GAAE,eAAe;;;;;;;YAK9B,YAAY;;;;eACZ,MAAM;;;;;YACC,MAAM,GAAE,eAAe,GAAG,qBAAqB;;;;;;;;;;;YAMtD,iBAAiB;;;;eACjB,MAAM;;;;;;UAKN,MAAM;;;;SACN,MAAM;;AAnCpB;;GAEG;AAEH;;;;;;GAMG;AAEH;;;;GAIG;AAEH;;;;;;GAMG;AAEH;;;;GAIG;AAEH;;;;GAIG;AAEH;;;;;;;;;;;GAWG;AACH;IACE;;;;;;;;;OASG;IACH;QAR4B,WAAW;QACX,YAAY;QACI,sBAAsB;;;QACzC,WAAW,GAAzB,GAAG;QACe,iBAAiB;QAClB,UAAU;QACV,OAAO;OAqClC;IAlCC,iBAA8C;IAC9C,2BAA2D;IAC3D,mBAAiE;IACjE,gBACgE;IAEhE,mBAAmD;IAEnD,wCAAwC;IACxC,eADW,kBAAkB,GAAG,IAAI,CACX;IAEzB,gDAAgD;IAChD,iBADW,QAAQ,iBAAiB,CAAC,GAAG,IAAI,CACjB;IAE3B,gCAAgC;IAChC,gBADW,IAAI,MAAM,EAAE,IAAI,CAAC,CACG;IAE/B,oCAAoC;IACpC,qBADW,cAAc,GAAG,IAAI,CACD;IAE/B,+BAA+B;IAC/B,iBADW,cAAc,EAAE,CACF;IAKvB,qCAA4D;IAG9D,yBAIE;IAGJ;;;;OAIG;IACH,uBAcC;IAED;;;;;OAKG;IACH,iBAaC;IAED;;;;OAIG;IACH,sBAGC;IAED;;;;;OAKG;IACH,6BAiBC;IAED;;;;OAIG;IACH,2BAsDC;IAED;;;;OAIG;IACH,oBA6CC;IAED;;;;;;;OAOG;IACH,sBAFa,QAAQ,iBAAiB,CAAC,CAqCtC;IAED;;;;;;;OAOG;IACH,oCAoCC;IAED;;;;;;OAMG;IACH,mBAFa,QAAQ,iBAAiB,GAAG,IAAI,CAAC,CA0B7C;IAED;;;;;;OAMG;IACH,gBAFa,QAAQ,iBAAiB,CAAC,CAOtC;IAED;;OAEG;IACH,mBAGC;IAED;;;;;;OAMG;IACH,0BA8BC;IAED;;;;;;;;;;OAUG;IACH,uBAFmB,GAAG,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,CAuDjD;IAED;;;;;OAKG;IACH,kEAGC;IAED;;;OAGG;IACH,WAFa,QAAQ,IAAI,CAAC,CAWzB;CACF"}