@catbee/utils
Version:
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.
890 lines (885 loc) • 29.4 kB
TypeScript
/*
* The MIT License
*
* Copyright (c) 2026 Catbee Technologies. https://catbee.in/license
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import * as prom_client from 'prom-client';
import express, { Express, Router } from 'express';
import http from 'node:http';
import https from 'node:https';
import { CatbeeServerConfig, CatbeeServerHooks } from '@catbee/utils/types';
/**
* Map of critical dependencies to their error messages.
*/
declare const DependencyErrors: {
express: string;
helmet: string;
cors: string;
compression: string;
'express-rate-limit': string;
'cookie-parser': string;
'@scalar/express-api-reference': string;
'prom-client': string;
};
/**
* Production-ready Express server with enterprise features.
*
* Core Features:
* - Security: Helmet, CORS, rate limiting, timeouts
* - Monitoring: Request logs, metrics, health checks
* - Performance: Compression, caching, static files
* - Reliability: Graceful shutdown, error handling
* - Developer UX: OpenAPI docs, debugging tools
* - Extensibility: Hooks, middleware, custom routes
*
* Designed for microservices and production workloads.
* Includes K8s readiness probes and zero-downtime support.
*/
declare class ExpressServer {
/** Prometheus client registry for metrics collection */
private readonly register;
/** HTTP server instance (null when not running) */
protected server: http.Server | https.Server | null;
/** Merged configuration with defaults applied */
protected config: CatbeeServerConfig;
/** User-defined lifecycle hooks */
protected hooks: CatbeeServerHooks;
/** Global API prefix (from config) */
protected globalPrefix: string;
/** Internal fallback router */
private readonly rootRouter;
/** User-supplied router */
private externalRouter?;
/** Internal Express app instance */
private readonly app;
/** Set of active WebSocket connections */
private readonly connections;
/** Flag indicating if the server is shutting down */
private isShuttingDown;
/**
* Collection of registered health check functions.
* These are executed when the health check endpoint is accessed.
*/
private readonly healthChecks;
/** Prometheus metrics for monitoring */
private readonly requestCounter?;
private readonly routeTimings?;
private readonly requestSizes?;
private readonly clientIPs?;
/** Promise that resolves when initialization (middleware + routes) is complete */
private readonly initPromise;
/**
* Initializes server with intelligent defaults and security best practices.
* All settings can be customized via config and hooks.
*
* Default Security:
* - Secure headers (Helmet)
* - Rate limiting
* - Request timeouts
* - Body size limits
* - CORS protection
*
* Default Monitoring:
* - Request/Response logging
* - Prometheus metrics
* - Health checks
* - Request tracing
*/
constructor(config: Partial<CatbeeServerConfig>, hooks?: CatbeeServerHooks);
/**
* Execute a lifecycle hook safely with comprehensive error handling.
* Prevents hook failures from crashing the server while logging issues.
*
* @param hook Name of the lifecycle hook to execute
* @param args Arguments to pass to the hook function
*/
private runHook;
/**
* Initialize the Express server with middleware and routes.
*/
private initialize;
/**
* Configure and register all middlewares in the optimal order.
*
* Middleware Order (CRITICAL - don't change without understanding implications):
* 1. Basic server configuration (trust proxy, x-powered-by)
* 2. Request ID generation (for tracing)
* 3. Request context setup (for logging correlation)
* 4. Timeout protection (prevents hanging requests)
* 5. Response time tracking (for performance monitoring)
* 6. Request logging (after ID/context setup)
* 7. Custom request hooks
* 8. Security middleware (rate limiting, CORS, Helmet)
* 9. Response compression
* 10. Static file serving
* 11. Request parsing (body parsing, cookies)
* 12. API documentation (OpenAPI)
* 13. Global headers
* 14. Custom response hooks
*/
protected setupMiddleware(): Promise<void>;
/**
* Set up basic middleware (trust proxy, request ID, context).
*/
private setupBasicMiddleware;
/**
* Set up security middleware (Helmet, CORS).
*/
private setupSecurityMiddleware;
/**
* Set up global headers middleware.
*/
private setupGlobalHeaders;
/**
* Set up request timeout middleware.
*/
private setupTimeoutMiddleware;
/**
* Set up response time tracking middleware.
*/
private setupResponseTimeMiddleware;
/**
* Set up rate limiting middleware.
*/
private setupRateLimitingMiddleware;
/**
* Set up request logging middleware.
*/
private setupRequestLoggingMiddleware;
/**
* Set up response compression middleware.
*/
private setupCompressionMiddleware;
/**
* Set up static file serving middleware.
*/
private setupStaticFilesMiddleware;
/**
* Set up body parsing middleware.
*/
private setupBodyParsingMiddleware;
/**
* Set up cookie parsing middleware.
*/
private setupCookieParsingMiddleware;
/**
* Set up OpenAPI documentation middleware.
*/
private setupOpenApiMiddleware;
/**
* Set up metrics tracking middleware.
*/
private setupMetricsMiddleware;
/**
* Configure server routes and error handling.
* Sets up in following order:
*
* 1. Built-in routes (health, metrics)
* 2. Application routes
* 3. 404 handler
* 4. Error handler
*/
protected setupRoutes(): Promise<void>;
/**
* Execute health check and return response.
*/
private handleHealthCheckRequest;
/**
* Execute all registered health checks and return results.
*/
private executeHealthChecks;
/**
* Register a new health check function for monitoring service dependencies.
*
* Health checks are executed when the health endpoint is accessed and
* help determine if the service is ready to handle requests.
*
* Examples:
* - Database connectivity
* - External service availability
* - File system access
* - Memory/CPU usage checks
*
* @param name Unique identifier for the check (used in detailed responses)
* @param check Function returning boolean or Promise<boolean> indicating health
* @returns This instance for method chaining
*/
registerHealthCheck(name: string, check: () => Promise<boolean> | boolean): this;
/**
* Run registered health checks and return whether the service is ready.
* Useful for readiness probes in deployment tooling.
*
* @returns Promise resolving to `true` when all checks pass, otherwise `false`.
*/
ready(): Promise<boolean>;
/**
* Get the underlying Express application instance.
* Use this for advanced Express features not exposed by this wrapper.
*
* @returns The raw Express app instance
*/
getApp(): Express;
/**
* Get the active HTTP/HTTPS server instance.
* Returns null if the server is not currently running.
*
* @returns The HTTP/HTTPS server instance or null
*/
getServer(): http.Server | https.Server | null;
/**
* Start the HTTP server and begin listening for requests.
*
* This method:
* - Executes beforeStart hooks
* - Binds to the configured host/port
* - Sets up error handling for startup failures
* - Executes afterStart hooks on success
* - Logs startup information
*
* @returns Promise resolving to the running HTTP server instance
* @throws Error if server fails to start or port is already in use
*/
start(): Promise<http.Server | https.Server>;
/**
* Create HTTP or HTTPS server instance.
*/
private createServerInstance;
/**
* Set up connection tracking for graceful shutdown.
*/
private setupConnectionTracking;
/**
* Set up error handling for server startup.
*/
private setupServerErrorHandling;
/**
* Log server startup information.
*/
private logServerStartInfo;
/**
* Stop the HTTP server gracefully.
*
* This method:
* - Executes beforeStop hooks
* - Stops accepting new connections
* - Waits for existing connections to finish
* - Closes the server
* - Executes afterStop hooks
* - Logs shutdown information
*
* Graceful shutdown ensures:
* - No requests are dropped
* - Resources are properly cleaned up
* - Monitoring systems are notified
*/
stop(force?: boolean): Promise<void>;
/**
* Perform graceful server shutdown with timeout.
*/
private gracefulShutdown;
/**
* Enable graceful shutdown on OS signals for production deployment.
*
* This is essential for:
* - Container orchestration (Docker, Kubernetes)
* - Process managers (PM2, systemd)
* - Load balancer health checks
* - Zero-downtime deployments
*
* @param signals Array of process signals to listen for (default: SIGINT, SIGTERM)
*/
enableGracefulShutdown(signals?: NodeJS.Signals[]): this;
/**
* Set an externally created base router.
* This will override the internal rootRouter.
*/
setBaseRouter(router: Router): this;
/**
* Create and register a new router (only used if not injecting one externally - use `setBaseRouter` instead).
*/
createRouter(prefix?: string): Router;
/**
* Register a new route handler with support for multiple HTTP methods.
* The route is automatically registered under the globalPrefix if set.
*
* @param methods Array of HTTP methods (get, post, put, delete, etc.)
* @param path Route path with Express path patterns support
* @param handlers One or more Express request handlers (middleware + final handler)
* @returns This instance for method chaining
*/
registerRoute(methods: Array<keyof Pick<Express, 'get' | 'post' | 'put' | 'delete' | 'patch' | 'options' | 'head'>>, path: string, ...handlers: Array<express.RequestHandler>): this;
/**
* Register custom middleware with optional path restriction.
*
* Use this for:
* - Adding authentication to specific routes
* - Custom logging or validation
* - Request transformation
* - Third-party middleware integration
*
* @param path Optional path prefix or middleware function if no path
* @param middleware Middleware handler (required if path is provided)
* @returns This instance for method chaining
*/
registerMiddleware(path: string | express.RequestHandler, middleware?: express.RequestHandler): this;
/**
* Register one or more middleware functions to be applied globally.
* This is a simpler alternative to registerMiddleware when you just want
* to add middleware without path restrictions.
*
* @param middlewares One or more Express middleware functions
* @returns This instance for method chaining
*/
useMiddleware(...middlewares: express.RequestHandler[]): this;
/**
* Get Prometheus registry (to add custom counters/histograms)
*
* @return {*} {client.Registry}
*/
getMetricsRegistry(): typeof prom_client.Registry;
/**
* Get server configuration
*
* @return {*} {CatbeeServerConfig}
*/
getConfig(): CatbeeServerConfig;
/**
* Wait until server initialization (middleware + routes) has completed.
* Useful for integration tests that inspect app before starting.
*/
waitUntilReady(): Promise<void>;
private normalizePath;
private normalizeRouteForMetrics;
/**
* Destroy all active connections (gracefully if possible).
* If a connection does not close cleanly, it will be force-destroyed.
*/
private destroyConnections;
private validateHttpsFiles;
private hasBuildMarker;
private throwDependancyError;
}
/**
* Builder class for creating and configuring an Express server configuration.
*
* This class provides a fluent interface to configure all aspects of the Express server
* including security settings, middleware, routing, and more.
*
* @example
* ```typescript
* const serverConfig = new ServerConfigBuilder()
* .withPort(3000)
* .withHost('localhost')
* .enableCors()
* .enableHelmet()
* .build();
* ```
*/
declare class ServerConfigBuilder {
private config;
/**
* Validates that a port number is valid and usable.
*
* @private
* @param port - The port number to validate
* @throws {Error} If port is not an integer or is outside the valid range (1-65535)
*/
private validatePort;
/**
* Sets the port the server will listen on.
*
* @param port - The port number (1-65535)
* @returns The builder instance for chaining
* @throws {Error} If port is invalid
* @default 3000 (can be overridden via PORT env variable)
*
* @example
* ```typescript
* builder.withPort(3000)
* ```
*/
withPort(port: number): this;
/**
* Sets the hostname the server will bind to.
*
* @param host - The hostname (e.g., 'localhost', '0.0.0.0', '127.0.0.1')
* @returns The builder instance for chaining
* @default '0.0.0.0' (can be overridden via HOST env variable)
*
* @example
* ```typescript
* builder.withHost('0.0.0.0') // Listen on all interfaces
* ```
*/
withHost(host: string): this;
/**
* Configures Cross-Origin Resource Sharing (CORS) for the server.
*
* @param opts - CORS options object or boolean (true to enable with defaults, false to disable)
* @returns The builder instance for chaining
* @default false (CORS is disabled by default)
*
* @example
* ```typescript
* // Enable CORS with default options
* builder.withCors(true)
*
* // Configure CORS with specific options
* builder.withCors({
* origin: ['https://example.com'],
* methods: ['GET', 'POST']
* })
* ```
*/
withCors(opts: CatbeeServerConfig['cors']): this;
/**
* Enables CORS with default settings
*
* @returns The builder instance for chaining
*/
enableCors(): this;
/**
* Disables CORS
*
* @returns The builder instance for chaining
*/
disableCors(): this;
/**
* Configures the Helmet middleware for setting HTTP security headers.
*
* @param opts - Helmet options object or boolean (true to enable with defaults, false to disable)
* @returns The builder instance for chaining
* @default false (Helmet is disabled by default)
*
* @example
* ```typescript
* // Enable Helmet with default settings
* builder.withHelmet(true)
*
* // Configure Helmet with specific options
* builder.withHelmet({
* contentSecurityPolicy: false,
* xssFilter: true
* })
* ```
*/
withHelmet(opts: CatbeeServerConfig['helmet']): this;
/**
* Enables Helmet with default settings
*
* @returns The builder instance for chaining
*/
enableHelmet(): this;
/**
* Disables Helmet
*
* @returns The builder instance for chaining
*/
disableHelmet(): this;
/**
* Configures response compression middleware.
*
* @param opts - Compression options object or boolean (true to enable with defaults, false to disable)
* @returns The builder instance for chaining
* @default false (Compression is disabled by default)
*
* @example
* ```typescript
* // Enable compression with default settings
* builder.withCompression(true)
*
* // Configure compression with specific options
* builder.withCompression({
* level: 6,
* threshold: 1024
* })
* ```
*/
withCompression(opts: CatbeeServerConfig['compression']): this;
/**
* Enables compression with default settings
*
* @returns The builder instance for chaining
*/
enableCompression(): this;
/**
* Disables compression
*
* @returns The builder instance for chaining
*/
disableCompression(): this;
/**
* Configures rate limiting to protect against brute-force attacks.
*
* @param opts - Rate limit configuration options
* @returns The builder instance for chaining
* @default - { enable: false, windowMs: 15 * 60 * 1000, max: 100, message: 'Too many requests', standardHeaders: true, legacyHeaders: false }
*
* @example
* ```typescript
* builder.withRateLimit({
* enable: true,
* windowMs: 15 * 60 * 1000, // 15 minutes
* max: 100 // limit each IP to 100 requests per windowMs
* })
* ```
*/
withRateLimit(opts: Partial<CatbeeServerConfig['rateLimit']>): this;
/**
* Enables rate limiting with default or custom settings
*
* @param opts - Optional rate limit configuration (max requests, window, etc.)
* @returns The builder instance for chaining
*/
enableRateLimit(opts?: Omit<Partial<NonNullable<CatbeeServerConfig['rateLimit']>>, 'enable'>): this;
/**
* Disables rate limiting
*
* @returns The builder instance for chaining
*/
disableRateLimit(): this;
/**
* Configures HTTP request logging middleware.
*
* @param opts - Request logging configuration options
* @returns The builder instance for chaining
* @default - { enable: true in dev/false in prod, ignorePaths: ['/healthz', '/favicon.ico', '/metrics', '/docs', '/.well-known'], skipNotFoundRoutes: false }
*
* @example
* ```typescript
* builder.withRequestLogging({
* enable: true,
* ignorePaths: ['/health', '/metrics'],
* skipNotFoundRoutes: true
* })
* ```
*/
withRequestLogging(opts: Partial<NonNullable<CatbeeServerConfig['requestLogging']>>): this;
/**
* Enables request logging with default or custom settings
*
* @param opts - Optional request logging configuration
* @returns The builder instance for chaining
*/
enableRequestLogging(opts?: Omit<Partial<NonNullable<CatbeeServerConfig['requestLogging']>>, 'enable'>): this;
/**
* Disables request logging
*
* @returns The builder instance for chaining
*/
disableRequestLogging(): this;
/**
* Configures server metrics collection and endpoints.
*
* @param opts - Metrics configuration options
* @returns The builder instance for chaining
* @default - { enable: false, path: '/metrics', withGlobalPrefix: false }
*
* @example
* ```typescript
* builder.withMetrics({
* enable: true,
* path: '/metrics'
* })
* ```
*/
withMetrics(opts: Partial<NonNullable<CatbeeServerConfig['metrics']>>): this;
/**
* Enables Prometheus metrics collection and endpoint
*
* @param opts - Optional metrics configuration
* @returns The builder instance for chaining
*/
enableMetrics(opts?: Omit<Partial<NonNullable<CatbeeServerConfig['metrics']>>, 'enable'>): this;
/**
* Disables Prometheus metrics
*
* @returns The builder instance for chaining
*/
disableMetrics(): this;
/**
* Configures server health check endpoint.
*
* @param opts - Health check configuration options
* @returns The builder instance for chaining
* @default - { path: '/healthz', detailed: true, withGlobalPrefix: false }
*
* @example
* ```typescript
* builder.withHealthCheck({
* path: '/health',
* detailed: true
* })
* ```
*/
withHealthCheck(opts: Partial<NonNullable<CatbeeServerConfig['healthCheck']>>): this;
/**
* Configures OpenAPI/Swagger documentation for the API.
*
* @param opts - OpenAPI configuration options
* @returns The builder instance for chaining
* @default - { enable: false, mountPath: '/docs', verbose: false, withGlobalPrefix: false }
*
* @example
* ```typescript
* builder.withOpenApi({
* enable: true,
* path: '/api-docs',
* filePath: './openapi.yaml'
* })
* ```
*/
withOpenApi(opts: Partial<NonNullable<CatbeeServerConfig['openApi']>>): this;
/**
* Enables OpenAPI documentation with required file path
*
* @param filePath - Path to OpenAPI specification file (required)
* @param opts - Optional OpenAPI configuration
* @returns The builder instance for chaining
*/
enableOpenApi(filePath: string, opts?: Omit<Partial<NonNullable<CatbeeServerConfig['openApi']>>, 'enable' | 'filePath'>): this;
/**
* Disables OpenAPI documentation
*
* @returns The builder instance for chaining
*/
disableOpenApi(): this;
/**
* Configures the server as a microservice with versioning.
*
* @param opts - Microservice configuration options including app name and service version
* @returns The builder instance for chaining
* @default - { isMicroservice: false, appName: 'express_app' }
*
* @example
* ```typescript
* builder.withMicroService({
* appName: 'user-service',
* serviceVersion: {
* enable: true,
* version: '1.2.3'
* }
* })
* ```
*/
withMicroService(opts: {
appName: NonNullable<CatbeeServerConfig['appName']>;
serviceVersion: Partial<NonNullable<CatbeeServerConfig['serviceVersion']>>;
}): this;
/**
* Configures the trust proxy settings to determine if X-Forwarded-* headers should be trusted.
*
* @param opts - Trust proxy configuration options
* @returns The builder instance for chaining
* @default false
*
* @example
* ```typescript
* // Trust proxy headers (useful when behind a load balancer)
* builder.withTrustProxy(true)
* ```
*/
withTrustProxy(opts: NonNullable<CatbeeServerConfig['trustProxy']>): this;
/**
* Configures the request ID middleware for tracing requests across services.
*
* @param opts - Request ID configuration options
* @returns The builder instance for chaining
* @default - { headerName: 'x-request-id', exposeHeader: true }
*
* @example
* ```typescript
* builder.withRequestId({
* headerName: 'X-Request-Id',
* generator: () => crypto.randomUUID()
* })
* ```
*/
withRequestId(opts: Partial<NonNullable<CatbeeServerConfig['requestId']>>): this;
/**
* Configures the response time middleware for measuring request processing times.
*
* @param opts - Response time configuration options
* @returns The builder instance for chaining
* @default - { enable: false, addHeader: true, logOnComplete: false }
*
* @example
* ```typescript
* builder.withResponseTime({
* enable: true,
* addHeader: true,
* logOnComplete: true
* })
* ```
*/
withResponseTime(opts: Partial<NonNullable<CatbeeServerConfig['responseTime']>>): this;
/**
* Enables response time tracking with default or custom settings
*
* @param opts - Optional response time configuration
* @returns The builder instance for chaining
*/
enableResponseTime(opts?: Omit<Partial<NonNullable<CatbeeServerConfig['responseTime']>>, 'enable'>): this;
/**
* Disables response time tracking
*
* @returns The builder instance for chaining
*/
disableResponseTime(): this;
/**
* Configures the body parser middleware options for parsing request bodies.
*
* @param opts - Body parser configuration options
* @returns The builder instance for chaining
* @default - { json: { limit: '1mb' }, urlencoded: { extended: true, limit: '1mb' } }
*
* @example
* ```typescript
* builder.withBodyParser({
* json: {
* limit: '1mb'
* },
* urlencoded: {
* extended: true,
* limit: '1mb'
* }
* })
* ```
*/
withBodyParser(opts: NonNullable<CatbeeServerConfig['bodyParser']>): this;
/**
* Configures cookie parsing middleware.
*
* @param opts - Cookie parser options or boolean (true to enable with defaults, false to disable)
* @returns The builder instance for chaining
* @default false
*
* @example
* ```typescript
* // Enable cookie parsing with default options
* builder.withCookies(true)
*
* // Enable cookie parsing with specific options
* builder.withCookies({
* secret: 'your-secret-key',
* secure: true
* })
* ```
*/
withCookies(opts: CatbeeServerConfig['cookieParser']): this;
/**
* Adds a static folder to serve files from.
*
* @param folder - Static folder configuration
* @returns The builder instance for chaining
*
* @example
* ```typescript
* builder.withStaticFolder({
* path: '/assets',
* directory: './public',
* options: { maxAge: '1d' }
* })
* ```
*/
withStaticFolder(folder: NonNullable<CatbeeServerConfig['staticFolders']>[number]): this;
/**
* Sets global headers to be included in all responses.
*
* @param headers - Object containing header name/value pairs or functions that return values
* @returns The builder instance for chaining
* @default - {}
*
* @example
* ```typescript
* builder.withGlobalHeaders({
* 'X-Powered-By': 'Catbee',
* 'Server-Time': () => new Date().toISOString()
* })
* ```
*/
withGlobalHeaders(headers: NonNullable<CatbeeServerConfig['globalHeaders']>): this;
/**
* Sets a global prefix for all routes.
*
* @param prefix - The prefix to prepend to all routes (e.g., '/api/v1')
* @returns The builder instance for chaining
* @default '/'
*
* @example
* ```typescript
* builder.withGlobalPrefix('/api/v1')
* ```
*/
withGlobalPrefix(prefix: string): this;
/**
* Applies custom configuration overrides directly.
*
* @param overrides - Custom configuration options to merge
* @returns The builder instance for chaining
*
* @example
* ```typescript
* builder.withCustom({
* port: 8080,
* customMiddleware: myMiddlewareFunction
* })
* ```
*/
withCustom(overrides: Partial<CatbeeServerConfig>): this;
/**
* Configures HTTPS server options.
*
* @param opts - HTTPS configuration (key, cert, ca, passphrase, etc.)
* @returns The builder instance for chaining
*
* @example
* ```typescript
* builder.withHttps({
* key: './localhost-key.pem',
* cert: './localhost-cert.pem'
* })
* ```
*/
withHttps(opts: NonNullable<CatbeeServerConfig['https']>): this;
/**
* Builds and returns the final server configuration.
*
* This method merges the user-specified configuration with default values,
* ensures all sections with 'enable' flags are properly structured, and
* produces the final configuration to be used by the server.
*
* @returns The complete ServerConfig object
*
* @example
* ```typescript
* const config = new ServerConfigBuilder()
* .withPort(3000)
* .withHost('localhost')
* .withCors(true)
* .build();
* ```
*/
build(): Readonly<CatbeeServerConfig>;
private mergeConfig;
private setEnabled;
}
export { DependencyErrors, ExpressServer, ServerConfigBuilder };