@catbee/utils 2.2.1 → 2.3.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/healthz-server/index.cjs +270 -58
- package/healthz-server/index.d.ts +118 -14
- package/healthz-server/index.mjs +270 -58
- package/package.json +1 -1
- package/server/index.cjs +131 -17
- package/server/index.d.ts +56 -3
- package/server/index.mjs +131 -17
- package/string/index.cjs +44 -2
- package/string/index.d.ts +36 -1
- package/string/index.mjs +42 -3
- package/types/index.d.ts +43 -15
- package/url/index.cjs +5 -4
- package/url/index.mjs +5 -4
|
@@ -26,28 +26,42 @@
|
|
|
26
26
|
* A health check function that can be synchronous or asynchronous.
|
|
27
27
|
* Receives an AbortSignal that is triggered when `checkTimeoutMs` is exceeded.
|
|
28
28
|
*
|
|
29
|
-
* > **Cancellation
|
|
30
|
-
* > `
|
|
31
|
-
* >
|
|
29
|
+
* > **Cancellation & Timeout Semantics**:
|
|
30
|
+
* > When `checkTimeoutMs` expires, the HTTP probe response immediately fast-fails with 503
|
|
31
|
+
* > and aborts the supplied `AbortSignal`.
|
|
32
|
+
* >
|
|
33
|
+
* > However, runtime cancellation in JavaScript is **strictly cooperative**. The probe server cannot
|
|
34
|
+
* > preemptively interrupt executing JavaScript or force-cancel asynchronous work that does not listen
|
|
35
|
+
* > to the signal. If a check executes asynchronous tasks without passing `signal`
|
|
36
|
+
* > (e.g., `await db.query(...)` without `{ signal }`), that task will continue running in the background.
|
|
37
|
+
* > To prevent background resource leaks, always pass `signal` to underlying drivers/clients
|
|
38
|
+
* > (e.g. `fetch(url, { signal })`, database clients, HTTP clients) or check `signal?.aborted`.
|
|
32
39
|
*
|
|
33
|
-
* - Return `true` (or resolve to true) to signal healthy.
|
|
40
|
+
* - Return `true` (or resolve to true, or resolve void without error) to signal healthy.
|
|
34
41
|
* - Return `false` (or resolve to false) to signal unhealthy.
|
|
35
42
|
* - Throw an error (or reject) to signal unhealthy with an error message.
|
|
36
43
|
*/
|
|
37
|
-
type HealthCheckFn = (signal?: AbortSignal) => boolean | Promise<boolean>;
|
|
44
|
+
type HealthCheckFn = (signal?: AbortSignal) => boolean | void | Promise<boolean | void>;
|
|
38
45
|
/**
|
|
39
46
|
* A readiness check function.
|
|
40
47
|
* Receives an AbortSignal that is triggered when `checkTimeoutMs` is exceeded.
|
|
41
48
|
*
|
|
42
|
-
* > **Cancellation
|
|
43
|
-
* > `
|
|
44
|
-
* >
|
|
49
|
+
* > **Cancellation & Timeout Semantics**:
|
|
50
|
+
* > When `checkTimeoutMs` expires, the HTTP probe response immediately fast-fails with 503
|
|
51
|
+
* > and aborts the supplied `AbortSignal`.
|
|
52
|
+
* >
|
|
53
|
+
* > However, runtime cancellation in JavaScript is **strictly cooperative**. The probe server cannot
|
|
54
|
+
* > preemptively interrupt executing JavaScript or force-cancel asynchronous work that does not listen
|
|
55
|
+
* > to the signal. If a check executes asynchronous tasks without passing `signal`
|
|
56
|
+
* > (e.g., `await db.query(...)` without `{ signal }`), that task will continue running in the background.
|
|
57
|
+
* > To prevent background resource leaks, always pass `signal` to underlying drivers/clients
|
|
58
|
+
* > (e.g. `fetch(url, { signal })`, database clients, HTTP clients) or check `signal?.aborted`.
|
|
45
59
|
*
|
|
46
|
-
* - Return `true` (or resolve to true) to signal ready.
|
|
60
|
+
* - Return `true` (or resolve to true, or resolve void without error) to signal ready.
|
|
47
61
|
* - Return `false` (or resolve to false) to signal not ready.
|
|
48
62
|
* - Throw an error (or reject) to signal not ready with an error.
|
|
49
63
|
*/
|
|
50
|
-
type ReadinessCheckFn = (signal?: AbortSignal) => boolean | Promise<boolean>;
|
|
64
|
+
type ReadinessCheckFn = (signal?: AbortSignal) => boolean | void | Promise<boolean | void>;
|
|
51
65
|
/**
|
|
52
66
|
* A named health check with an associated check function.
|
|
53
67
|
*/
|
|
@@ -56,10 +70,11 @@ interface NamedCheck {
|
|
|
56
70
|
name: string;
|
|
57
71
|
/**
|
|
58
72
|
* Check function — return false or throw to indicate failure.
|
|
73
|
+
* Resolving void without throwing is considered healthy/ready.
|
|
59
74
|
* Receives an AbortSignal that is triggered when the check times out.
|
|
60
|
-
* Cancellation via the signal is cooperative.
|
|
75
|
+
* Cancellation via the signal is cooperative; pass `signal` to underlying operations.
|
|
61
76
|
*/
|
|
62
|
-
check: (signal?: AbortSignal) => boolean | Promise<boolean>;
|
|
77
|
+
check: (signal?: AbortSignal) => boolean | void | Promise<boolean | void>;
|
|
63
78
|
}
|
|
64
79
|
/**
|
|
65
80
|
* Result of a single named health check.
|
|
@@ -148,14 +163,27 @@ interface CatbeeHealthzServerConfig {
|
|
|
148
163
|
/**
|
|
149
164
|
* Custom liveness check function. Runs *in addition to* `checks`.
|
|
150
165
|
* Return `false` or throw to indicate unhealthy.
|
|
166
|
+
* Can also be provided via `onLivenessCheck`.
|
|
151
167
|
*/
|
|
152
168
|
onHealthCheck?: HealthCheckFn;
|
|
169
|
+
/**
|
|
170
|
+
* Symmetrical alias for `onHealthCheck` — custom liveness check function.
|
|
171
|
+
* Runs *in addition to* `checks` on `/healthz`.
|
|
172
|
+
* Return `false` or throw to indicate unhealthy.
|
|
173
|
+
*/
|
|
174
|
+
onLivenessCheck?: HealthCheckFn;
|
|
153
175
|
/**
|
|
154
176
|
* Custom readiness check function. Runs *in addition to* `readinessChecks`.
|
|
155
177
|
* Return `false` or throw to indicate not ready.
|
|
156
178
|
*/
|
|
157
179
|
onReadinessCheck?: ReadinessCheckFn;
|
|
158
|
-
/**
|
|
180
|
+
/**
|
|
181
|
+
* Timeout (ms) per individual check before it's considered failed.
|
|
182
|
+
*
|
|
183
|
+
* When exceeded, the probe immediately fast-fails with status 503 and triggers
|
|
184
|
+
* `abort()` on the check's `AbortSignal`. Note that cancellation is cooperative;
|
|
185
|
+
* checks should forward `signal` to downstream clients (e.g. database drivers, fetch)
|
|
186
|
+
* to avoid orphaned background execution.
|
|
159
187
|
* - **default**: `5000`
|
|
160
188
|
* - **env**: `HEALTHZ_CHECK_TIMEOUT_MS`
|
|
161
189
|
*/
|
|
@@ -194,8 +222,9 @@ interface HealthzAddressInfo {
|
|
|
194
222
|
* "omitted" (-> fall back to `checks`) is distinguishable from
|
|
195
223
|
* "explicitly empty" (-> run nothing).
|
|
196
224
|
*/
|
|
197
|
-
interface ResolvedHealthzConfig extends Required<Omit<CatbeeHealthzServerConfig, 'onHealthCheck' | 'onReadinessCheck' | 'readinessChecks'>> {
|
|
225
|
+
interface ResolvedHealthzConfig extends Required<Omit<CatbeeHealthzServerConfig, 'onHealthCheck' | 'onLivenessCheck' | 'onReadinessCheck' | 'readinessChecks'>> {
|
|
198
226
|
onHealthCheck?: CatbeeHealthzServerConfig['onHealthCheck'];
|
|
227
|
+
onLivenessCheck?: CatbeeHealthzServerConfig['onLivenessCheck'];
|
|
199
228
|
onReadinessCheck?: CatbeeHealthzServerConfig['onReadinessCheck'];
|
|
200
229
|
readinessChecks?: CatbeeHealthzServerConfig['readinessChecks'];
|
|
201
230
|
}
|
|
@@ -259,6 +288,8 @@ declare class HealthzServer {
|
|
|
259
288
|
private readonly server;
|
|
260
289
|
private readonly config;
|
|
261
290
|
private readonly startedAt;
|
|
291
|
+
/** Running address info for the Healthz probe server */
|
|
292
|
+
private addressInfo;
|
|
262
293
|
/** Whether the health HTTP server is currently running and listening on its port */
|
|
263
294
|
private running;
|
|
264
295
|
/** Whether application startup has completed (for `/startupz`) */
|
|
@@ -266,6 +297,9 @@ declare class HealthzServer {
|
|
|
266
297
|
/** Whether the service is ready to receive traffic (for `/readyz`) */
|
|
267
298
|
private ready;
|
|
268
299
|
private shuttingDown;
|
|
300
|
+
private readonly livenessChecks;
|
|
301
|
+
private readonly readinessChecks;
|
|
302
|
+
private stoppingPromise?;
|
|
269
303
|
private readonly onSigterm;
|
|
270
304
|
private readonly onSigint;
|
|
271
305
|
private constructor();
|
|
@@ -279,33 +313,96 @@ declare class HealthzServer {
|
|
|
279
313
|
*/
|
|
280
314
|
static start(opts?: CatbeeHealthzServerConfig): Promise<HealthzAddressInfo | null>;
|
|
281
315
|
/** Whether the Healthz HTTP probe server is currently running and listening on its port. */
|
|
316
|
+
isRunning(): boolean;
|
|
317
|
+
/** Whether the Healthz HTTP probe server is currently running and listening on its port. */
|
|
282
318
|
static isRunning(): boolean;
|
|
283
319
|
/** Mark application startup as completed (switches `/startupz` to 200). */
|
|
320
|
+
markStartupComplete(): this;
|
|
321
|
+
/** Mark application startup as completed (switches `/startupz` to 200). */
|
|
284
322
|
static markStartupComplete(): void;
|
|
323
|
+
/** Set application startup completion status. */
|
|
324
|
+
setStartupComplete(complete: boolean): this;
|
|
325
|
+
/** Set application startup completion status. */
|
|
326
|
+
static setStartupComplete(complete: boolean): void;
|
|
327
|
+
/** Whether application startup has completed (for `/startupz`). */
|
|
328
|
+
isStartupComplete(): boolean;
|
|
285
329
|
/** Whether application startup has completed (for `/startupz`). */
|
|
286
330
|
static isStartupComplete(): boolean;
|
|
287
331
|
/** Mark the service as ready / not-ready for traffic. */
|
|
332
|
+
setReady(ready: boolean): this;
|
|
333
|
+
/** Mark the service as ready / not-ready for traffic. */
|
|
288
334
|
static setReady(ready: boolean): void;
|
|
289
335
|
/** Whether the service is currently marked as ready. */
|
|
336
|
+
isReady(): boolean;
|
|
337
|
+
/** Whether the service is currently marked as ready. */
|
|
290
338
|
static isReady(): boolean;
|
|
339
|
+
/** Get address info of this health server instance. */
|
|
340
|
+
getAddress(): HealthzAddressInfo | null;
|
|
341
|
+
/** Get address info of the active singleton health server. */
|
|
342
|
+
static getAddress(): HealthzAddressInfo | null;
|
|
343
|
+
/** Get port the health server is listening on. */
|
|
344
|
+
getPort(): number | undefined;
|
|
345
|
+
/** Get port the active singleton health server is listening on. */
|
|
346
|
+
static getPort(): number | undefined;
|
|
347
|
+
/** Get configured host. */
|
|
348
|
+
getHost(): string | undefined;
|
|
349
|
+
/** Get configured host of the active singleton health server. */
|
|
350
|
+
static getHost(): string | undefined;
|
|
351
|
+
/** Get full URL for this health server instance. */
|
|
352
|
+
getUrl(): string | undefined;
|
|
353
|
+
/** Get full URL for the active singleton health server. */
|
|
354
|
+
static getUrl(): string | undefined;
|
|
355
|
+
/** Gracefully stop this health server instance. */
|
|
356
|
+
stop(): Promise<void>;
|
|
291
357
|
/** Gracefully stop the health-check server. */
|
|
292
358
|
static stop(): Promise<void>;
|
|
293
359
|
static getInstance(): HealthzServer | undefined;
|
|
294
360
|
/**
|
|
295
361
|
* Register a named check on this HealthzServer instance dynamically.
|
|
362
|
+
* If a check with the same name already exists in the target probe, it is updated in-place.
|
|
296
363
|
*
|
|
297
364
|
* @param check - The named check to register
|
|
298
365
|
* @param type - Which probe to attach this check to ('readiness', 'liveness', or 'both')
|
|
299
366
|
* @returns This instance for chaining
|
|
300
367
|
*/
|
|
301
368
|
registerCheck(check: NamedCheck, type?: 'liveness' | 'readiness' | 'both'): this;
|
|
369
|
+
/**
|
|
370
|
+
* Unregister a named check on this HealthzServer instance.
|
|
371
|
+
*
|
|
372
|
+
* @param name - The name of the check to remove
|
|
373
|
+
* @param type - Which probe to remove from ('liveness', 'readiness', or 'both')
|
|
374
|
+
* @returns This instance for chaining
|
|
375
|
+
*/
|
|
376
|
+
unregisterCheck(name: string, type?: 'liveness' | 'readiness' | 'both'): this;
|
|
377
|
+
/**
|
|
378
|
+
* Get all registered checks on this instance.
|
|
379
|
+
*/
|
|
380
|
+
getChecks(): {
|
|
381
|
+
liveness: NamedCheck[];
|
|
382
|
+
readiness: NamedCheck[];
|
|
383
|
+
};
|
|
302
384
|
/**
|
|
303
385
|
* Register a named check on the active singleton HealthzServer instance (if started).
|
|
386
|
+
* If a check with the same name already exists in the target probe, it is updated in-place.
|
|
304
387
|
*
|
|
305
388
|
* @param check - The named check to register
|
|
306
389
|
* @param type - Which probe to attach this check to ('readiness', 'liveness', or 'both')
|
|
307
390
|
*/
|
|
308
391
|
static registerCheck(check: NamedCheck, type?: 'liveness' | 'readiness' | 'both'): void;
|
|
392
|
+
/**
|
|
393
|
+
* Unregister a named check on the active singleton HealthzServer instance (if started).
|
|
394
|
+
*
|
|
395
|
+
* @param name - Name of the check to remove
|
|
396
|
+
* @param type - Which probe to remove from ('liveness', 'readiness', or 'both')
|
|
397
|
+
*/
|
|
398
|
+
static unregisterCheck(name: string, type?: 'liveness' | 'readiness' | 'both'): void;
|
|
399
|
+
/**
|
|
400
|
+
* Get all registered checks on the active singleton HealthzServer instance.
|
|
401
|
+
*/
|
|
402
|
+
static getChecks(): {
|
|
403
|
+
liveness: NamedCheck[];
|
|
404
|
+
readiness: NamedCheck[];
|
|
405
|
+
};
|
|
309
406
|
private handleRequest;
|
|
310
407
|
/** `/healthz` — Liveness probe */
|
|
311
408
|
private handleLiveness;
|
|
@@ -316,6 +413,13 @@ declare class HealthzServer {
|
|
|
316
413
|
private runChecks;
|
|
317
414
|
private sendProbe;
|
|
318
415
|
private sendJson;
|
|
416
|
+
/**
|
|
417
|
+
* Executes a check function with timeout and cooperative cancellation.
|
|
418
|
+
*
|
|
419
|
+
* Note on cancellation: The AbortController aborts when `ms` expires, which signals cooperative
|
|
420
|
+
* consumers (e.g., fetch, pg, ioredis) to terminate their work. The attached .catch() on checkPromise
|
|
421
|
+
* prevents unhandled rejections if the check promise rejects after the timeout has already resolved.
|
|
422
|
+
*/
|
|
319
423
|
private executeCheck;
|
|
320
424
|
private initiateShutdown;
|
|
321
425
|
private cleanup;
|