@catbee/utils 2.1.1 → 2.2.0
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.
- package/README.md +1 -1
- package/config/index.cjs +5 -6
- package/config/index.mjs +5 -6
- package/healthz-server/index.cjs +42 -5
- package/healthz-server/index.d.ts +49 -23
- package/healthz-server/index.mjs +42 -5
- package/package.json +1 -1
- package/server/index.cjs +180 -101
- package/server/index.d.ts +60 -32
- package/server/index.mjs +183 -104
- package/types/index.d.ts +142 -47
package/README.md
CHANGED
|
@@ -55,7 +55,7 @@ const config = new ServerConfigBuilder()
|
|
|
55
55
|
.withCors({ origin: '*' })
|
|
56
56
|
.enableRateLimit({ max: 50, windowMs: 60000 })
|
|
57
57
|
.enableRequestLogging({ ignorePaths: ['/healthz', '/metrics'] })
|
|
58
|
-
.
|
|
58
|
+
.enableHealthzServer({ port: 8282 })
|
|
59
59
|
.enableOpenApi('./openapi.yaml', { mountPath: '/docs' })
|
|
60
60
|
.withGlobalHeaders({ 'X-Powered-By': 'Catbee' })
|
|
61
61
|
.withGlobalPrefix('/api')
|
package/config/index.cjs
CHANGED
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
var env = require('@catbee/utils/env');
|
|
28
28
|
var id = require('@catbee/utils/id');
|
|
29
29
|
var object = require('@catbee/utils/object');
|
|
30
|
+
var healthzServer = require('@catbee/utils/healthz-server');
|
|
30
31
|
|
|
31
32
|
var __defProp = Object.defineProperty;
|
|
32
33
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
@@ -78,10 +79,9 @@ var defaultServerConfig = {
|
|
|
78
79
|
verbose: env.Env.getBoolean("SERVER_OPENAPI_VERBOSE", false),
|
|
79
80
|
withGlobalPrefix: env.Env.getBoolean("SERVER_OPENAPI_WITH_GLOBAL_PREFIX", false)
|
|
80
81
|
},
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
withGlobalPrefix: env.Env.getBoolean("SERVER_HEALTH_CHECK_WITH_GLOBAL_PREFIX", false)
|
|
82
|
+
healthzServer: {
|
|
83
|
+
enable: env.Env.getBoolean("SERVER_HEALTHZ_ENABLE", env.Env.getBoolean("HEALTHZ_ENABLE", false)),
|
|
84
|
+
...healthzServer.getDefaultHealthzConfig()
|
|
85
85
|
},
|
|
86
86
|
requestTimeout: env.Env.getDuration("SERVER_REQUEST_TIMEOUT_MS", 0),
|
|
87
87
|
responseTime: {
|
|
@@ -98,8 +98,7 @@ var defaultServerConfig = {
|
|
|
98
98
|
enable: env.Env.getBoolean("SERVER_SERVICE_VERSION_ENABLE", false),
|
|
99
99
|
headerName: env.Env.get("SERVER_SERVICE_VERSION_HEADER_NAME", "x-service-version"),
|
|
100
100
|
version: env.Env.get("${npm_package_version}", "0.0.0")
|
|
101
|
-
}
|
|
102
|
-
skipHealthzChecksValidation: env.Env.getBoolean("SERVER_SKIP_HEALTHZ_CHECKS_VALIDATION", false)
|
|
101
|
+
}
|
|
103
102
|
};
|
|
104
103
|
var defaultCatbeeConfig = {
|
|
105
104
|
logger: {
|
package/config/index.mjs
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
import { Env } from '@catbee/utils/env';
|
|
26
26
|
import { uuid } from '@catbee/utils/id';
|
|
27
27
|
import { deepClone, deepObjMerge } from '@catbee/utils/object';
|
|
28
|
+
import { getDefaultHealthzConfig } from '@catbee/utils/healthz-server';
|
|
28
29
|
|
|
29
30
|
var __defProp = Object.defineProperty;
|
|
30
31
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
@@ -76,10 +77,9 @@ var defaultServerConfig = {
|
|
|
76
77
|
verbose: Env.getBoolean("SERVER_OPENAPI_VERBOSE", false),
|
|
77
78
|
withGlobalPrefix: Env.getBoolean("SERVER_OPENAPI_WITH_GLOBAL_PREFIX", false)
|
|
78
79
|
},
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
withGlobalPrefix: Env.getBoolean("SERVER_HEALTH_CHECK_WITH_GLOBAL_PREFIX", false)
|
|
80
|
+
healthzServer: {
|
|
81
|
+
enable: Env.getBoolean("SERVER_HEALTHZ_ENABLE", Env.getBoolean("HEALTHZ_ENABLE", false)),
|
|
82
|
+
...getDefaultHealthzConfig()
|
|
83
83
|
},
|
|
84
84
|
requestTimeout: Env.getDuration("SERVER_REQUEST_TIMEOUT_MS", 0),
|
|
85
85
|
responseTime: {
|
|
@@ -96,8 +96,7 @@ var defaultServerConfig = {
|
|
|
96
96
|
enable: Env.getBoolean("SERVER_SERVICE_VERSION_ENABLE", false),
|
|
97
97
|
headerName: Env.get("SERVER_SERVICE_VERSION_HEADER_NAME", "x-service-version"),
|
|
98
98
|
version: Env.get("${npm_package_version}", "0.0.0")
|
|
99
|
-
}
|
|
100
|
-
skipHealthzChecksValidation: Env.getBoolean("SERVER_SKIP_HEALTHZ_CHECKS_VALIDATION", false)
|
|
99
|
+
}
|
|
101
100
|
};
|
|
102
101
|
var defaultCatbeeConfig = {
|
|
103
102
|
logger: {
|
package/healthz-server/index.cjs
CHANGED
|
@@ -36,10 +36,11 @@ function getDefaultHealthzConfig() {
|
|
|
36
36
|
healthzPath: env.Env.get("HEALTHZ_PATH", "") || env.Env.get("SERVER_HEALTHZ_PATH", "") || env.Env.get("SERVER_HEALTH_CHECK_PATH", "/healthz"),
|
|
37
37
|
readyzPath: env.Env.get("HEALTHZ_READYZ_PATH", "") || env.Env.get("SERVER_READYZ_PATH", "/readyz"),
|
|
38
38
|
startupzPath: env.Env.get("HEALTHZ_STARTUPZ_PATH", "") || env.Env.get("SERVER_STARTUPZ_PATH", "/startupz"),
|
|
39
|
-
detailed: env.Env.getBoolean("HEALTHZ_DETAILED", env.Env.getBoolean("SERVER_HEALTH_CHECK_DETAILED_OUTPUT", true)),
|
|
39
|
+
detailed: env.Env.getBoolean("HEALTHZ_DETAILED", env.Env.getBoolean("SERVER_HEALTHZ_DETAILED", env.Env.getBoolean("SERVER_HEALTH_CHECK_DETAILED_OUTPUT", true))),
|
|
40
40
|
checks: [],
|
|
41
|
-
checkTimeoutMs: env.Env.getDuration("HEALTHZ_CHECK_TIMEOUT_MS", 5e3),
|
|
42
|
-
shutdownDelayMs: env.Env.getDuration("HEALTHZ_SHUTDOWN_DELAY_MS", 5e3)
|
|
41
|
+
checkTimeoutMs: env.Env.getDuration("HEALTHZ_CHECK_TIMEOUT_MS", env.Env.getDuration("SERVER_HEALTHZ_CHECK_TIMEOUT_MS", 5e3)),
|
|
42
|
+
shutdownDelayMs: env.Env.getDuration("HEALTHZ_SHUTDOWN_DELAY_MS", env.Env.getDuration("SERVER_HEALTHZ_SHUTDOWN_DELAY_MS", 5e3)),
|
|
43
|
+
handleSignals: true
|
|
43
44
|
};
|
|
44
45
|
}
|
|
45
46
|
__name(getDefaultHealthzConfig, "getDefaultHealthzConfig");
|
|
@@ -76,8 +77,16 @@ var HealthzServer = class _HealthzServer {
|
|
|
76
77
|
constructor(config) {
|
|
77
78
|
this.config = config;
|
|
78
79
|
this.server = http.createServer((req, res) => this.handleRequest(req, res));
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
if (this.config.handleSignals !== false) {
|
|
81
|
+
process.once("SIGTERM", this.onSigterm);
|
|
82
|
+
process.once("SIGINT", this.onSigint);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Returns default Healthz server configuration resolved from environment variables.
|
|
87
|
+
*/
|
|
88
|
+
static getDefaultConfig() {
|
|
89
|
+
return getDefaultHealthzConfig();
|
|
81
90
|
}
|
|
82
91
|
/**
|
|
83
92
|
* Boot the health-check server.
|
|
@@ -148,6 +157,34 @@ var HealthzServer = class _HealthzServer {
|
|
|
148
157
|
static getInstance() {
|
|
149
158
|
return _global[SINGLETON_KEY];
|
|
150
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* Register a named check on this HealthzServer instance dynamically.
|
|
162
|
+
*
|
|
163
|
+
* @param check - The named check to register
|
|
164
|
+
* @param type - Which probe to attach this check to ('readiness', 'liveness', or 'both')
|
|
165
|
+
* @returns This instance for chaining
|
|
166
|
+
*/
|
|
167
|
+
registerCheck(check, type = "readiness") {
|
|
168
|
+
if (type === "liveness" || type === "both") {
|
|
169
|
+
this.config.checks.push(check);
|
|
170
|
+
}
|
|
171
|
+
if (type === "readiness" || type === "both") {
|
|
172
|
+
this.config.readinessChecks ??= [
|
|
173
|
+
...this.config.checks
|
|
174
|
+
];
|
|
175
|
+
this.config.readinessChecks.push(check);
|
|
176
|
+
}
|
|
177
|
+
return this;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Register a named check on the active singleton HealthzServer instance (if started).
|
|
181
|
+
*
|
|
182
|
+
* @param check - The named check to register
|
|
183
|
+
* @param type - Which probe to attach this check to ('readiness', 'liveness', or 'both')
|
|
184
|
+
*/
|
|
185
|
+
static registerCheck(check, type = "readiness") {
|
|
186
|
+
_global[SINGLETON_KEY]?.registerCheck(check, type);
|
|
187
|
+
}
|
|
151
188
|
async handleRequest(req, res) {
|
|
152
189
|
const isHead = req.method === "HEAD";
|
|
153
190
|
if (req.method !== "GET" && !isHead) {
|
|
@@ -169,6 +169,13 @@ interface CatbeeHealthzServerConfig {
|
|
|
169
169
|
* - **env**: `HEALTHZ_SHUTDOWN_DELAY_MS`
|
|
170
170
|
*/
|
|
171
171
|
shutdownDelayMs?: number;
|
|
172
|
+
/**
|
|
173
|
+
* Whether the HealthzServer should register its own SIGTERM/SIGINT process signal listeners.
|
|
174
|
+
* When managed by an orchestrating server (such as Catbee ExpressServer), set this to `false`
|
|
175
|
+
* to prevent signal listener conflicts and allow coordinated teardown.
|
|
176
|
+
* - **default**: `true`
|
|
177
|
+
*/
|
|
178
|
+
handleSignals?: boolean;
|
|
172
179
|
}
|
|
173
180
|
/**
|
|
174
181
|
* Address information returned after the server starts listening.
|
|
@@ -179,6 +186,29 @@ interface HealthzAddressInfo {
|
|
|
179
186
|
port: number;
|
|
180
187
|
}
|
|
181
188
|
|
|
189
|
+
/**
|
|
190
|
+
* Resolved configuration with all defaults applied.
|
|
191
|
+
* Internal-only — consumers interact with `CatbeeHealthzServerConfig`.
|
|
192
|
+
*
|
|
193
|
+
* `readinessChecks` is kept as `NamedCheck[] | undefined` so that
|
|
194
|
+
* "omitted" (-> fall back to `checks`) is distinguishable from
|
|
195
|
+
* "explicitly empty" (-> run nothing).
|
|
196
|
+
*/
|
|
197
|
+
interface ResolvedHealthzConfig extends Required<Omit<CatbeeHealthzServerConfig, 'onHealthCheck' | 'onReadinessCheck' | 'readinessChecks'>> {
|
|
198
|
+
onHealthCheck?: CatbeeHealthzServerConfig['onHealthCheck'];
|
|
199
|
+
onReadinessCheck?: CatbeeHealthzServerConfig['onReadinessCheck'];
|
|
200
|
+
readinessChecks?: CatbeeHealthzServerConfig['readinessChecks'];
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Loads default Healthz server configuration from environment variables.
|
|
204
|
+
*/
|
|
205
|
+
declare function getDefaultHealthzConfig(): ResolvedHealthzConfig;
|
|
206
|
+
/**
|
|
207
|
+
* Merge user-supplied configuration with environment-resolved defaults.
|
|
208
|
+
* Undefined user values do not override resolved defaults.
|
|
209
|
+
*/
|
|
210
|
+
declare function resolveConfig(userConfig?: CatbeeHealthzServerConfig): ResolvedHealthzConfig;
|
|
211
|
+
|
|
182
212
|
/**
|
|
183
213
|
* Standalone HTTP health-check server designed for Kubernetes probes
|
|
184
214
|
* and microservice orchestration.
|
|
@@ -235,6 +265,10 @@ declare class HealthzServer {
|
|
|
235
265
|
private readonly onSigterm;
|
|
236
266
|
private readonly onSigint;
|
|
237
267
|
private constructor();
|
|
268
|
+
/**
|
|
269
|
+
* Returns default Healthz server configuration resolved from environment variables.
|
|
270
|
+
*/
|
|
271
|
+
static getDefaultConfig(): ResolvedHealthzConfig;
|
|
238
272
|
/**
|
|
239
273
|
* Boot the health-check server.
|
|
240
274
|
* Returns `null` if a server is already running in this process.
|
|
@@ -249,6 +283,21 @@ declare class HealthzServer {
|
|
|
249
283
|
/** Gracefully stop the health-check server. */
|
|
250
284
|
static stop(): Promise<void>;
|
|
251
285
|
static getInstance(): HealthzServer | undefined;
|
|
286
|
+
/**
|
|
287
|
+
* Register a named check on this HealthzServer instance dynamically.
|
|
288
|
+
*
|
|
289
|
+
* @param check - The named check to register
|
|
290
|
+
* @param type - Which probe to attach this check to ('readiness', 'liveness', or 'both')
|
|
291
|
+
* @returns This instance for chaining
|
|
292
|
+
*/
|
|
293
|
+
registerCheck(check: NamedCheck, type?: 'liveness' | 'readiness' | 'both'): this;
|
|
294
|
+
/**
|
|
295
|
+
* Register a named check on the active singleton HealthzServer instance (if started).
|
|
296
|
+
*
|
|
297
|
+
* @param check - The named check to register
|
|
298
|
+
* @param type - Which probe to attach this check to ('readiness', 'liveness', or 'both')
|
|
299
|
+
*/
|
|
300
|
+
static registerCheck(check: NamedCheck, type?: 'liveness' | 'readiness' | 'both'): void;
|
|
252
301
|
private handleRequest;
|
|
253
302
|
/** `/healthz` — Liveness probe */
|
|
254
303
|
private handleLiveness;
|
|
@@ -264,28 +313,5 @@ declare class HealthzServer {
|
|
|
264
313
|
private cleanup;
|
|
265
314
|
}
|
|
266
315
|
|
|
267
|
-
/**
|
|
268
|
-
* Resolved configuration with all defaults applied.
|
|
269
|
-
* Internal-only — consumers interact with `CatbeeHealthzServerConfig`.
|
|
270
|
-
*
|
|
271
|
-
* `readinessChecks` is kept as `NamedCheck[] | undefined` so that
|
|
272
|
-
* "omitted" (-> fall back to `checks`) is distinguishable from
|
|
273
|
-
* "explicitly empty" (-> run nothing).
|
|
274
|
-
*/
|
|
275
|
-
interface ResolvedHealthzConfig extends Required<Omit<CatbeeHealthzServerConfig, 'onHealthCheck' | 'onReadinessCheck' | 'readinessChecks'>> {
|
|
276
|
-
onHealthCheck?: CatbeeHealthzServerConfig['onHealthCheck'];
|
|
277
|
-
onReadinessCheck?: CatbeeHealthzServerConfig['onReadinessCheck'];
|
|
278
|
-
readinessChecks?: CatbeeHealthzServerConfig['readinessChecks'];
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* Loads default Healthz server configuration from environment variables.
|
|
282
|
-
*/
|
|
283
|
-
declare function getDefaultHealthzConfig(): ResolvedHealthzConfig;
|
|
284
|
-
/**
|
|
285
|
-
* Merge user-supplied configuration with environment-resolved defaults.
|
|
286
|
-
* Undefined user values do not override resolved defaults.
|
|
287
|
-
*/
|
|
288
|
-
declare function resolveConfig(userConfig?: CatbeeHealthzServerConfig): ResolvedHealthzConfig;
|
|
289
|
-
|
|
290
316
|
export { HealthzServer, getDefaultHealthzConfig, resolveConfig };
|
|
291
317
|
export type { CatbeeHealthzServerConfig, CheckResult, HealthCheckFn, HealthzAddressInfo, NamedCheck, ProbeResponse, ProbeStatus, ReadinessCheckFn, ResolvedHealthzConfig };
|
package/healthz-server/index.mjs
CHANGED
|
@@ -34,10 +34,11 @@ function getDefaultHealthzConfig() {
|
|
|
34
34
|
healthzPath: Env.get("HEALTHZ_PATH", "") || Env.get("SERVER_HEALTHZ_PATH", "") || Env.get("SERVER_HEALTH_CHECK_PATH", "/healthz"),
|
|
35
35
|
readyzPath: Env.get("HEALTHZ_READYZ_PATH", "") || Env.get("SERVER_READYZ_PATH", "/readyz"),
|
|
36
36
|
startupzPath: Env.get("HEALTHZ_STARTUPZ_PATH", "") || Env.get("SERVER_STARTUPZ_PATH", "/startupz"),
|
|
37
|
-
detailed: Env.getBoolean("HEALTHZ_DETAILED", Env.getBoolean("SERVER_HEALTH_CHECK_DETAILED_OUTPUT", true)),
|
|
37
|
+
detailed: Env.getBoolean("HEALTHZ_DETAILED", Env.getBoolean("SERVER_HEALTHZ_DETAILED", Env.getBoolean("SERVER_HEALTH_CHECK_DETAILED_OUTPUT", true))),
|
|
38
38
|
checks: [],
|
|
39
|
-
checkTimeoutMs: Env.getDuration("HEALTHZ_CHECK_TIMEOUT_MS", 5e3),
|
|
40
|
-
shutdownDelayMs: Env.getDuration("HEALTHZ_SHUTDOWN_DELAY_MS", 5e3)
|
|
39
|
+
checkTimeoutMs: Env.getDuration("HEALTHZ_CHECK_TIMEOUT_MS", Env.getDuration("SERVER_HEALTHZ_CHECK_TIMEOUT_MS", 5e3)),
|
|
40
|
+
shutdownDelayMs: Env.getDuration("HEALTHZ_SHUTDOWN_DELAY_MS", Env.getDuration("SERVER_HEALTHZ_SHUTDOWN_DELAY_MS", 5e3)),
|
|
41
|
+
handleSignals: true
|
|
41
42
|
};
|
|
42
43
|
}
|
|
43
44
|
__name(getDefaultHealthzConfig, "getDefaultHealthzConfig");
|
|
@@ -74,8 +75,16 @@ var HealthzServer = class _HealthzServer {
|
|
|
74
75
|
constructor(config) {
|
|
75
76
|
this.config = config;
|
|
76
77
|
this.server = createServer((req, res) => this.handleRequest(req, res));
|
|
77
|
-
|
|
78
|
-
|
|
78
|
+
if (this.config.handleSignals !== false) {
|
|
79
|
+
process.once("SIGTERM", this.onSigterm);
|
|
80
|
+
process.once("SIGINT", this.onSigint);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Returns default Healthz server configuration resolved from environment variables.
|
|
85
|
+
*/
|
|
86
|
+
static getDefaultConfig() {
|
|
87
|
+
return getDefaultHealthzConfig();
|
|
79
88
|
}
|
|
80
89
|
/**
|
|
81
90
|
* Boot the health-check server.
|
|
@@ -146,6 +155,34 @@ var HealthzServer = class _HealthzServer {
|
|
|
146
155
|
static getInstance() {
|
|
147
156
|
return _global[SINGLETON_KEY];
|
|
148
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* Register a named check on this HealthzServer instance dynamically.
|
|
160
|
+
*
|
|
161
|
+
* @param check - The named check to register
|
|
162
|
+
* @param type - Which probe to attach this check to ('readiness', 'liveness', or 'both')
|
|
163
|
+
* @returns This instance for chaining
|
|
164
|
+
*/
|
|
165
|
+
registerCheck(check, type = "readiness") {
|
|
166
|
+
if (type === "liveness" || type === "both") {
|
|
167
|
+
this.config.checks.push(check);
|
|
168
|
+
}
|
|
169
|
+
if (type === "readiness" || type === "both") {
|
|
170
|
+
this.config.readinessChecks ??= [
|
|
171
|
+
...this.config.checks
|
|
172
|
+
];
|
|
173
|
+
this.config.readinessChecks.push(check);
|
|
174
|
+
}
|
|
175
|
+
return this;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Register a named check on the active singleton HealthzServer instance (if started).
|
|
179
|
+
*
|
|
180
|
+
* @param check - The named check to register
|
|
181
|
+
* @param type - Which probe to attach this check to ('readiness', 'liveness', or 'both')
|
|
182
|
+
*/
|
|
183
|
+
static registerCheck(check, type = "readiness") {
|
|
184
|
+
_global[SINGLETON_KEY]?.registerCheck(check, type);
|
|
185
|
+
}
|
|
149
186
|
async handleRequest(req, res) {
|
|
150
187
|
const isHead = req.method === "HEAD";
|
|
151
188
|
if (req.method !== "GET" && !isHead) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@catbee/utils",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "A modular, production-grade utility toolkit for Node.js and TypeScript, designed for robust, scalable applications (including Express-based services). All utilities are tree-shakable and can be imported independently.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|