@catbee/utils 2.0.5 → 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/server/index.d.ts CHANGED
@@ -26,6 +26,7 @@ import express, { Express, Router } from 'express';
26
26
  import http from 'node:http';
27
27
  import https from 'node:https';
28
28
  import { CatbeeServerConfig, CatbeeServerHooks } from '@catbee/utils/types';
29
+ import { HealthzServer, HealthzAddressInfo, CatbeeHealthzServerConfig } from '@catbee/utils/healthz-server';
29
30
 
30
31
  /**
31
32
  * Map of critical dependencies to their error messages.
@@ -62,11 +63,11 @@ declare class ExpressServer {
62
63
  protected hooks: CatbeeServerHooks;
63
64
  /** Global API prefix (from config) */
64
65
  protected globalPrefix: string;
65
- /** Internal fallback router */
66
+ /** Primary root router mounted to the application */
66
67
  private readonly rootRouter;
67
- /** User-supplied router */
68
- private externalRouter?;
69
- /** Internal Express app instance */
68
+ /** Set of registered sub-routers to prevent duplicate mounting */
69
+ private readonly mountedRouters;
70
+ /** Express app instance */
70
71
  private readonly app;
71
72
  /** Set of active WebSocket connections */
72
73
  private readonly connections;
@@ -74,13 +75,18 @@ declare class ExpressServer {
74
75
  private isShuttingDown;
75
76
  /** Flag indicating if graceful shutdown handlers are registered */
76
77
  private gracefulShutdownRegistered;
77
- /**
78
- * Collection of registered health check functions.
79
- * These are executed when the health check endpoint is accessed.
80
- */
81
- private readonly healthChecks;
78
+ /** Map of registered signal listeners for clean teardown */
79
+ private readonly signalListeners;
80
+ /** Running address info for the Healthz probe server */
81
+ private healthzAddress?;
82
+ /** Named checks queued for Healthz liveness probe */
83
+ private readonly healthzChecks;
84
+ /** Named checks queued for Healthz readiness probe */
85
+ private readonly healthzReadinessChecks;
82
86
  /** Promise that resolves when initialization (middleware + routes) is complete */
83
87
  private readonly initPromise;
88
+ /** In-flight start promise to protect against concurrent start() calls */
89
+ private startPromise?;
84
90
  /**
85
91
  * Initializes server with intelligent defaults and security best practices.
86
92
  * All settings can be customized via config and hooks.
@@ -178,6 +184,10 @@ declare class ExpressServer {
178
184
  * Set up OpenAPI documentation middleware.
179
185
  */
180
186
  private setupOpenApiMiddleware;
187
+ /**
188
+ * Set up response preprocessing hook (applies global prefix if set).
189
+ */
190
+ private setupResponseHook;
181
191
  /**
182
192
  * Configure server routes and error handling.
183
193
  * Sets up in following order:
@@ -189,18 +199,18 @@ declare class ExpressServer {
189
199
  */
190
200
  protected setupRoutes(): Promise<void>;
191
201
  /**
192
- * Execute health check and return response.
202
+ * Whether the Healthz probe server is enabled.
193
203
  */
194
- private handleHealthCheckRequest;
204
+ isHealthzServerEnabled(): boolean;
195
205
  /**
196
- * Execute all registered health checks and return results.
206
+ * Get the graceful shutdown delay in milliseconds configured for HealthzServer.
197
207
  */
198
- private executeHealthChecks;
208
+ private getHealthzShutdownDelay;
199
209
  /**
200
- * Register a new health check function for monitoring service dependencies.
210
+ * Register a new health check function for monitoring service dependencies on the Healthz probe server.
201
211
  *
202
- * Health checks are executed when the health endpoint is accessed and
203
- * help determine if the service is ready to handle requests.
212
+ * By default, external dependencies (DB, Redis, etc.) are registered as `readiness` checks.
213
+ * Can also be registered as `liveness` or `both`.
204
214
  *
205
215
  * Examples:
206
216
  * - Database connectivity
@@ -209,17 +219,39 @@ declare class ExpressServer {
209
219
  * - Memory/CPU usage checks
210
220
  *
211
221
  * @param name Unique identifier for the check (used in detailed responses)
212
- * @param check Function returning boolean or Promise<boolean> indicating health
222
+ * @param check Function returning boolean or Promise<boolean> indicating health (supports optional AbortSignal)
223
+ * @param options Target probe type ('readiness' | 'liveness' | 'both') or options object
213
224
  * @returns This instance for method chaining
214
225
  */
215
- registerHealthCheck(name: string, check: () => Promise<boolean> | boolean): this;
226
+ registerHealthCheck(name: string, check: (signal?: AbortSignal) => Promise<boolean> | boolean, options?: 'readiness' | 'liveness' | 'both' | {
227
+ type?: 'readiness' | 'liveness' | 'both';
228
+ }): this;
216
229
  /**
217
- * Run registered health checks and return whether the service is ready.
218
- * Useful for readiness probes in deployment tooling.
230
+ * Mark the service as ready / not-ready for traffic on the Healthz probe server.
219
231
  *
220
- * @returns Promise resolving to `true` when all checks pass, otherwise `false`.
232
+ * @param ready Whether the service is ready to receive traffic
233
+ * @returns This instance for method chaining
221
234
  */
222
- ready(): Promise<boolean>;
235
+ setReady(ready: boolean): this;
236
+ /**
237
+ * Whether the service is currently marked as ready for traffic on the Healthz probe server.
238
+ */
239
+ isReady(): boolean;
240
+ /**
241
+ * Get the running HealthzServer instance (if started).
242
+ */
243
+ getHealthzServer(): HealthzServer | undefined;
244
+ /**
245
+ * Get the address info of the running HealthzServer (if started).
246
+ */
247
+ getHealthzAddress(): HealthzAddressInfo | null | undefined;
248
+ /**
249
+ * Returns whether the service is currently marked as ready for traffic on the Healthz probe server.
250
+ * Useful for readiness checks in deployment tooling.
251
+ *
252
+ * @returns `true` when ready, otherwise `false`.
253
+ */
254
+ ready(): boolean;
223
255
  /**
224
256
  * Get the underlying Express application instance.
225
257
  * Use this for advanced Express features not exposed by this wrapper.
@@ -238,28 +270,32 @@ declare class ExpressServer {
238
270
  * Start the HTTP server and begin listening for requests.
239
271
  *
240
272
  * This method:
273
+ * - Protects against concurrent start() invocations
274
+ * - Awaits server initialization (middleware + routes)
241
275
  * - Executes beforeStart hooks
276
+ * - Creates the HTTP/HTTPS server instance
277
+ * - Sets up error handling and connection tracking BEFORE listening
278
+ * - Executes onServerCreated hook BEFORE listening
242
279
  * - Binds to the configured host/port
243
- * - Sets up error handling for startup failures
244
- * - Executes afterStart hooks on success
245
- * - Logs startup information
280
+ * - Executes afterStart hooks on successful listen
281
+ * - Cleans up server reference and listeners on startup failure
246
282
  *
247
283
  * @returns Promise resolving to the running HTTP server instance
248
284
  * @throws Error if server fails to start or port is already in use
249
285
  */
250
286
  start(): Promise<http.Server | https.Server>;
251
287
  /**
252
- * Create HTTP or HTTPS server instance.
288
+ * Internal implementation of server startup.
289
+ */
290
+ private doStart;
291
+ /**
292
+ * Create HTTP or HTTPS server instance (without listening).
253
293
  */
254
294
  private createServerInstance;
255
295
  /**
256
296
  * Set up connection tracking for graceful shutdown.
257
297
  */
258
298
  private setupConnectionTracking;
259
- /**
260
- * Set up error handling for server startup.
261
- */
262
- private setupServerErrorHandling;
263
299
  /**
264
300
  * Log server startup information.
265
301
  */
@@ -297,14 +333,32 @@ declare class ExpressServer {
297
333
  * @param signals Array of process signals to listen for (default: SIGINT, SIGTERM)
298
334
  */
299
335
  enableGracefulShutdown(signals?: NodeJS.Signals[]): this;
336
+ /**
337
+ * Unregister graceful shutdown signal listeners.
338
+ * Useful for testing and dynamic server lifecycles to prevent memory and listener leaks.
339
+ */
340
+ disableGracefulShutdown(): this;
300
341
  /**
301
342
  * Destroy all active connections (gracefully if possible).
302
343
  * If a connection does not close cleanly, it will be force-destroyed.
303
344
  */
304
345
  private destroyConnections;
305
346
  /**
306
- * Set an externally created base router.
307
- * This will override the internal rootRouter.
347
+ * Mount a base router onto the server's root router.
348
+ *
349
+ * Note: This attaches the supplied router to the root router pipeline.
350
+ * Duplicate mounting of the same router instance is ignored.
351
+ *
352
+ * @param router The Express router instance to mount
353
+ * @returns This instance for method chaining
354
+ */
355
+ addBaseRouter(router: Router): this;
356
+ /**
357
+ * Alias for `addBaseRouter` (maintained for backward compatibility).
358
+ * Mounts the supplied router onto the server's root router.
359
+ *
360
+ * @param router The Express router instance to mount
361
+ * @returns This instance for method chaining
308
362
  */
309
363
  setBaseRouter(router: Router): this;
310
364
  /**
@@ -321,6 +375,34 @@ declare class ExpressServer {
321
375
  * @returns This instance for method chaining
322
376
  */
323
377
  registerRoute(methods: Array<keyof Pick<Express, 'get' | 'post' | 'put' | 'delete' | 'patch' | 'options' | 'head'>>, path: string, ...handlers: Array<express.RequestHandler>): this;
378
+ /**
379
+ * Register a GET route handler.
380
+ */
381
+ get(path: string, ...handlers: express.RequestHandler[]): this;
382
+ /**
383
+ * Register a POST route handler.
384
+ */
385
+ post(path: string, ...handlers: express.RequestHandler[]): this;
386
+ /**
387
+ * Register a PUT route handler.
388
+ */
389
+ put(path: string, ...handlers: express.RequestHandler[]): this;
390
+ /**
391
+ * Register a DELETE route handler.
392
+ */
393
+ delete(path: string, ...handlers: express.RequestHandler[]): this;
394
+ /**
395
+ * Register a PATCH route handler.
396
+ */
397
+ patch(path: string, ...handlers: express.RequestHandler[]): this;
398
+ /**
399
+ * Register an OPTIONS route handler.
400
+ */
401
+ options(path: string, ...handlers: express.RequestHandler[]): this;
402
+ /**
403
+ * Register a HEAD route handler.
404
+ */
405
+ head(path: string, ...handlers: express.RequestHandler[]): this;
324
406
  /**
325
407
  * Register custom middleware with optional path restriction.
326
408
  *
@@ -657,32 +739,36 @@ declare class ServerConfigBuilder {
657
739
  */
658
740
  disableRequestLogging(): this;
659
741
  /**
660
- * Configures server health check endpoint.
742
+ * Configures the dedicated Healthz probe HTTP server for Kubernetes.
661
743
  *
662
- * @param opts - Health check configuration options
744
+ * @param opts - Healthz server configuration options or boolean toggle
663
745
  * @returns The builder instance for chaining
664
- * @default - { path: '/healthz', detailed: true, withGlobalPrefix: false }
665
746
  *
666
747
  * @example
667
748
  * ```typescript
668
- * builder.withHealthCheck({
669
- * path: '/health',
670
- * detailed: true
749
+ * builder.withHealthzServer({
750
+ * port: 8282,
751
+ * shutdownDelayMs: 5000,
752
+ * readinessChecks: [
753
+ * { name: 'db', check: () => checkDb() }
754
+ * ]
671
755
  * })
672
756
  * ```
673
757
  */
674
- withHealthCheck(opts: Partial<NonNullable<CatbeeServerConfig['healthCheck']>>): this;
758
+ withHealthzServer(opts: NonNullable<CatbeeServerConfig['healthzServer']>): this;
675
759
  /**
676
- * Enables health check endpoint with default or custom settings
677
- * @param opts - Optional health check configuration
760
+ * Enables the dedicated Healthz probe HTTP server.
761
+ *
762
+ * @param opts - Optional Healthz server configuration options
678
763
  * @returns The builder instance for chaining
764
+ */
765
+ enableHealthzServer(opts?: Partial<CatbeeHealthzServerConfig>): this;
766
+ /**
767
+ * Disables the dedicated Healthz probe HTTP server.
679
768
  *
680
- * @example
681
- * ```typescript
682
- * builder.disableHealthCheck()
683
- * ```
769
+ * @returns The builder instance for chaining
684
770
  */
685
- disableHealthCheck(): this;
771
+ disableHealthzServer(): this;
686
772
  /**
687
773
  * Configures OpenAPI/Swagger documentation for the API.
688
774
  *