@catbee/utils 2.1.1 → 2.2.1

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/types/index.d.ts CHANGED
@@ -157,6 +157,134 @@ type Without<T, U> = {
157
157
  [P in keyof T as T[P] extends U ? never : P]: T[P];
158
158
  };
159
159
 
160
+ /**
161
+ * A health check function that can be synchronous or asynchronous.
162
+ * Receives an AbortSignal that is triggered when `checkTimeoutMs` is exceeded.
163
+ *
164
+ * > **Cancellation Note**: Cancellation is cooperative. The check function must listen to
165
+ * > `signal.aborted` or pass `signal` to underlying asynchronous APIs (e.g. database drivers, `fetch`).
166
+ * > Synchronous CPU-bound loops or operations that ignore `signal` cannot be forcibly stopped by JavaScript.
167
+ *
168
+ * - Return `true` (or resolve to true) to signal healthy.
169
+ * - Return `false` (or resolve to false) to signal unhealthy.
170
+ * - Throw an error (or reject) to signal unhealthy with an error message.
171
+ */
172
+ type HealthCheckFn = (signal?: AbortSignal) => boolean | Promise<boolean>;
173
+ /**
174
+ * A readiness check function.
175
+ * Receives an AbortSignal that is triggered when `checkTimeoutMs` is exceeded.
176
+ *
177
+ * > **Cancellation Note**: Cancellation is cooperative. The check function must listen to
178
+ * > `signal.aborted` or pass `signal` to underlying asynchronous APIs (e.g. database drivers, `fetch`).
179
+ * > Synchronous CPU-bound loops or operations that ignore `signal` cannot be forcibly stopped by JavaScript.
180
+ *
181
+ * - Return `true` (or resolve to true) to signal ready.
182
+ * - Return `false` (or resolve to false) to signal not ready.
183
+ * - Throw an error (or reject) to signal not ready with an error.
184
+ */
185
+ type ReadinessCheckFn = (signal?: AbortSignal) => boolean | Promise<boolean>;
186
+ /**
187
+ * A named health check with an associated check function.
188
+ */
189
+ interface NamedCheck {
190
+ /** Human-readable name for this check (e.g. 'database', 'redis', 'disk') */
191
+ name: string;
192
+ /**
193
+ * Check function — return false or throw to indicate failure.
194
+ * Receives an AbortSignal that is triggered when the check times out.
195
+ * Cancellation via the signal is cooperative.
196
+ */
197
+ check: (signal?: AbortSignal) => boolean | Promise<boolean>;
198
+ }
199
+ /**
200
+ * Configuration for the standalone healthz HTTP server.
201
+ */
202
+ interface CatbeeHealthzServerConfig {
203
+ /** Hostname / IP to bind to
204
+ * - **default**: `'0.0.0.0'`
205
+ * - **env**: `HEALTHZ_HOST` (fallback: `SERVER_HOST`, `HOST`)
206
+ */
207
+ host?: string;
208
+ /** Port to listen on
209
+ * - **default**: `8282`
210
+ * - **env**: `HEALTHZ_PORT` (fallback: `SERVER_HEALTHZ_PORT`)
211
+ */
212
+ port?: number;
213
+ /** Liveness probe path — Kubernetes `livenessProbe.httpGet.path`
214
+ * - **default**: `'/healthz'`
215
+ * - **env**: `HEALTHZ_PATH` (fallback: `SERVER_HEALTH_CHECK_PATH`)
216
+ */
217
+ healthzPath?: string;
218
+ /** Readiness probe path — Kubernetes `readinessProbe.httpGet.path`
219
+ * - **default**: `'/readyz'`
220
+ * - **env**: `HEALTHZ_READYZ_PATH` (fallback: `SERVER_READYZ_PATH`)
221
+ */
222
+ readyzPath?: string;
223
+ /** Startup probe path — Kubernetes `startupProbe.httpGet.path`
224
+ * - **default**: `'/startupz'`
225
+ * - **env**: `HEALTHZ_STARTUPZ_PATH` (fallback: `SERVER_STARTUPZ_PATH`)
226
+ */
227
+ startupzPath?: string;
228
+ /** Include individual check results in the JSON response
229
+ * - **default**: `true`
230
+ * - **env**: `HEALTHZ_DETAILED` (fallback: `SERVER_HEALTH_CHECK_DETAILED_OUTPUT`)
231
+ */
232
+ detailed?: boolean;
233
+ /**
234
+ * Named checks to run on the liveness endpoint (`/healthz`).
235
+ *
236
+ * > **Kubernetes Best Practice**: Keep liveness checks very lightweight (e.g. process is responsive,
237
+ * > event loop not blocked). Avoid placing external dependencies (DB, Redis, downstream APIs) here;
238
+ * > if a shared dependency encounters transient downtime, failing liveness causes Kubernetes to restart
239
+ * > the container, risking cascading restart storms.
240
+ * >
241
+ * > Place external dependency checks in `readinessChecks` instead.
242
+ */
243
+ checks?: NamedCheck[];
244
+ /**
245
+ * Named checks to run on the readiness endpoint (`/readyz`).
246
+ *
247
+ * Use this for external dependencies (DB, Redis, cache, message broker).
248
+ * If a dependency goes down, Kubernetes will temporarily remove the pod from traffic rotation
249
+ * without killing/restarting the container, allowing it to recover cleanly.
250
+ *
251
+ * - If **omitted** (`undefined`): falls back to `checks`.
252
+ * - If **explicitly empty** (`[]`): no readiness checks are run (traffic gated purely by `setReady(true)`).
253
+ */
254
+ readinessChecks?: NamedCheck[];
255
+ /**
256
+ * Custom liveness check function. Runs *in addition to* `checks`.
257
+ * Return `false` or throw to indicate unhealthy.
258
+ */
259
+ onHealthCheck?: HealthCheckFn;
260
+ /**
261
+ * Custom readiness check function. Runs *in addition to* `readinessChecks`.
262
+ * Return `false` or throw to indicate not ready.
263
+ */
264
+ onReadinessCheck?: ReadinessCheckFn;
265
+ /** Timeout (ms) per individual check before it's considered failed
266
+ * - **default**: `5000`
267
+ * - **env**: `HEALTHZ_CHECK_TIMEOUT_MS`
268
+ */
269
+ checkTimeoutMs?: number;
270
+ /**
271
+ * Graceful shutdown delay in milliseconds.
272
+ * After receiving SIGTERM/SIGINT the server immediately flips readiness
273
+ * to `false` and waits this many ms before closing — giving the load
274
+ * balancer time to drain traffic.
275
+ * - **default**: `5000`
276
+ * - **env**: `HEALTHZ_SHUTDOWN_DELAY_MS`
277
+ */
278
+ shutdownDelayMs?: number;
279
+ /**
280
+ * Whether the HealthzServer should register its own SIGTERM/SIGINT process signal listeners.
281
+ * When managed by an orchestrating server (such as Catbee ExpressServer), set this to `false`
282
+ * to prevent signal listener conflicts and allow coordinated teardown.
283
+ * - **default**: `true`
284
+ */
285
+ handleSignals?: boolean;
286
+ }
287
+
160
288
  /**
161
289
  * Server configuration for Catbee HTTP/Express server.
162
290
  * Designed with secure and high-performance defaults for production use.
@@ -370,35 +498,19 @@ interface CatbeeServerConfig {
370
498
  */
