@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.
710 lines (706 loc) • 20.9 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 express_rate_limit from 'express-rate-limit';
import { RequestHandler, Router } from 'express';
/**
* Supported HTTP methods for route decorators
*/
type HttpMethod = 'get' | 'post' | 'put' | 'patch' | 'delete' | 'options' | 'head' | 'trace' | 'connect';
/**
* Represents a route definition for controller methods
*/
interface RouteDefinition {
/** The URL path for this route */
path: string;
/** HTTP method for this route */
method: HttpMethod;
/** Name of the handler method in the controller class */
handlerName: string;
}
/**
* Parameter decoration definition for method parameters
*/
interface ParamDefinition {
/** Parameter position in method signature */
index: number;
/** Type of parameter (query, body, etc.) */
type: 'query' | 'param' | 'body' | 'req' | 'res' | 'logger' | 'reqHeader' | 'reqId' | 'cookie';
/** Optional key for extracting specific property */
key?: string;
/** Optional ParamOptions for advanced extraction */
options?: ParamOptions;
}
type CachedRateLimiter = {
limiter: ReturnType<typeof express_rate_limit.rateLimit>;
config: string;
};
type Constructor<T = any> = new (...args: any[]) => T;
declare class DIContainer {
private readonly instances;
private readonly constructing;
private readonly propertyInjections;
register<T>(target: Constructor<T>): void;
/**
* Register a property injection to be resolved when the target class is instantiated
*/
registerPropertyInjection(target: object, propertyKey: string | symbol, injectClass: Constructor): void;
get<T>(target: Constructor<T>): T;
private applyPropertyInjections;
clear(): void;
}
/**
* Injectable decorator for marking classes as injectable.
*
* @returns Class decorator that marks a class as injectable and registers it with the DI container.
*/
declare function Injectable(): ClassDecorator;
/**
* Inject decorator for injecting dependencies into class properties.
*
* @param targetClass - The class to inject
* @returns Property decorator that injects the specified class into the property
*/
declare function Inject<T>(targetClass: new (...args: any[]) => T): PropertyDecorator;
/**
* Inject function for retrieving instances from the DI container.
* @param targetClass - The class to inject
*
* @returns The instance of the requested class
*
* @example
* const a = inject(TestClass);
*/
declare function inject<T>(targetClass: new (...args: any[]) => T): T;
/**
* Decorator for GET HTTP method routes.
*
* @param path - URL path for the route
* @returns Method decorator
*
* @example
* ```ts
* @Get('/users')
* getUsers() {
* return this.userService.findAll();
* }
* ```
*/
declare const Get: (path: string) => MethodDecorator;
/**
* Decorator for POST HTTP method routes.
*
* @param path - URL path for the route
* @returns Method decorator
*
* @example
* ```ts
* @Post('/users')
* createUser(@Body() userData: any) {
* return this.userService.create(userData);
* }
* ```
*/
declare const Post: (path: string) => MethodDecorator;
/**
* Decorator for PUT HTTP method routes.
*
* @param path - URL path for the route
* @returns Method decorator
*/
declare const Put: (path: string) => MethodDecorator;
/**
* Decorator for PATCH HTTP method routes.
*
* @param path - URL path for the route
* @returns Method decorator
*/
declare const Patch: (path: string) => MethodDecorator;
/**
* Decorator for DELETE HTTP method routes.
*
* @param path - URL path for the route
* @returns Method decorator
*/
declare const Delete: (path: string) => MethodDecorator;
/**
* Decorator for OPTIONS HTTP method routes.
*
* @param path - URL path for the route
* @returns Method decorator
*/
declare const Options: (path: string) => MethodDecorator;
/**
* Decorator for HEAD HTTP method routes.
*
* @param path - URL path for the route
* @returns Method decorator
*/
declare const Head: (path: string) => MethodDecorator;
/**
* Decorator for TRACE HTTP method routes.
*
* @param path - URL path for the route
* @returns Method decorator
*/
declare const Trace: (path: string) => MethodDecorator;
/**
* Decorator for CONNECT HTTP method routes.
*
* @param path - URL path for the route
* @returns Method decorator
*/
declare const Connect: (path: string) => MethodDecorator;
/**
* Decorator that marks a class as a controller with a base path.
* Used as the entry point for routing configuration.
*
* @param basePath - Base URL path for all routes in this controller
* @returns Class decorator
*
* @example
* ```ts
* @Controller('/api/users')
* class UserController {
* // Controller methods...
* }
* ```
*/
declare function Controller(basePath: string): ClassDecorator;
/**
* Decorator that applies middleware to a controller method or an entire controller.
* Multiple middlewares can be applied and will execute in order.
*
* @param middlewares - Express middleware functions to apply
* @returns Method decorator or Class decorator
*
* @example
* ```ts
* @Get('/protected')
* @Use(authMiddleware, loggingMiddleware)
* getProtectedResource() {
* // This route is protected by auth middleware
* }
*
* @Controller('/api')
* @Use(commonMiddleware)
* class ApiController {
* // All routes in this controller use the middleware
* }
* ```
*/
declare function Use(...middlewares: RequestHandler[]): MethodDecorator & ClassDecorator;
type StrNumBool = 'string' | 'number' | 'boolean';
/**
* Options for parameter decorators
* @template T - Type of the parameter value after transformation
*
* @property type - Base type of the parameter (default: 'string')'
* @property dataType - Data structure type (single, array, object)
* @property delimiter - Delimiter for array types
* @property default - Default value if parameter is missing
* @property required - Whether the parameter is required
* @property throwError - Throw error on validation failure
* @property validate - Custom validation function
* @property transform - Custom transformation function
*/
interface ParamOptions<T = any> {
/** Base type of the parameter (default: 'string') */
type?: StrNumBool;
/** Data structure type (default: 'single') */
dataType?: 'single' | 'array' | 'object';
/** Delimiter for array types (default: ',') */
delimiter?: string;
/** Default value if parameter is missing */
default?: T;
/** Whether the parameter is required (default: false) */
required?: boolean;
/** Throw error on validation failure (default: true) */
throwError?: boolean;
/** Minimum value for number type */
min?: number;
/** Maximum value for number type */
max?: number;
/** Regex pattern the value must match */
pattern?: RegExp;
/** Name of the pattern for error messages */
patternName?: string;
/** Custom validation function */
validate?: (value: any) => boolean;
/** Custom transformation function */
transform?: (value: any) => any;
}
declare function createParamDecorator(type: ParamDefinition['type'], key?: string): (paramKey?: string) => ParameterDecorator;
declare function createParamDecoratorWithoutParam(type: ParamDefinition['type']): () => ParameterDecorator;
/**
* Decorator that extracts query parameters from request.
*
* @param paramKey - Optional key to extract specific query parameter
* @param options - Optional ParamOptions
* @returns Parameter decorator
*
* @example
* ```ts
* @Get('/search')
* search(@Query('term') term: string, @Query('page', { type: 'number', default: 1 }) page: number) {
* // term will contain the value of req.query.term
* // page will contain the numeric value of req.query.page or default to 1
* }
* ```
*/
declare const Query: (paramKey?: string, options?: ParamOptions) => ParameterDecorator;
/**
* Decorator that extracts route parameters from request.
*
* @param paramKey - Optional key to extract specific route parameter
* @param options - Optional ParamOptions
* @returns Parameter decorator
*
* @example
* ```ts
* @Get('/users/:id')
* getUser(@Param('id') id: string) {
* // id will contain the value of req.params.id
* }
* ```
*/
declare const Param: (paramKey?: string, options?: ParamOptions) => ParameterDecorator;
/**
* Decorator that extracts body or body property from request.
*
* @param paramKey - Optional key to extract specific body property
* @returns Parameter decorator
*
* @example
* ```ts
* @Post('/users')
* createUser(@Body() userData: any) {
* // userData will contain the entire req.body
* }
*
* @Post('/update')
* updateName(@Body('name') name: string) {
* // name will contain the value of req.body.name
* }
* ```
*/
declare const Body: (paramKey?: string) => ParameterDecorator;
/**
* Decorator that injects a logger instance.
* @returns Parameter decorator
*
* @example
* ```ts
* @Get('/log')
* log(@ReqLogger() logger: Logger) {
* logger.info('Logging request...');
* }
* ```
*/
declare const ReqLogger: () => ParameterDecorator;
/**
* Decorator that extracts request ID from headers.
* @returns Parameter decorator
*
* @example
* ```ts
* @Get('/data')
* getData(@ReqId() reqId: string) {
* // reqId will contain the value of req.headers['x-request-id'] or req.id
* }
* ```
*/
declare const ReqId: () => ParameterDecorator;
/**
* Decorator that extracts request headers.
* @param key - Optional key to extract specific header
* @returns Parameter decorator
*
* @example
* ```ts
* @Get('/data')
* getData(@ReqHeader('Authorization') authHeader: string) {
* // authHeader will contain the value of req.headers['authorization']
* }
* ```
*/
declare const ReqHeader: (paramKey?: string) => ParameterDecorator;
/**
* Decorator that extracts cookies from request.
* @param key - Optional key to extract specific cookie
* @returns Parameter decorator
*
* @example
* ```ts
* @Get('/data')
* getData(@ReqCookie('session_id') sessionId: string) {
* // sessionId will contain the value of req.cookies['session_id']
* }
* ```
*/
declare const ReqCookie: (paramKey?: string) => ParameterDecorator;
/**
* Decorator that injects the entire request object.
*
* @returns Parameter decorator
*
* @example
* ```ts
* @Get('/complex')
* complex(@Req() req: Request) {
* // Access the full request object
* console.log(req.headers);
* }
* ```
*/
declare const Req: () => ParameterDecorator;
/**
* Decorator that injects the response object.
*
* @returns Parameter decorator
*
* @example
* ```ts
* @Get('/custom')
* custom(@Res() res: Response) {
* // Direct access to response object
* return res.status(201).send('Created');
* }
* ```
*/
declare const Res: () => ParameterDecorator;
/**
* Decorator that sets a custom HTTP status code for a response.
*
* @param status - HTTP status code to use
* @returns Method decorator
*
* @example
* ```ts
* @Post('/users')
* @HttpCode(201)
* createUser(@Body() userData: any) {
* // Response will have 201 Created status code
* return { id: '123', ...userData };
* }
* ```
*/
declare function HttpCode(status: number): MethodDecorator;
/**
* Decorator that adds a custom HTTP header to the response.
*
* @param header - Header name-value pairs or a single header name and value
* @param value - Header value if a single header name is provided
* @returns Method decorator
*
* @example
* ```ts
* @Get('/data')
* @Header('Cache-Control', 'max-age=60')
* getData() {
* // Response will include the Cache-Control header
* return { data: '...' };
* }
*/
declare function Header(name: string, value: string): MethodDecorator & ClassDecorator;
/**
* Decorator that adds a custom HTTP headers to the response.
*
* @param headers - Header name-value pairs or a single header name and value
* @param value - Header value if a single header name is provided
* @returns Method decorator
*
* @example
* ```ts
* @Get('/data')
* @Headers('Cache-Control', 'max-age=60')
* getData() {
* // Response will include the Cache-Control header
* return { data: '...' };
* }
*
* @Get('/data/:id')
* @Headers({
* 'Cache-Control': 'max-age=60',
* 'X-Custom-Header': 'custom-value',
* 'Content-Security-Policy': "default-src 'self'"
* })
* getData() {
* // Response will include all specified headers
* return { data: '...' };
* }
*
* ```
*/
declare function Headers(headers: Record<string, string> | string, value?: string): MethodDecorator & ClassDecorator;
/**
* Decorator that registers a function to run before route handler execution.
* Useful for pre-processing or logging.
*
* @param fn - Function to execute before the handler
* @returns Method decorator or Class decorator
*
* @example
* ```ts
* @Get('/users/:id')
* @Before((req, res) => console.log(`Accessing user ${req.params.id}`))
* getUser(@Param('id') id: string) {
* // Function will log before this handler runs
* }
*
* @Controller('/api')
* @Before((req, res) => console.log(`API access: ${req.path}`))
* class ApiController {
* // Hook runs before all routes in this controller
* }
* ```
*/
declare function Before(fn: Function): MethodDecorator & ClassDecorator;
/**
* Decorator that registers a function to run after route handler execution.
* Can access the handler's result.
*
* @param fn - Function to execute after the handler
* @returns Method decorator or Class decorator
*
* @example
* ```ts
* @Get('/users/:id')
* @After((req, res, result) => console.log(`User data sent: ${JSON.stringify(result)}`))
* getUser(@Param('id') id: string) {
* // After this handler, the function will log the returned data
* return { id, name: 'Example' };
* }
*
* @Controller('/api')
* @After((req, res, result) => console.log(`API response: ${JSON.stringify(result)}`))
* class ApiController {
* // Hook runs after all routes in this controller
* }
* ```
*/
declare function After(fn: Function): MethodDecorator & ClassDecorator;
/**
* Decorator that requires specific roles for accessing a route.
* Must be used with authentication middleware.
*
* Check req.user.roles for user roles[].
*
* @param roles - List of roles that can access this route
* @returns Method decorator
*
* @example
* ```ts
* @Get('/admin/settings')
* @Roles('admin', 'superuser')
* getSettings() {
* // Only admins and superusers can access
* return { settings: [...] };
* }
* ```
*/
declare function Roles(...roles: string[]): MethodDecorator & ClassDecorator;
/**
* Decorator that redirects to another URL.
*
* @param url - URL to redirect to (can be absolute or relative)
* @param statusCode - HTTP status code for redirect (default: 302)
* @returns Method decorator
*
* @example
* ```ts
* @Get('/old-path')
* @Redirect('/new-path', 301)
* redirectToNewPath() {
* // This method won't be executed; automatic redirect happens
* }
*
* @Get('/dynamic-redirect')
* @Redirect()
* getDynamicRedirect() {
* // Return an object with url and optionally statusCode
* return { url: '/calculated-path', statusCode: 307 };
* }
* ```
*/
declare function Redirect(url?: string, statusCode?: number): MethodDecorator;
/**
* Decorator that adds caching to a route response.
*
* @param ttlSeconds - Time to live in seconds for the cache
* @returns Method decorator
*
* @example
* ```ts
* @Get('/data')
* @Cache(300) // Cache for 5 minutes
* getData() {
* return { data: 'expensive operation result' };
* }
* ```
*/
declare function Cache(ttlSeconds: number): MethodDecorator & ClassDecorator;
/**
* Decorator that applies rate limiting to a route.
* Note: Requires 'express-rate-limit' package to be installed.
*
* @param limit - Maximum number of requests allowed in the window
* @param windowMs - Time window in milliseconds
* @returns Method decorator
*
* Default Options:
* - standardHeaders: true
* - legacyHeaders: false
*
* @example
* ```ts
* @Post('/login')
* @RateLimit({ max: 5, windowMs: 60000, standardHeaders: true, legacyHeaders: false }) // 5 requests per minute
* login(@Body() credentials: LoginDto) {
* return this.authService.login(credentials);
* }
* ```
*/
declare function RateLimit(options: {
max: number;
windowMs: number;
standardHeaders?: boolean;
legacyHeaders?: boolean;
}): MethodDecorator & ClassDecorator;
/**
* Decorator that sets the content type for the response.
*
* @param type - MIME type for the response
* @returns Method decorator or Class decorator
*
* @example
* ```ts
* @Get('/download')
* @ContentType('application/pdf')
* downloadPdf() {
* return this.fileService.generatePdf();
* }
*
* @Controller('/api/json')
* @ContentType('application/json')
* class JsonApiController {
* // All routes in this controller use this content type
* }
* ```
*/
declare function ContentType(type: string): MethodDecorator & ClassDecorator;
/**
* Decorator that adds API versioning to a route.
*
* @param version - Version string for the API endpoint
* @param options - Versioning options
* @returns Method decorator
*
* Default Options:
* - addPrefix: true
* - addHeader: true
* - headerName: 'X-API-Version'
*
* @example
* ```ts
*
* @Get('/users')
* @Version('v2')
* getUsersV2() {
* return this.userService.findAllV2();
* }
*
* @Get('/users')
* @Version('v2', { addPrefix: true, addHeader: true, headerName: 'X-API-Version' })
* getUsersV2() {
* // Route becomes /v2/users
* return this.userService.findAllV2();
* }
* ```
*/
declare function Version(version: string, options?: {
addPrefix?: boolean;
addHeader?: boolean;
headerName?: string;
}): MethodDecorator & ClassDecorator;
/**
* Decorator that sets a timeout for route execution.
*
* @param ms - Timeout in milliseconds
* @returns Method decorator
*
* @example
* ```ts
* @Get('/slow-operation')
* @Timeout(30000) // 30 second timeout
* slowOperation() {
* return this.heavyService.processData();
* }
* ```
*/
declare function Timeout(ms: number): MethodDecorator & ClassDecorator;
/**
* Decorator that adds comprehensive logging to a route.
*
* @param options - Logging configuration options
* @returns Method decorator
*
* Default Options:
* - logEntry: true
* - logExit: true
* - logBody: false
* - logParams: false
* - logResponse: false
*
* @example
* ```ts
* @Post('/users')
* @Log({
* logEntry: true,
* logExit: true,
* logBody: true,
* logParams: true,
* logResponse: false
* })
* createUser(@Body() userData: any) {
* return this.userService.create(userData);
* }
* ```
*/
declare function Log(options?: {
logEntry?: boolean;
logExit?: boolean;
logBody?: boolean;
logParams?: boolean;
logResponse?: boolean;
}): MethodDecorator & ClassDecorator;
/**
* Registers all controller classes with the provided router.
* This function processes all decorators and sets up the Express routes.
*
* @param router - Express router instance
* @param controllers - Array of controller classes
*/
declare function registerControllers(router: Router, controllers: any[]): void;
export { After, Before, Body, Cache, Connect, ContentType, Controller, DIContainer, Delete, Get, Head, Header, Headers, HttpCode, Inject, Injectable, Log, Options, Param, Patch, Post, Put, Query, RateLimit, Redirect, Req, ReqCookie, ReqHeader, ReqId, ReqLogger, Res, Roles, Timeout, Trace, Use, Version, createParamDecorator, createParamDecoratorWithoutParam, inject, registerControllers };
export type { CachedRateLimiter, HttpMethod, ParamDefinition, ParamOptions, RouteDefinition };