371
499
  skipNotFoundRoutes?: boolean;
372
500
  };
373
- /** Health-check configuration
374
- * - **path**: `/healthz`
375
- * - **detailed**: `true`
376
- * - **withGlobalPrefix**: `false`
501
+ /** Standalone Healthz probe HTTP server configuration (for Kubernetes liveness, readiness, startup probes)
502
+ * - **default**: `false`
503
+ * - **env**: `SERVER_HEALTHZ_ENABLE` || `HEALTHZ_ENABLE`
504
+ *
505
+ * When enabled, ExpressServer automatically:
506
+ * - Starts `HealthzServer` on `server.start()`
507
+ * - Sets `HealthzServer.setReady(true)` after Express server is listening and ready
508
+ * - Sets `HealthzServer.setReady(false)` and gracefully drains/stops `HealthzServer` on `server.stop()`
509
+ * - Syncs health checks registered via `server.registerHealthCheck()` to `HealthzServer`
377
510
  */
378
- healthCheck?: {
379
- /** Health-check endpoint path
380
- * - **default**: `'/healthz'`
381
- * - **env**: `SERVER_HEALTH_CHECK_PATH`
382
- */
383
- path?: string;
384
- /** Include detailed check results in the response
385
- * - **default**: `true`
386
- * - **env**: `SERVER_HEALTH_CHECK_DETAILED_OUTPUT`
387
- */
388
- detailed?: boolean;
389
- /** Apply global route prefix
390
- * - **default**: `false`
391
- * - **env**: `SERVER_HEALTH_CHECK_WITH_GLOBAL_PREFIX`
392
- */
393
- withGlobalPrefix?: boolean;
394
- /** Custom health checks */
395
- checks?: Array<{
396
- /** Name of the health check */
397
- name: string;
398
- /** Check function that returns boolean or Promise<boolean> */
399
- check: () => Promise<boolean> | boolean;
400
- }>;
401
- };
511
+ healthzServer?: ToggleConfig<CatbeeHealthzServerConfig & {
512
+ enable?: boolean;
513
+ }>;
402
514
  /** Request timeout in ms
403
515
  * - **default**: `30000` (30 seconds)
404
516
  * - **env**: `SERVER_REQUEST_TIMEOUT_MS`
@@ -557,25 +669,8 @@ interface CatbeeServerHooks {
557
669
  /** Called before response is sent */
558
670
  onResponse?: (req: Request, res: Response, next: NextFunction) => void;
559
671
  }
560
- interface GlobalServerAddons {
561
- /**
562
- * Skip healthz endpoint even if health checks are configured
563
- * - **default**: `false`
564
- * - **env**: `SERVER_SKIP_HEALTHZ_CHECKS_VALIDATION`
565
- *
566
- * @additionalInfo
567
- * Set to true to return `200 OK` for `/healthz` without checks
568
- * Useful in environments where a simple liveness probe is needed
569
- * without performing actual health checks
570
- * Example: Kubernetes liveness probe
571
- * Note: This does not disable the health check functionality itself
572
- * Health checks can still be performed programmatically
573
- * or via other endpoints if needed
574
- */
575
- skipHealthzChecksValidation: boolean;
576
- }
577
672
  /** Combined global server configuration type */
578
- type CatbeeGlobalServerConfig = CatbeeServerConfig & GlobalServerAddons;
673
+ type CatbeeGlobalServerConfig = CatbeeServerConfig;
579
674
 
580
675
  /**
581
676
  * Generic API response format.
@@ -781,4 +876,4 @@ interface CatbeeConfig {
781
876
  }
782
877
 
783
878
  export { SortDirection };
784
- export type { ApiErrorResponse, ApiResponse, ApiSuccessResponse, AsyncOperationResponse, Awaited, BatchResponse, CatbeeConfig, CatbeeGlobalServerConfig, CatbeeServerConfig, CatbeeServerHooks, DeepPartial, DeepReadonly, DeepRequired, DeepStringifyOrNull, Func, GlobalServerAddons, IsEqual, KeysOfType, MaybePromise, Mutable, NonEmptyArray, Nullable, Optional, Optional2, Pagination, PaginationParams, PaginationResponse, PartialPick, PickByType, Primitive, RecordOptional, RequireAtLeastOne, StreamResponse, StringKeyedRecord, ToggleConfig, UnionToIntersection, ValueOf, WithPagination, Without, Writable };
879
+ export type { ApiErrorResponse, ApiResponse, ApiSuccessResponse, AsyncOperationResponse, Awaited, BatchResponse, CatbeeConfig, CatbeeGlobalServerConfig, CatbeeServerConfig, CatbeeServerHooks, DeepPartial, DeepReadonly, DeepRequired, DeepStringifyOrNull, Func, IsEqual, KeysOfType, MaybePromise, Mutable, NonEmptyArray, Nullable, Optional, Optional2, Pagination, PaginationParams, PaginationResponse, PartialPick, PickByType, Primitive, RecordOptional, RequireAtLeastOne, StreamResponse, StringKeyedRecord, ToggleConfig, UnionToIntersection, ValueOf, WithPagination, Without, Writable };