diff --git a/docs/@catbee/utils/config.md b/docs/@catbee/utils/config.md index 98c6a15..812af4f 100644 --- a/docs/@catbee/utils/config.md +++ b/docs/@catbee/utils/config.md @@ -1,24 +1,59 @@ +--- +sidebar_position: 1 +--- + # Config -Global configuration management for logging, cache, and runtime settings. Provides type-safe access and mutation of config values, with sensible defaults and environment variable support. +Global configuration management for logging, cache, and server settings. Provides type-safe access and mutation of config values, with sensible defaults and comprehensive environment variable support. ## API Summary -- [**`setConfig(value: Partial): void`**](#setconfig) – update configuration at runtime. -- [**`getConfig(): Config`**](#getconfig) – get the current configuration object. +- [**`getCatbeeGlobalConfig(): CatbeeConfig`**](#getcatbeeglobalconfig) – get the current global configuration object. +- [**`setCatbeeGlobalConfig(value: Partial): void`**](#setcatbeeglobalconfig) – update global configuration at runtime. +- [**`getCatbeeServerGlobalConfig(): CatbeeGlobalServerConfig`**](#getcatbeeserverglobalconfig) – get the current server configuration. +- [**`setCatbeeServerGlobalConfig(value: Partial): void`**](#setcatbeeserverglobalconfig) – update server configuration at runtime. +- [**`getConfig()`**](#getconfig-deprecated) – ⚠️ **deprecated** alias for `getCatbeeGlobalConfig`. +- [**`setConfig(value)`**](#setconfig-deprecated) – ⚠️ **deprecated** alias for `setCatbeeGlobalConfig`. --- ## Function Documentation & Usage Examples -### `setConfig()` +### `getCatbeeGlobalConfig()` + +Returns the current global Catbee configuration including logger, cache, and server settings. + +**Method Signature:** + +```ts +function getCatbeeGlobalConfig(): CatbeeConfig; +``` + +**Returns:** + +- The current Catbee configuration object (deep cloned). + +**Examples:** + +```ts +import { getCatbeeGlobalConfig } from '@catbee/utils/config'; + +const config = getCatbeeGlobalConfig(); +console.log(config.logger.level); // "info" or "debug" +console.log(config.cache.defaultTtl); // 3600000 +console.log(config.server.port); // 3000 +``` + +--- -Updates configuration settings at runtime. Only provided keys are merged; others remain unchanged. +### `setCatbeeGlobalConfig()` + +Updates global configuration settings at runtime. Merges the provided partial configuration with the existing one using deep object merge. **Method Signature:** ```ts -function setConfig(value: Partial): void; +function setCatbeeGlobalConfig(value: Partial): void; ``` **Parameters:** @@ -28,71 +63,253 @@ function setConfig(value: Partial): void; **Examples:** ```ts -import { setConfig, getConfig } from '@catbee/utils'; +import { setCatbeeGlobalConfig } from '@catbee/utils/config'; -setConfig({ - logger: { level: 'debug', name: 'custom-logger', pretty: true } +setCatbeeGlobalConfig({ + logger: { + level: 'debug', + name: 'my-app', + pretty: true, + colorize: true + }, + cache: { + defaultTtl: 7200000 // 2 hours + } }); ``` --- -### `getConfig()` +### `getCatbeeServerGlobalConfig()` -Returns the current configuration object. +Returns the current server-specific configuration. **Method Signature:** ```ts -function getConfig(): Config; +function getCatbeeServerGlobalConfig(): CatbeeGlobalServerConfig; ``` **Returns:** -- The current configuration object. +- The current server configuration object. **Examples:** ```ts -import { getConfig } from '@catbee/utils'; +import { getCatbeeServerGlobalConfig } from '@catbee/utils/config'; -const currentConfig = getConfig(); -console.log(currentConfig.logger.level); // "info" -console.log(currentConfig.cache.defaultTtl); // 3600000 +const serverConfig = getCatbeeServerGlobalConfig(); +console.log(serverConfig.port); // 3000 +console.log(serverConfig.host); // "0.0.0.0" +console.log(serverConfig.appName); // "catbee_server" ``` --- -## Default Values +### `setCatbeeServerGlobalConfig()` -All configuration options have sensible defaults: +Updates server configuration settings at runtime. Merges the provided partial server configuration with the existing one. + +**Method Signature:** ```ts -const config = { - logger: { - level: 'info', // Logging level - name: '@catbee/utils', // Logger name - pretty: true, // Pretty print logs (dev only) - singleLine: false // Single line pretty print - }, - cache: { - defaultTtl: 3600000 // Default cache TTL (1 hour, in ms) +function setCatbeeServerGlobalConfig(value: Partial): void; +``` + +**Parameters:** + +- `value`: Partial server configuration object to merge. + +**Examples:** + +```ts +import { setCatbeeServerGlobalConfig } from '@catbee/utils/config'; + +setCatbeeServerGlobalConfig({ + port: 8080, + cors: true, + helmet: true, + rateLimit: { + enable: true, + max: 200 } -}; +}); +``` + +--- + +### `getConfig()` (Deprecated) + +:::warning Deprecated +Use [`getCatbeeGlobalConfig()`](#getcatbeeglobalconfig) instead. +::: + +Alias for `getCatbeeGlobalConfig()`. Returns the current configuration object. + +**Method Signature:** + +```ts +function getConfig(): CatbeeConfig; +``` + +--- + +### `setConfig()` (Deprecated) + +:::warning Deprecated +Use [`setCatbeeGlobalConfig()`](#setcatbeeglobalconfig) instead. +::: + +Alias for `setCatbeeGlobalConfig()`. Updates configuration settings at runtime. + +**Method Signature:** + +```ts +function setConfig(value: Partial): void; ``` --- +## Default Values + +All configuration options have sensible defaults loaded from environment variables: + +### Logger Defaults + +```ts +logger: { + level: 'info', // 'debug' in dev/test, otherwise 'info' + name: '@catbee/utils', // or npm package name + pretty: false, // pretty-print logging + colorize: true, // colorize pretty output + singleLine: false, // single-line pretty output + dir: '' // log file directory (empty = disabled) +} +``` + +**Logger Environment Variables** + +For configuring logger behavior via environment variables, see the [Logger documentation](logger#environment-variables). + +### Cache Defaults + +```ts +cache: { + defaultTtl: 3600000 // 1 hour in milliseconds +} +``` + +**Cache Environment Variables** + +| Environment Variable | Type | Default/Value | Description | +| --------------------------- | -------- | ------------- | ---------------------------------------------- | +| `CACHE_DEFAULT_TTL_SECONDS` | `number` | `3600` | Default cache TTL in seconds (converted to ms) | + +### Server Defaults + +```ts +server: { + port: 3000, + host: '0.0.0.0', + cors: false, + helmet: false, + compression: false, + bodyParser: { + json: { limit: '1mb' }, + urlencoded: { extended: true, limit: '1mb' } + }, + cookieParser: false, + isMicroservice: false, + appName: 'catbee_server', + globalHeaders: {}, + rateLimit: { + enable: false, + windowMs: 900000, // 15 minutes + max: 100, + message: 'Too many requests, please try again later.', + standardHeaders: true, + legacyHeaders: false + }, + requestLogging: { + enable: true, // true in dev, false in production + ignorePaths: ['/healthz', '/favicon.ico', '/metrics', '/docs', '/.well-known'], + skipNotFoundRoutes: true + }, + trustProxy: false, + openApi: { + enable: false, + mountPath: '/docs', + verbose: false, + withGlobalPrefix: false + }, + healthCheck: { + path: '/healthz', + detailed: true, + withGlobalPrefix: false + }, + requestTimeout: 0, + responseTime: { + enable: false, + addHeader: true, + logOnComplete: false + }, + requestId: { + headerName: 'x-request-id', + exposeHeader: true, + generator: () => uuid() + }, + metrics: { + enable: false, + path: '/metrics', + withGlobalPrefix: false + }, + serviceVersion: { + enable: false, + headerName: 'x-service-version', + version: '0.0.0' + }, + skipHealthzChecksValidation: false +} +``` + +**Server Environment Variables** + +For overriding server config options via environment variables, see the [Express Server documentation](server#server-environment-variables). + ## Types +### CatbeeConfig + +Main configuration interface combining logger, cache, and server settings. + +```ts +interface CatbeeConfig { + logger?: { + level?: 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace'; + name?: string; + pretty?: boolean; + colorize?: boolean; + singleLine?: boolean; + dir?: string; + }; + cache: { + defaultTtl: number; // milliseconds + }; + server: CatbeeGlobalServerConfig; +} +``` + ### Logger Configuration ```ts interface LoggerConfig { - level: 'trace' | 'debug' | 'info' | 'warn' | 'error'; - name: string; - pretty: boolean; - singleLine: boolean; + level?: 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace'; /** Logging level */ + name?: string; /** Logger instance name */ + pretty?: boolean; /** Enable pretty-print logging */ + colorize?: boolean; /** Enable colorized output for pretty-print */ + singleLine?: boolean; /** Single line output for pretty-print (default: false) */ + dir?: string; /** Directory to write log files (empty = disabled) */ } ``` @@ -100,15 +317,45 @@ interface LoggerConfig { ```ts interface CacheConfig { - defaultTtl: number; // milliseconds + /** Default TTL (time to live) in milliseconds */ + defaultTtl: number; } ``` -### Config Object +### Server Configuration + +The server configuration is extensive. Key interfaces include: ```ts -interface Config { - logger: LoggerConfig; - cache: CacheConfig; +interface CatbeeGlobalServerConfig { + port: number; + host?: string; + cors?: boolean | CorsOptions; + helmet?: boolean | HelmetOptions; + compression?: boolean | CompressionOptions; + bodyParser?: { + json?: JsonParserOptions; + urlencoded?: UrlencodedParserOptions; + }; + cookieParser?: boolean | CookieParseOptions; + trustProxy?: boolean | number | string | string[]; + staticFolders?: StaticFolderConfig[]; + isMicroservice?: boolean; + appName?: string; + globalHeaders?: Record string)>; + rateLimit?: RateLimitConfig; + requestLogging?: RequestLoggingConfig; + healthCheck?: HealthCheckConfig; + requestTimeout?: number; + responseTime?: ResponseTimeConfig; + requestId?: RequestIdConfig; + globalPrefix?: string; + openApi?: OpenApiConfig; + metrics?: MetricsConfig; + serviceVersion?: ServiceVersionConfig; + https?: HttpsConfig; + skipHealthzChecksValidation?: boolean; } ``` + +For detailed server configuration options, see the [Express Server documentation](server) diff --git a/docs/@catbee/utils/express-server/express-server.md b/docs/@catbee/utils/express-server/express-server.md index 16c664d..fee9216 100644 --- a/docs/@catbee/utils/express-server/express-server.md +++ b/docs/@catbee/utils/express-server/express-server.md @@ -1,3 +1,7 @@ +--- +slug: ../server +--- + # Express Server Enterprise-grade Express server builder for secure, reliable, and observable APIs. @@ -77,10 +81,10 @@ Enterprise-grade Express server builder for secure, reliable, and observable API ## Interfaces & Types -### ServerConfig +### CatbeeServerConfig ```ts -interface ServerConfig { +interface CatbeeServerConfig { port: number; // default: 3000 host?: string; // default: '0.0.0.0' cors?: boolean | CorsOptions; // default: false @@ -91,31 +95,31 @@ interface ServerConfig { urlencoded?: { extended: boolean; limit: string }; // default: { extended: true, limit: '1mb' } }; cookieParser?: boolean | CookieParseOptions; // default: false - trustProxy?: boolean; // default: false + trustProxy?: boolean | number | string | string[]; // default: false staticFolders?: Array<{ path?: string; // default: '/' directory: string; - maxAge?: string; // default: undefined + maxAge?: string; // default: 0 etag?: boolean; // default: true immutable?: boolean; // default: false lastModified?: boolean; // default: true cacheControl?: boolean; // default: true }>; isMicroservice?: boolean; // default: false - appName?: string; // default: 'express_app' + appName?: string; // default: 'catbee_server' globalHeaders?: Record string)>; // default: {} rateLimit?: { enable: boolean; // default: false - windowMs?: number; // default: 900000 + windowMs?: number; // default: 900000 (15 min) max?: number; // default: 100 - message?: string; // default: 'Too many requests' + message?: string; // default: 'Too many requests, please try again later.' standardHeaders?: boolean; // default: true legacyHeaders?: boolean; // default: false }; requestLogging?: { enable: boolean; // default: true in dev, false in prod ignorePaths?: string[] | ((req: Request, res: Response) => boolean); // default: skips /healthz, /favicon.ico, /metrics, /docs, /.well-known - skipNotFoundRoutes?: boolean; // default: false + skipNotFoundRoutes?: boolean; // default: true }; healthCheck?: { path?: string; // default: '/healthz' @@ -123,7 +127,7 @@ interface ServerConfig { detailed?: boolean; // default: true withGlobalPrefix?: boolean; // default: false }; - requestTimeout?: number; // default: 30000 + requestTimeout?: number; // default: 0 (disabled) responseTime?: { enable: boolean; // default: false addHeader?: boolean; // default: true @@ -138,7 +142,7 @@ interface ServerConfig { openApi?: { enable: boolean; // default: false mountPath?: string; // default: '/docs' - filePath?: string; + filePath?: string; // required if enabled verbose?: boolean; // default: false withGlobalPrefix?: boolean; // default: false }; @@ -153,24 +157,24 @@ interface ServerConfig { version?: string | (() => string); // default: '0.0.0' }; https?: { - key: string; - cert: string; - ca?: string; - passphrase?: string; - [key: string]: any; + key: string; // Path to private key file (PEM) + cert: string; // Path to certificate file (PEM) + ca?: string; // Optional CA bundle path (PEM) + passphrase?: string; // Optional private key passphrase + [key: string]: any; // Additional Node.js https.ServerOptions }; } ``` -### ServerHooks +### CatbeeServerHooks ```ts -interface ServerHooks { +interface CatbeeServerHooks { beforeInit?: (server: ExpressServer) => Promise | void; afterInit?: (server: ExpressServer) => Promise | void; beforeStart?: (app: Express) => Promise | void; - afterStart?: (server: http.Server) => Promise | void; - beforeStop?: (server: http.Server) => Promise | void; + afterStart?: (server: http.Server | https.Server) => Promise | void; + beforeStop?: (server: http.Server | https.Server) => Promise | void; afterStop?: () => Promise | void; onError?: (error: Error, req: Request, res: Response, next: NextFunction) => void; onRequest?: (req: Request, res: Response, next: NextFunction) => void; @@ -180,58 +184,62 @@ interface ServerHooks { --- -## Environment Variables for Logging - -| Environment Variable | Default Value | Description | -| --------------------------- | ------------------------------ | ------------------------------------------- | -| `LOGGER_LEVEL` | `info` | Log level for the application | -| `LOGGER_NAME` | `process.env.npm_package_name` | Name of the logger | -| `LOGGER_PRETTY` | `true` | Enable pretty logging | -| `LOGGER_PRETTY_COLORIZE` | `true` | Enable colorized output for pretty-print | -| `LOGGER_PRETTY_SINGLE_LINE` | `false` | Enable single-line logging | -| `SERVER_SKIP_HEALTHZ` | `false` | Skip health checks route, always return 200 | - -## Default Values - -| Option | Default Value | Description | -| ---------------------------- | ------------------------------------------------------------------- | ------------------------------------------ | -| `port` | `3000` | Port for the server | -| `host` | `'0.0.0.0'` | Host for the server | -| `cors` | `false` | Enable CORS | -| `helmet` | `false` | Enable Helmet security headers | -| `compression` | `false` | Enable response compression | -| `bodyParser.json` | `{ limit: '1mb' }` | JSON body parser options | -| `bodyParser.urlencoded` | `{ extended: true, limit: '1mb' }` | URL-encoded body parser options | -| `cookieParser` | `false` | Enable cookie parsing | -| `trustProxy` | `false` | Enable proxy trust | -| `staticFolders` | `[]` | Static file serving folders | -| `isMicroservice` | `false` | Enable microservice mode | -| `appName` | `'express_app'` | Name of the application | -| `globalHeaders` | `{}` | Global headers to include in all responses | -| `rateLimit.enable` | `false` | Enable rate limiting | -| `rateLimit.windowMs` | `900000` (15 min) | Time window for rate limiting | -| `rateLimit.max` | `100` | Maximum number of requests in the window | -| `rateLimit.message` | `'Too many requests'` | Rate limit exceeded message | -| `requestLogging.enable` | `true` (dev), `false` (prod) | Enable request logging | -| `requestLogging.ignorePaths` | `['/healthz', '/favicon.ico', '/metrics', '/docs', '/.well-known']` | Paths to ignore for request logging | -| `healthCheck.path` | `'/healthz'` | Health check endpoint | -| `healthCheck.detailed` | `true` | Enable detailed health check response | -| `requestTimeout` | `30000` (30 sec) | Request timeout duration | -| `responseTime.enable` | `false` | Enable response time tracking | -| `responseTime.addHeader` | `true` | Add response time header | -| `responseTime.logOnComplete` | `false` | Log response time on completion | -| `requestId.headerName` | `'x-request-id'` | Request ID header name | -| `requestId.exposeHeader` | `true` | Expose Request ID header | -| `metrics.enable` | `false` | Enable metrics collection | -| `metrics.path` | `'/metrics'` | Metrics endpoint path | -| `serviceVersion.enable` | `false` | Enable service versioning | -| `serviceVersion.headerName` | `'x-service-version'` | Service version header name | -| `serviceVersion.version` | `'0.0.0'` | Service version | -| `openApi.enable` | `false` | Enable OpenAPI documentation | -| `openApi.mountPath` | `'/docs'` | OpenAPI documentation mount path | -| `openApi.verbose` | `false` | Enable verbose OpenAPI documentation | -| `openApi.withGlobalPrefix` | `false` | Use global prefix for OpenAPI routes | -| `globalPrefix` | `''` | Global prefix for all routes | +## Environment Variables + +### Logger Environment Variables + +For configuring logger behavior via environment variables, see the [Logger documentation](logger#environment-variables). + +### Server Environment Variables + +| Environment Variable | Type | Default/Value | Description | +| ---------------------------------------------- | ---------- | -------------------------------------------- | ----------------------------------- | +| `SERVER_PORT` | `number` | `${PORT}` or `3000` | Server port (overrides PORT) | +| `PORT` | `number` | `3000` | Fallback port if SERVER_PORT unset | +| `SERVER_HOST` | `string` | `${HOST}` or `0.0.0.0` | Server host (overrides HOST) | +| `HOST` | `string` | `0.0.0.0` | Fallback host if SERVER_HOST unset | +| `SERVER_CORS_ENABLE` | `boolean` | `false` | Enable CORS middleware | +| `SERVER_HELMET_ENABLE` | `boolean` | `false` | Enable Helmet security middleware | +| `SERVER_COMPRESSION_ENABLE` | `boolean` | `false` | Enable response compression | +| `SERVER_BODY_PARSER_JSON_LIMIT` | `string` | `1mb` | Max JSON body size | +| `SERVER_BODY_PARSER_URLENCODED_LIMIT` | `string` | `1mb` | Max URL-encoded body size | +| `SERVER_COOKIE_PARSER_ENABLE` | `boolean` | `false` | Enable cookie parser middleware | +| `SERVER_IS_MICROSERVICE` | `boolean` | `false` | Microservice mode flag | +| `SERVER_APP_NAME` | `string` | `${npm_package_name}` or `catbee_server` | Application/service name | +| `SERVER_GLOBAL_HEADERS` | `JSON` | `{}` | Global response headers | +| `SERVER_RATE_LIMIT_ENABLE` | `boolean` | `false` | Enable rate limiting | +| `SERVER_RATE_LIMIT_WINDOW_MS` | `duration` | `15m` | Rate limit window (ms or duration) | +| `SERVER_RATE_LIMIT_MAX` | `number` | `100` | Max requests per window | +| `SERVER_RATE_LIMIT_MESSAGE` | `string` | `Too many requests, please try again later.` | Rate limit error message | +| `SERVER_RATE_LIMIT_STANDARD_HEADERS` | `boolean` | `true` | Use standard rate limit headers | +| `SERVER_RATE_LIMIT_LEGACY_HEADERS` | `boolean` | `false` | Use legacy rate limit headers | +| `SERVER_REQUEST_LOGGING_ENABLE` | `boolean` | `true` in dev, `false` otherwise | Enable request logging | +| `SERVER_REQUEST_LOGGING_SKIP_NOT_FOUND_ROUTES` | `boolean` | `true` | Skip logging for 404 routes | +| `SERVER_TRUST_PROXY_ENABLE` | `boolean` | `false` | Trust proxy headers | +| `SERVER_OPENAPI_ENABLE` | `boolean` | `false` | Enable OpenAPI docs | +| `SERVER_OPENAPI_MOUNT_PATH` | `string` | `/docs` | OpenAPI docs mount path | +| `SERVER_OPENAPI_VERBOSE` | `boolean` | `false` | Verbose OpenAPI output | +| `SERVER_OPENAPI_WITH_GLOBAL_PREFIX` | `boolean` | `false` | Prefix OpenAPI routes | +| `SERVER_HEALTH_CHECK_PATH` | `string` | `/healthz` | Health check endpoint path | +| `SERVER_HEALTH_CHECK_DETAILED_OUTPUT` | `boolean` | `true` | Detailed health check output | +| `SERVER_HEALTH_CHECK_WITH_GLOBAL_PREFIX` | `boolean` | `false` | Prefix health check route | +| `SERVER_REQUEST_TIMEOUT_MS` | `duration` | `0` | Request timeout (ms or duration) | +| `SERVER_RESPONSE_TIME_ENABLE` | `boolean` | `false` | Enable response time tracking | +| `SERVER_RESPONSE_TIME_ADD_HEADER` | `boolean` | `true` | Add X-Response-Time header | +| `SERVER_RESPONSE_TIME_LOG_ON_COMPLETE` | `boolean` | `false` | Log response time on complete | +| `SERVER_REQUEST_ID_HEADER_NAME` | `string` | `x-request-id` | Request ID header name | +| `SERVER_REQUEST_ID_EXPOSE_HEADER` | `boolean` | `true` | Expose request ID header | +| `SERVER_METRICS_ENABLE` | `boolean` | `false` | Enable Prometheus metrics | +| `SERVER_METRICS_PATH` | `string` | `/metrics` | Metrics endpoint path | +| `SERVER_METRICS_WITH_GLOBAL_PREFIX` | `boolean` | `false` | Prefix metrics route | +| `SERVER_SERVICE_VERSION_ENABLE` | `boolean` | `false` | Enable service version header | +| `SERVER_SERVICE_VERSION_HEADER_NAME` | `string` | `x-service-version` | Service version header name | +| `SERVER_SERVICE_VERSION` | `string` | `${npm_package_version}` or `0.0.0` | Service version value | +| `SERVER_SKIP_HEALTHZ_CHECKS_VALIDATION` | `boolean` | `false` | Skip health checks if added | +| `npm_package_name` | `string` | `@catbee/utils` | Package name (from package.json) | +| `npm_package_version` | `string` | `0.0.0` | Package version (from package.json) | + +--- --- @@ -249,7 +257,7 @@ interface ServerHooks { ## Usage Example ```ts -import { ServerConfigBuilder, ExpressServer } from '@catbee/utils'; +import { ServerConfigBuilder, ExpressServer } from '@catbee/utils/server'; // Build server config with all major features const config = new ServerConfigBuilder() @@ -311,6 +319,10 @@ const server = new ExpressServer(config, { server.registerHealthCheck('database', async () => await checkDatabaseConnection()); server.registerHealthCheck('storage', () => require('fs').existsSync('./data')); +// Check if server is ready +const isReady = await server.ready(); +console.log('Server ready:', isReady); + // Register routes const router = server.createRouter('/users'); router.get('/', (req, res) => res.json({ users: [] })); @@ -410,17 +422,21 @@ Main server class with lifecycle hooks and utilities. **Method Signatures:** ```ts -new ExpressServer(config: ServerConfig, hooks?: ServerHooks) +new ExpressServer(config: Partial, hooks?: CatbeeServerHooks) .start(): Promise .stop(force?: boolean): Promise - .enableGracefulShutdown(signals?: NodeJS.Signals[]) - .registerHealthCheck(name: string, fn: () => Promise | boolean) + .enableGracefulShutdown(signals?: NodeJS.Signals[]): this + .registerHealthCheck(name: string, fn: () => Promise | boolean): this + .ready(): Promise + .getApp(): Express + .getServer(): http.Server | https.Server | null + .setBaseRouter(router: Router): this .createRouter(prefix?: string): Router - .registerRoute(methods: string[], path: string, ...handlers) - .registerMiddleware(path: string, middleware) - .useMiddleware(...middlewares) - .getMetricsRegistry(): client.Registry - .getConfig(): ServerConfig + .registerRoute(methods: string[], path: string, ...handlers): this + .registerMiddleware(path: string | RequestHandler, middleware?: RequestHandler): this + .useMiddleware(...middlewares: RequestHandler[]): this + .getMetricsRegistry(): Registry + .getConfig(): CatbeeServerConfig .waitUntilReady(): Promise ``` @@ -450,6 +466,13 @@ const server = new ExpressServer(config, { await server.start(); ``` +**Additional Methods:** + +- `getApp()` - Get the underlying Express application instance +- `getServer()` - Get the active HTTP/HTTPS server instance (null if not running) +- `ready()` - Check if all health checks pass (returns `Promise`) +- `waitUntilReady()` - Wait for server initialization to complete + --- ## Health Checks @@ -474,6 +497,12 @@ server.registerHealthCheck('database', async () => { return false; } }); + +// Check readiness programmatically +const isReady = await server.ready(); +if (isReady) { + console.log('All health checks passed'); +} ``` --- @@ -579,21 +608,21 @@ See [ServerConfigBuilder](#serverconfigbuilder) and [ExpressServer](#expressserv ## Core NPM Packages (always required) - `express` - The web framework -- `reflect-metadata` - Metadata reflection API - `pino` - Logging library --- ## Feature-specific NPM Packages (only if feature is enabled) -| Feature | Required NPM Packages | -| ------------------ | ------------------------------- | -| CORS | `cors` | -| Helmet (security) | `helmet` | -| Compression | `compression` | -| Cookie Parsing | `cookie-parser` | -| Rate Limiting | `express-rate-limit` | -| Prometheus Metrics | `prom-client` | -| OpenAPI Docs | `@scalar/express-api-reference` | +| Feature | Required NPM Packages | +| ------------------- | ------------------------------- | +| CORS | `cors` | +| Helmet (security) | `helmet` | +| Compression | `compression` | +| Cookie Parsing | `cookie-parser` | +| Rate Limiting | `express-rate-limit` | +| Metadata Reflection | `reflect-metadata` | +| Prometheus Metrics | `prom-client` | +| OpenAPI Docs | `@scalar/express-api-reference` | **Note:** These packages must be installed via `npm install ` if you enable the corresponding feature in your server config. diff --git a/docs/@catbee/utils/intro.md b/docs/@catbee/utils/intro.md index 72e280f..aaf40ee 100644 --- a/docs/@catbee/utils/intro.md +++ b/docs/@catbee/utils/intro.md @@ -1,5 +1,5 @@ --- -sidebar_position: 1 +sidebar_position: 0 --- # Introduction @@ -53,34 +53,91 @@ npm i @catbee/utils ## ⚡ Quick Start ```ts -import { chunk, sleep, getLogger, uuid, isEmail } from '@catbee/utils'; +import { ServerConfigBuilder, ExpressServer } from '@catbee/utils/server'; +import { Router } from 'express'; + +const config = new ServerConfigBuilder() + .withPort(3000) + .withCors({ origin: '*' }) + .enableRateLimit({ max: 50, windowMs: 60000 }) + .enableRequestLogging({ ignorePaths: ['/healthz', '/metrics'] }) + .withHealthCheck({ path: '/health', detailed: true }) + .enableOpenApi('./openapi.yaml', { mountPath: '/docs' }) + .withGlobalHeaders({ 'X-Powered-By': 'Catbee' }) + .withGlobalPrefix('/api') + .withRequestId({ headerName: 'X-Request-Id', exposeHeader: true }) + .enableResponseTime({ addHeader: true, logOnComplete: true }) + .build(); + +const server = new ExpressServer(config, { + beforeInit: srv => console.log('Initializing server...'), + afterInit: srv => console.log('Server initialized'), + beforeStart: app => console.log('Starting server...'), + afterStart: srv => console.log('Server started!'), + beforeStop: srv => console.log('Stopping server...'), + afterStop: () => console.log('Server stopped.'), + onRequest: (req, res, next) => { + console.log('Processing request:', req.method, req.url); + next(); + }, + onResponse: (req, res, next) => { + res.setHeader('X-Processed-By', 'ExpressServer'); + next(); + }, + onError: (err, req, res, next) => { + console.error('Custom error handler:', err); + res.status(500).json({ error: 'Custom error: ' + err.message }); + } +}); + +// Register routes +const router = server.createRouter('/users'); +router.get('/', (req, res) => res.json({ users: [] })); +router.post('/', (req, res) => res.json({ created: true })); + +// Or set a base router for all routes +const baseRouter = Router(); +baseRouter.use('/users', router); +server.setBaseRouter(baseRouter); + +server.registerHealthCheck('database', async () => await checkDatabaseConnection()); +server.useMiddleware(loggingMiddleware, errorMiddleware); + +await server.start(); +server.enableGracefulShutdown(); +``` -// Chunk an array -const result = chunk([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]] +--- -// Sleep for 1 second -await sleep(1000); +## 🏁 Usage -// Log with context -getLogger().info('App started'); +This library supports flexible import patterns to suit your needs: -// Generate a secure UUID -console.log(uuid()); // e.g. 2a563ec1-caf6-4fe2-b60c-9cf7fb1bdb7f +### Root-level imports (everything available) -// Basic validation -console.log(isEmail('user@example.com')); // true -``` +Import any utility directly from the root package: ---- +```ts +import { chunk, sleep, TTLCache, getLogger, ServerConfigBuilder } from '@catbee/utils'; +``` -## 🏁 Usage Philosophy +### Module-level imports (scoped imports) -Import only what you need to keep your bundle size small and your codebase clean: +Import only from specific modules for better organization and smaller bundles: ```ts -import { chunk, sleep, TTLCache, getLogger } from '@catbee/utils'; +// Import only server-related exports +import { ServerConfigBuilder, ExpressServer } from '@catbee/utils/server'; + +// Import only date utilities +import { formatDate, addDays, DateBuilder } from '@catbee/utils/date'; + +// Import only crypto utilities +import { hashPassword, verifyPassword } from '@catbee/utils/crypto'; ``` +Both import styles are fully supported and tree-shakable. Use whichever fits your project structure best! + --- ## ⚙️ Configuration @@ -95,7 +152,7 @@ Global configuration management with environment variable support: Enterprise-grade Express server utilities: -- [Express Server](express-server) – Fast, secure, and scalable server setup +- [Express Server](server) – Fast, secure, and scalable server setup --- @@ -103,30 +160,31 @@ Enterprise-grade Express server utilities: Explore the full suite of utilities, each with detailed API docs and examples: -- [Array Utilities](modules/array) – Advanced array manipulation -- [Async Utilities](modules/async) – Promise helpers, concurrency, timing -- [Cache Utilities](modules/cache) – In-memory caching with TTL -- [Context Store](modules/context-store) – Per-request context via AsyncLocalStorage -- [Crypto Utilities](modules/crypto) – Hashing, encryption, tokens -- [Date Utilities](modules/date) – Date/time manipulation -- [Decorators Utilities](modules/decorators) – TypeScript decorators for Express -- [Directory Utilities](modules/directory) – Directory and file system helpers -- [Environment Utilities](modules/environment) – Env variable management -- [Exception Utilities](modules/exception) – HTTP and error handling -- [File System Utilities](modules/file-system) – File operations -- [HTTP Status Codes](modules/http-status-codes) – Typed status codes -- [ID Utilities](modules/id) – UUID and ID generation -- [Logger Utilities](modules/logger) – Structured logging with Pino -- [Middleware Utilities](modules/middleware) – Express middleware collection -- [Object Utilities](modules/object) – Deep merge, flatten, pick/omit, etc. -- [Performance Utilities](modules/performance) – Timing, memoization, memory tracking -- [Request Utilities](modules/request) – HTTP request parameter parsing/validation -- [Response Utilities](modules/response) – Standardized API response formatting -- [Stream Utilities](modules/stream) – Stream conversion, batching, throttling, line splitting -- [String Utilities](modules/string) – Casing, masking, slugifying, formatting -- [Type Utilities](modules/type) – Type checking, conversion, guards -- [URL Utilities](modules/url) – URL parsing, query manipulation, normalization -- [Validate Utilities](modules/validation) – Input validation functions +- [Array](array) – Advanced array manipulation +- [Async](async) – Promise helpers, concurrency, timing +- [Cache](cache) – In-memory caching with TTL +- [Context Store](context-store) – Per-request context via AsyncLocalStorage +- [Crypto](crypto) – Hashing, encryption, tokens +- [Date](date) – Date/time manipulation +- [Decorator](decorator) – TypeScript decorators for Express +- [Directory](directory) – Directory and file system helpers +- [Environment](env) – Env variable management +- [Exception](exception) – HTTP and error handling +- [File System](fs) – File operations +- [HTTP Status Codes](http-status-codes) – Typed status codes +- [ID](id) – UUID and ID generation +- [Logger](logger) – Structured logging with Pino +- [Middleware](middleware) – Express middleware collection +- [Object](object) – Deep merge, flatten, pick/omit, etc. +- [Performance](performance) – Timing, memoization, memory tracking +- [Request](request) – HTTP request parameter parsing/validation +- [Response](response) – Standardized API response formatting +- [Stream](stream) – Stream conversion, batching, throttling, line splitting +- [String](string) – Casing, masking, slugifying, formatting +- [Type](type) – Type checking, conversion, guards +- [Types](types) – Common TypeScript types and interfaces +- [URL](url) – URL parsing, query manipulation, normalization +- [Validation](validation) – Input validation functions --- diff --git a/docs/@catbee/utils/modules/array.md b/docs/@catbee/utils/modules/array.md index 4043d02..40bbdc6 100644 --- a/docs/@catbee/utils/modules/array.md +++ b/docs/@catbee/utils/modules/array.md @@ -1,26 +1,44 @@ -# Array Utilities +--- +slug: ../array +--- + +# Array A collection of functions for handling arrays with type-safety and efficiency. Includes chunking, deduplication, flattening, grouping, sorting, partitioning, and more. All methods are fully typed. ## API Summary -- [**`chunk(array: T[], size: number): T[][]`**](#chunk) - splits an array into chunks of the specified size. -- [**`unique(array: T[], keyFn?: (item: T) => unknown): T[]`**](#unique) - removes duplicate items from an array, optionally by a key function. -- [**`flattenDeep(array: any[]): T[]`**](#flattendeep) - deeply flattens a nested array. -- [**`random(array: T[]): T | undefined`**](#random) - returns a random element from an array. -- [**`groupBy(array: T[], keyOrFn: keyof T | ((item: T) => string | number | symbol)): Record`**](#groupby) - groups array items by a key or function. -- [**`shuffle(array: T[]): T[]`**](#shuffle) - shuffles array elements randomly. -- [**`pluck(array: T[], key: K): T[K][]`**](#pluck) - extracts values for a given key from an array of objects. -- [**`difference(a: T[], b: T[]): T[]`**](#difference) - returns elements in array `a` not present in array `b`. -- [**`intersect(a: T[], b: T[]): T[]`**](#intersect) - returns elements common to both arrays. -- [**`mergeSort(array: T[], key: string | ((item: T) => any), direction?: "asc" | "desc"): T[]`**](#mergesort) - sorts array by key or function using merge sort. -- [**`zip(...arrays: T[][]): T[][]`**](#zip) - combines multiple arrays element-wise. -- [**`partition(array: T[], predicate: (item: T, index: number, array: T[]) => boolean): [T[], T[]]`**](#partition) - splits array into two arrays based on a predicate function. +- [**`chunk(array: readonly T[], size: number): T[][]`**](#chunk) - splits an array into chunks of the specified size. +- [**`unique(array: readonly T[], keyFn?: (item: T) => unknown): T[]`**](#unique) - removes duplicate items from an array, optionally by a key function. +- [**`flattenDeep(array: readonly unknown[]): T[]`**](#flattendeep) - deeply flattens a nested array (iterative, stack-based). +- [**`random(array: readonly T[]): T | undefined`**](#random) - returns a crypto-secure random element from an array. +- [**`groupBy(array: readonly T[], keyOrFn: keyof T | ((item: T) => string | number | symbol)): Record`**](#groupby) - groups array items by a key or function (supports nested keys). +- [**`shuffle(array: readonly T[]): T[]`**](#shuffle) - shuffles array elements using crypto-secure randomness (Fisher-Yates). +- [**`pluck(array: readonly T[], key: K): T[K][]`**](#pluck) - extracts values for a given key from an array of objects. +- [**`difference(a: readonly T[], b: readonly T[]): T[]`**](#difference) - returns elements in array `a` not present in array `b`. +- [**`intersect(a: readonly T[], b: readonly T[]): T[]`**](#intersect) - returns elements common to both arrays. +- [**`mergeSort(array: readonly T[], key: string | ((item: T) => unknown), direction?: "asc" | "desc", compareFn?: (a: T, b: T) => number): T[]`**](#mergesort) - sorts array by key or function using merge sort (supports nested keys). +- [**`zip(...arrays: ReadonlyArray[]): T[][]`**](#zip) - combines multiple arrays element-wise. +- [**`partition(array: readonly T[], predicate: (item: T, index: number, array: readonly T[]) => boolean): [T[], T[]]`**](#partition) - splits array into two arrays based on a predicate function. - [**`range(start: number, end: number, step?: number): number[]`**](#range) - creates an array of numbers in a range. -- [**`take(array: T[], n?: number): T[]`**](#take) - takes the first `n` elements from an array. -- [**`takeWhile(array: T[], predicate: (item: T, index: number) => boolean): T[]`**](#takewhile) - takes elements from array while predicate is true. -- [**`compact(array: T[]): NonNullable[]`**](#compact) - removes falsy values from an array. -- [**`countBy(array: T[], keyFn: (item: T) => string | number | symbol): Record`**](#countby) - counts occurrences of items in an array by a key function. +- [**`take(array: readonly T[], n?: number): T[]`**](#take) - takes the first `n` elements from an array. +- [**`takeWhile(array: readonly T[], predicate: (item: T, index: number) => boolean): T[]`**](#takewhile) - takes elements from array while predicate is true. +- [**`compact(array: readonly T[]): NonNullable[]`**](#compact) - removes falsy values from an array. +- [**`countBy(array: readonly T[], keyFn: (item: T) => string | number | symbol): Record`**](#countby) - counts occurrences of items in an array by a key function. +- [**`toggle(array: readonly T[], item: T): T[]`**](#toggle) - toggles an item in array (adds if not present, removes if present). +- [**`secureIndex(max: number): number`**](#secureindex) - returns a cryptographically secure random index. +- [**`secureRandom(array: readonly T[]): T | undefined`**](#securerandom) - returns a secure random element from an array. +- [**`findLast(array: readonly T[], predicate: (item: T, index: number, array: readonly T[]) => boolean): T | undefined`**](#findlast) - returns the last element matching predicate. +- [**`findLastIndex(array: readonly T[], predicate: (item: T, index: number, array: readonly T[]) => boolean): number`**](#findlastindex) - returns the index of the last element matching predicate. +- [**`chunkBy(array: readonly T[], predicate: (item: T, index: number, array: readonly T[]) => boolean): T[][]`**](#chunkby) - splits array into chunks based on predicate. +- [**`remove(array: readonly T[], value: T): T[]`**](#remove) - removes all occurrences of a value. +- [**`isSorted(array: readonly T[], direction?: 'asc' | 'desc', compareFn?: (a: T, b: T) => number): boolean`**](#issorted) - checks if array is sorted. +- [**`headOfArr(array: readonly T[]): T | undefined`**](#headofarr) - returns the first element of an array. +- [**`lastOfArr(array: readonly T[]): T | undefined`**](#lastofarr) - returns the last element of an array. +- [**`drop(array: readonly T[], n: number): T[]`**](#drop) - drops the first n elements from an array. +- [**`dropWhile(array: readonly T[], predicate: (item: T, index: number) => boolean): T[]`**](#dropwhile) - drops elements from the start while predicate returns true. +- [**`maxBy(array: readonly T[], keyOrFn: keyof T | ((item: T) => number)): T | undefined`**](#maxby) - finds the element with the maximum value. +- [**`minBy(array: readonly T[], keyOrFn: keyof T | ((item: T) => number)): T | undefined`**](#minby) - finds the element with the minimum value. --- @@ -33,22 +51,27 @@ Splits an array into chunks of the specified size. **Method Signature:** ```ts -function chunk(array: T[], size: number): T[][]; +function chunk(array: readonly T[], size: number): T[][]; ``` **Parameters:** - `array`: The input array to be chunked. -- `size`: The size of each chunk. +- `size`: The size of each chunk (must be a positive integer). **Returns:** - An array of chunks, each containing up to `size` elements. +**Throws:** + +- `TypeError` if array is not an array. +- `Error` if chunk size is not a positive integer. + **Examples:** ```ts -import { chunk } from '@catbee/utils'; +import { chunk } from '@catbee/utils/array'; chunk([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]] ``` @@ -62,7 +85,7 @@ Removes duplicate items from an array, optionally by a key function. **Method Signature:** ```ts -function unique(array: T[], keyFn?: (item: T) => unknown): T[]; +function unique(array: readonly T[], keyFn?: (item: T) => unknown): T[]; ``` **Parameters:** @@ -77,7 +100,7 @@ function unique(array: T[], keyFn?: (item: T) => unknown): T[]; **Examples:** ```ts -import { unique } from '@catbee/utils'; +import { unique } from '@catbee/utils/array'; unique([1, 2, 2, 3, 1]); // [1, 2, 3] unique(users, user => user.id); // [{ id: 1, ... }, { id: 2, ... }] @@ -87,12 +110,12 @@ unique(users, user => user.id); // [{ id: 1, ... }, { id: 2, ... }] ### `flattenDeep()` -Deeply flattens a nested array. +Deeply flattens a nested array to a single-level array using an iterative, stack-based approach. **Method Signature:** ```ts -function flattenDeep(array: any[]): T[]; +function flattenDeep(array: readonly unknown[]): T[]; ``` **Parameters:** @@ -106,7 +129,7 @@ function flattenDeep(array: any[]): T[]; **Examples:** ```ts -import { flattenDeep } from '@catbee/utils'; +import { flattenDeep } from '@catbee/utils/array'; flattenDeep([1, [2, [3, [4]], 5]]); // [1, 2, 3, 4, 5] ``` @@ -115,12 +138,12 @@ flattenDeep([1, [2, [3, [4]], 5]]); // [1, 2, 3, 4, 5] ### `random()` -Returns a random element from an array. +Returns a cryptographically secure random element from an array using Node.js crypto. **Method Signature:** ```ts -function random(array: T[]): T | undefined; +function random(array: readonly T[]): T | undefined; ``` **Parameters:** @@ -134,7 +157,7 @@ function random(array: T[]): T | undefined; **Examples:** ```ts -import { random } from '@catbee/utils'; +import { random } from '@catbee/utils/array'; random(['a', 'b', 'c']); // 'a' or 'b' or 'c' ``` @@ -143,35 +166,35 @@ random(['a', 'b', 'c']); // 'a' or 'b' or 'c' ### `groupBy()` -Groups array items by a key or function. +Groups array items by a key or function. Supports nested keys using dot notation (e.g., `'user.profile.age'`). **Method Signature:** ```ts -function groupBy(array: T[], key: keyof T): Record; +function groupBy(array: readonly T[], key: keyof T): Record; ``` ```ts -function groupBy(array: T[], keyFn: (item: T) => K): Record; +function groupBy(array: readonly T[], keyFn: (item: T) => K): Record; ``` ```ts -function groupBy(array: T[], keyOrFn: keyof T | ((item: T) => string | number | symbol)): Record; +function groupBy(array: readonly T[], keyOrFn: keyof T | ((item: T) => string | number | symbol)): Record; ``` **Parameters:** - `array`: The input array to group items from. -- `keyOrFn`: The key or function to group items by. +- `keyOrFn`: The key (supports nested dot notation) or function to group items by. **Returns:** -- An object where keys are the group identifiers and values are arrays of grouped items. +- An object where keys are the group identifiers and values are readonly arrays of grouped items. **Examples:** ```ts -import { groupBy } from '@catbee/utils'; +import { groupBy } from '@catbee/utils/array'; const users = [ { name: 'Alice', role: 'admin' }, @@ -186,12 +209,12 @@ groupBy(users, user => user.name[0]); // { A: [...], B: [...], ... } ### `shuffle()` -Shuffles array elements randomly. +Shuffles array elements using the Fisher-Yates algorithm with cryptographically secure randomness. **Method Signature:** ```ts -function shuffle(array: T[]): T[]; +function shuffle(array: readonly T[]): T[]; ``` **Parameters:** @@ -202,10 +225,14 @@ function shuffle(array: T[]): T[]; - A new shuffled array of type `T[]`. Does not mutate the original array. +**Throws:** + +- `TypeError` if array is not an array. + **Examples:** ```ts -import { shuffle } from '@catbee/utils'; +import { shuffle } from '@catbee/utils/array'; shuffle([1, 2, 3, 4]); // [3, 1, 4, 2] (order will vary) ``` @@ -219,7 +246,7 @@ Extracts values for a given key from an array of objects. **Method Signature:** ```ts -function pluck(array: T[], key: K): T[K][]; +function pluck(array: readonly T[], key: K): T[K][]; ``` **Parameters:** @@ -229,12 +256,12 @@ function pluck(array: T[], key: K): T[K][]; **Returns:** -- An array of plucked values. +- An array of plucked values. Returns undefined for missing properties. **Examples:** ```ts -import { pluck } from '@catbee/utils'; +import { pluck } from '@catbee/utils/array'; const users = [{ name: 'Alice' }, { name: 'Bob' }]; pluck(users, 'name'); // ['Alice', 'Bob'] @@ -249,7 +276,7 @@ Returns elements in array `a` not present in array `b`. **Method Signature:** ```ts -function difference(a: T[], b: T[]): T[]; +function difference(a: readonly T[], b: readonly T[]): T[]; ``` **Parameters:** @@ -260,7 +287,7 @@ function difference(a: T[], b: T[]): T[]; **Examples:** ```ts -import { difference } from '@catbee/utils'; +import { difference } from '@catbee/utils/array'; difference([1, 2, 3, 4], [2, 4]); // [1, 3] ``` @@ -274,7 +301,7 @@ Returns elements common to both arrays. **Method Signature:** ```ts -function intersect(a: T[], b: T[]): T[]; +function intersect(a: readonly T[], b: readonly T[]): T[]; ``` **Parameters:** @@ -285,7 +312,7 @@ function intersect(a: T[], b: T[]): T[]; **Examples:** ```ts -import { intersect } from '@catbee/utils'; +import { intersect } from '@catbee/utils/array'; intersect([1, 2, 3], [2, 3, 4]); // [2, 3] ``` @@ -294,28 +321,38 @@ intersect([1, 2, 3], [2, 3, 4]); // [2, 3] ### `mergeSort()` -Sorts array by key or function using merge sort. +Sorts array by key or function using merge sort (O(n log n)). Supports nested keys using dot notation (e.g., `'profile.age'`). Missing/undefined keys are sorted to the end (asc) or start (desc). **Method Signature:** ```ts -function mergeSort(array: T[], key: string | ((item: T) => any), direction?: 'asc' | 'desc'): T[]; +function mergeSort( + array: readonly T[], + key: string | ((item: T) => unknown), + direction?: 'asc' | 'desc', + compareFn?: (a: T, b: T) => number +): T[]; ``` **Parameters:** - `array`: The input array to sort. -- `key`: The key or function to sort by. -- `direction`: The sort direction, either "asc" or "desc". +- `key`: The key (supports nested dot notation) or function to sort by. +- `direction`: The sort direction, either "asc" or "desc" (default: "asc"). +- `compareFn`: Optional custom compare function. **Returns:** - A new sorted array of type `T[]`. Does not mutate the original array. +**Throws:** + +- `TypeError` if array is not an array. + **Examples:** ```ts -import { mergeSort } from '@catbee/utils'; +import { mergeSort } from '@catbee/utils/array'; const items = [{ value: 3 }, { value: 1 }, { value: 2 }]; mergeSort(items, item => item.value); // [{ value: 1 }, { value: 2 }, { value: 3 }] @@ -331,7 +368,7 @@ Combines multiple arrays element-wise. **Method Signature:** ```ts -function zip(...arrays: T[][]): T[][]; +function zip(...arrays: ReadonlyArray[]): T[][]; ``` **Parameters:** @@ -340,12 +377,16 @@ function zip(...arrays: T[][]): T[][]; **Returns:** -- A new array of type `T[][]`. Does not mutate the original arrays. +- A new array of type `T[][]`. Output length equals the length of the shortest input array. Does not mutate the original arrays. + +**Throws:** + +- `TypeError` if any argument is not an array. **Examples:** ```ts -import { zip } from '@catbee/utils'; +import { zip } from '@catbee/utils/array'; zip([1, 2], ['a', 'b'], [true, false]); // [[1, 'a', true], [2, 'b', false]] ``` @@ -354,17 +395,27 @@ zip([1, 2], ['a', 'b'], [true, false]); // [[1, 'a', true], [2, 'b', false]] ### `partition()` -Splits array into two arrays based on a predicate function. +Splits array into two arrays based on a predicate function. Supports type-guard narrowing. **Method Signature:** ```ts -function partition(array: T[], predicate: (item: T, index: number, array: T[]) => boolean): [T[], T[]]; +// Type guard overload +function partition( + array: readonly T[], + predicate: (item: T, index: number, array: readonly T[]) => item is U +): [U[], Exclude[]] + +// Boolean predicate overload +function partition( + array: readonly T[], + predicate: (item: T, index: number, array: readonly T[]) => boolean +): [T[], T[]] ``` **Parameters:** -- `arrays`: The input arrays to combine. +- `array`: The input array to partition. - `predicate`: A function that returns true to include the item in the first array. - `item`: The current item being processed. - `index`: The index of the current item. @@ -372,12 +423,12 @@ function partition(array: T[], predicate: (item: T, index: number, array: T[] **Returns:** -- A new array of type `T[][]`. Does not mutate the original arrays. +- A tuple of two arrays: `[matched, unmatched]`. Does not mutate the original array. **Examples:** ```ts -import { partition } from '@catbee/utils'; +import { partition } from '@catbee/utils/array'; partition([1, 2, 3, 4], n => n % 2 === 0); // [[2, 4], [1, 3]] ``` @@ -404,10 +455,15 @@ function range(start: number, end: number, step?: number): number[]; - An array of numbers in the specified range. +**Throws:** + +- `TypeError` if arguments are not finite numbers. +- `Error` if step is zero. + **Examples:** ```ts -import { range } from '@catbee/utils'; +import { range } from '@catbee/utils/array'; range(0, 5); // [0, 1, 2, 3, 4] range(2, 10, 2); // [2, 4, 6, 8] @@ -422,7 +478,7 @@ Takes the first `n` elements from an array. **Method Signature:** ```ts -function take(array: T[], n?: number): T[]; +function take(array: readonly T[], n?: number): T[]; ``` **Parameters:** @@ -437,7 +493,7 @@ function take(array: T[], n?: number): T[]; **Examples:** ```ts -import { take } from '@catbee/utils'; +import { take } from '@catbee/utils/array'; take([1, 2, 3, 4], 2); // [1, 2] ``` @@ -451,7 +507,7 @@ Takes elements from array while predicate is true. **Method Signature:** ```ts -function takeWhile(array: T[], predicate: (item: T, index: number) => boolean): T[]; +function takeWhile(array: readonly T[], predicate: (item: T, index: number) => boolean): T[]; ``` **Parameters:** @@ -463,12 +519,12 @@ function takeWhile(array: T[], predicate: (item: T, index: number) => boolean **Returns:** -- A new array containing the leading elements that satisfy the predicate. Does not mutate the original array +- A new array containing the leading elements that satisfy the predicate. Does not mutate the original array. **Examples:** ```ts -import { takeWhile } from '@catbee/utils'; +import { takeWhile } from '@catbee/utils/array'; takeWhile([1, 2, 3, 2, 1], n => n < 3); // [1, 2] ``` @@ -477,12 +533,12 @@ takeWhile([1, 2, 3, 2, 1], n => n < 3); // [1, 2] ### `compact()` -Removes falsy values from an array. +Removes falsy values from an array. Falsy values are: `false`, `null`, `0`, `""`, `undefined`, and `NaN`. **Method Signature:** ```ts -function compact(array: T[]): NonNullable[]; +function compact(array: readonly T[]): NonNullable[]; ``` **Parameters:** @@ -496,7 +552,7 @@ function compact(array: T[]): NonNullable[]; **Examples:** ```ts -import { compact } from '@catbee/utils'; +import { compact } from '@catbee/utils/array'; compact([0, 1, false, 2, '', 3, null, undefined]); // [1, 2, 3] ``` @@ -510,7 +566,7 @@ Counts occurrences by key or function. **Method Signature:** ```ts -function countBy(array: T[], keyFn: (item: T) => string | number | symbol): Record; +function countBy(array: readonly T[], keyFn: (item: T) => string | number | symbol): Record; ``` **Parameters:** @@ -525,7 +581,434 @@ function countBy(array: T[], keyFn: (item: T) => string | number | symbol): R **Examples:** ```ts -import { countBy } from '@catbee/utils'; +import { countBy } from '@catbee/utils/array'; countBy(['cat', 'dog', 'cat'], pet => pet); // { cat: 2, dog: 1 } ``` + +--- + +### `toggle()` + +Toggles an item in array (adds if not present, removes if present). + +**Method Signature:** + +```ts +function toggle(array: readonly T[], item: T): T[]; +``` + +**Parameters:** + +- `array`: The input array. +- `item`: The item to toggle. + +**Returns:** + +- A new array with the item toggled. + +**Examples:** + +```ts +import { toggle } from '@catbee/utils/array'; + +toggle([1, 2, 3], 2); // [1, 3] +toggle([1, 3], 2); // [1, 3, 2] +``` + +--- + +### `secureIndex()` + +Returns a cryptographically secure random index for an array. Used internally for secure pick/shuffle operations. + +**Method Signature:** + +```ts +function secureIndex(max: number): number; +``` + +**Parameters:** + +- `max`: Upper bound (exclusive). + +**Returns:** + +- A secure random integer in range `[0, max)`. + +**Throws:** + +- `RangeError` if `max` is not a positive integer. + +**Examples:** + +```ts +import { secureIndex } from '@catbee/utils/array'; + +secureIndex(10); // 3 (unpredictable) +``` + +--- + +### `secureRandom()` + +Returns a secure random element from an array using Node.js crypto. + +**Method Signature:** + +```ts +function secureRandom(array: readonly T[]): T | undefined; +``` + +**Parameters:** + +- `array`: The input array. + +**Returns:** + +- A random element or undefined if array is empty. + +**Examples:** + +```ts +import { secureRandom } from '@catbee/utils/array'; + +secureRandom(['a', 'b', 'c']); // 'b' (cryptographically secure) +``` + +--- + +### `findLast()` + +Returns the last element in the array that satisfies the provided testing function. + +**Method Signature:** + +```ts +function findLast( + array: readonly T[], + predicate: (item: T, index: number, array: readonly T[]) => boolean +): T | undefined; +``` + +**Parameters:** + +- `array`: The input array. +- `predicate`: Function to test each element. + +**Returns:** + +- The found element, or undefined if not found. + +**Examples:** + +```ts +import { findLast } from '@catbee/utils/array'; + +findLast([1, 2, 3, 4], n => n > 2); // 4 +``` + +--- + +### `findLastIndex()` + +Returns the index of the last element in the array that satisfies the provided testing function. + +**Method Signature:** + +```ts +function findLastIndex( + array: readonly T[], + predicate: (item: T, index: number, array: readonly T[]) => boolean +): number; +``` + +**Parameters:** + +- `array`: The input array. +- `predicate`: Function to test each element. + +**Returns:** + +- The index, or -1 if not found. + +**Examples:** + +```ts +import { findLastIndex } from '@catbee/utils/array'; + +findLastIndex([1, 2, 3, 4], n => n > 2); // 3 +``` + +--- + +### `chunkBy()` + +Splits an array into chunks based on a predicate function. Each chunk starts when predicate returns true. + +**Method Signature:** + +```ts +function chunkBy( + array: readonly T[], + predicate: (item: T, index: number, array: readonly T[]) => boolean +): T[][]; +``` + +**Parameters:** + +- `array`: The input array. +- `predicate`: Function to determine chunk boundaries. + +**Returns:** + +- Array of chunked arrays. + +**Examples:** + +```ts +import { chunkBy } from '@catbee/utils/array'; + +chunkBy([1, 2, 1, 3, 1], n => n === 1); // [[1, 2], [1, 3], [1]] +``` + +--- + +### `remove()` + +Removes all occurrences of a value from an array. + +**Method Signature:** + +```ts +function remove(array: readonly T[], value: T): T[]; +``` + +**Parameters:** + +- `array`: The input array. +- `value`: Value to remove. + +**Returns:** + +- New array with value removed. + +**Examples:** + +```ts +import { remove } from '@catbee/utils/array'; + +remove([1, 2, 3, 2, 1], 2); // [1, 3, 1] +``` + +--- + +### `isSorted()` + +Checks if an array is sorted in ascending or descending order. + +**Method Signature:** + +```ts +function isSorted( + array: readonly T[], + direction?: 'asc' | 'desc', + compareFn?: (a: T, b: T) => number +): boolean; +``` + +**Parameters:** + +- `array`: The input array. +- `direction`: Sort direction (default: 'asc'). +- `compareFn`: Optional compare function. + +**Returns:** + +- True if sorted, false otherwise. + +**Examples:** + +```ts +import { isSorted } from '@catbee/utils/array'; + +isSorted([1, 2, 3, 4]); // true +isSorted([4, 3, 2, 1], 'desc'); // true +isSorted([1, 3, 2]); // false +``` + +--- + +### `headOfArr()` + +Returns the first element of an array, or undefined if empty. + +**Method Signature:** + +```ts +function headOfArr(array: readonly T[]): T | undefined; +``` + +**Parameters:** + +- `array`: The input array. + +**Returns:** + +- The first element or undefined. + +**Examples:** + +```ts +import { headOfArr } from '@catbee/utils/array'; + +headOfArr([1, 2, 3]); // 1 +headOfArr([]); // undefined +``` + +--- + +### `lastOfArr()` + +Returns the last element of an array, or undefined if empty. + +**Method Signature:** + +```ts +function lastOfArr(array: readonly T[]): T | undefined; +``` + +**Parameters:** + +- `array`: The input array. + +**Returns:** + +- The last element or undefined. + +**Examples:** + +```ts +import { lastOfArr } from '@catbee/utils/array'; + +lastOfArr([1, 2, 3]); // 3 +lastOfArr([]); // undefined +``` + +--- + +### `drop()` + +Drops the first n elements from an array. + +**Method Signature:** + +```ts +function drop(array: readonly T[], n: number): T[]; +``` + +**Parameters:** + +- `array`: The source array. +- `n`: Number of elements to drop. + +**Returns:** + +- Array with first n elements removed. + +**Examples:** + +```ts +import { drop } from '@catbee/utils/array'; + +drop([1, 2, 3, 4, 5], 2); // [3, 4, 5] +``` + +--- + +### `dropWhile()` + +Drops elements from the start of an array while predicate returns true. + +**Method Signature:** + +```ts +function dropWhile(array: readonly T[], predicate: (item: T, index: number) => boolean): T[]; +``` + +**Parameters:** + +- `array`: The source array. +- `predicate`: Condition function. + +**Returns:** + +- Array with elements dropped. + +**Examples:** + +```ts +import { dropWhile } from '@catbee/utils/array'; + +dropWhile([1, 2, 3, 4, 1], x => x < 3); // [3, 4, 1] +``` + +--- + +### `maxBy()` + +Finds the element with the maximum value for a given key or function. + +**Method Signature:** + +```ts +function maxBy(array: readonly T[], keyOrFn: keyof T | ((item: T) => number)): T | undefined; +``` + +**Parameters:** + +- `array`: The source array. +- `keyOrFn`: Property key or function. + +**Returns:** + +- Element with maximum value. + +**Examples:** + +```ts +import { maxBy } from '@catbee/utils/array'; + +maxBy([{a: 1}, {a: 5}, {a: 3}], 'a'); // {a: 5} +maxBy([{a: 1}, {a: 5}, {a: 3}], x => x.a); // {a: 5} +``` + +--- + +### `minBy()` + +Finds the element with the minimum value for a given key or function. + +**Method Signature:** + +```ts +function minBy(array: readonly T[], keyOrFn: keyof T | ((item: T) => number)): T | undefined; +``` + +**Parameters:** + +- `array`: The source array. +- `keyOrFn`: Property key or function. + +**Returns:** + +- Element with minimum value. + +**Examples:** + +```ts +import { minBy } from '@catbee/utils/array'; + +minBy([{a: 1}, {a: 5}, {a: 3}], 'a'); // {a: 1} +minBy([{a: 1}, {a: 5}, {a: 3}], x => x.a); // {a: 1} +``` diff --git a/docs/@catbee/utils/modules/async.md b/docs/@catbee/utils/modules/async.md index aa2e98f..6fb4820 100644 --- a/docs/@catbee/utils/modules/async.md +++ b/docs/@catbee/utils/modules/async.md @@ -1,4 +1,8 @@ -# Async Utilities +--- +slug: ../async +--- + +# Async Functions for handling asynchronous operations with better control flow and error handling. Includes helpers for sleep, debounce, throttle, retry, batching, concurrency, memoization, aborting, deferred promises, waterfall, rate limiting, circuit breaker, and more. All methods are fully typed. @@ -21,6 +25,8 @@ Functions for handling asynchronous operations with better control flow and erro - [**`rateLimit(fn: (...args: any[]) => Promise, maxCalls: number, interval: number): (...args: any[]) => Promise`**](#ratelimit) - Limits the rate of async function calls. - [**`circuitBreaker(fn: (...args: any[]) => Promise, options?: CircuitBreakerOptions): (...args: any[]) => Promise`**](#circuitbreaker) - Protects against cascading failures with a circuit breaker. - [**`runWithConcurrency(tasks: (() => Promise)[], options: { concurrency?: number; onProgress?: (completed: number, total: number) => void, signal?: AbortSignal }): Promise`**](#runwithconcurrency) - Runs async tasks with concurrency and progress control. +- [**`optionalRequire(name: string): T | null`**](#optionalrequire) - Optionally requires a module, returning null if not found. +- [**`raceWithValue(promises: Promise[]): Promise<{ value: T; index: number }>`**](#racewithvalue) - Races promises and returns the first fulfilled value with its index. --- @@ -36,11 +42,17 @@ interface TaskQueue { } interface CircuitBreakerOptions { + /** Number of consecutive failures before opening circuit (default: 5) */ failureThreshold?: number; + /** Time in milliseconds to wait before trying again (default: 10000) */ resetTimeout?: number; + /** Number of successful calls to close the circuit again (default: 1) */ successThreshold?: number; + /** Callback when circuit opens */ onOpen?: () => void; + /** Callback when circuit closes */ onClose?: () => void; + /** Callback when circuit enters half-open state */ onHalfOpen?: () => void; } @@ -49,6 +61,10 @@ enum CircuitBreakerState { OPEN = 'OPEN', HALF_OPEN = 'HALF_OPEN' } + +class CircuitBreakerOpenError extends Error { + constructor(message?: string); +} ``` --- @@ -76,7 +92,7 @@ function sleep(ms: number): Promise; **Examples:** ```ts -import { sleep } from '@catbee/utils'; +import { sleep } from '@catbee/utils/async'; await sleep(1000); // pauses for 1 second ``` @@ -90,7 +106,7 @@ Debounces function calls, only invoking after delay. Provides `.cancel()` and `. **Method Signature:** ```ts -function debounce(fn: T, delay: number): T & { cancel(): void; flush(): void }; +function debounce void>(fn: T, delay: number): typeof fn & { cancel(): void; flush(): void }; ``` **Parameters:** @@ -105,7 +121,7 @@ function debounce(fn: T, delay: number): T & { cancel(): void; flush(): void **Examples:** ```ts -import { debounce } from '@catbee/utils'; +import { debounce } from '@catbee/utils/async'; const log = debounce((msg: string) => console.log(msg), 300); log('Hello'); @@ -123,7 +139,11 @@ Throttles function calls to a maximum rate. **Method Signature:** ```ts -function throttle(fn: T, limit: number, opts?: { leading?: boolean; trailing?: boolean }): (...args: Parameters) => void; +function throttle void>( + fn: T, + limit: number, + opts?: { leading?: boolean; trailing?: boolean } +): (...args: Parameters) => void; ``` **Parameters:** @@ -132,7 +152,7 @@ function throttle(fn: T, limit: number, opts?: { leading?: boolean; trailing? - `limit`: The time window in milliseconds. - `opts`: Options to control leading/trailing invocation. - `leading`: If true, invoke on the leading edge (default: true). - - `trailing`: If true, invoke on the trailing edge (default: true). + - `trailing`: If true, invoke on the trailing edge (default: false). **Returns:** @@ -141,7 +161,7 @@ function throttle(fn: T, limit: number, opts?: { leading?: boolean; trailing? **Examples:** ```ts -import { throttle } from '@catbee/utils'; +import { throttle } from '@catbee/utils/async'; const throttled = throttle(() => console.log('Tick'), 1000); throttled(); // Will log immediately @@ -164,7 +184,7 @@ async function retry(fn: () => Promise, retries?: number, delay?: number, - `fn`: The async function to retry. - `retries`: Maximum number of retries (default: 3). -- `delay`: Initial delay between retries in milliseconds (default: 0). +- `delay`: Initial delay between retries in milliseconds (default: 500). - `backoff`: Whether to use exponential backoff (default: false). - `onRetry`: Optional callback invoked on each retry attempt. - `error`: The error from the failed attempt. @@ -177,7 +197,7 @@ async function retry(fn: () => Promise, retries?: number, delay?: number, **Examples:** ```ts -import { retry } from '@catbee/utils'; +import { retry } from '@catbee/utils/async'; const data = await retry(fetchData, 3, 500, true, (err, attempt) => { console.log(`Attempt ${attempt} failed: ${err.message}`); @@ -209,7 +229,7 @@ function withTimeout(promise: Promise, ms: number, message?: string): Prom **Examples:** ```ts -import { withTimeout } from '@catbee/utils'; +import { withTimeout } from '@catbee/utils/async'; await withTimeout(fetch('/api'), 2000, 'Request timed out'); ``` @@ -218,7 +238,9 @@ await withTimeout(fetch('/api'), 2000, 'Request timed out'); ### `runInBatches()` -Runs async tasks in batches with concurrency limit. +Runs async tasks in true batches with concurrency limit. Each batch runs in parallel, but batches run sequentially. All tasks in a batch start at the same time; the next batch waits for full completion. + +**Note:** For more granular concurrency control, use `createTaskQueue()` or `runWithConcurrency()`. **Method Signature:** @@ -229,7 +251,7 @@ function runInBatches(tasks: (() => Promise)[], limit: number): Promise(tasks: (() => Promise)[], limit: number): Promise(fn: (...args: TArgs) = **Examples:** ```ts -import { singletonAsync } from '@catbee/utils'; +import { singletonAsync } from '@catbee/utils/async'; const fetchOnce = singletonAsync(async (url: string) => fetch(url).then(r => r.json())); ``` @@ -295,7 +317,7 @@ function settleAll(tasks: (() => Promise)[]): Promise Promise.resolve(1), @@ -328,7 +350,7 @@ function createTaskQueue(limit: number): TaskQueue; **Examples:** ```ts -import { createTaskQueue, sleep } from '@catbee/utils'; +import { createTaskQueue, sleep } from '@catbee/utils/async'; const queue = createTaskQueue(2); queue(() => sleep(1000).then(() => 'A')).then(console.log); @@ -359,7 +381,7 @@ function runInSeries(tasks: (() => Promise)[]): Promise; **Examples:** ```ts -import { runInSeries } from '@catbee/utils'; +import { runInSeries } from '@catbee/utils/async'; const tasks = [ () => Promise.resolve(1), @@ -395,7 +417,7 @@ function memoizeAsync(fn: (...args: Args) => Promise, **Examples:** ```ts -import { memoizeAsync } from '@catbee/utils'; +import { memoizeAsync } from '@catbee/utils/async'; const fetchUser = memoizeAsync(async (id: number) => fetch(`/user/${id}`).then(r => r.json()), { ttl: 60000 }); await fetchUser(1); // Fetched from API @@ -427,7 +449,7 @@ function abortable(promise: Promise, signal: AbortSignal, abortValue?: any **Examples:** ```ts -import { abortable } from '@catbee/utils'; +import { abortable } from '@catbee/utils/async'; const controller = new AbortController(); const promise = abortable(fetch('/api'), controller.signal, 'Aborted'); @@ -453,7 +475,7 @@ function createDeferred(): [Promise, (value: T | PromiseLike) => void, **Examples:** ```ts -import { createDeferred } from '@catbee/utils'; +import { createDeferred } from '@catbee/utils/async'; const [promise, resolve, reject] = createDeferred(); setTimeout(() => resolve(42), 100); @@ -483,7 +505,7 @@ function waterfall(fns: Array<(input: any) => Promise>): (initialValue: **Examples:** ```ts -import { waterfall } from '@catbee/utils'; +import { waterfall } from '@catbee/utils/async'; const pipeline = waterfall([ async (x) => x + 1, @@ -516,7 +538,7 @@ function rateLimit(fn: (...args: any[]) => Promise, maxCalls: number, inte - A rate-limited version of the function. ```ts -import { rateLimit } from '@catbee/utils'; +import { rateLimit } from '@catbee/utils/async'; const limitedFetch = rateLimit(fetch, 2, 1000); await limitedFetch('/api/1'); @@ -538,25 +560,44 @@ function circuitBreaker(fn: (...args: any[]) => Promise, options?: Circuit - `fn`: The async function to wrap with a circuit breaker. - `options`: Optional settings: - - `failureThreshold`: Number of failures to open the circuit (default: 5). + - `failureThreshold`: Number of consecutive failures to open the circuit (default: 5). - `resetTimeout`: Time in milliseconds to wait before attempting to close the circuit (default: 10000). - - `successThreshold`: Number of successful calls to close the circuit from half-open state (default: 2). + - `successThreshold`: Number of successful calls to close the circuit from half-open state (default: 1). - `onOpen`: Callback invoked when the circuit opens. - `onClose`: Callback invoked when the circuit closes. - `onHalfOpen`: Callback invoked when the circuit transitions to half-open. **Returns:** -- A function that returns a promise resolving to the result of the original function or rejects if the circuit is open. +- A function that returns a promise resolving to the result of the original function or throws `CircuitBreakerOpenError` if the circuit is open. **Examples:** ```ts -import { circuitBreaker } from '@catbee/utils'; - -const protectedFetch = circuitBreaker(async url => - fetch(url).then(r => r.json()), { failureThreshold: 3 } +import { circuitBreaker, CircuitBreakerOpenError } from '@catbee/utils/async'; + +const protectedFetch = circuitBreaker( + async (url) => { + const response = await fetch(url); + if (!response.ok) throw new Error(`HTTP error: ${response.status}`); + return response.json(); + }, + { + failureThreshold: 3, + resetTimeout: 30000, + onOpen: () => console.log('Circuit breaker opened'), + onClose: () => console.log('Circuit breaker closed') + } ); + +// Will throw CircuitBreakerOpenError after failureThreshold consecutive failures +try { + const data = await protectedFetch('https://api.example.com'); +} catch (error) { + if (error instanceof CircuitBreakerOpenError) { + console.log('Service is currently unavailable, please try again later'); + } +} ``` --- @@ -575,7 +616,7 @@ function runWithConcurrency(tasks: (() => Promise)[], options?: { concurre - `tasks`: An array of functions returning promises. - `options`: Optional settings: - - `concurrency`: Maximum number of concurrent tasks (default: 5). + - `concurrency`: Maximum number of concurrent tasks (default: 3). - `onProgress`: Optional callback invoked with completed and total task counts. - `signal`: Optional AbortSignal to cancel the operation. @@ -586,10 +627,86 @@ function runWithConcurrency(tasks: (() => Promise)[], options?: { concurre **Examples:** ```ts -import { runWithConcurrency } from '@catbee/utils'; +import { runWithConcurrency } from '@catbee/utils/async'; -const results = await runWithConcurrency(tasks, { - concurrency: 2, - onProgress: (done, total) => console.log(done, total) -}); +const urls = ['https://example.com/1', 'https://example.com/2', /* many more */]; + +// Process up to 5 requests at a time, with progress reporting +const results = await runWithConcurrency( + urls.map(url => () => fetch(url).then(res => res.json())), + { + concurrency: 5, + onProgress: (completed, total) => { + console.log(`Progress: ${completed}/${total}`); + } + } +); +``` + +--- + +### `optionalRequire()` + +Attempts to require a module, returning null if it cannot be found. + +**Method Signature:** + +```ts +function optionalRequire(name: string): T | null; +``` + +**Parameters:** + +- `name`: The name of the module to require. + +**Returns:** + +- The required module, or `null` if it cannot be loaded. + +**Examples:** + +```ts +import { optionalRequire } from '@catbee/utils/async'; + +const optional = optionalRequire('optional-dependency'); +if (optional) { + optional.doSomething(); +} else { + console.log('Optional dependency not available'); +} +``` + +--- + +### `raceWithValue()` + +Races promises and returns the first fulfilled value along with its index. + +**Method Signature:** + +```ts +async function raceWithValue(promises: Promise[]): Promise<{ value: T; index: number }>; +``` + +**Parameters:** + +- `promises`: An array of promises to race. + +**Returns:** + +- A promise that resolves with an object containing the value and index of the first fulfilled promise. + +**Examples:** + +```ts +import { raceWithValue } from '@catbee/utils/async'; + +const promises = [ + fetch('https://api1.example.com').then(r => r.json()), + fetch('https://api2.example.com').then(r => r.json()), + fetch('https://api3.example.com').then(r => r.json()) +]; + +const { value, index } = await raceWithValue(promises); +console.log(Fastest response from API :, value); ``` diff --git a/docs/@catbee/utils/modules/cache.md b/docs/@catbee/utils/modules/cache.md index 4f5982a..c949b06 100644 --- a/docs/@catbee/utils/modules/cache.md +++ b/docs/@catbee/utils/modules/cache.md @@ -1,17 +1,22 @@ -# Cache Utilities +--- +slug: ../cache +--- + +# Cache In-memory TTL cache with advanced features for efficient data caching and retrieval. Provides a flexible, type-safe cache class with TTL, LRU eviction, auto-cleanup, and batch operations. ## API Summary - [**`TTLCache`**](#function-documentation--usage-examples) - Generic class with key type `K` and value type `V`. -- [**`set(key: K, value: V, ttlMs?: number): void`**](#set) - Add or update a cache entry, optionally with a custom TTL. +- [**`set(key: K, value: V): void`**](#set) - Add or update a cache entry with the default TTL. +- [**`setWithTTL(key: K, value: V, ttlMs: number): void`**](#setwithttl) - Add or update a cache entry with a custom TTL. - [**`get(key: K): V | undefined`**](#get) - Retrieve a value from cache. - [**`has(key: K): boolean`**](#has) - Check if key exists in cache. - [**`delete(key: K): boolean`**](#delete) - Remove a key from cache. - [**`clear(): void`**](#clear) - Remove all entries from cache. - [**`size(): number`**](#size) - Get the number of entries in cache. -- [**`cleanup(): void`**](#cleanup) - Remove expired entries. +- [**`cleanup(): number`**](#cleanup) - Remove expired entries and return count removed. - [**`keys(): K[]`**](#keys) - Get all valid keys in the cache. - [**`values(): V[]`**](#values) - Get all valid values in the cache. - [**`entries(): Array<[K, V]>`**](#entries) - Get all valid key-value pairs. @@ -29,9 +34,12 @@ In-memory TTL cache with advanced features for efficient data caching and retrie ```ts interface TTLCacheOptions { - ttlMs?: number; // Default TTL in milliseconds - maxSize?: number; // Maximum number of entries - autoCleanupMs?: number; // Auto cleanup interval + /** Default time-to-live in milliseconds for cache entries */ + ttlMs?: number; + /** Maximum number of entries to keep in cache (uses LRU eviction policy) */ + maxSize?: number; + /** Auto-cleanup interval in milliseconds (disabled if 0 or negative) */ + autoCleanupMs?: number; } ``` @@ -40,32 +48,54 @@ interface TTLCacheOptions { ## Function Documentation & Usage Examples ```ts -import { TTLCache } from '@catbee/utils'; +import { TTLCache } from '@catbee/utils/cache'; const cache = new TTLCache({ ttlMs: 60000, maxSize: 1000, autoCleanupMs: 30000 }); ``` ### `set()` -Add or update a cache entry, optionally with a custom TTL. +Add or update a cache entry with the default TTL. **Method Signature:** ```ts -set(key: K, value: V, ttlMs?: number): void +set(key: K, value: V): void ``` **Parameters:** - `key`: The cache key. - `value`: The cache value. -- `ttlMs`: Optional time-to-live in milliseconds. **Examples:** ```ts cache.set('foo', 42); -cache.set('bar', 99, 5000); // custom TTL +``` + +--- + +### `setWithTTL()` + +Add or update a cache entry with a custom TTL. + +**Method Signature:** + +```ts +setWithTTL(key: K, value: V, ttlMs: number): void +``` + +**Parameters:** + +- `key`: The cache key. +- `value`: The cache value. +- `ttlMs`: Time-to-live in milliseconds. + +**Examples:** + +```ts +cache.setWithTTL('bar', 99, 5000); // 5 second TTL ``` --- @@ -192,18 +222,23 @@ const count = cache.size(); ### `cleanup()` -Remove expired entries. +Remove expired entries and return the count of removed entries. **Method Signature:** ```ts -cleanup(): void +cleanup(): number ``` +**Returns:** + +- The number of entries removed. + **Examples:** ```ts const removed = cache.cleanup(); +console.log(`Removed ${removed} expired entries`); ``` --- @@ -383,17 +418,22 @@ Get cache statistics. **Method Signature:** ```ts -stats(): object +stats(): { size: number; validEntries: number; expiredEntries: number; maxSize?: number } ``` **Returns:** -- An object containing cache statistics like hits, misses, size, etc. +- An object containing: + - `size`: Total number of entries in cache (including expired) + - `validEntries`: Number of valid (non-expired) entries + - `expiredEntries`: Number of expired entries + - `maxSize`: Maximum size limit (if configured) **Examples:** ```ts const stats = cache.stats(); +console.log(`Cache: ${stats.validEntries}/${stats.size} valid, max: ${stats.maxSize}`); ``` --- diff --git a/docs/@catbee/utils/modules/context-store.md b/docs/@catbee/utils/modules/context-store.md index b1f89b6..c7256f7 100644 --- a/docs/@catbee/utils/modules/context-store.md +++ b/docs/@catbee/utils/modules/context-store.md @@ -1,3 +1,7 @@ +--- +slug: ../context-store +--- + # Context Store Per-request context using AsyncLocalStorage. Provides a type-safe API for storing and retrieving request-scoped data (such as request ID, logger, user, etc.) across async calls. Includes helpers for Express integration, context extension, and symbol-based keys. All methods are fully typed. @@ -21,6 +25,8 @@ Other exports: - [**`StoreKeys`**](#interface--types) – Predefined symbols for context keys. - [**`getRequestId(): string | undefined`**](#getrequestid) – Retrieves the current request ID from context. +- [**`getFromContext(key: symbol): T | undefined`**](#getfromcontext) – Type-safe getter for common context values. +- [**`TypedContextKey`**](#typedcontextkey) – Type-safe wrapper class for accessing and modifying context values. --- @@ -52,7 +58,7 @@ export interface Store { ### Express Middleware ```ts -import { ContextStore, StoreKeys, getLogger } from '@catbee/utils'; +import { ContextStore, StoreKeys, getLogger } from '@catbee/utils/context-store'; import express from 'express'; import crypto from 'crypto'; @@ -120,7 +126,7 @@ getInstance(): AsyncLocalStorage **Examples:** ```ts -import { ContextStore } from '@catbee/utils'; +import { ContextStore } from '@catbee/utils/context-store'; const storage = ContextStore.getInstance(); const store = storage.getStore(); @@ -145,7 +151,7 @@ getAll(): Store | undefined **Examples:** ```ts -import { ContextStore } from '@catbee/utils'; +import { ContextStore } from '@catbee/utils/context-store'; const allValues = ContextStore.getAll(); ``` @@ -174,7 +180,7 @@ run(store: Store, callback: () => T): T **Examples:** ```ts -import { ContextStore } from '@catbee/utils'; +import { ContextStore } from '@catbee/utils/context-store'; ContextStore.run({ [StoreKeys.REQUEST_ID]: 'id' }, () => { // Context is active here @@ -200,10 +206,14 @@ set(key: symbol, value: T): void - `key`: A symbol key to identify the value. - `value`: The value to store. +**Throws:** + +- `Error` if called outside an active context (not within a `.run()` call). + **Examples:** ```ts -import { ContextStore } from '@catbee/utils'; +import { ContextStore } from '@catbee/utils/context-store'; ContextStore.set(StoreKeys.REQUEST_ID, 'id'); ContextStore.set(StoreKeys.USER, { @@ -235,7 +245,7 @@ get(key: symbol): T | undefined **Examples:** ```ts -import { ContextStore } from '@catbee/utils'; +import { ContextStore } from '@catbee/utils/context-store'; const user = ContextStore.get<{ id: number; name: string }>(StoreKeys.USER); ``` @@ -261,7 +271,7 @@ has(key: symbol): boolean - `true` if the key exists, otherwise `false`. ```ts -import { ContextStore } from '@catbee/utils'; +import { ContextStore } from '@catbee/utils/context-store'; if (ContextStore.has(StoreKeys.USER)) { // user exists in context @@ -288,10 +298,14 @@ delete(key: symbol): boolean - `true` if the key was found and deleted, otherwise `false`. +**Throws:** + +- `Error` if called outside an active context. + **Examples:** ```ts -import { ContextStore } from '@catbee/utils'; +import { ContextStore } from '@catbee/utils/context-store'; ContextStore.delete(StoreKeys.USER); ``` @@ -312,10 +326,14 @@ patch(values: Partial>): void - `values`: An object containing key-value pairs to update in the context. +**Throws:** + +- `Error` if called outside an active context. + **Examples:** ```ts -import { ContextStore } from '@catbee/utils'; +import { ContextStore } from '@catbee/utils/context-store'; ContextStore.patch({ [StoreKeys.USER]: { id: 456, name: 'Bob' }, @@ -345,14 +363,21 @@ withValue(key: symbol, value: unknown, callback: () => T): T - The return value of the callback. +**Throws:** + +- `Error` if called outside an active context. + **Examples:** ```ts -import { ContextStore } from '@catbee/utils'; +import { ContextStore } from '@catbee/utils/context-store'; ContextStore.withValue(StoreKeys.USER, { id: 789 }, () => { // user is temporarily set here + const user = ContextStore.get(StoreKeys.USER); + console.log(user); // { id: 789 } }); +// Original value is restored after callback completes ``` --- @@ -407,7 +432,7 @@ createExpressMiddleware(initialValuesFactory?: (req: any) => Partial(key: symbol): T | undefined +``` + +**Parameters:** + +- `key`: The store key symbol to retrieve. + +**Returns:** + +- The typed value from the store or `undefined` if not found. + +**Examples:** + +```ts +import { getFromContext, StoreKeys } from '@catbee/utils/context-store'; + +const user = getFromContext<{ id: number; name: string }>(StoreKeys.USER); +const logger = getFromContext(StoreKeys.LOGGER); +``` + +--- + +### `TypedContextKey` + +Type-safe wrapper class for accessing and modifying context values with specific types. + +**Class Definition:** + +```ts +class TypedContextKey { + constructor(symbol: symbol, defaultValue?: T); + get(): T | undefined; + set(value: T): void; + exists(): boolean; + delete(): boolean; +} +``` + +**Constructor Parameters:** + +- `symbol`: The unique symbol for this key. +- `defaultValue`: Optional default value if key is not found. + +**Methods:** + +- **`get()`**: Gets the current value for this key, or the default value if not found. +- **`set(value: T)`**: Sets the value for this key. +- **`exists()`**: Checks if this key exists in the context. +- **`delete()`**: Deletes this key from the context. + +**Examples:** + +```ts +import { TypedContextKey, StoreKeys } from '@catbee/utils/context-store'; + +interface User { + id: number; + name: string; + email: string; +} + +// Create a typed key with type safety +const userKey = new TypedContextKey(StoreKeys.USER); + +// Type-safe operations +userKey.set({ id: 1, name: 'Alice', email: 'alice@example.com' }); +const user = userKey.get(); // Type: User | undefined + +if (userKey.exists()) { + console.log('User is in context'); +} + +userKey.delete(); + +// With default value +const requestIdKey = new TypedContextKey(StoreKeys.REQUEST_ID, 'default-id'); +const id = requestIdKey.get(); // Returns 'default-id' if not set +``` diff --git a/docs/@catbee/utils/modules/crypto.md b/docs/@catbee/utils/modules/crypto.md index dd51876..593c9dd 100644 --- a/docs/@catbee/utils/modules/crypto.md +++ b/docs/@catbee/utils/modules/crypto.md @@ -1,4 +1,8 @@ -# Crypto Utilities +--- +slug: ../crypto +--- + +# Crypto Secure cryptographic functions for encryption, hashing, and token generation. Includes hashing, HMAC, random string and API key generation, timing-safe comparison, AES encryption/decryption, and JWT-like token creation/verification. All methods are fully typed. @@ -19,6 +23,11 @@ Secure cryptographic functions for encryption, hashing, and token generation. In - [**`decrypt(encryptedData: EncryptionResult, key: string | Buffer, options?): Promise`**](#decrypt) - Decrypt AES encrypted data. - [**`createSignedToken(payload: object, secret: string, expiresInSeconds?: number): string`**](#createsignedtoken) - Create a signed token (JWT-like). - [**`verifySignedToken(token: string, secret: string): object | null`**](#verifysignedtoken) - Verify and decode a signed token. +- [**`pbkdf2Hash(password: string, salt: string | Buffer, keyLength?: number, iterations?: number): Promise`**](#pbkdf2hash) - Derive a cryptographic key using PBKDF2. +- [**`generateNonce(byteLength?: number, encoding?: BinaryToTextEncoding): string`**](#generatenonce) - Generate a cryptographically secure nonce. +- [**`secureRandomInt(min: number, max: number): number`**](#securerandomint) - Generate a cryptographically secure random integer in a range. +- [**`hashPassword(password: string, saltLength?: number, keyLength?: number): Promise`**](#hashpassword) - Hash a password using scrypt. +- [**`verifyPassword(password: string, hash: string): Promise`**](#verifypassword) - Verify a password against a scrypt hash. --- @@ -28,22 +37,34 @@ Secure cryptographic functions for encryption, hashing, and token generation. In type BufferEncoding = 'ascii' | 'utf8' | 'utf-8' | 'utf16le' | 'utf-16le' | 'ucs2' | 'ucs-2' | 'base64' | 'base64url' | 'latin1' | 'binary' | 'hex'; interface EncryptionOptions { - algorithm?: string; + /** Algorithm to use (default: aes-256-gcm) */ + algorithm?: CipherGCMTypes; + /** Input encoding for plaintext if string (default: utf8) */ inputEncoding?: BufferEncoding; - outputEncoding?: string; + /** Output encoding for ciphertext (default: hex) */ + outputEncoding?: BinaryToTextEncoding; } interface DecryptionOptions { - algorithm?: string; - inputEncoding?: string; + /** Algorithm to use (default: aes-256-gcm) */ + algorithm?: CipherGCMTypes; + /** Input encoding for ciphertext if string (default: hex) */ + inputEncoding?: BinaryToTextEncoding; + /** Output encoding for plaintext (default: utf8) */ outputEncoding?: BufferEncoding; } interface EncryptionResult { + /** Encrypted data (string or Buffer based on options) */ ciphertext: string | Buffer; + /** Initialization vector */ iv: Buffer; + /** Authentication tag (for GCM mode) */ authTag?: Buffer; + /** Algorithm used */ algorithm: string; + /** Salt used for key derivation */ + salt: Buffer; } ``` @@ -75,7 +96,7 @@ function hmac(algorithm: string, input: string, secret: string, encoding?: Binar **Examples:** ```ts -import { hmac } from '@catbee/utils'; +import { hmac } from '@catbee/utils/crypto'; hmac('sha256', 'data', 'secret'); // '...'/ ``` @@ -105,7 +126,7 @@ function hash(algorithm: string, input: string, encoding?: BinaryToTextEncoding) **Examples:** ```ts -import { hash } from '@catbee/utils'; +import { hash } from '@catbee/utils/crypto'; hash('sha256', 'data'); // '...' ``` @@ -134,7 +155,7 @@ function sha256Hmac(input: string, secret: string): string; **Examples:** ```ts -import { sha256Hmac } from '@catbee/utils'; +import { sha256Hmac } from '@catbee/utils/crypto'; sha256Hmac('data', 'secret'); // '...' ``` @@ -163,7 +184,7 @@ function sha1(input: string, encoding?: BinaryToTextEncoding): string; **Examples:** ```ts -import { sha1 } from '@catbee/utils'; +import { sha1 } from '@catbee/utils/crypto'; sha1('data'); // '...' ``` @@ -192,7 +213,7 @@ function sha256(input: string, encoding?: BinaryToTextEncoding): string; **Examples:** ```ts -import { sha256 } from '@catbee/utils'; +import { sha256 } from '@catbee/utils/crypto'; sha256('data'); // '...' ``` @@ -220,7 +241,7 @@ function md5(input: string): string; **Examples:** ```ts -import { md5 } from '@catbee/utils'; +import { md5 } from '@catbee/utils/crypto'; md5('data'); // '...' ``` @@ -244,7 +265,7 @@ function randomString(): string; **Examples:** ```ts -import { randomString } from '@catbee/utils'; +import { randomString } from '@catbee/utils/crypto'; randomString(); // '...' ``` @@ -272,7 +293,7 @@ function generateRandomBytes(byteLength?: number): Buffer; **Examples:** ```ts -import { generateRandomBytes } from '@catbee/utils'; +import { generateRandomBytes } from '@catbee/utils/crypto'; generateRandomBytes(16); // ``` @@ -301,7 +322,7 @@ function generateRandomBytesAsString(byteLength?: number, encoding?: BinaryToTex **Examples:** ```ts -import { generateRandomBytesAsString } from '@catbee/utils'; +import { generateRandomBytesAsString } from '@catbee/utils/crypto'; generateRandomBytesAsString(16, 'hex'); // '...' ``` @@ -321,18 +342,19 @@ function generateApiKey(prefix?: string, byteLength?: number): string; **Parameters:** - `prefix`: An optional prefix for the API key. -- `byteLength`: The number of random bytes to generate. Default is 32. +- `byteLength`: The number of random bytes to generate. Default is 24. **Returns:** -- A string containing the generated API key. +- A string containing the generated API key (limited to 32 characters after prefix). **Examples:** ```ts -import { generateApiKey } from '@catbee/utils'; +import { generateApiKey } from '@catbee/utils/crypto'; -generateApiKey('catbee_', 32); // 'catbee_...' +generateApiKey('catbee_', 24); // 'catbee_...' +generateApiKey(); // Returns 32-character key without prefix ``` --- @@ -356,12 +378,18 @@ function safeCompare(a: string | Buffer | Uint8Array, b: string | Buffer | Uint8 - `true` if the inputs are equal, otherwise `false`. +**Throws:** + +- `Error` if inputs are not strings, Buffers, or Uint8Arrays. + **Examples:** ```ts -import { safeCompare } from '@catbee/utils'; +import { safeCompare } from '@catbee/utils/crypto'; safeCompare('abc', 'abc'); // true +safeCompare('abc', 'def'); // false +safeCompare(Buffer.from('abc'), Buffer.from('abc')); // true ``` --- @@ -387,14 +415,15 @@ function encrypt(data: string | Buffer, key: string | Buffer, options?: Encrypti **Returns:** -- A Promise that resolves to the encryption result. +- A Promise that resolves to the encryption result (includes ciphertext, iv, authTag, algorithm, and salt). **Examples:** ```ts -import { encrypt, EncryptionResult } from '@catbee/utils'; +import { encrypt, EncryptionResult } from '@catbee/utils/crypto'; const encrypted = await encrypt('secret', 'password'); +// encrypted contains: { ciphertext, iv, authTag, algorithm, salt } ``` --- @@ -425,7 +454,7 @@ function decrypt(encryptedData: EncryptionResult, key: string | Buffer, options? **Examples:** ```ts -import { decrypt, EncryptionResult } from '@catbee/utils'; +import { decrypt, EncryptionResult } from '@catbee/utils/crypto'; const decrypted = await decrypt(encrypted, 'password'); ``` @@ -446,7 +475,7 @@ function createSignedToken(payload: object, secret: string, expiresInSeconds?: n - `payload`: The payload to include in the token. - `secret`: The secret key to sign the token. -- `expiresInSeconds`: Optional expiration time in seconds. +- `expiresInSeconds`: Optional expiration time in seconds (default: 3600). **Returns:** @@ -455,9 +484,10 @@ function createSignedToken(payload: object, secret: string, expiresInSeconds?: n **Examples:** ```ts -import { createSignedToken } from '@catbee/utils'; +import { createSignedToken } from '@catbee/utils/crypto'; const token = createSignedToken({ userId: 1 }, 'secret', 3600); +const defaultToken = createSignedToken({ userId: 1 }, 'secret'); // Expires in 1 hour ``` --- @@ -484,7 +514,174 @@ function verifySignedToken(token: string, secret: string): object | null; **Examples:** ```ts -import { verifySignedToken } from '@catbee/utils'; +import { verifySignedToken } from '@catbee/utils/crypto'; const payload = verifySignedToken(token, 'secret'); +if (payload) { + console.log('Valid token:', payload); +} else { + console.log('Invalid or expired token'); +} +``` + +--- + +### `pbkdf2Hash()` + +Derive a cryptographic key using PBKDF2 (Password-Based Key Derivation Function 2) with SHA-256. + +**Method Signature:** + +```ts +async function pbkdf2Hash(password: string, salt: string | Buffer, keyLength?: number, iterations?: number): Promise; +``` + +**Parameters:** + +- `password`: Password to derive key from. +- `salt`: Cryptographic salt (use unique per password). +- `keyLength`: Output key length in bytes (default: 32). +- `iterations`: Number of hashing iterations (default: 310000, OWASP recommended). + +**Returns:** + +- A Promise that resolves to the derived key as a Buffer. + +**Examples:** + +```ts +import { pbkdf2Hash } from '@catbee/utils/crypto'; + +const key = await pbkdf2Hash('myPassword', 'mySalt'); +const customKey = await pbkdf2Hash('myPassword', 'mySalt', 64, 500000); // 64-byte key, 500k iterations +``` + +--- + +### `generateNonce()` + +Generate a cryptographically secure nonce (number used once). + +**Method Signature:** + +```ts +function generateNonce(byteLength?: number, encoding?: BinaryToTextEncoding): string; +``` + +**Parameters:** + +- `byteLength`: Length of nonce in bytes (default: 16). +- `encoding`: Output encoding (default: 'hex'). + +**Returns:** + +- A nonce string in the specified encoding. + +**Examples:** + +```ts +import { generateNonce } from '@catbee/utils/crypto'; + +const nonce = generateNonce(); // 16-byte hex nonce +const b64Nonce = generateNonce(16, 'base64'); // Base64-encoded nonce +``` + +--- + +### `secureRandomInt()` + +Generate a cryptographically secure random integer in a specified range. + +**Method Signature:** + +```ts +function secureRandomInt(min: number, max: number): number; +``` + +**Parameters:** + +- `min`: Minimum value (inclusive). +- `max`: Maximum value (inclusive). + +**Returns:** + +- A random integer between min and max (inclusive). + +**Throws:** + +- `Error` if min is greater than max. + +**Examples:** + +```ts +import { secureRandomInt } from '@catbee/utils/crypto'; + +const random = secureRandomInt(1, 100); // Random number 1-100 +const diceRoll = secureRandomInt(1, 6); // Random dice roll +``` + +--- + +### `hashPassword()` + +Hash a password using scrypt (memory-hard function). + +**Method Signature:** + +```ts +async function hashPassword(password: string, saltLength?: number, keyLength?: number): Promise; +``` + +**Parameters:** + +- `password`: The password to hash. +- `saltLength`: Length of salt in bytes (default: 16). +- `keyLength`: Length of derived key in bytes (default: 32). + +**Returns:** + +- A Promise that resolves to a hash string in the format `salt:hash` (both base64-encoded). + +**Examples:** + +```ts +import { hashPassword } from '@catbee/utils/crypto'; + +const hash = await hashPassword('myPassword'); +// Store hash in database +await db.users.update({ id: userId }, { passwordHash: hash }); +``` + +--- + +### `verifyPassword()` + +Verify a password against a scrypt hash. + +**Method Signature:** + +```ts +async function verifyPassword(password: string, hash: string): Promise; +``` + +**Parameters:** + +- `password`: The password to verify. +- `hash`: The hash to verify against (from `hashPassword()`). + +**Returns:** + +- A Promise that resolves to `true` if the password matches, otherwise `false`. + +**Examples:** + +```ts +import { verifyPassword } from '@catbee/utils/crypto'; + +const isValid = await verifyPassword('myPassword', storedHash); +if (isValid) { + console.log('Password is correct'); +} else { + console.log('Invalid password'); +} ``` diff --git a/docs/@catbee/utils/modules/date.md b/docs/@catbee/utils/modules/date.md index 7b10a8a..f6d5b5a 100644 --- a/docs/@catbee/utils/modules/date.md +++ b/docs/@catbee/utils/modules/date.md @@ -1,10 +1,21 @@ -# Date Utilities +--- +slug: ../date +--- + +# Date A set of utilities for parsing, formatting, and manipulating dates in Node.js and TypeScript. These methods provide type-safe helpers for common date operations such as formatting, parsing, arithmetic, and comparison. ## API Summary +### Classes and Constants + +- [**`DateBuilder`**](#datebuilder) - Fluent date builder class for chainable date operations. +- [**`DateConstants`**](#dateconstants) - Constants for time conversions. + +### Functions + - [**`formatDate(date: Date | number, options?: DateFormatOptions): string`**](#formatdate) - Formats a date according to a pattern, locale, and time zone. - [**`formatRelativeTime(date: Date | number, now?: Date | number, locale?: string | string[]): string`**](#formatrelativetime) - Formats a date as relative time (e.g., "5 minutes ago", "in 3 days"). - [**`parseDate(input: string | number, fallback?: Date): Date | null`**](#parsedate) - Parses a date string or timestamp into a Date object. @@ -15,6 +26,18 @@ These methods provide type-safe helpers for common date operations such as forma - [**`isBetween(date: Date | number, start: Date | number, end: Date | number, inclusive?: boolean): boolean`**](#isbetween) - Checks if a date is between two other dates. - [**`isLeapYear(year: number | Date): boolean`**](#isleapyear) - Checks if a year is a leap year. - [**`daysInMonth(year: number | Date, month?: number): number`**](#daysinmonth) - Gets the number of days in a month. +- [**`formatDuration(ms: number): string`**](#formatduration) - Formats a duration in milliseconds to a human-readable string. +- [**`parseDuration(value: string | number): number`**](#parseduration) - Parses a duration string or number into milliseconds. +- [**`getDateFromDuration(input: string): Date`**](#getdatefromduration) - Converts a duration string into a Date object representing the future time. +- [**`isWeekend(date: Date | number): boolean`**](#isweekend) - Checks if a date is on a weekend. +- [**`isToday(date: Date | number): boolean`**](#istoday) - Checks if a date is today. +- [**`isFuture(date: Date | number): boolean`**](#isfuture) - Checks if a date is in the future. +- [**`isPast(date: Date | number): boolean`**](#ispast) - Checks if a date is in the past. +- [**`addDays(date: Date | number, days: number): Date`**](#adddays) - Adds days to a date. +- [**`addMonths(date: Date | number, months: number): Date`**](#addmonths) - Adds months to a date. +- [**`addYears(date: Date | number, years: number): Date`**](#addyears) - Adds years to a date. +- [**`quarterOf(date: Date | number): 1 | 2 | 3 | 4`**](#quarterof) - Gets the quarter of the year for a date. +- [**`weekOfYear(date: Date | number): number`**](#weekofyear) - Gets the ISO week number of the year. --- @@ -45,7 +68,7 @@ import { isLeapYear, daysInMonth, DateFormatOptions -} from "@catbee/utils"; +} from '@catbee/utils/date'; // Format today's date console.log(formatDate(new Date(), { format: 'yyyy-MM-dd' })); @@ -74,6 +97,199 @@ console.log(daysInMonth(2023, 1)); // 28 ## Function Documentation & Usage Examples +--- + +### `DateBuilder` + +Fluent date builder class for chainable date manipulation and building. All methods return a new instance, making it immutable. + +**Class Overview:** + +```ts +class DateBuilder { + constructor(date?: Date | number | string); + static from(date: Date | number | string): DateBuilder; + static now(): DateBuilder; + static of(year: number, month: number, day: number, hour?: number, minute?: number, second?: number, millisecond?: number): DateBuilder; + static parse(dateString: string): DateBuilder; + static fromDuration(duration: string): DateBuilder; + + // Setters + year(year: number): DateBuilder; + month(month: number): DateBuilder; // 1-12, not 0-11 + day(day: number): DateBuilder; + hour(hour: number): DateBuilder; + minute(minute: number): DateBuilder; + second(second: number): DateBuilder; + millisecond(millisecond: number): DateBuilder; + + // Addition/Subtraction + addYears(years: number): DateBuilder; + addMonths(months: number): DateBuilder; + addDays(days: number): DateBuilder; + addHours(hours: number): DateBuilder; + addMinutes(minutes: number): DateBuilder; + addSeconds(seconds: number): DateBuilder; + addMilliseconds(milliseconds: number): DateBuilder; + subtractYears(years: number): DateBuilder; + subtractMonths(months: number): DateBuilder; + subtractDays(days: number): DateBuilder; + subtractHours(hours: number): DateBuilder; + subtractMinutes(minutes: number): DateBuilder; + subtractSeconds(seconds: number): DateBuilder; + + // Start/End of Period + startOfSecond(): DateBuilder; + startOfMinute(): DateBuilder; + startOfHour(): DateBuilder; + startOfDay(): DateBuilder; + startOfWeek(): DateBuilder; + startOfMonth(): DateBuilder; + startOfQuarter(): DateBuilder; + startOfYear(): DateBuilder; + endOfSecond(): DateBuilder; + endOfMinute(): DateBuilder; + endOfHour(): DateBuilder; + endOfDay(): DateBuilder; + endOfWeek(): DateBuilder; + endOfMonth(): DateBuilder; + endOfQuarter(): DateBuilder; + endOfYear(): DateBuilder; + + // Comparisons + isBefore(other: Date | DateBuilder): boolean; + isAfter(other: Date | DateBuilder): boolean; + isSame(other: Date | DateBuilder): boolean; + isBetween(start: Date | DateBuilder, end: Date | DateBuilder, inclusive?: boolean): boolean; + isToday(): boolean; + isFuture(): boolean; + isPast(): boolean; + isWeekend(): boolean; + isLeapYear(): boolean; + + // Getters + getYear(): number; + getMonth(): number; // 1-12, not 0-11 + getDay(): number; + getDayOfWeek(): number; + getHour(): number; + getMinute(): number; + getSecond(): number; + getMillisecond(): number; + getQuarter(): 1 | 2 | 3 | 4; + getWeekOfYear(): number; + getDaysInMonth(): number; + getTime(): number; + getUnixTimestamp(): number; + + // Difference + diff(other: Date | DateBuilder, unit?: 'milliseconds' | 'seconds' | 'minutes' | 'hours' | 'days' | 'months' | 'years'): number; + diffFromNow(unit?: 'milliseconds' | 'seconds' | 'minutes' | 'hours' | 'days' | 'months' | 'years'): number; + + // Formatting + format(options?: DateFormatOptions | string): string; + formatRelative(locale?: string | string[]): string; + toISOString(): string; + toUTCString(): string; + toDateString(): string; + toTimeString(): string; + toLocaleString(locales?: string | string[], options?: Intl.DateTimeFormatOptions): string; + toJSON(): string; + + // Conversion + build(): Date; + toDate(): Date; + clone(): DateBuilder; + valueOf(): number; + toString(): string; +} +``` + +**Examples:** + +```ts +import { DateBuilder } from '@catbee/utils/date'; + +// Create a specific date +const date = new DateBuilder() + .year(2024) + .month(5) + .day(15) + .hour(14) + .minute(30) + .build(); + +// Chain operations +const nextWeek = DateBuilder.now() + .addDays(7) + .startOfDay() + .build(); + +// Use static factory methods +const christmas = DateBuilder.of(2024, 12, 25).build(); +const futureDate = DateBuilder.fromDuration('5m').build(); + +// Fluent comparisons +const isValid = DateBuilder.now() + .isBetween(startDate, endDate); + +// Format dates +const formatted = DateBuilder.now() + .addMonths(2) + .endOfMonth() + .format('yyyy-MM-dd'); // "2026-03-31" + +// Complex date manipulation +const result = DateBuilder.from(new Date('2024-01-15')) + .addYears(1) + .addMonths(6) + .startOfMonth() + .addDays(10) + .format(); // "2025-07-11" +``` + +--- + +### `DateConstants` + +Constants for time conversions. + +**Constants:** + +```ts +const DateConstants = { + MILLISECONDS_IN_SECOND: 1000, + MILLISECONDS_IN_MINUTE: 60000, + MILLISECONDS_IN_HOUR: 3600000, + MILLISECONDS_IN_DAY: 86400000, + MILLISECONDS_IN_WEEK: 604800000, + MILLISECONDS_IN_YEAR: 31536000000, + MILLISECONDS_IN_LEAP_YEAR: 31622400000, + MONTHS_IN_YEAR: 12, + DAYS_IN_WEEK: 7, + HOURS_IN_DAY: 24, + MINUTES_IN_HOUR: 60, + SECONDS_IN_MINUTE: 60 +}; +``` + +**Examples:** + +```ts +import { DateConstants } from '@catbee/utils/date'; + +// Convert 5 days to milliseconds +const fiveDaysMs = 5 * DateConstants.MILLISECONDS_IN_DAY; + +// Convert 2 weeks to hours +const twoWeeksHours = (2 * DateConstants.MILLISECONDS_IN_WEEK) / DateConstants.MILLISECONDS_IN_HOUR; + +// Calculate timeout in milliseconds +const timeout = 30 * DateConstants.MILLISECONDS_IN_MINUTE; // 30 minutes +``` + +--- + ### `formatDate()` Formats a date according to a pattern, locale, and time zone. @@ -88,7 +304,7 @@ function formatDate(date: Date | number, options?: DateFormatOptions): string; - `date`: The date to format (Date object or timestamp). - `options`: Optional formatting options: - - `format`: The date format pattern (default: 'PPpp'). + - `format`: The date format pattern (default: 'yyyy-MM-dd'). - `locale`: The locale(s) to use for formatting (default: system locale). - `timeZone`: The time zone to use (default: system time zone). @@ -99,7 +315,7 @@ function formatDate(date: Date | number, options?: DateFormatOptions): string; **Examples:** ```ts -import { formatDate } from '@catbee/utils'; +import { formatDate } from '@catbee/utils/date'; formatDate(new Date(), { format: 'yyyy-MM-dd' }); // '2023-05-15' formatDate(new Date(), { format: 'yyyy-MM-dd HH:mm:ss' }); // '2023-05-15 14:30:22' @@ -131,7 +347,7 @@ function formatRelativeTime(date: Date | number, now?: Date | number, locale?: s **Examples:** ```ts -import { formatRelativeTime } from '@catbee/utils'; +import { formatRelativeTime } from '@catbee/utils/date'; formatRelativeTime(Date.now() - 1000 * 60 * 5); // "5 minutes ago" formatRelativeTime(Date.now() + 1000 * 60 * 60 * 24 * 3); // "in 3 days" @@ -161,7 +377,7 @@ function parseDate(input: string | number, fallback?: Date): Date | null; **Examples:** ```ts -import { parseDate } from '@catbee/utils'; +import { parseDate } from '@catbee/utils/date'; parseDate('2023-05-15'); // Date object for May 15, 2023 parseDate('invalid', new Date()); // Returns current date as fallback @@ -183,7 +399,7 @@ function dateDiff(date1: Date | number, date2?: Date | number, unit?: 'milliseco - `date1`: The first date (Date object or timestamp). - `date2`: The second date (Date object or timestamp). Defaults to the current date if not provided. -- `unit`: The unit for the difference ('milliseconds', 'seconds', 'minutes', 'hours', 'days', 'months', 'years'). Default is 'milliseconds'. +- `unit`: The unit for the difference ('milliseconds', 'seconds', 'minutes', 'hours', 'days', 'months', 'years'). Default is 'days'. **Returns:** @@ -192,7 +408,7 @@ function dateDiff(date1: Date | number, date2?: Date | number, unit?: 'milliseco **Examples:** ```ts -import { dateDiff } from '@catbee/utils'; +import { dateDiff } from '@catbee/utils/date'; dateDiff(new Date('2023-05-15'), new Date('2023-05-10'), 'days'); // 5 dateDiff(new Date('2023-05-15T10:00:00'), new Date('2023-05-15T06:00:00'), 'hours'); // 4 @@ -223,7 +439,7 @@ function addToDate(date: Date | number, amount: number, unit: 'milliseconds' | ' **Examples:** ```ts -import { addToDate } from '@catbee/utils'; +import { addToDate } from '@catbee/utils/date'; addToDate(new Date('2023-05-15'), 5, 'days'); // Date for May 20, 2023 addToDate(new Date('2023-05-15T10:00:00'), -2, 'hours'); // Date for May 15, 2023 08:00:00 @@ -253,7 +469,7 @@ function startOf(date: Date | number, unit: 'second' | 'minute' | 'hour' | 'day' **Examples:** ```ts -import { startOf } from '@catbee/utils'; +import { startOf } from '@catbee/utils/date'; startOf(new Date('2023-05-15T14:30:00'), 'day'); // Date for May 15, 2023 00:00:00 startOf(new Date('2023-05-15'), 'month'); // Date for May 1, 2023 @@ -283,7 +499,7 @@ function endOf(date: Date | number, unit: 'second' | 'minute' | 'hour' | 'day' | **Examples:** ```ts -import { endOf } from '@catbee/utils'; +import { endOf } from '@catbee/utils/date'; endOf(new Date('2023-05-15T14:30:00'), 'day'); // Date for May 15, 2023 23:59:59.999 endOf(new Date('2023-05-15'), 'month'); // Date for May 31, 2023 23:59:59.999 @@ -315,7 +531,7 @@ function isBetween(date: Date | number, start: Date | number, end: Date | number **Examples:** ```ts -import { isBetween } from '@catbee/utils'; +import { isBetween } from '@catbee/utils/date'; const date = new Date('2023-05-15'); const start = new Date('2023-05-10'); @@ -347,7 +563,7 @@ function isLeapYear(year: number | Date): boolean; **Examples:** ```ts -import { isLeapYear } from '@catbee/utils'; +import { isLeapYear } from '@catbee/utils/date'; isLeapYear(2024); // true isLeapYear(new Date('2023-01-01')); // false @@ -377,8 +593,375 @@ function daysInMonth(year: number | Date, month?: number): number; **Examples:** ```ts -import { daysInMonth } from '@catbee/utils'; +import { daysInMonth } from '@catbee/utils/date'; daysInMonth(2024, 1); // 29 (February in a leap year) daysInMonth(new Date('2023-05-15')); // 31 (May 2023) ``` + +--- + +### `formatDuration()` + +Formats a duration given in milliseconds to a human-readable string. + +**Method Signature:** + +```ts +function formatDuration(ms: number): string; +``` + +**Parameters:** + +- `ms`: Duration in milliseconds. + +**Returns:** + +- A human-readable duration string (e.g., "1h 1m 1s", "1m 30s 61ms"). + +**Examples:** + +```ts +import { formatDuration } from '@catbee/utils/date'; + +formatDuration(3661000); // "1h 1m 1s" +formatDuration(90061); // "1m 30s 61ms" +formatDuration(0); // "0ms" +``` + +--- + +### `parseDuration()` + +Parses a duration string or number into milliseconds. + +**Method Signature:** + +```ts +function parseDuration(value: string | number): number; +``` + +**Parameters:** + +- `value`: Duration string (e.g., "1y", "2w", "3d", "4h", "5m", "6s", "7ms", "1w2d3h") or number in milliseconds. + +**Returns:** + +- Duration in milliseconds. + +**Throws:** + +- `Error` if the duration string is invalid. + +**Examples:** + +```ts +import { parseDuration } from '@catbee/utils/date'; + +parseDuration('5m'); // 300000 (5 minutes) +parseDuration('2h'); // 7200000 (2 hours) +parseDuration('1d'); // 86400000 (1 day) +parseDuration('1w2d3h'); // Complex duration +parseDuration(60000); // 60000 (plain milliseconds) +``` + +--- + +### `getDateFromDuration()` + +Converts a duration string into a Date object representing the future time from now. + +**Method Signature:** + +```ts +function getDateFromDuration(input: string): Date; +``` + +**Parameters:** + +- `input`: Duration string (e.g., "5m", "2h", "1d"). + +**Returns:** + +- Date object representing the future time. + +**Throws:** + +- `Error` if the duration is invalid or non-positive. + +**Examples:** + +```ts +import { getDateFromDuration } from '@catbee/utils/date'; + +getDateFromDuration('5m'); // Date 5 minutes from now +getDateFromDuration('2h'); // Date 2 hours from now +getDateFromDuration('1d'); // Date 1 day from now +``` + +--- + +### `isWeekend()` + +Checks if a date is on a weekend (Saturday or Sunday). + +**Method Signature:** + +```ts +function isWeekend(date: Date | number): boolean; +``` + +**Parameters:** + +- `date`: The date to check. + +**Returns:** + +- `true` if the date is a weekend, otherwise `false`. + +**Examples:** + +```ts +import { isWeekend } from '@catbee/utils/date'; + +isWeekend(new Date('2024-01-06')); // true (Saturday) +isWeekend(new Date('2024-01-08')); // false (Monday) +``` + +--- + +### `isToday()` + +Checks if a date is today. + +**Method Signature:** + +```ts +function isToday(date: Date | number): boolean; +``` + +**Parameters:** + +- `date`: The date to check. + +**Returns:** + +- `true` if the date is today, otherwise `false`. + +**Examples:** + +```ts +import { isToday } from '@catbee/utils/date'; + +isToday(new Date()); // true +isToday(new Date('2020-01-01')); // false +``` + +--- + +### `isFuture()` + +Checks if a date is in the future. + +**Method Signature:** + +```ts +function isFuture(date: Date | number): boolean; +``` + +**Parameters:** + +- `date`: The date to check. + +**Returns:** + +- `true` if the date is in the future, otherwise `false`. + +**Examples:** + +```ts +import { isFuture } from '@catbee/utils/date'; + +isFuture(new Date('2099-01-01')); // true +isFuture(new Date('2020-01-01')); // false +``` + +--- + +### `isPast()` + +Checks if a date is in the past. + +**Method Signature:** + +```ts +function isPast(date: Date | number): boolean; +``` + +**Parameters:** + +- `date`: The date to check. + +**Returns:** + +- `true` if the date is in the past, otherwise `false`. + +**Examples:** + +```ts +import { isPast } from '@catbee/utils/date'; + +isPast(new Date('2020-01-01')); // true +isPast(new Date('2099-01-01')); // false +``` + +--- + +### `addDays()` + +Adds days to a date. + +**Method Signature:** + +```ts +function addDays(date: Date | number, days: number): Date; +``` + +**Parameters:** + +- `date`: The base date. +- `days`: Number of days to add (can be negative to subtract). + +**Returns:** + +- New date with days added. + +**Examples:** + +```ts +import { addDays } from '@catbee/utils/date'; + +addDays(new Date('2024-01-15'), 7); // 2024-01-22 +addDays(new Date('2024-01-15'), -3); // 2024-01-12 +``` + +--- + +### `addMonths()` + +Adds months to a date. + +**Method Signature:** + +```ts +function addMonths(date: Date | number, months: number): Date; +``` + +**Parameters:** + +- `date`: The base date. +- `months`: Number of months to add (can be negative to subtract). + +**Returns:** + +- New date with months added. + +**Examples:** + +```ts +import { addMonths } from '@catbee/utils/date'; + +addMonths(new Date('2024-01-31'), 1); // 2024-02-29 (leap year) +addMonths(new Date('2024-03-31'), 1); // 2024-04-30 (April has 30 days) +``` + +--- + +### `addYears()` + +Adds years to a date. + +**Method Signature:** + +```ts +function addYears(date: Date | number, years: number): Date; +``` + +**Parameters:** + +- `date`: The base date. +- `years`: Number of years to add (can be negative to subtract). + +**Returns:** + +- New date with years added. + +**Examples:** + +```ts +import { addYears } from '@catbee/utils/date'; + +addYears(new Date('2024-01-15'), 5); // 2029-01-15 +addYears(new Date('2024-01-15'), -2); // 2022-01-15 +``` + +--- + +### `quarterOf()` + +Gets the quarter of the year for a date (1-4). + +**Method Signature:** + +```ts +function quarterOf(date: Date | number): 1 | 2 | 3 | 4; +``` + +**Parameters:** + +- `date`: The date. + +**Returns:** + +- Quarter number (1, 2, 3, or 4). + +**Examples:** + +```ts +import { quarterOf } from '@catbee/utils/date'; + +quarterOf(new Date('2024-03-15')); // 1 (Q1: Jan-Mar) +quarterOf(new Date('2024-07-15')); // 3 (Q3: Jul-Sep) +quarterOf(new Date('2024-12-31')); // 4 (Q4: Oct-Dec) +``` + +--- + +### `weekOfYear()` + +Gets the ISO week number of the year for a date. + +**Method Signature:** + +```ts +function weekOfYear(date: Date | number): number; +``` + +**Parameters:** + +- `date`: The date. + +**Returns:** + +- Week number (1-53). + +**Examples:** + +```ts +import { weekOfYear } from '@catbee/utils/date'; + +weekOfYear(new Date('2024-01-15')); // 3 +weekOfYear(new Date('2024-12-31')); // 1 (of next year) +``` + +--- diff --git a/docs/@catbee/utils/modules/decorators.md b/docs/@catbee/utils/modules/decorator.md similarity index 90% rename from docs/@catbee/utils/modules/decorators.md rename to docs/@catbee/utils/modules/decorator.md index 84b74a2..fdaf34b 100644 --- a/docs/@catbee/utils/modules/decorators.md +++ b/docs/@catbee/utils/modules/decorator.md @@ -1,4 +1,8 @@ -# Decorators Utilities +--- +slug: ../decorator +--- + +# Decorator TypeScript decorators for Express. These utilities provide a declarative way to define Express routes, middleware, parameter extraction, response customization, and access control using TypeScript decorators. @@ -41,6 +45,7 @@ These utilities provide a declarative way to define Express routes, middleware, - [**`@Log(options?: LogOptions): MethodDecorator & ClassDecorator`**](#log) - Add comprehensive logging to routes. - [**`@Injectable(): ClassDecorator`**](#injectable) - Mark a class as injectable for DI. - [**`@Inject(targetClass: Constructor): PropertyDecorator`**](#inject) - Inject a dependency into a class property. +- [**`inject(targetClass: Constructor): T`**](#inject-function) - Retrieve an instance from the DI container. --- @@ -78,24 +83,36 @@ interface RouteDefinition { // Parameter decoration definition for method parameters interface ParamDefinition { index: number; - type: 'query' | 'param' | 'body' | 'req' | 'res' | 'logger' | 'reqHeader'; + type: 'query' | 'param' | 'body' | 'req' | 'res' | 'logger' | 'reqHeader' | 'reqId' | 'cookie'; key?: string; options?: ParamOptions; } // Parameter options for advanced extraction interface ParamOptions { + /** Base type of the parameter (default: 'string') */ type?: 'string' | 'number' | 'boolean'; + /** 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; } @@ -157,7 +174,7 @@ function registerControllers(router: Router, controllers: any[]): void; ```ts import express, { Router } from 'express'; -import { registerControllers } from '@catbee/utils'; +import { registerControllers } from '@catbee/utils/decorator'; import { ExampleController } from './controllers/example.controller'; const router: Router = express.Router(); @@ -187,7 +204,7 @@ Marks a class as a controller with a base path. **Examples:** ```ts -import { Controller } from '@catbee/utils'; +import { Controller } from '@catbee/utils/decorator'; @Controller('/api') class ExampleController { @@ -218,7 +235,7 @@ Defines a route for the GET HTTP method. **Examples:** ```ts -import { Get, Param } from '@catbee/utils'; +import { Get, Param } from '@catbee/utils/decorator'; @Get('/items/:id') getItem(@Param('id') id: string) { @@ -249,7 +266,7 @@ Defines a route for the POST HTTP method. **Examples:** ```ts -import { Post, Body } from '@catbee/utils'; +import { Post, Body } from '@catbee/utils/decorator'; @Post('/items') createItem(@Body() item: any) { @@ -280,7 +297,7 @@ Defines a route for the PUT HTTP method. **Examples:** ```ts -import { Put, Param, Body } from '@catbee/utils'; +import { Put, Param, Body } from '@catbee/utils/decorator'; @Put('/items/:id') updateItem(@Param('id') id: string, @Body() update: any) { @@ -311,7 +328,7 @@ Defines a route for the PATCH HTTP method. **Examples:** ```ts -import { Patch, Param, Body } from '@catbee/utils'; +import { Patch, Param, Body } from '@catbee/utils/decorator'; @Patch('/items/:id') patchItem(@Param('id') id: string, @Body() patch: any) { @@ -342,7 +359,7 @@ Defines a route for the DELETE HTTP method. **Examples:** ```ts -import { Delete, Param } from '@catbee/utils'; +import { Delete, Param } from '@catbee/utils/decorator'; @Delete('/items/:id') deleteItem(@Param('id') id: string) { @@ -373,7 +390,7 @@ Defines a route for the OPTIONS HTTP method. **Examples:** ```ts -import { Options } from '@catbee/utils'; +import { Options } from '@catbee/utils/decorator'; @Options('/items') optionsItems() { @@ -404,7 +421,7 @@ Defines a route for the HEAD HTTP method. **Examples:** ```ts -import { Head, Param } from '@catbee/utils'; +import { Head, Param } from '@catbee/utils/decorator'; @Head('/items/:id') headItem(@Param('id') id: string) { @@ -435,7 +452,7 @@ Defines a route for the TRACE HTTP method. **Examples:** ```ts -import { Trace } from '@catbee/utils'; +import { Trace } from '@catbee/utils/decorator'; @Trace('/trace') traceRoute() { @@ -466,7 +483,7 @@ Defines a route for the CONNECT HTTP method. **Examples:** ```ts -import { Connect } from '@catbee/utils'; +import { Connect } from '@catbee/utils/decorator'; @Connect('/connect') connectRoute() { @@ -497,7 +514,7 @@ Applies Express middleware(s) to a route or entire controller. **Examples:** ```ts -import { Use, Get, Req } from '@catbee/utils'; +import { Use, Get, Req } from '@catbee/utils/decorator'; import { authMiddleware, loggingMiddleware } from './middlewares'; // Method-level middleware @@ -543,7 +560,7 @@ Extracts query parameters from the request with optional type conversion and val **Examples:** ```ts -import { Get, Query } from '@catbee/utils'; +import { Get, Query } from '@catbee/utils/decorator'; @Get('/search') search( @@ -567,7 +584,20 @@ search( @Query('sort', { transform: (val) => val.toUpperCase(), validate: (val) => ['ASC', 'DESC'].includes(val) - }) sort: string + }) sort: string, + + // With pattern validation + @Query('email', { + pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, + patternName: 'valid email' + }) email: string, + + // With min/max validation for numbers + @Query('age', { + type: 'number', + min: 18, + max: 100 + }) age: number ) { return { results: [], term, page, ids, minPrice, sort }; } @@ -611,7 +641,7 @@ Extracts route parameters from the request with optional type conversion and val **Examples:** ```ts -import { Get, Param } from '@catbee/utils'; +import { Get, Param } from '@catbee/utils/decorator'; @Get('/users/:id') getUser( @@ -622,7 +652,14 @@ getUser( @Param('id', { type: 'number', validate: (id) => id > 0 - }) numericId: number + }) numericId: number, + + // With min/max validation + @Param('id', { + type: 'number', + min: 1, + max: 999999 + }) validatedId: number ) { return { userId: id, numericId }; } @@ -665,7 +702,7 @@ Extracts body or body property from the request. **Examples:** ```ts -import { Post, Body } from '@catbee/utils'; +import { Post, Body } from '@catbee/utils/decorator'; @Post('/users') createUser(@Body() userData: any) { @@ -697,7 +734,7 @@ Injects the entire request object. **Examples:** ```ts -import { Get, Req } from '@catbee/utils'; +import { Get, Req } from '@catbee/utils/decorator'; @Get('/info') info(@Req() req: any) { @@ -724,7 +761,7 @@ Injects the response object. **Examples:** ```ts -import { Get, Res } from '@catbee/utils'; +import { Get, Res } from '@catbee/utils/decorator'; @Get('/custom') custom(@Res() res: any) { @@ -755,7 +792,7 @@ Extracts headers from the request. **Examples:** ```ts -import { Get, ReqHeader } from '@catbee/utils'; +import { Get, ReqHeader } from '@catbee/utils/decorator'; @Get('/data') getData( @@ -799,7 +836,7 @@ Extracts cookies from the request. **Examples:** ```ts -import { Get, ReqCookie } from '@catbee/utils'; +import { Get, ReqCookie } from '@catbee/utils/decorator'; @Get('/data') getData(@ReqCookie('session_id') sessionId: string) { @@ -826,7 +863,7 @@ Injects a logger instance. **Examples:** ```ts -import { Get, ReqLogger } from '@catbee/utils'; +import { Get, ReqLogger } from '@catbee/utils/decorator'; @Get('/log') logData(@ReqLogger() logger: any, @Param('id') id: string) { @@ -860,7 +897,7 @@ Extracts the request ID from the request headers or the request object. **Examples:** ```ts -import { Get, ReqId } from '@catbee/utils'; +import { Get, ReqId } from '@catbee/utils/decorator'; @Get('/data') getData(@ReqId() reqId: string) { @@ -891,7 +928,7 @@ Sets a custom HTTP status code for the response. **Examples:** ```ts -import { Post, HttpCode, Body } from '@catbee/utils'; +import { Post, HttpCode, Body } from '@catbee/utils/decorator'; @Post('/users') @HttpCode(201) @@ -924,7 +961,7 @@ Adds a custom HTTP header to the response. **Examples:** ```ts -import { Get, Header } from '@catbee/utils'; +import { Get, Header } from '@catbee/utils/decorator'; @Get('/data') @Header('Cache-Control', 'max-age=60') @@ -963,7 +1000,7 @@ Adds multiple custom HTTP headers to the response. **Examples:** ```ts -import { Get, Headers } from '@catbee/utils'; +import { Get, Headers } from '@catbee/utils/decorator'; // Single header @Get('/data') @@ -1017,7 +1054,7 @@ Runs a function before the route handler. **Examples:** ```ts -import { Before, Get, Param } from '@catbee/utils'; +import { Before, Get, Param } from '@catbee/utils/decorator'; // Method-level hook @Get('/users/:id') @@ -1064,7 +1101,7 @@ Runs a function after the route handler, can access the result. **Examples:** ```ts -import { After, Get, Param } from '@catbee/utils'; +import { After, Get, Param } from '@catbee/utils/decorator'; // Method-level hook @Get('/users/:id') @@ -1112,7 +1149,7 @@ Redirects to another URL. **Examples:** ```ts -import { Get, Redirect } from '@catbee/utils'; +import { Get, Redirect } from '@catbee/utils/decorator'; @Get('/old-path') @Redirect('/new-path', 301) @@ -1148,7 +1185,7 @@ Requires specific roles for accessing a route. **Examples:** ```ts -import { Roles, Get, Controller } from '@catbee/utils'; +import { Roles, Get, Controller } from '@catbee/utils/decorator'; // Method-level roles @Get('/admin/settings') @@ -1203,7 +1240,7 @@ Adds caching headers to route responses for client-side caching. **Examples:** ```ts -import { Get, Cache, Controller } from '@catbee/utils'; +import { Get, Cache, Controller } from '@catbee/utils/decorator'; // Method-level cache @Get('/static-data') @@ -1268,7 +1305,7 @@ Applies rate limiting to routes using express-rate-limit. **Examples:** ```ts -import { Post, RateLimit, Body, Controller } from '@catbee/utils'; +import { Post, RateLimit, Body, Controller } from '@catbee/utils/decorator'; // Method-level rate limit @Post('/login') @@ -1323,7 +1360,7 @@ Sets the content type header for the response. **Examples:** ```ts -import { Get, ContentType, Controller } from '@catbee/utils'; +import { Get, ContentType, Controller } from '@catbee/utils/decorator'; // Method-level content type @Get('/download/pdf') @@ -1387,7 +1424,7 @@ Adds API versioning to routes with configurable prefix and header options. **Examples:** ```ts -import { Get, Version, Controller } from '@catbee/utils'; +import { Get, Version, Controller } from '@catbee/utils/decorator'; // Method-level versioning @Get('/users') @@ -1443,7 +1480,7 @@ Sets execution timeout for route handlers to prevent long-running requests. **Examples:** ```ts -import { Get, Timeout, Controller } from '@catbee/utils'; +import { Get, Timeout, Controller } from '@catbee/utils/decorator'; // Method-level timeout @Get('/slow-operation') @@ -1516,7 +1553,7 @@ Adds comprehensive logging to routes for monitoring and debugging. **Examples:** ```ts -import { Post, Log, Body, Param, Controller } from '@catbee/utils'; +import { Post, Log, Body, Param, Controller } from '@catbee/utils/decorator'; // Method-level logging @Post('/users') @@ -1569,7 +1606,7 @@ Marks a class as injectable for dependency injection. **Examples:** ```ts -import { Injectable } from '@catbee/utils'; +import { Injectable } from '@catbee/utils/decorator'; @Injectable() class UserService { @@ -1602,7 +1639,7 @@ Injects a dependency into a class property or via constructor. **Examples (Property Injection):** ```ts -import { Injectable, Inject, Controller } from '@catbee/utils'; +import { Injectable, Inject, Controller } from '@catbee/utils/decorator'; @Injectable() class UserService { @@ -1621,7 +1658,7 @@ class UserController { **Examples (Constructor Injection):** ```ts -import { Injectable, Controller } from '@catbee/utils'; +import { Injectable, Controller } from '@catbee/utils/decorator'; @Injectable() class UserService { @@ -1638,7 +1675,7 @@ class UserController { --- -### `inject()` +### `inject()` {#inject-function} A function to manually inject a dependency outside of class decorators. @@ -1655,7 +1692,7 @@ function inject(targetClass: Constructor): T; **Examples:** ```ts -import { inject, Controller } from '@catbee/utils'; +import { inject, Controller } from '@catbee/utils/decorator'; @Injectable() class UserService { @@ -1693,7 +1730,7 @@ Many decorators can be applied at the controller level and will be inherited by **Examples:** ```ts -import { Controller, Get, Post, Cache, Headers, Roles, Version, Use, ContentType, Before, After } from '@catbee/utils'; +import { Controller, Get, Post, Cache, Headers, Roles, Version, Use, ContentType, Before, After } from '@catbee/utils/decorator'; import { authMiddleware, loggingMiddleware } from './middleware'; @Controller('/api/admin') diff --git a/docs/@catbee/utils/modules/directory.md b/docs/@catbee/utils/modules/directory.md index 8136295..8d22836 100644 --- a/docs/@catbee/utils/modules/directory.md +++ b/docs/@catbee/utils/modules/directory.md @@ -1,4 +1,8 @@ -# Directory Utilities +--- +slug: ../directory +--- + +# Directory Helpers for file and directory operations. These utilities provide async functions for creating, deleting, copying, moving, listing, watching, and analyzing directories and files. @@ -34,14 +38,13 @@ These utilities provide async functions for creating, deleting, copying, moving, export interface FindFilesByPatternOptions { cwd?: string; dot?: boolean; - nodir?: boolean; } // Type for createTempDir options export interface CreateTempDirOptions { prefix?: string; parentDir?: string; - cleanup?: boolean; + cleanup?: boolean; // default: false } // Type for getDirStats result @@ -88,7 +91,7 @@ function ensureDir(dirPath: string): Promise; **Examples:** ```ts -import { ensureDir } from '@catbee/utils'; +import { ensureDir } from '@catbee/utils/directory'; await ensureDir('./data/logs'); ``` @@ -117,7 +120,7 @@ function listFiles(dirPath: string, recursive?: boolean): Promise; **Examples:** ```ts -import { listFiles } from '@catbee/utils'; +import { listFiles } from '@catbee/utils/directory'; const files = await listFiles('./data', true); // recursive ``` @@ -145,7 +148,7 @@ function deleteDirRecursive(dirPath: string): Promise; **Examples:** ```ts -import { deleteDirRecursive } from '@catbee/utils'; +import { deleteDirRecursive } from '@catbee/utils/directory'; await deleteDirRecursive('./old-backups'); ``` @@ -173,7 +176,7 @@ function isDirectory(pathStr: string): Promise; **Examples:** ```ts -import { isDirectory } from '@catbee/utils'; +import { isDirectory } from '@catbee/utils/directory'; const isDir = await isDirectory('./data'); ``` @@ -202,7 +205,7 @@ function copyDir(src: string, dest: string): Promise; **Examples:** ```ts -import { copyDir } from '@catbee/utils'; +import { copyDir } from '@catbee/utils/directory'; await copyDir('./src', './backup/src'); ``` @@ -231,7 +234,7 @@ function moveDir(src: string, dest: string): Promise; **Examples:** ```ts -import { moveDir } from '@catbee/utils'; +import { moveDir } from '@catbee/utils/directory'; await moveDir('./temp', './archive/temp'); ``` @@ -259,7 +262,7 @@ function emptyDir(dirPath: string): Promise; **Examples:** ```ts -import { emptyDir } from '@catbee/utils'; +import { emptyDir } from '@catbee/utils/directory'; await emptyDir('./cache'); ``` @@ -287,7 +290,7 @@ function getDirSize(dirPath: string): Promise; **Examples:** ```ts -import { getDirSize } from '@catbee/utils'; +import { getDirSize } from '@catbee/utils/directory'; const size = await getDirSize('./uploads'); ``` @@ -316,7 +319,7 @@ function watchDir(dirPath: string, callback: (eventType: 'rename' | 'change', fi **Examples:** ```ts -import { watchDir } from '@catbee/utils'; +import { watchDir } from '@catbee/utils/directory'; const stop = watchDir('./data', (event, file) => { console.log(event, file); @@ -342,7 +345,6 @@ function findFilesByPattern(pattern: string, options?: FindFilesByPatternOptions - `options`: Optional settings: - `cwd`: The base directory to search (default: process.cwd()). - `dot`: Whether to include dotfiles (default: false). - - `nodir`: Whether to exclude directories from results (default: true). **Returns:** @@ -351,7 +353,7 @@ function findFilesByPattern(pattern: string, options?: FindFilesByPatternOptions **Examples:** ```ts -import { findFilesByPattern } from '@catbee/utils'; +import { findFilesByPattern } from '@catbee/utils/directory'; const jpgs = await findFilesByPattern('**/*.jpg', { cwd: './images' }); ``` @@ -380,7 +382,7 @@ function getSubdirectories(dirPath: string, recursive?: boolean): Promise; **Examples:** ```ts -import { ensureEmptyDir } from '@catbee/utils'; +import { ensureEmptyDir } from '@catbee/utils/directory'; await ensureEmptyDir('./temp'); ``` @@ -429,7 +431,7 @@ function createTempDir(options?: CreateTempDirOptions): Promise<{ path: string; - `options`: Optional settings: - `prefix`: Prefix for the temp directory name (default: 'tmp-'). - `parentDir`: Parent directory to create the temp dir in (default: system temp dir). - - `cleanup`: Whether to provide a cleanup function (default: true). + - `cleanup`: Whether to register cleanup on process exit (default: false). **Returns:** @@ -440,7 +442,7 @@ function createTempDir(options?: CreateTempDirOptions): Promise<{ path: string; **Examples:** ```ts -import { createTempDir } from '@catbee/utils'; +import { createTempDir } from '@catbee/utils/directory'; const { path, cleanup } = await createTempDir({ prefix: 'session-' }); // ...use path... @@ -471,7 +473,7 @@ function findNewestFile(dirPath: string, recursive?: boolean): Promise **Examples:** ```ts -import { findInDir } from '@catbee/utils'; +import { findInDir } from '@catbee/utils/directory'; const jpgs = await findInDir('./content', (p, stat) => p.endsWith('.jpg'), true); ``` @@ -562,7 +564,7 @@ function watchDirRecursive(dirPath: string, callback: (eventType: 'rename' | 'ch **Examples:** ```ts -import { watchDirRecursive } from '@catbee/utils'; +import { watchDirRecursive } from '@catbee/utils/directory'; const stop = await watchDirRecursive('./data', (event, file) => { console.log(event, file); @@ -596,7 +598,7 @@ function getDirStats(dirPath: string): Promise; **Examples:** ```ts -import { getDirStats } from '@catbee/utils'; +import { getDirStats } from '@catbee/utils/directory'; const stats = await getDirStats('./data'); console.log(stats.fileCount, stats.dirCount, stats.totalSize); @@ -628,7 +630,7 @@ function walkDir(dirPath: string, options: WalkDirOptions): Promise; **Examples:** ```ts -import { walkDir } from '@catbee/utils'; +import { walkDir } from '@catbee/utils/directory'; await walkDir('./data', { visitorFn: entry => { diff --git a/docs/@catbee/utils/modules/environment.md b/docs/@catbee/utils/modules/env.md similarity index 85% rename from docs/@catbee/utils/modules/environment.md rename to docs/@catbee/utils/modules/env.md index b7a978f..03f6c31 100644 --- a/docs/@catbee/utils/modules/environment.md +++ b/docs/@catbee/utils/modules/env.md @@ -1,4 +1,8 @@ -# Environment Utilities +--- +slug: ../env +--- + +# Environment Type-safe environment variable management for Node.js applications. @@ -28,14 +32,14 @@ Provides a single `Env` class with static methods for reading, validating, and t - [**`get(key: string, defaultValue: string): string`**](#get) - Gets a string environment variable, with variable expansion and fallback. - [**`getRequired(key: string): string`**](#getrequired) - Gets a required string environment variable, throws if missing. - [**`getOrFail(key: string): string`**](#getorfail) - Alias for `getRequired`. - - [**`getNumber(key: string, defaultValue: number, options?: { min?: number; max?: number }): number`**](#getnumber) - Gets a number environment variable, throws if not a valid number. + - [**`getNumber(key: string, defaultValue: number): number`**](#getnumber) - Gets a number environment variable, throws if not a valid number. - [**`getNumberRequired(key: string): number`**](#getnumberrequired) - Gets a required number environment variable, throws if missing or invalid. - [**`getInteger(key: string, defaultValue: number, options?: { min?: number; max?: number }): number`**](#getinteger) - Gets an integer environment variable, with optional min/max validation. - - [**`getBoolean(key: string, defaultValue?: boolean): boolean`**](#getboolean) - Gets a boolean environment variable. Accepts `true`, `1`, `yes`, `on` as true; `false`, `0`, `no`, `off` as false. + - [**`getBoolean(key: string, defaultValue = false): boolean`**](#getboolean) - Gets a boolean environment variable. Accepts `true`, `1`, `yes`, `on` as true; `false`, `0`, `no`, `off` as false. - [**`getBooleanRequired(key: string): boolean`**](#getbooleanrequired) - Gets a required boolean environment variable, throws if missing or invalid. - - [**`getJSON(key: string, defaultValue: T): T`**](#getjson) - Parses a JSON object from an environment variable. - - [**`getArray(key: string, defaultValue?: T[], splitter?: string, transform?: (item: string) => T): T[]`**](#getarray) - Parses a delimited string as an array, with optional transformation. - - [**`getNumberArray(key: string, defaultValue?: number[], splitter?: string): number[]`**](#getnumberarray) - Parses a delimited string as an array of numbers. + - [**`getJSON(key: string, defaultValue: T): T`**](#getjson) - Parses a JSON object from an environment variable. + - [**`getArray(key: string, defaultValue = [], splitter = ',', transform?: (item: string) => T): T[]`**](#getarray) - Parses a delimited string as an array, with optional transformation. + - [**`getNumberArray(key: string, defaultValue = [], splitter = ','): number[]`**](#getnumberarray) - Parses a delimited string as an array of numbers. - [**`getEnum(key: string, defaultValue: T, allowedValues: readonly T[]): T`**](#getenum) - Gets an enum environment variable, throws if not a valid value. - [**`getNumberEnum(key: string, defaultValue: number, allowedValues: number[]): number`**](#getnumberenum) - Gets a number enum environment variable, throws if not a valid value. - [**`getUrl(key: string, defaultValue: string, options?: UrlOptions): string`**](#geturl) - Gets a URL environment variable, with optional validation. @@ -43,10 +47,10 @@ Provides a single `Env` class with static methods for reading, validating, and t - [**`getPath(key: string, defaultValue: string, options?: PathOptions): string`**](#getpath) - Gets a path environment variable, with optional validation. - [**`getPort(key: string, defaultValue: number): number`**](#getport) - Gets and validates a port number (0-65535). - [**`getDate(key: string, defaultValue?: string | Date): Date`**](#getdate) - Gets a date environment variable as a `Date` object. - - [**`getDuration(key: string, defaultValue?: string | number): number`**](#getduration) - Parses a duration string (e.g., `1h30m`) as milliseconds. - - [**`getSafeEnv(sensitiveKeys?: string[]): Record`**](#getsafeenv) - Returns all environment variables with sensitive values masked. + - [**`getDuration(key: string, defaultValue = '0'): number`**](#getduration) - Parses a duration string (e.g., `1h30m`) as milliseconds. + - [**`getSafeEnv(sensitiveKeys = ['password', 'secret', 'key', 'token', 'auth']): Record`**](#getsafeenv) - Returns all environment variables with sensitive values masked. - [**`getWithDefault(key: string, defaultFn: () => string): string`**](#getwithdefault) - Gets a value, or generates a default using a function. - - [**`loadFromFile(path: string): Record`**](#loadfromfile) - Loads environment variables from a `.env` file (does not overwrite existing). + - [**`loadFromFile(path = '.env'): Record`**](#loadfromfile) - Loads environment variables from a `.env` file (does not overwrite existing). - [**`has(key: string): boolean`**](#has) - Checks if an environment variable exists. - [**`delete(key: string): void`**](#delete) - Deletes an environment variable (useful for tests). - [**`clearCache(): void`**](#clearcache) - Clears the internal cache of parsed values. @@ -102,7 +106,7 @@ interface PathOptions { ### `Env` class ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; ``` ### `isDev()` @@ -157,7 +161,7 @@ set(key: string, value: string): void **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.set('FOO', 'bar'); ``` @@ -181,7 +185,7 @@ getAll(): Record **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; const allEnv = Env.getAll(); // { NODE_ENV: 'development', FOO: 'bar', ... } ``` @@ -210,7 +214,7 @@ get(key: string, defaultValue: string): string **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.get('FOO', 'default'); // 'bar' if set, otherwise 'default' Env.get('DATABASE_URL', 'postgres://${DB_HOST}/db'); // expands ${DB_HOST} @@ -243,7 +247,7 @@ getRequired(key: string): string **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.getRequired('API_KEY'); // throws if not set ``` @@ -275,7 +279,7 @@ getOrFail(key: string): string **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.getOrFail('SECRET'); // throws if not set ``` @@ -289,16 +293,13 @@ Gets a number environment variable, throws if not a valid number. **Method Signature:** ```ts -getNumber(key: string, defaultValue: number, options: { min?: number; max?: number } = {}): number +getNumber(key: string, defaultValue: number): number ``` **Parameters:** - `key`: The name of the environment variable to get. - `defaultValue`: The value to return if the environment variable is not set. -- `options`: Optional min/max validation. - - `min`: Minimum allowed value. - - `max`: Maximum allowed value. **Returns:** @@ -312,10 +313,9 @@ getNumber(key: string, defaultValue: number, options: { min?: number; max?: numb **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.getNumber('PORT', 3000); // 3000 if not set, otherwise parsed value -Env.getNumber('TIMEOUT', 5000, { min: 1000, max: 10000 }); // throws if out of range ``` --- @@ -345,7 +345,7 @@ getNumberRequired(key: string): number **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.getNumberRequired('TIMEOUT'); // throws if not set or not a number ``` @@ -382,7 +382,7 @@ getInteger(key: string, defaultValue: number, options: { min?: number; max?: num **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.getInteger('PORT', 8080, { min: 1024, max: 65535 }); // throws if not integer or out of range ``` @@ -396,13 +396,13 @@ Gets a boolean environment variable. Accepts `true`, `1`, `yes`, `on` as true; ` **Method Signature:** ```ts -getBoolean(key: string, defaultValue?: boolean): boolean +getBoolean(key: string, defaultValue = false): boolean ``` **Parameters:** - `key`: The name of the environment variable to get. -- `defaultValue`: The value to return if the environment variable is not set. +- `defaultValue`: The value to return if the environment variable is not set (default: false). **Returns:** @@ -415,7 +415,7 @@ getBoolean(key: string, defaultValue?: boolean): boolean **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.getBoolean('DEBUG', false); // true if DEBUG=yes, otherwise false ``` @@ -447,7 +447,7 @@ getBooleanRequired(key: string): boolean **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.getBooleanRequired('FEATURE_ENABLED'); // throws if not set or invalid ``` @@ -461,7 +461,7 @@ Parses a JSON object from an environment variable. **Method Signature:** ```ts -getJSON(key: string, defaultValue: T): T +getJSON(key: string, defaultValue: T): T ``` **Parameters:** @@ -480,7 +480,7 @@ getJSON(key: string, defaultValue: T): T **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.getJSON('CONFIG', { debug: false }); // parses JSON string or returns default ``` @@ -494,14 +494,14 @@ Parses a delimited string as an array, with optional transformation. **Method Signature:** ```ts -getArray(key: string, defaultValue?: T[], splitter: string = ',', transform?: (item: string) => T): T[] +getArray(key: string, defaultValue = [], splitter = ',', transform?: (item: string) => T): T[] ``` **Parameters:** - `key`: The name of the environment variable to get. -- `defaultValue`: The value to return if the environment variable is not set. -- `splitter`: The string used to split the environment variable value into an array. +- `defaultValue`: The value to return if the environment variable is not set (default: []). +- `splitter`: The string used to split the environment variable value into an array (default: ','). - `transform`: An optional function to transform each item in the array. **Returns:** @@ -515,7 +515,7 @@ getArray(key: string, defaultValue?: T[], splitter: string = ',', transform?: **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.getArray('ALLOWED_IPS'); // ['127.0.0.1', '192.168.1.1'] Env.getArray('PORTS', [], ',', p => parseInt(p, 10)); // [3000, 8080] @@ -530,14 +530,14 @@ Parses a delimited string as an array of numbers. **Method Signature:** ```ts -getNumberArray(key: string, defaultValue?: number[], splitter: string = ','): number[] +getNumberArray(key: string, defaultValue = [], splitter = ','): number[] ``` **Parameters:** - `key`: The name of the environment variable to get. -- `defaultValue`: The value to return if the environment variable is not set. -- `splitter`: The string used to split the environment variable value into an array. +- `defaultValue`: The value to return if the environment variable is not set (default: []). +- `splitter`: The string used to split the environment variable value into an array (default: ','). **Returns:** @@ -550,7 +550,7 @@ getNumberArray(key: string, defaultValue?: number[], splitter: string = ','): nu **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.getNumberArray('ALLOWED_PORTS'); // [80, 443, 3000] ``` @@ -584,7 +584,7 @@ getEnum(key: string, defaultValue: T, allowedValues: readonly **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.getEnum('LOG_LEVEL', 'info', ['debug', 'info', 'warn', 'error'] as const); ``` @@ -618,7 +618,7 @@ getNumberEnum(key: string, defaultValue: number, allowedValues: number[]): numbe **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.getNumberEnum('NODE_VERSION', 16, [14, 16, 18]); ``` @@ -656,7 +656,7 @@ getUrl(key: string, defaultValue: string, options?: UrlOptions): string **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.getUrl('API_URL', 'https://api.example.com', { protocols: ['https'], @@ -693,7 +693,7 @@ getEmail(key: string, defaultValue: string): string **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.getEmail('SUPPORT_EMAIL', 'support@example.com'); // throws if invalid ``` @@ -730,7 +730,7 @@ getPath(key: string, defaultValue: string, options?: PathOptions): string **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.getPath('CONFIG_PATH', './config.json', { mustExist: true, @@ -766,7 +766,7 @@ getPort(key: string, defaultValue: number): number **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.getPort('PORT', 3000); // throws if out of range ``` @@ -780,13 +780,13 @@ Gets a date environment variable as a `Date` object. **Method Signature:** ```ts -getDate(key: string, defaultValue?: string | Date): Date +getDate(key: string, defaultValue: string | Date = new Date()): Date ``` **Parameters:** - `key`: The name of the environment variable to get. -- `defaultValue`: The default value to return if the environment variable is not set or invalid. +- `defaultValue`: The default value to return if the environment variable is not set or invalid (default: new Date()). **Returns:** @@ -799,7 +799,7 @@ getDate(key: string, defaultValue?: string | Date): Date **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.getDate('EXPIRY_DATE', new Date()); // parses ISO string or returns default ``` @@ -832,7 +832,7 @@ getDuration(key: string, defaultValue?: string | number): number **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.getDuration('CACHE_TTL', '2h30m'); // 9000000 ``` @@ -846,12 +846,12 @@ Returns all environment variables with sensitive values masked. **Method Signature:** ```ts -getSafeEnv(sensitiveKeys?: string[]): Record +getSafeEnv(sensitiveKeys: string[] = ['password', 'secret', 'key', 'token', 'auth']): Record ``` **Parameters:** -- `sensitiveKeys`: Optional list of keys to mask (default: `['password', 'secret', 'key']`). +- `sensitiveKeys`: Optional list of keys to mask (default: `['password', 'secret', 'key', 'token', 'auth']`). **Returns:** @@ -860,7 +860,7 @@ getSafeEnv(sensitiveKeys?: string[]): Record **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.getSafeEnv(['password', 'secret', 'key']); // { NODE_ENV: 'development', API_KEY: '******', ... } ``` @@ -889,7 +889,7 @@ getWithDefault(key: string, defaultFn: () => string): string **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.getWithDefault('HOSTNAME', () => require('os').hostname()); ``` @@ -903,12 +903,12 @@ Loads environment variables from a `.env` file (does not overwrite existing). **Method Signature:** ```ts -loadFromFile(path: string): Record +loadFromFile(path: string = '.env'): Record ``` **Parameters:** -- `path`: The path to the `.env` file to load. +- `path`: Path to the .env file (default: '.env'). **Returns:** @@ -917,7 +917,7 @@ loadFromFile(path: string): Record **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.loadFromFile('.env.development'); ``` @@ -945,7 +945,7 @@ has(key: string): boolean **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.has('API_KEY'); // true or false ``` @@ -969,7 +969,7 @@ delete(key: string): void **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.delete('FOO'); ``` @@ -989,7 +989,7 @@ clearCache(): void **Examples:** ```ts -import { Env } from '@catbee/utils'; +import { Env } from '@catbee/utils/env'; Env.clearCache(); ``` diff --git a/docs/@catbee/utils/modules/exception.md b/docs/@catbee/utils/modules/exception.md index 99e9f0c..ad4bda9 100644 --- a/docs/@catbee/utils/modules/exception.md +++ b/docs/@catbee/utils/modules/exception.md @@ -1,4 +1,8 @@ -# Exception Utilities +--- +slug: ../exception +--- + +# Exception Standard HTTP and general error handling. @@ -81,7 +85,7 @@ class HttpError extends ErrorResponse { **Example:** ```ts -import { HttpError } from '@catbee/utils'; +import { HttpError } from '@catbee/utils/exception'; throw new HttpError(401, 'Unauthorized access'); ``` @@ -111,7 +115,7 @@ class InternalServerErrorException extends ErrorResponse { **Example:** ```ts -import { InternalServerErrorException } from '@catbee/utils'; +import { InternalServerErrorException } from '@catbee/utils/exception'; throw new InternalServerErrorException(); ``` @@ -141,7 +145,7 @@ class UnauthorizedException extends ErrorResponse { **Example:** ```ts -import { UnauthorizedException } from '@catbee/utils'; +import { UnauthorizedException } from '@catbee/utils/exception'; throw new UnauthorizedException('Login required'); ``` @@ -171,7 +175,7 @@ class BadRequestException extends ErrorResponse { **Example:** ```ts -import { BadRequestException } from '@catbee/utils'; +import { BadRequestException } from '@catbee/utils/exception'; throw new BadRequestException('Invalid input'); ``` @@ -196,7 +200,7 @@ Represents a 404 Not Found Error. **Example:** ```ts -import { NotFoundException } from '@catbee/utils'; +import { NotFoundException } from '@catbee/utils/exception'; throw new NotFoundException('User not found'); ``` @@ -221,7 +225,7 @@ Represents a 403 Forbidden Error. **Example:** ```ts -import { ForbiddenException } from '@catbee/utils'; +import { ForbiddenException } from '@catbee/utils/exception'; throw new ForbiddenException(); ``` @@ -251,7 +255,7 @@ class ConflictException extends ErrorResponse { **Example:** ```ts -import { ConflictException } from '@catbee/utils'; +import { ConflictException } from '@catbee/utils/exception'; throw new ConflictException('Resource already exists'); ``` @@ -281,7 +285,7 @@ class BadGatewayException extends ErrorResponse { **Example:** ```ts -import { BadGatewayException } from '@catbee/utils'; +import { BadGatewayException } from '@catbee/utils/exception'; throw new BadGatewayException(); ``` @@ -311,7 +315,7 @@ class TooManyRequestsException extends ErrorResponse { **Example:** ```ts -import { TooManyRequestsException } from '@catbee/utils'; +import { TooManyRequestsException } from '@catbee/utils/exception'; throw new TooManyRequestsException(); ``` @@ -341,7 +345,7 @@ class ServiceUnavailableException extends ErrorResponse { **Example:** ```ts -import { ServiceUnavailableException } from '@catbee/utils'; +import { ServiceUnavailableException } from '@catbee/utils/exception'; throw new ServiceUnavailableException(); ``` @@ -371,7 +375,7 @@ class GatewayTimeoutException extends ErrorResponse { **Example:** ```ts -import { GatewayTimeoutException } from '@catbee/utils'; +import { GatewayTimeoutException } from '@catbee/utils/exception'; throw new GatewayTimeoutException(); ``` @@ -398,7 +402,7 @@ class UnprocessableEntityException extends ErrorResponse { **Example:** ```ts -import { UnprocessableEntityException } from '@catbee/utils'; +import { UnprocessableEntityException } from '@catbee/utils/exception'; throw new UnprocessableEntityException('Validation failed', { field: 'email' }); ``` @@ -429,7 +433,7 @@ class MethodNotAllowedException extends ErrorResponse { **Example:** ```ts -import { MethodNotAllowedException } from '@catbee/utils'; +import { MethodNotAllowedException } from '@catbee/utils/exception'; throw new MethodNotAllowedException('Use GET', ['GET']); ``` @@ -459,7 +463,7 @@ class NotAcceptableException extends ErrorResponse { **Example:** ```ts -import { NotAcceptableException } from '@catbee/utils'; +import { NotAcceptableException } from '@catbee/utils/exception'; throw new NotAcceptableException(); ``` @@ -489,7 +493,7 @@ class RequestTimeoutException extends ErrorResponse { **Example:** ```ts -import { RequestTimeoutException } from '@catbee/utils'; +import { RequestTimeoutException } from '@catbee/utils/exception'; throw new RequestTimeoutException(); ``` @@ -519,7 +523,7 @@ class UnsupportedMediaTypeException extends ErrorResponse { **Example:** ```ts -import { UnsupportedMediaTypeException } from '@catbee/utils'; +import { UnsupportedMediaTypeException } from '@catbee/utils/exception'; throw new UnsupportedMediaTypeException(); ``` @@ -549,7 +553,7 @@ class PayloadTooLargeException extends ErrorResponse { **Example:** ```ts -import { PayloadTooLargeException } from '@catbee/utils'; +import { PayloadTooLargeException } from '@catbee/utils/exception'; throw new PayloadTooLargeException(); ``` @@ -579,7 +583,7 @@ class InsufficientStorageException extends ErrorResponse { **Example:** ```ts -import { InsufficientStorageException } from '@catbee/utils'; +import { InsufficientStorageException } from '@catbee/utils/exception'; throw new InsufficientStorageException(); ``` @@ -607,7 +611,7 @@ function isHttpError(error: unknown): error is ErrorResponse; **Example:** ```ts -import { isHttpError } from '@catbee/utils'; +import { isHttpError } from '@catbee/utils/exception'; if (isHttpError(err)) { /* handle HTTP error */ @@ -638,7 +642,7 @@ function createHttpError(status: number, message?: string): ErrorResponse; **Example:** ```ts -import { createHttpError } from '@catbee/utils'; +import { createHttpError } from '@catbee/utils/exception'; throw createHttpError(404, 'Not found'); ``` @@ -666,7 +670,7 @@ function hasErrorShape(error: unknown): error is { message: string; status?: num **Example:** ```ts -import { hasErrorShape } from '@catbee/utils'; +import { hasErrorShape } from '@catbee/utils/exception'; if (hasErrorShape(err)) { console.log(err.message); @@ -696,7 +700,7 @@ function getErrorMessage(error: unknown): string; **Example:** ```ts -import { getErrorMessage } from '@catbee/utils'; +import { getErrorMessage } from '@catbee/utils/exception'; const msg = getErrorMessage(err); ``` @@ -724,7 +728,7 @@ function withErrorHandling Promise>(handler: **Example:** ```ts -import { withErrorHandling } from '@catbee/utils'; +import { withErrorHandling } from '@catbee/utils/exception'; const safeHandler = withErrorHandling(async req => { // ...handler code... diff --git a/docs/@catbee/utils/modules/file-system.md b/docs/@catbee/utils/modules/fs.md similarity index 58% rename from docs/@catbee/utils/modules/file-system.md rename to docs/@catbee/utils/modules/fs.md index e442726..799ba3c 100644 --- a/docs/@catbee/utils/modules/file-system.md +++ b/docs/@catbee/utils/modules/fs.md @@ -1,4 +1,8 @@ -# File System Utilities +--- +slug: ../fs +--- + +# File System A comprehensive set of utilities for common file system operations including reading, writing, and manipulating files and directories. These utilities provide promise-based wrappers around Node.js fs operations with proper error handling and simplified interfaces. @@ -6,21 +10,25 @@ A comprehensive set of utilities for common file system operations including rea - [**`fileExists(path: string): Promise`**](#fileexists) - Checks if a file or directory exists. - [**`readJsonFile(path: string): Promise`**](#readjsonfile) - Reads and parses a JSON file. -- [**`writeJsonFile(path: string, data: any, space?: number): Promise`**](#writejsonfile) - Writes an object to a file as JSON. -- [**`deleteFileIfExists(path: string): Promise`**](#deletefileifexists) - Deletes a file if it exists. -- [**`readTextFile(path: string, encoding?: string): Promise`**](#readtextfile) - Reads a text file. -- [**`writeTextFile(path: string, content: string, encoding?: string): Promise`**](#writetextfile) - Writes text content to a file. -- [**`appendTextFile(path: string, content: string, encoding?: string): Promise`**](#appendtextfile) - Appends text content to a file. -- [**`copyFile(source: string, destination: string, overwrite?: boolean): Promise`**](#copyfile) - Copies a file. -- [**`moveFile(oldPath: string, newPath: string): Promise`**](#movefile) - Moves a file. +- [**`readJsonOrDefault(path: string, defaultValue: T): Promise`**](#readjsonordefault) - Reads a JSON file or returns a default value on error. +- [**`writeJsonFile(path: string, data: any, space = 2): Promise`**](#writejsonfile) - Writes an object to a file as JSON. +- [**`deleteFileIfExists(path: string): Promise`**](#deletefileifexists) - Deletes a file if it exists. +- [**`readFile(path: string, encoding = 'utf-8'): Promise`**](#readfile) - Reads a file asynchronously. +- [**`readFileSync(path: string, encoding = 'utf-8'): string`**](#readfilesync) - Reads a file synchronously. +- [**`safeReadFile(path: string, encoding = 'utf-8'): Promise<{ data: string | null; error: Error | null }>`**](#safereadfile) - Safely reads a file with error handling. +- [**`safeReadFileSync(path: string, encoding = 'utf-8'): { data: string | null; error: Error | null }`**](#safereadfilesync) - Safely reads a file synchronously with error handling. +- [**`writeFile(path: string, content: string, encoding = 'utf-8'): Promise`**](#writefile) - Writes text content to a file. +- [**`appendFile(path: string, content: string, encoding = 'utf-8'): Promise`**](#appendfile) - Appends text content to a file. +- [**`copyFile(source: string, destination: string, overwrite = false): Promise`**](#copyfile) - Copies a file. +- [**`moveFile(oldPath: string, newPath: string): Promise`**](#movefile) - Moves a file. - [**`getFileStats(path: string): Promise`**](#getfilestats) - Gets file stats if the file exists. -- [**`createTempFile(options?: { content?: string }): Promise`**](#createtempfile) - Creates a temporary file with optional content. +- [**`createTempFile(options?): Promise`**](#createtempfile) - Creates a temporary file with optional content. - [**`streamFile(source: string, destination: string): Promise`**](#streamfile) - Streams a file from source to destination. -- [**`readDirectory(dirPath: string, options?: { withFileTypes?: boolean }): Promise`**](#readdirectory) - Reads a directory and returns file names. -- [**`createDirectory(dirPath: string, recursive?: boolean): Promise`**](#createdirectory) - Creates a directory if it doesn't exist. -- [**`safeReadJsonFile(path: string): Promise`**](#safereadjsonfile) - Safely reads and parses a JSON file with error details. +- [**`readDirectory(dirPath: string, options?): Promise`**](#readdirectory) - Reads a directory and returns file names. +- [**`createDirectory(dirPath: string, recursive = true): Promise`**](#createdirectory) - Creates a directory if it doesn't exist. +- [**`safeReadJsonFile(path: string): Promise<{ data: T | null; error: Error | null }>`**](#safereadjsonfile) - Safely reads and parses a JSON file with error details. - [**`isFile(path: string): Promise`**](#isfile) - Checks if a path points to a file (not a directory). -- [**`getFileSize(path: string): Promise`**](#getfilesize) - Gets the size of a file in bytes. +- [**`getFileSize(path: string): Promise`**](#getfilesize) - Gets the size of a file in bytes. - [**`readFileBuffer(path: string): Promise`**](#readfilebuffer) - Reads a file as a Buffer. --- @@ -48,7 +56,7 @@ async function fileExists(path: string): Promise; **Example:** ```ts -import { fileExists } from '@catbee/utils'; +import { fileExists } from '@catbee/utils/fs'; if (await fileExists('./config.json')) { console.log('Configuration file exists'); @@ -78,7 +86,7 @@ async function readJsonFile(path: string): Promise; **Example:** ```ts -import { readJsonFile } from '@catbee/utils'; +import { readJsonFile } from '@catbee/utils/fs'; interface Config { apiKey: string; @@ -93,6 +101,36 @@ if (config) { --- +### `readJsonOrDefault` + +Reads and parses a JSON file, returning a default value if reading or parsing fails. + +**Method Signature:** + +```ts +async function readJsonOrDefault(path: string, defaultValue: T): Promise; +``` + +**Parameters:** + +- `path` (string): The path to the JSON file. +- `defaultValue` (T): The value to return if the file doesn't exist or parsing fails. + +**Returns:** + +- `Promise`: Resolves to the parsed JSON object or the default value. + +**Example:** + +```ts +import { readJsonOrDefault } from '@catbee/utils/fs'; + +const config = await readJsonOrDefault('./config.json', { debug: false, port: 3000 }); +console.log(`Server will run on port ${config.port}`); +``` + +--- + ### `writeJsonFile` Writes a JavaScript object to a file as formatted JSON. @@ -100,14 +138,14 @@ Writes a JavaScript object to a file as formatted JSON. **Method Signature:** ```ts -async function writeJsonFile(path: string, data: any, space?: number): Promise; +async function writeJsonFile(path: string, data: any, space = 2): Promise; ``` **Parameters:** - `path` (string): The path to the file where JSON should be written. - `data` (any): The JavaScript object to serialize as JSON. -- `space` (number, optional): Number of spaces for indentation in the JSON file. +- `space` (number, optional): Number of spaces for indentation in the JSON file (default: 2). **Returns:** @@ -116,7 +154,7 @@ async function writeJsonFile(path: string, data: any, space?: number): Promise; **Example:** ```ts -import { deleteFileIfExists } from '@catbee/utils'; +import { deleteFileIfExists } from '@catbee/utils/fs'; const deleted = await deleteFileIfExists('./temp.log'); console.log(deleted ? "File deleted or didn't exist" : 'Deletion failed'); @@ -158,53 +196,160 @@ console.log(deleted ? "File deleted or didn't exist" : 'Deletion failed'); --- -### `readTextFile` +### `readFile` -Reads a text file from the specified path. +Reads a file from the specified path asynchronously. **Method Signature:** ```ts -async function readTextFile(path: string, encoding?: BufferEncoding): Promise; +async function readFile(path: string, encoding: BufferEncoding = 'utf-8'): Promise; ``` **Parameters:** -- `path` (string): The path to the text file. -- `encoding` (BufferEncoding, optional): The text encoding to use. Defaults to 'utf-8'. +- `path` (string): The path to the file. +- `encoding` (BufferEncoding, optional): The text encoding to use (default: 'utf-8'). **Returns:** -- `Promise`: Resolves to the file content as a string, or null if the file doesn't exist or reading fails. +- `Promise`: Resolves to the file content as a string. + +**Throws:** + +- An error if the file doesn't exist or reading fails. + +**Example:** + +```ts +import { readFile } from '@catbee/utils/fs'; + +const content = await readFile('./README.md'); +console.log('File content:', content.substring(0, 100) + '...'); +``` + +--- + +### `readFileSync` + +Reads a file from the specified path synchronously. + +**Method Signature:** + +```ts +function readFileSync(path: string, encoding: BufferEncoding = 'utf-8'): string; +``` + +**Parameters:** + +- `path` (string): The path to the file. +- `encoding` (BufferEncoding, optional): The text encoding to use (default: 'utf-8'). + +**Returns:** + +- `string`: The file content as a string. + +**Throws:** + +- An error if the file doesn't exist or reading fails. **Example:** ```ts -import { readTextFile } from '@catbee/utils'; +import { readFileSync } from '@catbee/utils/fs'; -const content = await readTextFile('./README.md'); -if (content) { - console.log('File content:', content.substring(0, 100) + '...'); +const content = readFileSync('./config.json'); +``` + +--- + +### `safeReadFile` + +Safely reads a file and returns its content along with any error encountered. + +**Method Signature:** + +```ts +async function safeReadFile(path: string, encoding: BufferEncoding = 'utf-8'): Promise<{ data: string | null; error: Error | null }>; +``` + +**Parameters:** + +- `path` (string): The path to the file. +- `encoding` (BufferEncoding, optional): The text encoding to use (default: 'utf-8'). + +**Returns:** + +- `Promise<{ data: string | null; error: Error | null }>`: Object containing: + - `data`: The file content as a string, or null if reading failed. + - `error`: An error object if reading failed, or null if successful. + +**Example:** + +```ts +import { safeReadFile } from '@catbee/utils/fs'; + +const { data, error } = await safeReadFile('./config.txt'); +if (error) { + console.error('Error reading file:', error.message); +} else { + console.log('File content:', data); +} +``` + +--- + +### `safeReadFileSync` + +Safely reads a file synchronously and returns its content along with any error encountered. + +**Method Signature:** + +```ts +function safeReadFileSync(path: string, encoding: BufferEncoding = 'utf-8'): { data: string | null; error: Error | null }; +``` + +**Parameters:** + +- `path` (string): The path to the file. +- `encoding` (BufferEncoding, optional): The text encoding to use (default: 'utf-8'). + +**Returns:** + +- `{ data: string | null; error: Error | null }`: Object containing: + - `data`: The file content as a string, or null if reading failed. + - `error`: An error object if reading failed, or null if successful. + +**Example:** + +```ts +import { safeReadFileSync } from '@catbee/utils/fs'; + +const { data, error } = safeReadFileSync('./config.txt'); +if (error) { + console.error('Error reading file:', error.message); +} else { + console.log('File content:', data); } ``` --- -### `writeTextFile` +### `writeFile` Writes text content to a file. **Method Signature:** ```ts -async function writeTextFile(path: string, content: string, encoding?: BufferEncoding): Promise; +async function writeFile(path: string, content: string, encoding: BufferEncoding = 'utf-8'): Promise; ``` **Parameters:** - `path` (string): The path to the file where text should be written. - `content` (string): The text content to write to the file. -- `encoding` (BufferEncoding, optional): The text encoding to use. Defaults to 'utf-8'. +- `encoding` (BufferEncoding, optional): The text encoding to use (default: 'utf-8'). **Returns:** @@ -213,9 +358,9 @@ async function writeTextFile(path: string, content: string, encoding?: BufferEnc **Example:** ```ts -import { writeTextFile } from '@catbee/utils'; +import { writeFile } from '@catbee/utils/fs'; -const success = await writeTextFile('./log.txt', 'Application started', 'utf-8'); +const success = await writeFile('./log.txt', 'Application started'); if (success) { console.log('Log entry written'); } @@ -223,21 +368,21 @@ if (success) { --- -### `appendTextFile` +### `appendFile` Appends text content to a file. **Method Signature:** ```ts -async function appendTextFile(path: string, content: string, encoding?: BufferEncoding): Promise; +async function appendFile(path: string, content: string, encoding: BufferEncoding = 'utf-8'): Promise; ``` **Parameters:** - `path` (string): The path to the file where text should be appended. - `content` (string): The text content to append to the file. -- `encoding` (BufferEncoding, optional): The text encoding to use. Defaults to 'utf-8'. +- `encoding` (BufferEncoding, optional): The text encoding to use (default: 'utf-8'). **Returns:** @@ -246,9 +391,9 @@ async function appendTextFile(path: string, content: string, encoding?: BufferEn **Example:** ```ts -import { appendTextFile } from '@catbee/utils'; +import { appendFile } from '@catbee/utils/fs'; -const success = await appendTextFile('./log.txt', '\nNew log entry at ' + new Date().toISOString()); +const success = await appendFile('./log.txt', '\nNew log entry at ' + new Date().toISOString()); if (success) { console.log('Log entry appended'); } @@ -263,14 +408,14 @@ Copies a file from source to destination. **Method Signature:** ```ts -async function copyFile(source: string, destination: string, overwrite?: boolean): Promise; +async function copyFile(source: string, destination: string, overwrite = false): Promise; ``` **Parameters:** - `source` (string): The path to the source file. - `destination` (string): The path to the destination file. -- `overwrite` (boolean, optional): Whether to overwrite the destination file if it exists. Defaults to false. +- `overwrite` (boolean, optional): Whether to overwrite the destination file if it exists (default: false). **Returns:** @@ -279,7 +424,7 @@ async function copyFile(source: string, destination: string, overwrite?: boolean **Example:** ```ts -import { copyFile } from '@catbee/utils'; +import { copyFile } from '@catbee/utils/fs'; const copied = await copyFile('./original.txt', './backup.txt', true); console.log(copied ? 'File copied successfully' : 'Copy failed'); @@ -309,7 +454,7 @@ async function moveFile(oldPath: string, newPath: string): Promise; **Example:** ```ts -import { moveFile } from '@catbee/utils'; +import { moveFile } from '@catbee/utils/fs'; const moved = await moveFile('./temp.txt', './processed/temp.txt'); console.log(moved ? 'File moved successfully' : 'Move failed'); @@ -339,7 +484,7 @@ async function getFileStats(path: string): Promise; **Example:** ```ts -import { getFileStats } from '@catbee/utils'; +import { getFileStats } from '@catbee/utils/fs'; const stats = await getFileStats('./document.pdf'); if (stats) { @@ -358,9 +503,9 @@ Creates a temporary file with optional content. ```ts interface TempFileOptions { - prefix?: string; - extension?: string; - dir?: string; + prefix?: string; // default: 'tmp-' + extension?: string; // default: '' + dir?: string; // default: os.tmpdir() content?: string | Buffer; } @@ -370,9 +515,9 @@ async function createTempFile(options?: TempFileOptions): Promise; **Parameters:** - `options` (TempFileOptions, optional): Configuration options for the temporary file. - - `prefix` (string, optional): Prefix for the temp file name. - - `extension` (string, optional): File extension (e.g., '.txt'). - - `dir` (string, optional): Directory to create the temp file in. Defaults to the system temp directory. + - `prefix` (string, optional): Prefix for the temp file name (default: 'tmp-'). + - `extension` (string, optional): File extension (e.g., '.txt') (default: ''). + - `dir` (string, optional): Directory to create the temp file in (default: system temp directory). - `content` (string | Buffer, optional): Initial content to write to the temp file. **Returns:** @@ -382,7 +527,7 @@ async function createTempFile(options?: TempFileOptions): Promise; **Example:** ```ts -import { createTempFile } from '@catbee/utils'; +import { createTempFile } from '@catbee/utils/fs'; const tempPath = await createTempFile({ prefix: 'upload-', @@ -416,7 +561,7 @@ async function streamFile(source: string, destination: string): Promise; **Example:** ```ts -import { streamFile } from '@catbee/utils'; +import { streamFile } from '@catbee/utils/fs'; try { await streamFile('./largefile.mp4', './backup/largefile.mp4'); @@ -454,10 +599,14 @@ async function readDirectory(dirPath: string, options?: ReadDirectoryOptions): P - `Promise`: Resolves to an array of file names or paths in the directory. +**Throws:** + +- An error if the directory cannot be read. + **Example:** ```ts -import { readDirectory } from '@catbee/utils'; +import { readDirectory } from '@catbee/utils/fs'; // Get all JavaScript files with full paths const jsFiles = await readDirectory('./src', { @@ -476,13 +625,13 @@ Creates a directory if it doesn't exist. **Method Signature:** ```ts -async function createDirectory(dirPath: string, recursive?: boolean): Promise; +async function createDirectory(dirPath: string, recursive = true): Promise; ``` **Parameters:** - `dirPath` (string): The path of the directory to create. -- `recursive` (boolean, optional): If true, creates parent directories as needed. Defaults to false. +- `recursive` (boolean, optional): If true, creates parent directories as needed (default: true). **Returns:** @@ -491,7 +640,7 @@ async function createDirectory(dirPath: string, recursive?: boolean): Promise { - data: T | null; - error: Error | null; -} - -async function safeReadJsonFile(path: string): Promise>; +async function safeReadJsonFile(path: string): Promise<{ data: T | null; error: Error | null }>; ``` **Parameters:** @@ -522,14 +666,14 @@ async function safeReadJsonFile(path: string): Promise>`: Resolves to an object containing: +- `Promise<{ data: T | null; error: Error | null }>`: Resolves to an object containing: - `data` (T | null): The parsed JSON object, or null if reading/parsing failed. - `error` (Error | null): An error object if an error occurred, or null if successful. **Example:** ```ts -import { safeReadJsonFile } from '@catbee/utils'; +import { safeReadJsonFile } from '@catbee/utils/fs'; const { data, error } = await safeReadJsonFile('./config.json'); if (error) { @@ -562,7 +706,7 @@ async function isFile(path: string): Promise; **Example:** ```ts -import { isFile } from '@catbee/utils'; +import { isFile } from '@catbee/utils/fs'; if (await isFile('./path/to/item')) { console.log('This is a file'); @@ -594,7 +738,7 @@ async function getFileSize(path: string): Promise; **Example:** ```ts -import { getFileSize } from '@catbee/utils'; +import { getFileSize } from '@catbee/utils/fs'; const size = await getFileSize('./document.pdf'); if (size >= 0) { @@ -627,7 +771,7 @@ async function readFileBuffer(path: string): Promise; **Example:** ```ts -import { readFileBuffer } from '@catbee/utils'; +import { readFileBuffer } from '@catbee/utils/fs'; const buffer = await readFileBuffer('./image.png'); if (buffer) { diff --git a/docs/@catbee/utils/modules/http-status-codes.md b/docs/@catbee/utils/modules/http-status-codes.md index e221835..5cec323 100644 --- a/docs/@catbee/utils/modules/http-status-codes.md +++ b/docs/@catbee/utils/modules/http-status-codes.md @@ -1,3 +1,7 @@ +--- +slug: ../http-status-codes +--- + # HTTP Status Codes Typed enum for HTTP status codes, providing standardized numeric status codes for HTTP responses. This utility helps maintain consistency in API responses and simplifies error handling with proper typing support. @@ -15,11 +19,14 @@ Typed enum for HTTP status codes, providing standardized numeric status codes fo `HttpStatusCodes` is a TypeScript enum that provides strongly-typed HTTP status codes with comprehensive descriptions. Status codes are organized into five categories: -- [**1xx**: Informational responses](#1xx---informational) -- [**2xx**: Successful responses](#2xx---success) -- [**3xx**: Redirection messages](#3xx---redirection) -- [**4xx**: Client error responses](#4xx---client-error) -- [**5xx**: Server error responses](#5xx---server-error) +- [Type Definition](#type-definition) +- [Example Usage](#example-usage) +- [Status Code Categories](#status-code-categories) + - [`1xx - Informational`](#1xx---informational) + - [`2xx - Success`](#2xx---success) + - [`3xx - Redirection`](#3xx---redirection) + - [`4xx - Client Error`](#4xx---client-error) + - [`5xx - Server Error`](#5xx---server-error) ## Type Definition @@ -40,7 +47,7 @@ export enum HttpStatusCodes { ## Example Usage ```ts -import { HttpStatusCodes } from '@catbee/utils'; +import { HttpStatusCodes } from '@catbee/utils/http-status-codes'; // Sending responses with appropriate status codes res.status(HttpStatusCodes.BAD_REQUEST).send('Invalid payload'); @@ -55,7 +62,7 @@ res.status(HttpStatusCodes.BAD_REQUEST).send('Invalid payload'); These status codes indicate that the request was received and understood. ```ts -import { HttpStatusCodes } from '@catbee/utils'; +import { HttpStatusCodes } from '@catbee/utils/http-status-codes'; // Example: Indicate request was received but processing continues res.status(HttpStatusCodes.PROCESSING).send(); @@ -74,7 +81,7 @@ res.status(HttpStatusCodes.PROCESSING).send(); These status codes indicate that the client's request was successfully received, understood, and accepted. ```ts -import { HttpStatusCodes } from '@catbee/utils'; +import { HttpStatusCodes } from '@catbee/utils/http-status-codes'; // Example: Resource created successfully res.status(HttpStatusCodes.CREATED).json({ id: 123, name: 'New Resource' }); @@ -106,7 +113,7 @@ async function processRequest() { These status codes indicate that further action needs to be taken by the client to complete the request. ```ts -import { HttpStatusCodes } from '@catbee/utils'; +import { HttpStatusCodes } from '@catbee/utils/http-status-codes'; // Example: Permanent redirect res.status(HttpStatusCodes.MOVED_PERMANENTLY) @@ -134,7 +141,7 @@ res.status(HttpStatusCodes.TEMPORARY_REDIRECT) These status codes indicate that the client seems to have made an error. ```ts -import { HttpStatusCodes } from '@catbee/utils'; +import { HttpStatusCodes } from '@catbee/utils/http-status-codes'; // Example: Authentication required function requireAuth(req, res, next) { @@ -173,7 +180,7 @@ function rateLimit(req, res, next) { These status codes indicate that the server failed to fulfill a valid request. ```ts -import { HttpStatusCodes } from '@catbee/utils'; +import { HttpStatusCodes } from '@catbee/utils/http-status-codes'; // Example: Handling server errors try { diff --git a/docs/@catbee/utils/modules/id.md b/docs/@catbee/utils/modules/id.md index a316127..f4ba53d 100644 --- a/docs/@catbee/utils/modules/id.md +++ b/docs/@catbee/utils/modules/id.md @@ -1,4 +1,8 @@ -# ID Utilities +--- +slug: ../id +--- + +# ID A collection of utility functions for generating unique identifiers and random values using cryptographically strong methods. These utilities help you create UUIDs, compact IDs, random strings, and numbers for various application needs. @@ -31,7 +35,7 @@ function uuid(): string; **Example:** ```ts -import { uuid } from '@catbee/utils'; +import { uuid } from '@catbee/utils/id'; const id = uuid(); // Result: "c0de1234-5678-9abc-def0-123456789abc" @@ -46,7 +50,7 @@ Generates a nanoid-style random ID that is URL-safe and has customizable length. **Method Signature:** ```ts -function nanoId(length?: number): string; +function nanoId(length = 21): string; ``` **Parameters:** @@ -60,7 +64,7 @@ function nanoId(length?: number): string; **Example:** ```ts -import { nanoId } from '@catbee/utils'; +import { nanoId } from '@catbee/utils/id'; // Default length (21) const id1 = nanoId(); @@ -80,7 +84,7 @@ Generates a cryptographically strong random hexadecimal string. **Method Signature:** ```ts -function randomHex(byteLength?: number): string; +function randomHex(byteLength = 16): string; ``` **Parameters:** @@ -94,7 +98,7 @@ function randomHex(byteLength?: number): string; **Example:** ```ts -import { randomHex } from '@catbee/utils'; +import { randomHex } from '@catbee/utils/id'; // Default length (16 bytes = 32 chars) const hex = randomHex(); @@ -129,7 +133,7 @@ function randomInt(min: number, max: number): number; **Example:** ```ts -import { randomInt } from '@catbee/utils'; +import { randomInt } from '@catbee/utils/id'; // Random number between 1 and 100 const roll = randomInt(1, 100); @@ -148,7 +152,7 @@ Generates a cryptographically strong random base64 string (URL-safe, no padding) **Method Signature:** ```ts -function randomBase64(byteLength?: number): string; +function randomBase64(byteLength = 16): string; ``` **Parameters:** @@ -162,7 +166,7 @@ function randomBase64(byteLength?: number): string; **Example:** ```ts -import { randomBase64 } from '@catbee/utils'; +import { randomBase64 } from '@catbee/utils/id'; // Default length (16 bytes) const token = randomBase64(); diff --git a/docs/@catbee/utils/modules/logger.md b/docs/@catbee/utils/modules/logger.md index e9e7a53..72a556b 100644 --- a/docs/@catbee/utils/modules/logger.md +++ b/docs/@catbee/utils/modules/logger.md @@ -1,29 +1,34 @@ -# Logger Utilities +--- +slug: ../logger +--- + +# Logger Structured logging with Pino. This module provides a robust logging system with features like hierarchical loggers, request-scoped logging, error handling, and sensitive data redaction. ## API Summary -- [**`getLogger(newInstance?: boolean): Logger`**](#getlogger) - Retrieves the current logger instance from the request context or falls back to the global logger. +- [**`getLogger(newInstance = false): Logger`**](#getlogger) - Retrieves the current logger instance from the request context or falls back to the global logger. - [**`createChildLogger(bindings: Record, parentLogger?: Logger): Logger`**](#createchildlogger) - Creates a child logger with additional context that will be included in all log entries. -- [**`createRequestLogger(requestId: string, additionalContext?: Record): Logger`**](#createrequestlogger) - Creates a request-scoped logger with request ID and stores it in the async context. -- [**`logError(error: Error | unknown, message?: string, context?: Record): void`**](#logerror) - Utility to safely log errors with proper stack trace extraction +- [**`createRequestLogger(requestId: string, additionalContext = {}): Logger`**](#createrequestlogger) - Creates a request-scoped logger with request ID and stores it in the async context. +- [**`logError(error: Error | string, message?: string, context?: Record): void`**](#logerror) - Utility to safely log errors with proper stack trace extraction - [**`resetLogger(): void`**](#resetlogger) - Resets the global logger to its default configuration. - [**`getRedactCensor(): (key: string) => boolean`**](#getredactcensor) - Gets the current global redaction censor function used for log redaction. - [**`setRedactCensor(censor: (key: string) => boolean): void`**](#setredactcensor) - Sets the global redaction censor function used throughout the application for log redaction. -- [**`addRedactFields(fields: string | string[]): void`**](#addredactfields) - Extends the current redaction function with additional fields to redact. +- [**`addRedactFields(fields: string[]): void`**](#addredactfields) - **Deprecated:** Use `addSensitiveFields` instead. Extends the current redaction function with additional fields to redact. - [**`setSensitiveFields(fields: string[]): void`**](#setsensitivefields) - Replaces the default list of sensitive fields with a new list. -- [**`addSensitiveFields(fields: string | string[]): void`**](#addsensitivefields) - Adds additional field names to the default sensitive fields list. +- [**`addSensitiveFields(fields: string[]): void`**](#addsensitivefields) - Adds additional field names to the default sensitive fields list. ## Environment Variables -| Environment Variable | Default Value | Description | -| --------------------------- | ------------------------------ | ---------------------------------------- | -| `LOGGER_LEVEL` | `info` | Log level for the application | -| `LOGGER_NAME` | `process.env.npm_package_name` | Name of the logger | -| `LOGGER_PRETTY` | `true` | Enable pretty logging | -| `LOGGER_PRETTY_COLORIZE` | `true` | Enable colorized output for pretty-print | -| `LOGGER_PRETTY_SINGLE_LINE` | `false` | Enable single-line logging | +| Environment Variable | Type | Default/Value | Description | +| --------------------------- | --------- | ------------------------------------- | ------------------------------------------------------------------ | +| `LOGGER_LEVEL` | `string` | `debug` in dev/test, otherwise `info` | Logging level (`fatal`, `error`, `warn`, `info`, `debug`, `trace`) | +| `LOGGER_NAME` | `string` | `@catbee/utils` | Logger instance name | +| `LOGGER_PRETTY` | `boolean` | `false` | Enable pretty-print logging | +| `LOGGER_PRETTY_COLORIZE` | `boolean` | `true` | Enable colorized output for pretty-print | +| `LOGGER_PRETTY_SINGLE_LINE` | `boolean` | `false` | Single-line output for pretty-print | +| `LOGGER_DIR` | `string` | `''` (empty) | Directory to write log files (empty = disabled) | ## Interfaces & Types @@ -38,7 +43,7 @@ export type LoggerLevels = pino.Level; // 'fatal' | 'error' | 'warn' | 'info' | ## Example Usage ```ts -import { getLogger } from '@catbee/utils'; +import { getLogger } from '@catbee/utils/logger'; const logger = getLogger(); @@ -63,7 +68,7 @@ Retrieves the current logger instance from the request context or falls back to **Method Signature:** ```ts -function getLogger(newInstance?: boolean): Logger; +function getLogger(newInstance = false): Logger; ``` **Parameters:** @@ -77,7 +82,7 @@ function getLogger(newInstance?: boolean): Logger; **Example:** ```ts -import { getLogger } from '@catbee/utils'; +import { getLogger } from '@catbee/utils/logger'; // Get current logger (request-scoped if in request context, or global) const logger = getLogger(); @@ -113,7 +118,7 @@ function createChildLogger(bindings: Record, parentLogger?: Logger) **Example:** ```ts -import { getLogger, createChildLogger } from '@catbee/utils'; +import { getLogger, createChildLogger } from '@catbee/utils/logger'; // Create a module-specific logger const moduleLogger = createChildLogger({ module: 'payments' }); @@ -138,13 +143,13 @@ Creates a request-scoped logger with request ID and stores it in the async conte **Method Signature:** ```ts -function createRequestLogger(requestId: string, additionalContext?: Record): Logger; +function createRequestLogger(requestId: string, additionalContext: Record = {}): Logger; ``` **Parameters:** - `requestId`: Unique identifier for the request (e.g., UUID). -- `additionalContext` (optional): Additional key-value pairs to include in all log entries for this request. +- `additionalContext` (optional): Additional key-value pairs to include in all log entries for this request (default: {}). **Returns:** @@ -153,7 +158,7 @@ function createRequestLogger(requestId: string, additionalContext?: Record): void; +function logError(error: Error | string, message?: string, context?: Record): void; ``` **Parameters:** -- `error`: The error object or any value to log as an error. +- `error`: The error object or string to log as an error. - `message` (optional): Additional message to log alongside the error. - `context` (optional): Additional context to include in the log entry. **Example:** ```ts -import { logError } from '@catbee/utils'; +import { logError } from '@catbee/utils/logger'; try { throw new Error('Something went wrong'); @@ -200,7 +205,7 @@ try { logError(error, 'Failed during processing', { operation: 'dataSync' }); } -// Works with any error type +// Works with string errors too logError('Invalid input', 'Validation error'); ``` @@ -219,7 +224,7 @@ function resetLogger(): void; **Example:** ```ts -import { resetLogger } from '@catbee/utils'; +import { resetLogger } from '@catbee/utils/logger'; // After modifying logger configuration for tests afterEach(() => { @@ -248,7 +253,7 @@ function getRedactCensor(): (value: unknown, path: string[], sensitiveFields?: s **Example:** ```ts -import { getRedactCensor } from '@catbee/utils'; +import { getRedactCensor } from '@catbee/utils/logger'; const currentCensor = getRedactCensor(); // Use current censor in custom logic @@ -276,7 +281,7 @@ function setRedactCensor(fn: (value: unknown, path: string[], sensitiveFields?: **Example:** ```ts -import { setRedactCensor } from '@catbee/utils'; +import { setRedactCensor } from '@catbee/utils/logger'; // Custom censor that redacts only specific values setRedactCensor((value, path, sensitiveFields) => { @@ -290,6 +295,10 @@ setRedactCensor((value, path, sensitiveFields) => { ### `addRedactFields()` +:::warning Deprecated +This function is deprecated. Use [`addSensitiveFields()`](#addsensitivefields) instead. +::: + Extends the current redaction function with additional fields to redact. **Method Signature:** @@ -305,7 +314,7 @@ function addRedactFields(fields: string[]): void; **Example:** ```ts -import { addRedactFields } from '@catbee/utils'; +import { addRedactFields } from '@catbee/utils/logger'; // Add custom fields to be redacted in all logs addRedactFields(['customerId', 'accountNumber', 'ssn']); @@ -330,7 +339,7 @@ function setSensitiveFields(fields: string[]): void; **Example:** ```ts -import { setSensitiveFields } from '@catbee/utils'; +import { setSensitiveFields } from '@catbee/utils/logger'; // Replace default sensitive fields with a custom list setSensitiveFields(['password', 'ssn', 'creditCard']); @@ -355,7 +364,7 @@ function addSensitiveFields(fields: string[]): void; **Example:** ```ts -import { addSensitiveFields } from '@catbee/utils'; +import { addSensitiveFields } from '@catbee/utils/logger'; // Add domain-specific sensitive fields to the default list addSensitiveFields(['socialSecurityNumber', 'medicalRecordNumber']); diff --git a/docs/@catbee/utils/modules/middleware.md b/docs/@catbee/utils/modules/middleware.md index 3451568..3352736 100644 --- a/docs/@catbee/utils/modules/middleware.md +++ b/docs/@catbee/utils/modules/middleware.md @@ -1,4 +1,8 @@ -# Middleware Utilities +--- +slug: ../middleware +--- + +# Middleware Express middleware collection for handling common web server requirements including request identification, timing, error handling, timeouts, and request context management. @@ -6,9 +10,10 @@ Express middleware collection for handling common web server requirements includ - [**`requestId(options: { headerName?: string; exposeHeader?: boolean; generator?: () => string; })`**](#requestid) - Attaches a unique request ID to each request for tracing and correlation between logs. - [**`responseTime(options?: { addHeader?: boolean; logOnComplete?: boolean; })`**](#responsetime) - Measures request processing time and adds it to response headers or logs. -- [**`timeout(timeoutMs?: number)`**](#timeout) - Aborts requests that take too long to process. -- [**`setupRequestContext()`**](#setuprequestcontext) - Creates an Express middleware that initializes a per-request context. +- [**`timeout(timeoutMs = 30000)`**](#timeout) - Aborts requests that take too long to process. +- [**`setupRequestContext(options?: { headerName?: string; autoLog?: boolean })`**](#setuprequestcontext) - Creates an Express middleware that initializes a per-request context. - [**`errorHandler(options?: ErrorHandlerOptions)`**](#errorhandler) - Global error handling middleware with enhanced features. +- [**`healthCheck(options?: { path?: string; checks?: Array<{ name: string; check: () => Promise | boolean }>; detailed?: boolean })`**](#healthcheck) - Health check middleware for service status and custom checks. ### Interfaces and Types @@ -26,7 +31,7 @@ export interface ErrorHandlerOptions { ## Example Usage ```ts -import { requestId, responseTime, errorHandler } from '@catbee/utils'; +import { requestId, responseTime, errorHandler } from '@catbee/utils/middleware'; app.use(requestId({ headerName: 'X-Request-ID' })); // Sets X-Request-ID header and attaches req.id app.use(setupRequestContext({ autoLog: true, headerName: 'X-Request-ID' })); // Initializes request context with req.id and sets up logging @@ -47,7 +52,11 @@ Attaches a unique request ID to each request for tracing and correlation between **Method Signature:** ```ts -function requestId(options?: { headerName?: string; exposeHeader?: boolean; generator?: () => string }): Middleware; +function requestId(options?: { + headerName?: string; // default: 'X-Request-ID' + exposeHeader?: boolean; // default: true + generator?: () => string; // default: uuid +}): Middleware; ``` **Parameters:** @@ -65,7 +74,7 @@ function requestId(options?: { headerName?: string; exposeHeader?: boolean; gene ```ts import express from 'express'; -import { requestId } from '@catbee/utils'; +import { requestId } from '@catbee/utils/middleware'; const app = express(); // Basic usage with defaults @@ -90,7 +99,10 @@ Measures request processing time and adds it to response headers or logs. **Method Signature:** ```ts -function responseTime(options?: { addHeader?: boolean; logOnComplete?: boolean }): Middleware; +function responseTime(options?: { + addHeader?: boolean; // default: true + logOnComplete?: boolean; // default: false +}): Middleware; ``` **Parameters:** @@ -107,7 +119,7 @@ function responseTime(options?: { addHeader?: boolean; logOnComplete?: boolean } ```ts import express from 'express'; -import { responseTime } from '@catbee/utils'; +import { responseTime } from '@catbee/utils/middleware'; const app = express(); // Basic usage @@ -131,7 +143,7 @@ Aborts requests that take too long to process. **Method Signature:** ```ts -function timeout(timeoutMs?: number): Middleware; +function timeout(timeoutMs = 30000): Middleware; ``` **Parameters:** @@ -146,7 +158,7 @@ function timeout(timeoutMs?: number): Middleware; ```ts import express from 'express'; -import { timeout } from '@catbee/utils'; +import { timeout } from '@catbee/utils/middleware'; const app = express(); // Default 30-second timeout @@ -165,7 +177,10 @@ Creates an Express middleware that initializes a per-request context. **Method Signature:** ```ts -function setupRequestContext(options?: { headerName?: string; autoLog?: boolean }): Middleware; +function setupRequestContext(options?: { + headerName?: string; // default: 'x-request-id' + autoLog?: boolean; // default: true +}): Middleware; ``` **Parameters:** @@ -182,7 +197,7 @@ function setupRequestContext(options?: { headerName?: string; autoLog?: boolean ```ts import express from 'express'; -import { setupRequestContext } from '@catbee/utils'; +import { setupRequestContext } from '@catbee/utils/middleware'; const app = express(); // Basic usage with defaults @@ -207,6 +222,11 @@ Global error handling middleware with enhanced features. ```ts function errorHandler(options?: ErrorHandlerOptions): Middleware; + +interface ErrorHandlerOptions { + logErrors?: boolean; // default: true + includeDetails?: boolean; // default: false +} ``` **Parameters:** @@ -223,7 +243,7 @@ function errorHandler(options?: ErrorHandlerOptions): Middleware; ```ts import express from 'express'; -import { errorHandler } from '@catbee/utils'; +import { errorHandler } from '@catbee/utils/middleware'; const app = express(); // Basic error handler @@ -237,3 +257,65 @@ app.use( }) ); ``` + +--- + +### `healthCheck()` + +Health check middleware for service status and custom checks. + +**Method Signature:** + +```ts +function healthCheck(options?: { + path?: string; // default: '/healthz' + checks?: Array<{ name: string; check: () => Promise | boolean }>; + detailed?: boolean; // default: true +}): Middleware; +``` + +**Parameters:** + +- `options?: object` - Health check options + - `path?: string` - Health check endpoint path (default: '/healthz') + - `checks?: Array<{name: string; check: () => Promise | boolean}>` - Custom health checks to run + - `detailed?: boolean` - Show detailed check results (default: true) + +**Returns:** + +- `Middleware` - Express middleware function + +**Example:** + +```ts +import express from 'express'; +import { healthCheck } from '@catbee/utils/middleware'; +const app = express(); + +// Basic health check +app.use(healthCheck()); + +// Health check with custom checks +app.use( + healthCheck({ + path: '/health', + detailed: true, + checks: [ + { + name: 'database', + check: async () => { + // Check database connection + return db.isConnected(); + } + }, + { + name: 'redis', + check: async () => { + // Check Redis connection + return redis.ping(); + } + } + ] + }) +); +``` diff --git a/docs/@catbee/utils/modules/modules.md b/docs/@catbee/utils/modules/modules.md index 5ce7f07..9f7541a 100644 --- a/docs/@catbee/utils/modules/modules.md +++ b/docs/@catbee/utils/modules/modules.md @@ -4,30 +4,30 @@ A comprehensive set of utility functions for Node.js and TypeScript projects. In ## Modules -- [Array Utilities](array) – Array manipulation and transformation functions -- [Async Utilities](async) – Promise helpers, concurrency, and timing -- [Cache Utilities](cache) – In-memory caching with TTL support -- [Context Store](context-store) – Per-request context using AsyncLocalStorage -- [Crypto Utilities](crypto) – Hashing, encryption, and token functions -- [Date Utilities](date) – Date and time manipulation functions -- [Decorators Utilities](decorators) – TypeScript decorators for Express -- [Directory Utilities](directory) – Directory and file system helpers -- [Environment Utilities](environment) – Environment variable management -- [Exception Utilities](exception) – HTTP and general error handling -- [File System Utilities](file-system) – File operations and utilities -- [HTTP Status Codes](http-status-codes) – Typed HTTP status codes -- [ID Utilities](id) – UUID and ID generation functions -- [Logger Utilities](logger) – Structured logging with Pino -- [Middleware Utilities](middleware) – Express middleware collection -- [Object Utilities](object) – Object manipulation and deep merge -- [Performance Utilities](performance) – Timing, memoization, and memory tracking -- [Request Utilities](request) – HTTP request parameter parsing and validation -- [Response Utilities](response) – Standardized API response formatting -- [Stream Utilities](stream) – Stream conversion, batching, throttling, and line splitting -- [String Utilities](string) – String formatting, casing, masking, and transformation -- [Type Utilities](type) – Type checking, conversion, and type guards -- [URL Utilities](url) – URL parsing, query manipulation, and normalization -- [Validate Utilities](validation) – Input validation functions +- [Array](../array) – Array manipulation and transformation functions +- [Async](../async) – Promise helpers, concurrency, and timing +- [Cache](../cache) – In-memory caching with TTL support +- [Context Store](../context-store) – Per-request context using AsyncLocalStorage +- [Crypto](../crypto) – Hashing, encryption, and token functions +- [Date](../date) – Date and time manipulation functions +- [Decorator](../decorator) – TypeScript decorators for Express +- [Directory](../directory) – Directory and file system helpers +- [Environment](../env) – Environment variable management +- [Exception](../exception) – HTTP and general error handling +- [File System](../fs) – File operations and utilities +- [HTTP Status Codes](../http-status-codes) – Typed HTTP status codes +- [ID](../id) – UUID and ID generation functions +- [Logger](../logger) – Structured logging with Pino +- [Middleware](../middleware) – Express middleware collection +- [Object](../object) – Object manipulation and deep merge +- [Performance](../performance) – Timing, memoization, and memory tracking +- [Request](../request) – HTTP request parameter parsing and validation +- [Response](../response) – Standardized API response formatting +- [Stream](../stream) – Stream conversion, batching, throttling, and line splitting +- [String](../string) – String formatting, casing, masking, and transformation +- [Type](../type) – Type checking, conversion, and type guards +- [URL](../url) – URL parsing, query manipulation, and normalization +- [Validation](../validation) – Input validation functions --- diff --git a/docs/@catbee/utils/modules/object.md b/docs/@catbee/utils/modules/object.md index 8ccba53..97e669d 100644 --- a/docs/@catbee/utils/modules/object.md +++ b/docs/@catbee/utils/modules/object.md @@ -1,4 +1,8 @@ -# Object Utilities +--- +slug: ../object +--- + +# Object Helpers for manipulating objects. Includes methods for picking/omitting keys, deep merging, flattening, path-based access, deep equality, filtering, mapping, freezing, and more. All methods are fully typed. @@ -9,15 +13,19 @@ Helpers for manipulating objects. Includes methods for picking/omitting keys, de - [**`omit(obj, keys)`**](#omit) - omits specific keys from an object. - [**`deepObjMerge(target, ...sources)`**](#deepobjmerge) - deeply merges multiple objects into the target object. - [**`isPlainObject(value)`**](#isplainobject) - checks if a value is a plain object. -- [**`flattenObject(obj, prefix?)`**](#flattenobject) - flattens a nested object. +- [**`flattenObject(obj, prefix = '')`**](#flattenobject) - flattens a nested object. - [**`getValueByPath(obj, path)`**](#getvaluebypath) - gets a value by a dot/bracket notation path. - [**`setValueByPath(obj, path, value)`**](#setvaluebypath) - sets a value by a dot/bracket notation path. - [**`isEqual(a, b)`**](#isequal) - performs deep equality check between two values. - [**`filterObject(obj, predicate)`**](#filterobject) - filters object properties based on a predicate function. - [**`mapObject(obj, mapFn)`**](#mapobject) - maps object values using a mapping function. +- [**`deepClone(value)`**](#deepclone) - deeply clones any value. - [**`deepFreeze(obj)`**](#deepfreeze) - deeply freezes an object. - [**`isObject(value)`**](#isobject) - checks if a value is an object. -- [**`getAllPaths(obj, parentPath?)`**](#getallpaths) - gets all paths in an object using dot notation. +- [**`getAllPaths(obj, parentPath = '')`**](#getallpaths) - gets all paths in an object using dot notation. +- [**`invert(obj)`**](#invert) - inverts an object's keys and values. +- [**`invertBy(obj, keyFn)`**](#invertby) - inverts an object using a function to generate keys. +- [**`transform(obj, fn)`**](#transform) - transforms object values using a mapping function. --- @@ -44,7 +52,7 @@ function isObjEmpty(obj: Record): boolean; **Examples:** ```ts -import { isObjEmpty } from '@catbee/utils'; +import { isObjEmpty } from '@catbee/utils/object'; isObjEmpty({}); // true isObjEmpty({ a: 1 }); // false @@ -59,7 +67,7 @@ Returns a new object with only the specified keys. **Method Signature:** ```ts -function pick(obj: T, keys: K[]): Pick; +function pick(obj: T, keys: K[]): Pick; ``` **Parameters:** @@ -74,7 +82,7 @@ function pick(obj: T, keys: K[]): Pick; **Examples:** ```ts -import { pick } from '@catbee/utils'; +import { pick } from '@catbee/utils/object'; pick({ a: 1, b: 2 }, ['a']); // { a: 1 } ``` @@ -88,7 +96,7 @@ Returns a new object with the specified keys omitted. **Method Signature:** ```ts -function omit(obj: T, keys: K[]): Omit; +function omit(obj: T, keys: K[]): Omit; ``` **Parameters:** @@ -103,7 +111,7 @@ function omit(obj: T, keys: K[]): Omit; **Examples:** ```ts -import { omit } from '@catbee/utils'; +import { omit } from '@catbee/utils/object'; omit({ a: 1, b: 2 }, ['a']); // { b: 2 } ``` @@ -132,7 +140,7 @@ function deepObjMerge(target: T, ...sources: any[]): T; **Examples:** ```ts -import { deepObjMerge } from '@catbee/utils'; +import { deepObjMerge } from '@catbee/utils/object'; const a = { x: 1, y: { z: 2 } }; const b = { y: { w: 3 } }; @@ -162,7 +170,7 @@ function isPlainObject(value: any): value is Record; **Examples:** ```ts -import { isPlainObject } from '@catbee/utils'; +import { isPlainObject } from '@catbee/utils/object'; isPlainObject({}); // true isPlainObject([]); // false @@ -178,13 +186,13 @@ Flattens a nested object using dot notation for keys. **Method Signature:** ```ts -function flattenObject(obj: T, prefix?: string): Record; +function flattenObject>(obj: T, prefix = ''): Record; ``` **Parameters:** - `obj`: The object to flatten. -- `prefix` (optional): A prefix to prepend to all keys. +- `prefix` (optional): A prefix to prepend to all keys (default: ''). **Returns:** @@ -193,7 +201,7 @@ function flattenObject(obj: T, prefix?: string): Record; **Examples:** ```ts -import { flattenObject } from '@catbee/utils'; +import { flattenObject } from '@catbee/utils/object'; flattenObject({ a: { b: 1 } }); // { 'a.b': 1 } ``` @@ -207,7 +215,7 @@ Safely gets the value of a deeply nested key using dot/bracket notation. **Method Signature:** ```ts -function getValueByPath(obj: T, path: string): any; +function getValueByPath(obj: T, path: string): any; ``` **Parameters:** @@ -222,7 +230,7 @@ function getValueByPath(obj: T, path: string): any; **Examples:** ```ts -import { getValueByPath } from '@catbee/utils'; +import { getValueByPath } from '@catbee/utils/object'; getValueByPath({ a: { b: [{ c: 5 }] } }, 'a.b[0].c'); // 5 ``` @@ -231,12 +239,12 @@ getValueByPath({ a: { b: [{ c: 5 }] } }, 'a.b[0].c'); // 5 ### `setValueByPath()` -Sets a value at a deeply nested key using dot/bracket notation. +Sets a value at a deeply nested key using dot/bracket notation. Returns a new object with the value set (does not mutate the original). **Method Signature:** ```ts -function setValueByPath(obj: T, path: string, value: any): T; +function setValueByPath(obj: T, path: string, value: any): T; ``` **Parameters:** @@ -247,15 +255,17 @@ function setValueByPath(obj: T, path: string, value: any): T; **Returns:** -- The mutated target object. +- The new object with the value set (original object is not mutated). **Examples:** ```ts -import { setValueByPath } from '@catbee/utils'; +import { setValueByPath } from '@catbee/utils/object'; const obj = { a: {} }; -setValueByPath(obj, 'a.b.c', 10); // obj is now { a: { b: { c: 10 } } } +const result = setValueByPath(obj, 'a.b.c', 10); +// result is { a: { b: { c: 10 } } } +// obj remains { a: {} } (not mutated) ``` --- @@ -282,7 +292,7 @@ function isEqual(a: any, b: any): boolean; **Examples:** ```ts -import { isEqual } from '@catbee/utils'; +import { isEqual } from '@catbee/utils/object'; isEqual({ x: [1, 2] }, { x: [1, 2] }); // true isEqual({ x: 1 }, { x: 2 }); // false @@ -297,13 +307,13 @@ Filters object properties based on a predicate function. **Method Signature:** ```ts -function filterObject(obj: T, predicate: (value: any, key: string, obj: T) => boolean): Partial; +function filterObject(obj: T, predicate: (value: any, key: string, obj: T) => boolean): Partial; ``` **Parameters:** - `obj`: The source object. -- `predicate`: A function that returns `true` to keep the property, or `false to omit it. +- `predicate`: A function that returns `true` to keep the property, or `false` to omit it. - `value`: The current property value. - `key`: The current property key. - `obj`: The original object. @@ -315,7 +325,7 @@ function filterObject(obj: T, predicate: (value: any, key: string, obj: T) => **Examples:** ```ts -import { filterObject } from '@catbee/utils'; +import { filterObject } from '@catbee/utils/object'; filterObject({ a: 1, b: 2 }, v => v > 1); // { b: 2 } ``` @@ -329,7 +339,7 @@ Maps object values to new values using a mapping function. **Method Signature:** ```ts -function mapObject(obj: T, mapFn: (value: any, key: string, obj: T) => U): Record; +function mapObject(obj: T, mapFn: (value: any, key: string, obj: T) => U): Record; ``` **Parameters:** @@ -347,13 +357,44 @@ function mapObject(obj: T, mapFn: (value: any, key: string, obj: T) => U): **Examples:** ```ts -import { mapObject } from '@catbee/utils'; +import { mapObject } from '@catbee/utils/object'; mapObject({ a: 1, b: 2 }, v => v * 2); // { a: 2, b: 4 } ``` --- +### `deepClone()` + +Deeply clones any value, including objects, arrays, and built-in types. + +**Method Signature:** + +```ts +function deepClone(value: T): T; +``` + +**Parameters:** + +- `value`: The value to deeply clone. + +**Returns:** + +- A fully deep-cloned copy of the input. + +**Examples:** + +```ts +import { deepClone } from '@catbee/utils/object'; + +const original = { a: { b: [1, 2, 3] }, date: new Date() }; +const cloned = deepClone(original); +cloned.a.b.push(4); +// original.a.b is still [1, 2, 3] +``` + +--- + ### `deepFreeze()` Recursively freezes an object and all its properties. @@ -361,7 +402,7 @@ Recursively freezes an object and all its properties. **Method Signature:** ```ts -function deepFreeze(obj: T): Readonly; +function deepFreeze(obj: T): Readonly; ``` **Parameters:** @@ -375,7 +416,7 @@ function deepFreeze(obj: T): Readonly; **Examples:** ```ts -import { deepFreeze } from '@catbee/utils'; +import { deepFreeze } from '@catbee/utils/object'; const frozen = deepFreeze({ a: { b: 2 } }); // frozen.a.b = 3 // Throws error in strict mode @@ -404,7 +445,7 @@ function isObject(value: unknown): value is Record; **Examples:** ```ts -import { isObject } from '@catbee/utils'; +import { isObject } from '@catbee/utils/object'; isObject({}); // true isObject(null); // false @@ -420,13 +461,13 @@ Gets all paths in an object using dot notation. **Method Signature:** ```ts -function getAllPaths(obj: Record, parentPath?: string): string[]; +function getAllPaths(obj: Record, parentPath = ''): string[]; ``` **Parameters:** - `obj`: The object to extract paths from. -- `parentPath` (optional): A prefix to prepend to all paths. +- `parentPath` (optional): A prefix to prepend to all paths (default: ''). **Returns:** @@ -435,7 +476,100 @@ function getAllPaths(obj: Record, parentPath?: string): string[]; **Examples:** ```ts -import { getAllPaths } from '@catbee/utils'; +import { getAllPaths } from '@catbee/utils/object'; getAllPaths({ a: { b: 1 }, c: 2 }); // ['a', 'a.b', 'c'] ``` + +--- + +### `invert()` + +Inverts an object's keys and values. + +**Method Signature:** + +```ts +function invert>(obj: T): Record; +``` + +**Parameters:** + +- `obj`: The object to invert. + +**Returns:** + +- A new object with keys and values swapped. + +**Examples:** + +```ts +import { invert } from '@catbee/utils/object'; + +invert({ a: 'x', b: 'y' }); // { x: 'a', y: 'b' } +``` + +--- + +### `invertBy()` + +Inverts an object using a function to generate keys from values. + +**Method Signature:** + +```ts +function invertBy>( + obj: T, + keyFn: (value: any) => string +): Record; +``` + +**Parameters:** + +- `obj`: The object to invert. +- `keyFn`: A function that generates new keys from values. + +**Returns:** + +- An inverted object with arrays of keys grouped by the generated key. + +**Examples:** + +```ts +import { invertBy } from '@catbee/utils/object'; + +invertBy({ a: 1, b: 2, c: 1 }, v => String(v)); +// { '1': ['a', 'c'], '2': ['b'] } +``` + +--- + +### `transform()` + +Transforms object values using a mapping function. + +**Method Signature:** + +```ts +function transform, U>( + obj: T, + fn: (value: any, key: string) => U +): Record; +``` + +**Parameters:** + +- `obj`: The source object. +- `fn`: A transformation function that takes a value and key, returning the transformed value. + +**Returns:** + +- A new object with transformed values. + +**Examples:** + +```ts +import { transform } from '@catbee/utils/object'; + +transform({ a: 1, b: 2 }, v => v * 2); // { a: 2, b: 4 } +``` diff --git a/docs/@catbee/utils/modules/performance.md b/docs/@catbee/utils/modules/performance.md index 71a8ee3..4b235b6 100644 --- a/docs/@catbee/utils/modules/performance.md +++ b/docs/@catbee/utils/modules/performance.md @@ -1,4 +1,8 @@ -# Performance Utilities +--- +slug: ../performance +--- + +# Performance A collection of utilities for measuring, tracking, and optimizing performance in Node.js applications. Includes timing functions for synchronous and asynchronous code, memoization with TTL and cache size, decorators for timing, and memory usage tracking. All methods are fully typed. @@ -17,8 +21,8 @@ A collection of utilities for measuring, tracking, and optimizing performance in ```ts export interface TimingOptions { label?: string; - log?: boolean; - logLevel?: 'trace' | 'debug' | 'info' | 'warn' | 'error'; + log?: boolean; // default: false + logLevel?: 'trace' | 'debug' | 'info' | 'warn' | 'error'; // default: 'debug' } export interface TimingResult { @@ -34,10 +38,10 @@ export interface TimingResult { ```ts { - ttl?: number; - maxSize?: number; - cacheKey?: (...args: any[]) => string; - autoCleanupMs?: number; + ttl?: number; // default: undefined (no expiration) + maxSize?: number; // default: undefined (unlimited) + cacheKey?: (...args: any[]) => string; // default: JSON.stringify + autoCleanupMs?: number; // default: undefined (no auto cleanup) } ``` @@ -62,13 +66,13 @@ Measures the execution time of a synchronous function. **Method Signature:** ```ts -function timeSync(fn: () => T, options?: TimingOptions): { result: T; timing: TimingResult }; +function timeSync(fn: () => T, options: TimingOptions = {}): { result: T; timing: TimingResult }; ``` **Parameters:** - `fn`: The synchronous function to time. -- `options`: Optional timing options. +- `options`: Optional timing options (default: {}). - `label`: A label for the timing operation. - `log`: Whether to log the timing result. - `logLevel`: The log level to use if logging is enabled. @@ -80,7 +84,7 @@ function timeSync(fn: () => T, options?: TimingOptions): { result: T; timing: **Example:** ```ts -import { timeSync } from '@catbee/utils'; +import { timeSync } from '@catbee/utils/performance'; const { result, timing } = timeSync( () => { @@ -104,13 +108,13 @@ Measures the execution time of an asynchronous function. **Method Signature:** ```ts -function timeAsync(fn: () => Promise, options?: TimingOptions): Promise<{ result: T; timing: TimingResult }>; +function timeAsync(fn: () => Promise, options: TimingOptions = {}): Promise<{ result: T; timing: TimingResult }>; ``` **Parameters:** - `fn`: The asynchronous function to time. -- `options`: Optional timing options. +- `options`: Optional timing options (default: {}). - `label`: A label for the timing operation. - `log`: Whether to log the timing result. - `logLevel`: The log level to use if logging is enabled. @@ -122,7 +126,7 @@ function timeAsync(fn: () => Promise, options?: TimingOptions): Promise<{ **Example:** ```ts -import { timeAsync } from '@catbee/utils'; +import { timeAsync } from '@catbee/utils/performance'; const { result, timing } = await timeAsync( async () => { @@ -144,12 +148,12 @@ Method decorator for timing function execution. **Method Signature:** ```ts -function timed(options?: TimingOptions): MethodDecorator; +function timed(options: TimingOptions = {}): MethodDecorator; ``` **Parameters:** -- `options`: Optional timing options. +- `options`: Optional timing options (default: {}). - `label`: A label for the timing operation. - `log`: Whether to log the timing result. - `logLevel`: The log level to use if logging is enabled. @@ -161,7 +165,7 @@ function timed(options?: TimingOptions): MethodDecorator; **Example:** ```ts -import { timed } from '@catbee/utils'; +import { timed } from '@catbee/utils/performance'; class DataService { @timed({ log: true, logLevel: 'info' }) @@ -181,17 +185,25 @@ Caches function results with optional TTL and max cache size. **Method Signature:** ```ts -function memoize(fn: (...args: Args) => T, options?: { ttl?: number; maxSize?: number; cacheKey?: (...args: Args) => string; autoCleanupMs?: number }): (...args: Args) => T; +function memoize( + fn: (...args: Args) => T, + options: { + ttl?: number; // default: undefined (no expiration) + maxSize?: number; // default: undefined (unlimited) + cacheKey?: (...args: Args) => string; // default: JSON.stringify + autoCleanupMs?: number; // default: undefined (no auto cleanup) + } = {} +): (...args: Args) => T; ``` **Parameters:** - `fn`: The function to memoize. -- `options`: Optional memoization options. - - `ttl`: Time-to-live for cache entries in milliseconds. - - `maxSize`: Maximum number of entries in the cache. - - `cacheKey`: A function to generate cache keys from arguments. - - `autoCleanupMs`: Interval for automatic cache cleanup. +- `options`: Optional memoization options (default: {}). + - `ttl`: Time-to-live for cache entries in milliseconds (default: undefined). + - `maxSize`: Maximum number of entries in the cache (default: undefined). + - `cacheKey`: A function to generate cache keys from arguments (default: JSON.stringify). + - `autoCleanupMs`: Interval for automatic cache cleanup in milliseconds (default: undefined). **Returns:** @@ -200,7 +212,7 @@ function memoize(fn: (...args: Args) => T, options?: { tt **Example:** ```ts -import { memoize } from '@catbee/utils'; +import { memoize } from '@catbee/utils/performance'; const calculateFactorial = memoize( (n: number): number => n <= 1 ? 1 : n * calculateFactorial(n - 1), @@ -220,15 +232,21 @@ Tracks memory usage for a function execution. **Method Signature:** ```ts -function trackMemoryUsage(fn: () => T, options?: { log?: boolean; label?: string }): { result: T; memoryUsage: { before: NodeJS.MemoryUsage; after: NodeJS.MemoryUsage; diff: Record } }; +function trackMemoryUsage( + fn: () => T, + options: { + log?: boolean; // default: false + label?: string; // default: function name + } = {} +): { result: T; memoryUsage: { before: NodeJS.MemoryUsage; after: NodeJS.MemoryUsage; diff: Record } }; ``` **Parameters:** - `fn`: The function to track memory usage for. -- `options`: Optional tracking options. - - `log`: Whether to log the memory usage result. - - `label`: A label for the memory tracking operation. +- `options`: Optional tracking options (default: {}). + - `log`: Whether to log the memory usage result (default: false). + - `label`: A label for the memory tracking operation (default: function name). **Returns:** @@ -237,7 +255,7 @@ function trackMemoryUsage(fn: () => T, options?: { log?: boolean; label?: str **Example:** ```ts -import { trackMemoryUsage } from '@catbee/utils'; +import { trackMemoryUsage } from '@catbee/utils/performance'; const { result, memoryUsage } = trackMemoryUsage( () => { diff --git a/docs/@catbee/utils/modules/request.md b/docs/@catbee/utils/modules/request.md index 1985ca0..872bfe3 100644 --- a/docs/@catbee/utils/modules/request.md +++ b/docs/@catbee/utils/modules/request.md @@ -1,14 +1,19 @@ -# Request Utilities +--- +slug: ../request +--- + +# Request Helpers for parsing and validating HTTP request parameters. Includes utilities for parsing numbers and booleans, extracting pagination, sorting, and filter parameters from query objects. All methods are fully typed. ## API Summary -- [**`parseNumberParam(value: string | undefined, options?: ValidationOptions)`**](#parsenumberparam) - Safely parses a string parameter to a number. -- [**`parseBooleanParam(value: string | undefined, options?: ValidationOptions)`**](#parsebooleanparam) - Safely parses a string parameter to a boolean. -- [**`extractPaginationParams(query: Record, defaultPage?: number, defaultLimit?: number, maxLimitSize?: number)`**](#extractpaginationparams) - Extracts pagination parameters from query. -- [**`extractSortParams(query: Record, allowedFields: string[], defaultSort?: { sortBy: string; sortOrder: 'asc' | 'desc' })`**](#extractsortparams) - Extracts sorting parameters from query. -- [**`extractFilterParams(query: Record, allowedFilters: string[])`**](#extractfilterparams) - Extracts filter parameters from query. +- [**`getPaginationParams(req: Request): WithPagination`**](#getpaginationparams) - Extracts pagination, sorting, and search params from a request. +- [**`parseNumberParam(value: string | undefined, options = {})`**](#parsenumberparam) - Safely parses a string parameter to a number. +- [**`parseBooleanParam(value: string | undefined, options = {})`**](#parsebooleanparam) - Safely parses a string parameter to a boolean. +- [**`extractPaginationParams(query, defaultPage = 1, defaultLimit = 20, maxLimitSize = 100)`**](#extractpaginationparams) - Extracts pagination parameters from query. +- [**`extractSortParams(query, allowedFields, defaultSort?)`**](#extractsortparams) - Extracts sorting parameters from query. +- [**`extractFilterParams(query, allowedFilters)`**](#extractfilterparams) - Extracts filter parameters from query. --- @@ -27,12 +32,59 @@ export interface ValidationResult { value: T | null; error?: string; } + +export interface WithPagination { + page: number; + limit: number; + sortBy: string; + sortOrder: 'asc' | 'desc'; + search?: string; +} ``` --- ## Function Documentation & Usage Examples +### `getPaginationParams()` + +Extracts pagination, sorting, and optional search parameters from an Express request. + +**Method Signature:** + +```ts +function getPaginationParams(req: Request): WithPagination; +``` + +**Parameters:** + +- `req`: The Express request object. + +**Returns:** + +- An object containing: + - `page`: The validated page number (default: 1). + - `limit`: The validated limit number (default: 20, max: 100). + - `sortBy`: The field to sort by (default: 'createdAt'). + - `sortOrder`: The sort direction (default: 'desc'). + - `search`: Optional search string from query. + - Additional query parameters as type T. + +**Example:** + +```ts +import { getPaginationParams } from '@catbee/utils/request'; + +app.get('/api/users', (req, res) => { + const params = getPaginationParams(req); + // params = { page: 1, limit: 20, sortBy: 'createdAt', sortOrder: 'desc', search: '...' } + const users = await userService.findAll(params); + res.json(users); +}); +``` + +--- + ### `parseNumberParam()` Safely parses a string parameter to a number, with validation and error handling. @@ -40,13 +92,13 @@ Safely parses a string parameter to a number, with validation and error handling **Method Signature:** ```ts -function parseNumberParam(value: string | undefined, options?: ValidationOptions): ValidationResult; +function parseNumberParam(value: string | undefined, options: ValidationOptions = {}): ValidationResult; ``` **Parameters:** - `value`: The string value to parse. -- `options`: Optional validation options: +- `options`: Optional validation options (default: {}): - `throwOnError`: If true, throws an error on invalid input. - `errorMessage`: Custom error message for invalid input. - `defaultValue`: Default value to return if input is invalid or undefined. @@ -62,7 +114,7 @@ function parseNumberParam(value: string | undefined, options?: ValidationOptions **Examples:** ```ts -import { parseNumberParam } from '@catbee/utils'; +import { parseNumberParam } from '@catbee/utils/request'; const result = parseNumberParam(req.query.id, { required: true }); if (result.isValid) { @@ -79,13 +131,13 @@ Safely parses a string parameter to a boolean, supporting various formats. **Method Signature:** ```ts -function parseBooleanParam(value: string | undefined, options?: ValidationOptions): ValidationResult; +function parseBooleanParam(value: string | undefined, options: ValidationOptions = {}): ValidationResult; ``` **Parameters:** - `value`: The string value to parse. -- `options`: Optional validation options: +- `options`: Optional validation options (default: {}): - `throwOnError`: If true, throws an error on invalid input. - `errorMessage`: Custom error message for invalid input. - `defaultValue`: Default value to return if input is invalid or undefined. @@ -101,7 +153,7 @@ function parseBooleanParam(value: string | undefined, options?: ValidationOption **Examples:** ```ts -import { parseBooleanParam } from '@catbee/utils'; +import { parseBooleanParam } from '@catbee/utils/request'; const result = parseBooleanParam(req.query.active, { defaultValue: false }); if (result.isValid) { @@ -121,7 +173,7 @@ Extracts validated pagination parameters (`page`, `limit`) from query object. function extractPaginationParams( query: Record, defaultPage: number = 1, - defaultLimit: number = 10, + defaultLimit: number = 20, maxLimitSize: number = 100 ): { page: number; limit: number }; ``` @@ -129,9 +181,9 @@ function extractPaginationParams( **Parameters:** - `query`: The query object from which to extract parameters. -- `defaultPage`: Default page number if not provided (default is 1). -- `defaultLimit`: Default limit if not provided (default is 10). -- `maxLimitSize`: Maximum allowed limit size (default is 100). +- `defaultPage`: Default page number if not provided (default: 1). +- `defaultLimit`: Default limit if not provided (default: 20). +- `maxLimitSize`: Maximum allowed limit size (default: 100). **Returns:** @@ -142,7 +194,7 @@ function extractPaginationParams( **Example:** ```ts -import { extractPaginationParams } from '@catbee/utils'; +import { extractPaginationParams } from '@catbee/utils/request'; const { page, limit } = extractPaginationParams(req.query, 1, 20, 100); ``` @@ -156,10 +208,12 @@ Extracts sorting parameters (`sortBy`, `sortOrder`) from query object, validatin **Method Signature:** ```ts +const DEFAULT_SORT = { sortBy: 'createdAt', sortOrder: 'desc' }; + function extractSortParams( query: Record, allowedFields: string[], - defaultSort?: { sortBy: string; sortOrder: 'asc' | 'desc' } + defaultSort: { sortBy: string; sortOrder: 'asc' | 'desc' } = DEFAULT_SORT ): { sortBy: string; sortOrder: 'asc' | 'desc' }; ``` @@ -167,7 +221,7 @@ function extractSortParams( - `query`: The query object from which to extract parameters. - `allowedFields`: Array of allowed fields for sorting. -- `defaultSort`: Optional default sorting if parameters are not provided. +- `defaultSort`: Optional default sorting if parameters are not provided (default: `{ sortBy: 'createdAt', sortOrder: 'desc' }`). **Returns:** @@ -178,7 +232,7 @@ function extractSortParams( **Example:** ```ts -import { extractSortParams } from '@catbee/utils'; +import { extractSortParams } from '@catbee/utils/request'; const { sortBy, sortOrder } = extractSortParams(req.query, ['name', 'createdAt'], { sortBy: 'name', sortOrder: 'asc' }); ``` @@ -210,7 +264,7 @@ function extractFilterParams( **Example:** ```ts -import { extractFilterParams } from '@catbee/utils'; +import { extractFilterParams } from '@catbee/utils/request'; const filters = extractFilterParams(req.query, ['status', 'type']); ``` diff --git a/docs/@catbee/utils/modules/response.md b/docs/@catbee/utils/modules/response.md index 29d7aec..df8622e 100644 --- a/docs/@catbee/utils/modules/response.md +++ b/docs/@catbee/utils/modules/response.md @@ -1,4 +1,8 @@ -# Response Utilities +--- +slug: ../response +--- + +# Response Helpers for standardized API responses. Includes classes and functions for success, error, paginated, no-content, and redirect responses, plus utilities for creating and sending responses. All methods are fully typed. @@ -14,12 +18,11 @@ Helpers for standardized API responses. Includes classes and functions for succe ### Functions -- [**`createSuccessResponse(data: T, message?: string): SuccessResponse`**](#createsuccessresponse) - Creates a standard success response. -- [**`createErrorResponse(message: string, statusCode?: number): ErrorResponse`**](#createerrorresponse) - Creates a standard error response. -- [**`createFinalErrorResponse(req: Request, status: number, message: string, error?: any, options?: { includeDetails?: boolean }): ErrorResponse`**](#createfinalerrorresponse) - Creates a final error response. -- [**`createPaginatedResponse(allItems: T[], page: number, pageSize: number, message?: string): PaginatedResponse`**](#createpaginatedresponse) - Creates a paginated response from array data. +- [**`createSuccessResponse(data: T, message = 'Success'): SuccessResponse`**](#createsuccessresponse) - Creates a standard success response. +- [**`createErrorResponse(message: string, statusCode = 500): ErrorResponse`**](#createerrorresponse) - Creates a standard error response. +- [**`createFinalErrorResponse(req: Request, status: number, message: string, error?: any, options?: { includeDetails?: boolean }): ApiErrorResponse`**](#createfinalerrorresponse) - Creates a final error response. +- [**`createPaginatedResponse(allItems: T[], page: number, pageSize: number, message = 'Success'): PaginatedResponse`**](#createpaginatedresponse) - Creates a paginated response from array data. - [**`sendResponse(res: Response, apiResponse: ApiResponse): void`**](#sendresponse) - Adapter to convert API responses to Express.js response format. -- [**`isApiResponse(value: any): value is ApiResponse`**](#isapiresponse) - Checks if a value is an API response. --- @@ -57,24 +60,24 @@ Standard HTTP response wrapper for successful responses. ```ts class SuccessResponse implements ApiResponse { - message: string; - error: false; - data: T; - timestamp: string; - requestId: string; - constructor(message: string, data: T); + message: string = 'Success'; + error: boolean = false; + data: T | null = null; + timestamp: string = new Date().toISOString(); + requestId: string = getRequestId() ?? uuid(); + constructor(message: string, data?: T); } ``` **Parameters:** - `message`: A message describing the response. -- `data`: The response data of type T. +- `data`: Optional response data of type T (default: null). **Examples:** ```ts -import { SuccessResponse } from '@catbee/utils'; +import { SuccessResponse } from '@catbee/utils/response'; const resp = new SuccessResponse<{ id: number }>('OK', { id: 1 }); /* @@ -102,27 +105,25 @@ Wrapper for error responses, extends native Error. **Method Signature:** ```ts -class ErrorResponse extends Error implements ApiErrorResponse { - error: true; - message: string; - timestamp: string; - requestId: string; +class ErrorResponse extends Error implements Omit, 'data'> { status: number; - path?: string; - stack?: string[]; - constructor(message: string, statusCode: number); + error: boolean = true; + message: string; + timestamp: string = new Date().toISOString(); + requestId: string = getRequestId() ?? uuid(); + constructor(message: string, status: number = 500); } ``` **Parameters:** - `message`: A message describing the error. -- `statusCode`: The HTTP status code for the error. +- `status`: The HTTP status code for the error (default: 500). **Examples:** ```ts -import { ErrorResponse } from '@catbee/utils'; +import { ErrorResponse } from '@catbee/utils/response'; const errResp = new ErrorResponse('Not found', 404); /* @@ -155,44 +156,42 @@ Response with paginated data, extends SuccessResponse. ```ts class PaginatedResponse extends SuccessResponse { - constructor(items: T[], pagination: { total: number; page: number; pageSize: number }, message: string) { - super(message, items); - this.pagination = { - total: pagination.total, - page: pagination.page, - pageSize: pagination.pageSize, - totalPages: Math.ceil(pagination.total / pagination.pageSize) - }; - } + total: number; + page: number; + pageSize: number; + totalPages: number; + hasNext: boolean; + hasPrevious: boolean; + constructor(items: T[], pagination: { total: number; page: number; pageSize: number }, message: string = 'Success'); } ``` **Parameters:** -- `data`: An array of items of type T. +- `items`: An array of items of type T. - `pagination`: An object containing pagination details: - `total`: Total number of items. - `page`: Current page number. - `pageSize`: Number of items per page. -- `message`: A message describing the response (default is 'Success'). +- `message`: A message describing the response (default: 'Success'). **Examples:** ```ts -import { PaginatedResponse } from '@catbee/utils'; +import { PaginatedResponse } from '@catbee/utils/response'; -const paged = new PaginatedResponse([1, 2, 3], { total: 100, page: 1, pageSize: 3 }); +const paged = new PaginatedResponse([1, 2, 3], { total: 100, page: 1, pageSize: 3 }, 'Success'); /* { - message: 'OK', + message: 'Success', error: false, data: [1, 2, 3], - pagination: { - total: 100, - page: 1, - pageSize: 3, - totalPages: 34 - }, + total: 100, + page: 1, + pageSize: 3, + totalPages: 34, + hasNext: true, + hasPrevious: false, timestamp: '2023-10-05T12:00:00Z', requestId: 'a2ef4c8d-1234-5678-90ab-cdef12345678' } @@ -219,24 +218,24 @@ Specialized response for operations that don't return data (HTTP 204). Works onl **Method Signature:** ```ts -class NoContentResponse extends SuccessResponse { - constructor(message: string); +class NoContentResponse extends SuccessResponse { + constructor(message: string = 'Operation completed successfully'); } ``` **Parameters:** -- `message`: A message describing the response. +- `message`: A message describing the response (default: 'Operation completed successfully'). **Examples:** ```ts -import { NoContentResponse, sendResponse } from '@catbee/utils'; +import { NoContentResponse, sendResponse } from '@catbee/utils/response'; -const resp = new NoContentResponse('No content'); +const resp = new NoContentResponse(); // Uses default message /* { - message: 'No content', + message: 'Operation completed successfully', error: false, data: null, timestamp: '2023-10-05T12:00:00Z', @@ -259,31 +258,32 @@ Specialized response for redirects. **Method Signature:** ```ts -class RedirectResponse extends SuccessResponse { - constructor(url: string, statusCode: number); +class RedirectResponse { + redirectUrl: string; + statusCode: number; + isRedirect: boolean = true; + requestId: string = getRequestId() ?? uuid(); + constructor(url: string, statusCode: number = 302); } ``` **Parameters:** - `url`: The URL to redirect to. -- `statusCode`: The HTTP status code for the redirect (default is 302). +- `statusCode`: The HTTP status code for the redirect (default: 302). **Examples:** ```ts -import { RedirectResponse, sendResponse } from '@catbee/utils'; +import { RedirectResponse, sendResponse } from '@catbee/utils/response'; const redirect = new RedirectResponse('https://example.com', 302); /* { - message: 'Redirecting to https://example.com', - error: false, - data: null, - timestamp: '2023-10-05T12:00:00Z', - requestId: 'a2ef4c8d-1234-5678-90ab-cdef12345678', - url: 'https://example.com', - statusCode: 302 + redirectUrl: 'https://example.com', + statusCode: 302, + isRedirect: true, + requestId: 'a2ef4c8d-1234-5678-90ab-cdef12345678' } */ app.get('/old-route', (req, res) => { @@ -301,13 +301,13 @@ Creates a standard success response. **Method Signature:** ```ts -function createSuccessResponse(data: T, message?: string): SuccessResponse; +function createSuccessResponse(data: T, message: string = 'Success'): SuccessResponse; ``` **Parameters:** - `data`: The response data of type T. -- `message`: A message describing the response (default is 'Success'). +- `message`: A message describing the response (default: 'Success'). **Returns:** @@ -316,7 +316,7 @@ function createSuccessResponse(data: T, message?: string): SuccessResponse **Example:** ```ts -import { createSuccessResponse } from '@catbee/utils'; +import { createSuccessResponse } from '@catbee/utils/response'; const resp = createSuccessResponse({ id: 1 }, 'OK'); /* @@ -344,13 +344,13 @@ Creates a standard error response. **Method Signature:** ```ts -function createErrorResponse(message: string, statusCode?: number): ErrorResponse; +function createErrorResponse(message: string, statusCode: number = 500): ErrorResponse; ``` **Parameters:** - `message`: A message describing the error. -- `statusCode`: The HTTP status code for the error (default is 500). +- `statusCode`: The HTTP status code for the error (default: 500). **Returns:** @@ -359,7 +359,7 @@ function createErrorResponse(message: string, statusCode?: number): ErrorRespons **Example:** ```ts -import { createErrorResponse } from '@catbee/utils'; +import { createErrorResponse } from '@catbee/utils/response'; const errResp = createErrorResponse('Not found', 404); /* @@ -397,7 +397,7 @@ function createFinalErrorResponse( message: string, error?: any, options?: { includeDetails?: boolean } -): ErrorResponse; +): ApiErrorResponse; ``` **Parameters:** @@ -407,14 +407,14 @@ function createFinalErrorResponse( - `message`: A message describing the error. - `error`: Optional original error object for additional details. - `options`: Optional settings. - - `includeDetails`: Whether to include error details in the response (default is false). + - `includeDetails`: Whether to include error details in the response (default: false). Only includes stack trace in development mode. **Returns:** -- An ErrorResponse object. +- An ApiErrorResponse object. ```ts -import { Env, createFinalErrorResponse } from '@catbee/utils'; +import { Env, createFinalErrorResponse } from '@catbee/utils/response'; const errResp = createFinalErrorResponse(new Error('Something went wrong')); /* @@ -453,7 +453,7 @@ function createPaginatedResponse( allItems: T[], page: number, pageSize: number, - message?: string + message: string = 'Success' ): PaginatedResponse; ``` @@ -462,7 +462,7 @@ function createPaginatedResponse( - `allItems`: The full array of items to paginate. - `page`: The current page number (1-based). - `pageSize`: The number of items per page. -- `message`: A message describing the response (default is 'Success'). +- `message`: A message describing the response (default: 'Success'). **Returns:** @@ -471,7 +471,7 @@ function createPaginatedResponse( **Example:** ```ts -import { createPaginatedResponse } from '@catbee/utils'; +import { createPaginatedResponse } from '@catbee/utils/response'; const resp = createPaginatedResponse([1, 2, 3, 4, 5], 1, 2); /* @@ -479,12 +479,12 @@ const resp = createPaginatedResponse([1, 2, 3, 4, 5], 1, 2); message: 'Success', error: false, data: [1, 2], - pagination: { - total: 5, - page: 1, - pageSize: 2, - totalPages: 3 - }, + total: 5, + page: 1, + pageSize: 2, + totalPages: 3, + hasNext: true, + hasPrevious: false, timestamp: '2023-10-05T12:00:00Z', requestId: 'a2ef4c8d-1234-5678-90ab-cdef12345678' } @@ -508,7 +508,7 @@ Adapter to convert API responses to Express.js response format. **Method Signature:** ```ts -function sendResponse(res: Response, apiResponse: ApiResponse): void; +function sendResponse(res: Response, apiResponse: SuccessResponse | ErrorResponse | RedirectResponse): void; ``` **Parameters:** @@ -519,7 +519,7 @@ function sendResponse(res: Response, apiResponse: ApiResponse): void; **Example:** ```ts -import { sendResponse } from '@catbee/utils'; +import { sendResponse } from '@catbee/utils/response'; app.get('/item', (req, res) => { const item = getItemFromDatabase(req.params.id); @@ -529,31 +529,3 @@ app.get('/item', (req, res) => { sendResponse(res, createSuccessResponse(item, 'Item fetched')); }); ``` - ---- - -### `isApiResponse()` - -Checks if a value is an API response. - -**Method Signature:** - -```ts -function isApiResponse(value: any): value is ApiResponse; -``` - -**Parameters:** - -- `value`: The value to check. - -**Returns:** - -- `true` if the value is an API response, otherwise `false`. - -```ts -import { isApiResponse } from '@catbee/utils'; - -if (isApiResponse(resp)) { - /* ... */ -} -``` diff --git a/docs/@catbee/utils/modules/stream.md b/docs/@catbee/utils/modules/stream.md index 5050674..b4d4eee 100644 --- a/docs/@catbee/utils/modules/stream.md +++ b/docs/@catbee/utils/modules/stream.md @@ -1,4 +1,8 @@ -# Stream Utilities +--- +slug: ../stream +--- + +# Stream Helpers for working with Node.js streams, including conversion between buffers/strings and streams, batching, throttling, and line splitting. All methods are fully typed. @@ -6,10 +10,10 @@ Helpers for working with Node.js streams, including conversion between buffers/s - [**`bufferToStream(data: Buffer | string): Readable`**](#buffertostream) - Converts a buffer or string to a readable stream. - [**`streamToBuffer(stream: Readable): Promise`**](#streamtobuffer) - Converts a readable stream to a buffer. -- [**`streamToString(stream: Readable, encoding?: BufferEncoding): Promise`**](#streamtostring) - Converts a readable stream to a string. +- [**`streamToString(stream: Readable, encoding = 'utf8'): Promise`**](#streamtostring) - Converts a readable stream to a string. - [**`createThrottleStream(bytesPerSecond: number): Transform`**](#createthrottlestream) - Creates a transform stream that limits the rate of data flow. -- [**`createBatchStream(size: number, options?: { objectMode?: boolean }): Transform`**](#createbatchstream) - Creates a transform stream that batches data into chunks of specified size. -- [**`createLineStream(options?: { encoding?: BufferEncoding; includeNewlines?: boolean }): Transform`**](#createlinestream) - Creates a transform stream that splits text data by newlines. +- [**`createBatchStream(size: number, options = {}): Transform`**](#createbatchstream) - Creates a transform stream that batches data into chunks of specified size. +- [**`createLineStream(options = {}): Transform`**](#createlinestream) - Creates a transform stream that splits text data by newlines. --- @@ -55,7 +59,7 @@ function bufferToStream(data: Buffer | string): Readable; **Example:** ```ts -import { bufferToStream } from '@catbee/utils'; +import { bufferToStream } from '@catbee/utils/stream'; import fs from 'fs'; // Convert string to stream @@ -90,7 +94,7 @@ async function streamToBuffer(stream: Readable): Promise; **Example:** ```ts -import { streamToBuffer } from '@catbee/utils'; +import { streamToBuffer } from '@catbee/utils/stream'; import fs from 'fs'; async function processFile() { @@ -136,7 +140,7 @@ async function streamToString(stream: Readable, encoding: BufferEncoding = 'utf8 **Example:** ```ts -import { streamToString } from '@catbee/utils'; +import { streamToString } from '@catbee/utils/stream'; import fs from 'fs'; import { request } from 'https'; @@ -186,7 +190,7 @@ function createThrottleStream(bytesPerSecond: number): Transform; **Example:** ```ts -import { createThrottleStream } from '@catbee/utils'; +import { createThrottleStream } from '@catbee/utils/stream'; import fs from 'fs'; // Limit download speed to simulate slow connection for testing @@ -216,8 +220,8 @@ function createBatchStream(size: number, options: { objectMode?: boolean } = {}) **Parameters:** - `size` - Size of each batch (items for object mode, bytes for binary mode) -- `options` - Stream options - - `objectMode` - Whether to operate in object mode (default: false) +- `options` - Stream options (default: {}) + - `objectMode` - Whether to operate in object mode (default: true) **Returns:** @@ -226,7 +230,7 @@ function createBatchStream(size: number, options: { objectMode?: boolean } = {}) **Example:** ```ts -import { createBatchStream, createLineStream } from '@catbee/utils'; +import { createBatchStream, createLineStream } from '@catbee/utils/stream'; import fs from 'fs'; // Process a large log file in batches of 100 lines @@ -268,7 +272,7 @@ function createLineStream( **Parameters:** -- `options` - Options for the line stream +- `options` - Options for the line stream (default: {}) - `encoding` - Character encoding (default: 'utf8') - `includeNewlines` - Whether to include newline characters in output (default: false) @@ -279,7 +283,7 @@ function createLineStream( **Example:** ```ts -import { createLineStream } from '@catbee/utils'; +import { createLineStream } from '@catbee/utils/stream'; import fs from 'fs'; // Count lines in a file diff --git a/docs/@catbee/utils/modules/string.md b/docs/@catbee/utils/modules/string.md index 428eccf..71d6662 100644 --- a/docs/@catbee/utils/modules/string.md +++ b/docs/@catbee/utils/modules/string.md @@ -1,4 +1,8 @@ -# String Utilities +--- +slug: ../string +--- + +# String Helpers for manipulating and formatting strings. Includes casing conversions, masking, slugifying, truncating, reversing, counting, and more. All methods are fully typed. @@ -11,11 +15,16 @@ Helpers for manipulating and formatting strings. Includes casing conversions, ma - [**`truncate(str: string, len: number)`**](#truncate) - Truncates a string to a specific length, appending '...' if truncated. - [**`toPascalCase(str: string)`**](#topascalcase) - Converts a string to PascalCase. - [**`toSnakeCase(str: string)`**](#tosnakecase) - Converts a string to snake_case. -- [**`mask(str: string, visibleStart?: number, visibleEnd?: number, maskChar?: string)`**](#mask) - Masks a string by replacing characters with a mask character. +- [**`mask(str: string, visibleStart = 0, visibleEnd = 0, maskChar = '*')`**](#mask) - Masks a string by replacing characters with a mask character. - [**`stripHtml(str: string)`**](#striphtml) - Removes all HTML tags from a string. - [**`equalsIgnoreCase(a: string, b: string)`**](#equalsignorecase) - Performs case-insensitive string comparison. - [**`reverse(str: string)`**](#reverse) - Reverses a string. -- [**`countOccurrences(str: string, substring: string, caseSensitive?: boolean)`**](#countoccurrences) - Counts occurrences of a substring within a string. +- [**`countOccurrences(str: string, substring: string, caseSensitive = true)`**](#countoccurrences) - Counts occurrences of a substring within a string. +- [**`toTitleCase(str: string)`**](#totitlecase) - Converts a string to Title Case. +- [**`escapeRegex(str: string)`**](#escaperegex) - Escapes special regex characters in a string. +- [**`unescapeHtml(str: string)`**](#unescapehtml) - Unescapes HTML entities in a string. +- [**`isBlank(str: string)`**](#isblank) - Checks if a string is blank (empty or only whitespace). +- [**`ellipsis(str: string, maxLength: number, suffix = '...')`**](#ellipsis) - Truncates a string with an ellipsis, ensuring word boundaries. --- @@ -42,7 +51,7 @@ function capitalize(str: string): string; **Example:** ```ts -import { capitalize } from '@catbee/utils'; +import { capitalize } from '@catbee/utils/string'; capitalize('hello world'); // "Hello world" ``` @@ -70,7 +79,7 @@ function toKebabCase(str: string): string; **Example:** ```ts -import { toKebabCase } from '@catbee/utils'; +import { toKebabCase } from '@catbee/utils/string'; toKebabCase('FooBar test'); // "foo-bar-test" ``` @@ -98,7 +107,7 @@ function toCamelCase(str: string): string; **Example:** ```ts -import { toCamelCase } from '@catbee/utils'; +import { toCamelCase } from '@catbee/utils/string'; toCamelCase('foo-bar_test'); // "fooBarTest" ``` @@ -126,7 +135,7 @@ function slugify(str: string): string; **Example:** ```ts -import { slugify } from '@catbee/utils'; +import { slugify } from '@catbee/utils/string'; slugify('Hello World!'); // "hello-world" ``` @@ -155,7 +164,7 @@ function truncate(str: string, len: number): string; **Example:** ```ts -import { truncate } from '@catbee/utils'; +import { truncate } from '@catbee/utils/string'; truncate('This is a long sentence.', 10); // "This is a ..." ``` @@ -183,7 +192,7 @@ function toPascalCase(str: string): string; **Example:** ```ts -import { toPascalCase } from '@catbee/utils'; +import { toPascalCase } from '@catbee/utils/string'; toPascalCase('foo-bar'); // "FooBar" ``` @@ -211,7 +220,7 @@ function toSnakeCase(str: string): string; **Example:** ```ts -import { toSnakeCase } from '@catbee/utils'; +import { toSnakeCase } from '@catbee/utils/string'; toSnakeCase('FooBar test'); // "foo_bar_test" ``` @@ -231,9 +240,9 @@ function mask(str: string, visibleStart?: number, visibleEnd?: number, maskChar? **Parameters:** - `str`: The string to mask. -- `visibleStart`: Number of characters to leave visible at the start (default is 0). -- `visibleEnd`: Number of characters to leave visible at the end (default is 0). -- `maskChar`: The character to use for masking (default is '\*'). +- `visibleStart`: Number of characters to leave visible at the start (default: 0). +- `visibleEnd`: Number of characters to leave visible at the end (default: 0). +- `maskChar`: The character to use for masking (default: '\*'). **Returns:** @@ -242,7 +251,7 @@ function mask(str: string, visibleStart?: number, visibleEnd?: number, maskChar? **Example:** ```ts -import { mask } from '@catbee/utils'; +import { mask } from '@catbee/utils/string'; mask('hello world', 2, 5, '*'); // "he***world" ``` @@ -270,7 +279,7 @@ function stripHtml(str: string): string; **Example:** ```ts -import { stripHtml } from '@catbee/utils'; +import { stripHtml } from '@catbee/utils/string'; stripHtml('
Hello world
'); // "Hello world" ``` @@ -299,7 +308,7 @@ function equalsIgnoreCase(a: string, b: string): boolean; **Example:** ```ts -import { equalsIgnoreCase } from '@catbee/utils'; +import { equalsIgnoreCase } from '@catbee/utils/string'; equalsIgnoreCase('Hello', 'hello'); // true ``` @@ -327,7 +336,7 @@ function reverse(str: string): string; **Example:** ```ts -import { reverse } from '@catbee/utils'; +import { reverse } from '@catbee/utils/string'; reverse('abc'); // "cba" ``` @@ -341,14 +350,14 @@ Counts occurrences of a substring within a string. **Method Signature:** ```ts -function countOccurrences(str: string, substring: string, caseSensitive?: boolean): number; +function countOccurrences(str: string, substring: string, caseSensitive: boolean = true): number; ``` **Parameters:** - `str`: The string to search within. - `substring`: The substring to count. -- `caseSensitive`: Whether the search should be case-sensitive (default is false). +- `caseSensitive`: Whether the search should be case-sensitive (default: true). **Returns:** @@ -357,8 +366,158 @@ function countOccurrences(str: string, substring: string, caseSensitive?: boolea **Example:** ```ts -import { countOccurrences } from '@catbee/utils'; +import { countOccurrences } from '@catbee/utils/string'; + +countOccurrences('banana', 'an'); // 2 (case-sensitive) +countOccurrences('Banana', 'an'); // 1 (case-sensitive, only lowercase 'an') +countOccurrences('Banana', 'an', false); // 2 (case-insensitive) +``` + +--- + +### `toTitleCase()` + +Converts a string to Title Case (each word capitalized). + +**Method Signature:** + +```ts +function toTitleCase(str: string): string; +``` + +**Parameters:** + +- `str`: The string to convert. + +**Returns:** + +- The Title Case string. + +**Example:** + +```ts +import { toTitleCase } from '@catbee/utils/string'; + +toTitleCase('hello world'); // "Hello World" +toTitleCase('the quick brown fox'); // "The Quick Brown Fox" +``` + +--- + +### `escapeRegex()` + +Escapes special regex characters in a string. + +**Method Signature:** + +```ts +function escapeRegex(str: string): string; +``` + +**Parameters:** + +- `str`: The string to escape. + +**Returns:** + +- The string with regex characters escaped. + +**Example:** + +```ts +import { escapeRegex } from '@catbee/utils/string'; + +escapeRegex('Hello (world)'); // "Hello \\(world\\)" +escapeRegex('$100.00'); // "\\$100\\.00" +``` + +--- + +### `unescapeHtml()` + +Unescapes HTML entities in a string. + +**Method Signature:** + +```ts +function unescapeHtml(str: string): string; +``` + +**Parameters:** + +- `str`: The HTML string with entities. + +**Returns:** + +- The string with HTML entities unescaped. + +**Example:** + +```ts +import { unescapeHtml } from '@catbee/utils/string'; + +unescapeHtml('<div>Hello</div>'); // "
Hello
" +unescapeHtml('"Hello"'); // '"Hello"' +``` + +--- + +### `isBlank()` + +Checks if a string is blank (empty or only whitespace). + +**Method Signature:** + +```ts +function isBlank(str: string): boolean; +``` + +**Parameters:** + +- `str`: The string to check. + +**Returns:** + +- `true` if the string is blank, otherwise `false`. + +**Example:** + +```ts +import { isBlank } from '@catbee/utils/string'; + +isBlank(' '); // true +isBlank(''); // true +isBlank('hello'); // false +``` + +--- + +### `ellipsis()` + +Truncates a string with an ellipsis, ensuring word boundaries. + +**Method Signature:** + +```ts +function ellipsis(str: string, maxLength: number, suffix: string = '...'): string; +``` + +**Parameters:** + +- `str`: The string to truncate. +- `maxLength`: Maximum length of the result. +- `suffix`: Suffix to append when truncated (default: '...'). + +**Returns:** + +- The truncated string with suffix. + +**Example:** + +```ts +import { ellipsis } from '@catbee/utils/string'; -countOccurrences('banana', 'an'); // 2 -countOccurrences('Banana', 'an', false); // 2 +ellipsis('The quick brown fox', 10); // "The quick..." +ellipsis('Short', 10); // "Short" +ellipsis('The quick brown fox', 15, '…'); // "The quick…" ``` diff --git a/docs/@catbee/utils/modules/type.md b/docs/@catbee/utils/modules/type.md index 9910602..84369da 100644 --- a/docs/@catbee/utils/modules/type.md +++ b/docs/@catbee/utils/modules/type.md @@ -1,4 +1,8 @@ -# Type Utilities +--- +slug: ../type +--- + +# Type A set of utility functions for type checking, conversion, and type guards in TypeScript. Includes primitives/type guards, conversion helpers, and type enforcement. All methods are fully typed. @@ -6,11 +10,16 @@ A set of utility functions for type checking, conversion, and type guards in Typ - [**`isPrimitiveType(value: unknown, type: PrimitiveType): boolean`**](#isprimitivetype) - Checks if a value is of a specific primitive type. - [**`getTypeOf(value: unknown): string`**](#gettypeof) - Returns the primitive type of a value as a string. -- [**`isArrayOf(value: unknown, itemTypeGuard: (item: unknown) => item is T): value is T[]`**](#isarrayof) - Type guard for checking if a value is an array of a specific type. -- [**`toStr(value: unknown, defaultValue?: string): string`**](#tostr) - Converts a value to a string. -- [**`toNum(value: unknown, defaultValue?: number): number`**](#tonum) - Converts a value to a number. -- [**`toBool(value: unknown, defaultValue?: boolean): boolean`**](#tobool) - Converts a value to a boolean. -- [**`ensureType(value: unknown, expectedType: string, defaultValue: T): T`**](#ensuretype) - Ensures a value matches the expected type, or provides a default. +- [**`isArrayOf(value, itemTypeGuard): value is T[]`**](#isarrayof) - Type guard for checking if a value is an array of a specific type. +- [**`toStr(value: unknown, defaultValue = ''): string`**](#tostr) - Converts a value to a string. +- [**`toNum(value: unknown, defaultValue = 0): number`**](#tonum) - Converts a value to a number. +- [**`toBool(value: unknown, defaultValue = false): boolean`**](#tobool) - Converts a value to a boolean. +- [**`ensureType(value: unknown, expectedType: string, defaultValue: T): T`**](#ensuretype) - Ensures a value matches the expected type, or provides a default. +- [**`isDefined(value: T | null | undefined): value is T`**](#isdefined) - Checks if a value is neither null nor undefined. +- [**`isEmpty(value: any): boolean`**](#isempty) - Checks if a value is empty. +- [**`isIterable(value: unknown): value is Iterable`**](#isiterable) - Type guard to check if a value is iterable. +- [**`isAsyncIterable(value: unknown): value is AsyncIterable`**](#isasynciterable) - Type guard to check if a value is async iterable. +- [**`assertType(value, guard, message?): asserts value is T`**](#asserttype) - Asserts that a value is of a specific type, throwing an error if not. --- @@ -46,7 +55,7 @@ function isPrimitiveType(value: unknown, type: PrimitiveType): boolean; **Example:** ```ts -import { isPrimitiveType } from '@catbee/utils'; +import { isPrimitiveType } from '@catbee/utils/type'; isPrimitiveType('hello', 'string'); // true isPrimitiveType(42, 'number'); // true @@ -77,7 +86,7 @@ getTypeOf(value: unknown): string; **Example:** ```ts -import { getTypeOf } from '@catbee/utils'; +import { getTypeOf } from '@catbee/utils/type'; getTypeOf('hello'); // 'string' getTypeOf(42); // 'number' @@ -109,7 +118,7 @@ function isArrayOf(value: unknown, itemTypeGuard: (item: unknown) => item is **Example:** ```ts -import { isArrayOf } from '@catbee/utils'; +import { isArrayOf } from '@catbee/utils/type'; isArrayOf([1, 2, 3], (item): item is number => typeof item === 'number'); // true isArrayOf(['a', 'b'], (item): item is string => typeof item === 'string'); // true @@ -125,13 +134,13 @@ Converts a value to a string. Returns a default value if conversion fails. **Method Signature:** ```ts -function toStr(value: unknown, defaultValue?: string): string; +function toStr(value: unknown, defaultValue: string = ''): string; ``` **Parameters:** - `value`: The value to convert. -- `defaultValue`: The default string to return if conversion fails (optional). +- `defaultValue`: The default string to return if conversion fails (default: ''). **Returns:** @@ -140,7 +149,7 @@ function toStr(value: unknown, defaultValue?: string): string; **Example:** ```ts -import { toStr } from '@catbee/utils'; +import { toStr } from '@catbee/utils/type'; toStr(42); // '42' toStr({ a: 1 }); // '{"a":1}' @@ -156,13 +165,13 @@ Converts a value to a number. Returns a default value if conversion fails. **Method Signature:** ```ts -function toNum(value: unknown, defaultValue?: number): number; +function toNum(value: unknown, defaultValue: number = 0): number; ``` **Parameters:** - `value`: The value to convert. -- `defaultValue`: The default number to return if conversion fails (optional). +- `defaultValue`: The default number to return if conversion fails (default: 0). **Returns:** @@ -171,7 +180,7 @@ function toNum(value: unknown, defaultValue?: number): number; **Example:** ```ts -import { toNum } from '@catbee/utils'; +import { toNum } from '@catbee/utils/type'; toNum('42'); // 42 toNum('abc', 0); // 0 @@ -187,13 +196,13 @@ Converts a value to a boolean. Returns a default value if conversion fails. **Method Signature:** ```ts -function toBool(value: unknown, defaultValue?: boolean): boolean; +function toBool(value: unknown, defaultValue: boolean = false): boolean; ``` **Parameters:** - `value`: The value to convert. -- `defaultValue`: The default boolean to return if conversion fails (optional). +- `defaultValue`: The default boolean to return if conversion fails (default: false). **Returns:** @@ -202,7 +211,7 @@ function toBool(value: unknown, defaultValue?: boolean): boolean; **Example:** ```ts -import { toBool } from '@catbee/utils'; +import { toBool } from '@catbee/utils/type'; toBool('true'); // true toBool('no'); // false @@ -235,8 +244,191 @@ function ensureType(value: unknown, expectedType: string, defaultValue: T): T **Example:** ```ts -import { ensureType } from '@catbee/utils'; +import { ensureType } from '@catbee/utils/type'; ensureType(42, 'number', 0); // 42 ensureType('42', 'number', 0); // 0 ensureType(undefined, 'string', 'default'); // 'default' ``` + +--- + +### `isDefined()` + +Checks whether a value is neither null nor undefined. Useful in filter chains and guards. + +**Method Signature:** + +```ts +function isDefined(value: T | null | undefined): value is T; +``` + +**Parameters:** + +- `value`: The value to check. + +**Returns:** + +- `true` when value is not null or undefined, otherwise `false`. + +**Example:** + +```ts +import { isDefined } from '@catbee/utils/type'; + +isDefined(42); // true +isDefined('hello'); // true +isDefined(null); // false +isDefined(undefined); // false + +// Useful in filter chains +const values = [1, null, 2, undefined, 3]; +const defined = values.filter(isDefined); // [1, 2, 3] +``` + +--- + +### `isEmpty()` + +Checks whether a value is empty. Supports strings, arrays, maps, sets, and plain objects. + +**Method Signature:** + +```ts +function isEmpty(value: any): boolean; +``` + +**Parameters:** + +- `value`: The value to inspect. + +**Returns:** + +- `true` when value is considered empty, otherwise `false`. + +**Example:** + +```ts +import { isEmpty } from '@catbee/utils/type'; + +isEmpty(''); // true +isEmpty(' '); // true (whitespace-only strings) +isEmpty([]); // true +isEmpty({}); // true +isEmpty(new Map()); // true +isEmpty(new Set()); // true +isEmpty(null); // true +isEmpty(undefined); // true + +isEmpty('hello'); // false +isEmpty([1, 2, 3]); // false +isEmpty({ key: 'value' }); // false +``` + +--- + +### `isIterable()` + +Type guard to check if a value is iterable. + +**Method Signature:** + +```ts +function isIterable(value: unknown): value is Iterable; +``` + +**Parameters:** + +- `value`: The value to check. + +**Returns:** + +- `true` if value is iterable, otherwise `false`. + +**Example:** + +```ts +import { isIterable } from '@catbee/utils/type'; + +isIterable([1, 2, 3]); // true +isIterable('hello'); // true +isIterable(new Set([1, 2])); // true +isIterable(new Map([[1, 'a']])); // true +isIterable(42); // false +isIterable({}); // false +``` + +--- + +### `isAsyncIterable()` + +Type guard to check if a value is async iterable. + +**Method Signature:** + +```ts +function isAsyncIterable(value: unknown): value is AsyncIterable; +``` + +**Parameters:** + +- `value`: The value to check. + +**Returns:** + +- `true` if value is async iterable, otherwise `false`. + +**Example:** + +```ts +import { isAsyncIterable } from '@catbee/utils/type'; + +async function* asyncGenerator() { + yield 1; + yield 2; +} + +isAsyncIterable(asyncGenerator()); // true +isAsyncIterable([1, 2, 3]); // false +isAsyncIterable('hello'); // false +``` + +--- + +### `assertType()` + +Asserts that a value is of a specific type, throwing an error if not. + +**Method Signature:** + +```ts +function assertType( + value: unknown, + guard: (value: unknown) => value is T, + message?: string +): asserts value is T; +``` + +**Parameters:** + +- `value`: The value to assert. +- `guard`: Type guard function to check the value. +- `message`: Optional custom error message if assertion fails. + +**Returns:** + +- Asserts value is of type T or throws TypeError. + +**Example:** + +```ts +import { assertType } from '@catbee/utils/type'; + +function processString(value: unknown) { + assertType(value, (v): v is string => typeof v === 'string', 'Expected string'); + // TypeScript now knows value is a string + return value.toUpperCase(); +} + +processString('hello'); // 'HELLO' +processString(42); // throws TypeError: Expected string +``` diff --git a/docs/@catbee/utils/modules/types.md b/docs/@catbee/utils/modules/types.md new file mode 100644 index 0000000..9a6ce5b --- /dev/null +++ b/docs/@catbee/utils/modules/types.md @@ -0,0 +1,1203 @@ +--- +slug: ../types +--- + +# Types + +TypeScript type utilities and interface definitions for building type-safe applications. Includes utility types for common patterns, API response structures, pagination interfaces, and more. + +## API Summary + +### Type Utilities + +- [**`ToggleConfig`**](#toggleconfigt) - Configurable toggle that can be boolean or custom config object. +- [**`Nullable`**](#nullablet) - Value that can be null or undefined. +- [**`Optional`**](#optionalt) - Value that may or may not be present. +- [**`DeepPartial`**](#deeppartialt) - Makes all properties deeply optional. +- [**`DeepReadonly`**](#deepreadonlyt) - Makes all properties readonly recursively. +- [**`UnionToIntersection`**](#uniontointersectionu) - Converts union types to intersection. +- [**`MaybePromise`**](#maybepromiset) - Value that can be synchronous or promise. +- [**`StringKeyedRecord`**](#stringkeyedrecordt) - Record with string keys. +- [**`Func`**](#funca-r) - Generic function type. +- [**`PartialPick`**](#partialpickt-k) - Partial pick combination. +- [**`DeepStringifyOrNull`**](#deepstringifyornullt) - Deeply stringifies properties or makes them null. +- [**`NonEmptyArray`**](#nonemptyarrayt) - Array with at least one element. +- [**`ValueOf`**](#valueoft) - Union of all property values. +- [**`Mutable`**](#mutablet) - Removes readonly from all properties. +- [**`KeysOfType`**](#keysoftypet-u) - Gets keys whose values match type U. +- [**`RequireAtLeastOne`**](#requireatleastonet-k) - Requires at least one key to be present. +- [**`RecordOptional`**](#recordoptionalk-t) - Record with optional keys. +- [**`Primitive`**](#primitive) - All primitive TypeScript types. +- [**`Awaited`**](#awaitedt) - Unwraps Promise types. +- [**`PickByType`**](#pickbytypet-u) - Picks properties of specific type. +- [**`DeepRequired`**](#deeprequiredt) - Makes all properties required recursively. +- [**`IsEqual`**](#isequalt-u) - Type-level equality check. +- [**`Writable`**](#writablet) - Removes readonly modifier. +- [**`Optional2`**](#optional2t-k) - Makes specific keys optional. +- [**`Without`**](#withoutt-u) - Excludes properties of specific type. + +### API Response Types + +- [**`ApiResponse`**](#apiresponset) - Generic API response wrapper. +- [**`ApiSuccessResponse`**](#apisuccessresponset) - Strongly typed success response. +- [**`ApiErrorResponse`**](#apierrorresponse) - Error response with metadata. +- [**`Pagination`**](#paginationt) - Generic pagination structure. +- [**`PaginationResponse`**](#paginationresponset) - Alias for paginated response. +- [**`BatchResponse`**](#batchresponset) - Batch operation response. +- [**`AsyncOperationResponse`**](#asyncoperationresponse) - Async operation response. +- [**`StreamResponse`**](#streamresponse) - Streaming operation response. +- [**`PaginationParams`**](#paginationparams) - Pagination parameters. +- [**`WithPagination`**](#withpaginationt) - Combines pagination with additional data. +- [**`SortDirection`**](#sortdirection) - Sort direction enum. + +--- + +## Type Utilities + +### `ToggleConfig` + +A type representing a configurable toggle that can be `true`, `false`, or a custom configuration object. + +**Type Definition:** + +```ts +type ToggleConfig = boolean | T; +``` + +**Examples:** + +```ts +import { ToggleConfig } from '@catbee/utils/types'; + +interface CacheConfig { + ttl: number; + maxSize: number; +} + +// Can be boolean +const simpleCache: ToggleConfig = true; + +// Or custom config +const advancedCache: ToggleConfig = { + ttl: 3600, + maxSize: 1000 +}; +``` + +--- + +### `Nullable` + +A type representing a value that can be `null` or `undefined`. + +**Type Definition:** + +```ts +type Nullable = T | null | undefined; +``` + +**Examples:** + +```ts +import { Nullable } from '@catbee/utils/types'; + +const userId: Nullable = getUserId(); // string | null | undefined + +function processUser(id: Nullable) { + if (id != null) { + // id is number here + } +} +``` + +--- + +### `Optional` + +A type representing a value that may or may not be present. + +**Type Definition:** + +```ts +type Optional = T | undefined; +``` + +**Examples:** + +```ts +import { Optional } from '@catbee/utils/types'; + +const config: Optional<{ apiKey: string }> = loadConfig(); + +function setTitle(title: Optional) { + document.title = title ?? 'Default Title'; +} +``` + +--- + +### `DeepPartial` + +Makes all properties of `T` deeply optional, recursively applying `Partial` to nested objects. + +**Type Definition:** + +```ts +type DeepPartial = { + [P in keyof T]?: T[P] extends object ? DeepPartial : T[P]; +}; +``` + +**Examples:** + +```ts +import { DeepPartial } from '@catbee/utils/types'; + +interface User { + id: number; + profile: { + name: string; + address: { + street: string; + city: string; + }; + }; +} + +const partialUser: DeepPartial = { + profile: { + address: { + city: 'New York' // street is optional + } + } +}; +``` + +--- + +### `DeepReadonly` + +Makes all properties of `T` readonly recursively. + +**Type Definition:** + +```ts +type DeepReadonly = { + readonly [P in keyof T]: T[P] extends object ? DeepReadonly : T[P]; +}; +``` + +**Examples:** + +```ts +import { DeepReadonly } from '@catbee/utils/types'; + +interface Config { + database: { + host: string; + port: number; + }; +} + +const config: DeepReadonly = { + database: { host: 'localhost', port: 5432 } +}; + +// config.database.host = 'other'; // Error: Cannot assign to 'host' because it is a read-only property +``` + +--- + +### `UnionToIntersection` + +Converts a union of types into an intersection. + +**Type Definition:** + +```ts +type UnionToIntersection = (U extends any ? (x: U) => void : never) extends (x: infer I) => void ? I : never; +``` + +**Examples:** + +```ts +import { UnionToIntersection } from '@catbee/utils/types'; + +type A = { a: string }; +type B = { b: number }; +type C = A | B; + +type Combined = UnionToIntersection; // { a: string } & { b: number } + +const obj: Combined = { a: 'test', b: 123 }; +``` + +--- + +### `MaybePromise` + +A type representing a value that can be either synchronous or a promise. + +**Type Definition:** + +```ts +type MaybePromise = T | Promise; +``` + +**Examples:** + +```ts +import { MaybePromise } from '@catbee/utils/types'; + +function processData(data: MaybePromise) { + if (data instanceof Promise) { + return data.then(str => str.toUpperCase()); + } + return data.toUpperCase(); +} + +// Works with both +processData('hello'); +processData(Promise.resolve('hello')); +``` + +--- + +### `StringKeyedRecord` + +A type representing a record with string keys and values of type `T`. + +**Type Definition:** + +```ts +type StringKeyedRecord = Record; +``` + +**Examples:** + +```ts +import { StringKeyedRecord } from '@catbee/utils/types'; + +const userAges: StringKeyedRecord = { + alice: 30, + bob: 25, + charlie: 35 +}; +``` + +--- + +### `Func` + +A generic function type that returns `R` and optionally receives arguments `A`. + +**Type Definition:** + +```ts +type Func = (...args: A) => R; +``` + +**Examples:** + +```ts +import { Func } from '@catbee/utils/types'; + +const add: Func<[number, number], number> = (a, b) => a + b; + +const greet: Func<[string], string> = (name) => `Hello, ${name}`; + +const noArgs: Func<[], void> = () => console.log('Called'); +``` + +--- + +### `PartialPick` + +A type representing partial pick from `T` (combines `Partial` and `Pick`). + +**Type Definition:** + +```ts +type PartialPick = Partial> & Omit; +``` + +**Examples:** + +```ts +import { PartialPick } from '@catbee/utils/types'; + +interface User { + id: number; + name: string; + email: string; + age: number; +} + +// Make name and email optional, keep id and age required +type UserUpdate = PartialPick; + +const update: UserUpdate = { + id: 1, + age: 30 + // name and email are optional +}; +``` + +--- + +### `DeepStringifyOrNull` + +Deeply stringifies all properties of `T` or makes them null. + +**Type Definition:** + +```ts +type DeepStringifyOrNull = T extends string | number | bigint | boolean | symbol | null | undefined + ? string | null + : T extends Array + ? Array> + : T extends object + ? { [K in keyof T]: DeepStringifyOrNull } + : string | null; +``` + +**Examples:** + +```ts +import { DeepStringifyOrNull } from '@catbee/utils/types'; + +interface Data { + id: number; + items: { name: string; count: number }[]; +} + +type StringifiedData = DeepStringifyOrNull; +// { id: string | null; items: { name: string | null; count: string | null }[] } +``` + +--- + +### `NonEmptyArray` + +A type representing an array with at least one element. + +**Type Definition:** + +```ts +type NonEmptyArray = [T, ...T[]]; +``` + +**Examples:** + +```ts +import { NonEmptyArray } from '@catbee/utils/types'; + +function getFirst(arr: NonEmptyArray): T { + return arr[0]; // Safe - array always has at least one element +} + +const valid: NonEmptyArray = [1, 2, 3]; +// const invalid: NonEmptyArray = []; // Error +``` + +--- + +### `ValueOf` + +A type representing the union of all property values of `T`. + +**Type Definition:** + +```ts +type ValueOf = T[keyof T]; +``` + +**Examples:** + +```ts +import { ValueOf } from '@catbee/utils/types'; + +interface Status { + pending: 'PENDING'; + active: 'ACTIVE'; + completed: 'COMPLETED'; +} + +type StatusValue = ValueOf; // 'PENDING' | 'ACTIVE' | 'COMPLETED' + +const status: StatusValue = 'ACTIVE'; +``` + +--- + +### `Mutable` + +Makes all properties of `T` mutable (removes readonly). + +**Type Definition:** + +```ts +type Mutable = { + -readonly [P in keyof T]: T[P]; +}; +``` + +**Examples:** + +```ts +import { Mutable } from '@catbee/utils/types'; + +interface ReadonlyUser { + readonly id: number; + readonly name: string; +} + +type MutableUser = Mutable; + +const user: MutableUser = { id: 1, name: 'Alice' }; +user.name = 'Bob'; // OK - properties are mutable +``` + +--- + +### `KeysOfType` + +Gets the keys of `T` whose values are assignable to `U`. + +**Type Definition:** + +```ts +type KeysOfType = { + [K in keyof T]: T[K] extends U ? K : never; +}[keyof T]; +``` + +**Examples:** + +```ts +import { KeysOfType } from '@catbee/utils/types'; + +interface Mixed { + id: number; + name: string; + age: number; + email: string; +} + +type StringKeys = KeysOfType; // 'name' | 'email' +type NumberKeys = KeysOfType; // 'id' | 'age' +``` + +--- + +### `RequireAtLeastOne` + +Requires at least one of the keys in `K` to be present in `T`. + +**Type Definition:** + +```ts +type RequireAtLeastOne = K extends keyof T + ? { [P in K]-?: T[P] } & Omit + : never; +``` + +**Examples:** + +```ts +import { RequireAtLeastOne } from '@catbee/utils/types'; + +interface Contact { + email?: string; + phone?: string; +} + +type ContactRequired = RequireAtLeastOne; + +const valid: ContactRequired = { email: 'test@example.com' }; +// const invalid: ContactRequired = {}; // Error - at least one required +``` + +--- + +### `RecordOptional` + +A record type with optional keys. + +**Type Definition:** + +```ts +type RecordOptional = { + [P in K]?: T; +}; +``` + +**Examples:** + +```ts +import { RecordOptional } from '@catbee/utils/types'; + +type OptionalSettings = RecordOptional<'theme' | 'language' | 'timezone', string>; + +const settings: OptionalSettings = { + theme: 'dark' + // language and timezone are optional +}; +``` + +--- + +### `Primitive` + +Represents all primitive TypeScript types. + +**Type Definition:** + +```ts +type Primitive = string | number | boolean | bigint | symbol | undefined | null; +``` + +**Examples:** + +```ts +import { Primitive } from '@catbee/utils/types'; + +function isPrimitive(value: any): value is Primitive { + return value !== Object(value); +} + +isPrimitive('hello'); // true +isPrimitive(123); // true +isPrimitive({}); // false +``` + +--- + +### `Awaited` + +Recursively unwraps Promise types to get their resolved value type. + +**Type Definition:** + +```ts +type Awaited = T extends Promise ? Awaited : T; +``` + +**Examples:** + +```ts +import { Awaited } from '@catbee/utils/types'; + +type A = Awaited>; // string +type B = Awaited>>; // number + +async function getData(): Promise<{ id: number }> { + return { id: 1 }; +} + +type Data = Awaited>; // { id: number } +``` + +--- + +### `PickByType` + +Picks properties from `T` that are of type `U`. + +**Type Definition:** + +```ts +type PickByType = { + [P in keyof T as T[P] extends U ? P : never]: T[P]; +}; +``` + +**Examples:** + +```ts +import { PickByType } from '@catbee/utils/types'; + +interface User { + id: number; + name: string; + age: number; + active: boolean; +} + +type UserStrings = PickByType; // { name: string } +type UserNumbers = PickByType; // { id: number; age: number } +``` + +--- + +### `DeepRequired` + +Makes all properties of `T` required recursively. + +**Type Definition:** + +```ts +type DeepRequired = { + [P in keyof T]-?: T[P] extends object ? DeepRequired : T[P]; +}; +``` + +**Examples:** + +```ts +import { DeepRequired } from '@catbee/utils/types'; + +interface PartialConfig { + database?: { + host?: string; + port?: number; + }; +} + +type RequiredConfig = DeepRequired; +// { database: { host: string; port: number } } +``` + +--- + +### `IsEqual` + +Type-level equality check that returns `true` or `false` as a type. + +**Type Definition:** + +```ts +type IsEqual = (() => G extends T ? 1 : 2) extends () => G extends U ? 1 : 2 ? true : false; +``` + +**Examples:** + +```ts +import { IsEqual } from '@catbee/utils/types'; + +type Test1 = IsEqual; // true +type Test2 = IsEqual; // false +type Test3 = IsEqual<{ a: string }, { a: string }>; // true +``` + +--- + +### `Writable` + +Makes all properties of an object writable (removes readonly). + +**Type Definition:** + +```ts +type Writable = { -readonly [P in keyof T]: T[P] }; +``` + +**Examples:** + +```ts +import { Writable } from '@catbee/utils/types'; + +interface ReadonlyConfig { + readonly apiKey: string; + readonly timeout: number; +} + +type WritableConfig = Writable; + +const config: WritableConfig = { apiKey: 'abc', timeout: 5000 }; +config.apiKey = 'xyz'; // OK +``` + +--- + +### `Optional2` + +Makes specific keys `K` of type `T` optional. + +**Type Definition:** + +```ts +type Optional2 = Omit & Partial>; +``` + +**Examples:** + +```ts +import { Optional2 } from '@catbee/utils/types'; + +interface User { + id: number; + name: string; + email: string; +} + +type UserWithOptionalEmail = Optional2; + +const user: UserWithOptionalEmail = { + id: 1, + name: 'Alice' + // email is optional +}; +``` + +--- + +### `Without` + +Creates a type with all properties of `T` except those with types assignable to `U`. + +**Type Definition:** + +```ts +type Without = { + [P in keyof T as T[P] extends U ? never : P]: T[P]; +}; +``` + +**Examples:** + +```ts +import { Without } from '@catbee/utils/types'; + +interface Mixed { + id: number; + name: string; + count: number; + active: boolean; +} + +type WithoutNumbers = Without; // { name: string; active: boolean } +``` + +--- + +## API Response Types + +### `ApiResponse` + +Generic API response format used to wrap any successful or failed response from the server. + +**Interface Definition:** + +```ts +interface ApiResponse { + data: T | null; + error: boolean; + message: string; + requestId: string; + timestamp: string; +} +``` + +**Properties:** + +- `data`: Payload returned from the API (any shape depending on endpoint). +- `error`: Indicates whether an error occurred (`true` = error, `false` = success). +- `message`: Success message describing the result of the operation. +- `requestId`: Unique request ID for traceability in logs. +- `timestamp`: ISO timestamp when the response was generated. + +**Examples:** + +```ts +import { ApiResponse } from '@catbee/utils/types'; + +const response: ApiResponse<{ id: number; name: string }> = { + data: { id: 1, name: 'Item' }, + error: false, + message: 'Success', + requestId: 'req-123', + timestamp: '2026-01-02T10:00:00Z' +}; +``` + +--- + +### `ApiSuccessResponse` + +Success response structure with strongly typed data. + +**Interface Definition:** + +```ts +interface ApiSuccessResponse extends ApiResponse { + error: false; + status?: number; +} +``` + +**Properties:** + +- Extends `ApiResponse` +- `error`: Always `false` for success responses. +- `status`: HTTP status code (usually 200). + +**Examples:** + +```ts +import { ApiSuccessResponse } from '@catbee/utils/types'; + +const success: ApiSuccessResponse<{ userId: string }> = { + data: { userId: 'user-123' }, + error: false, + message: 'User created successfully', + requestId: 'req-456', + timestamp: '2026-01-02T10:00:00Z', + status: 201 +}; +``` + +--- + +### `ApiErrorResponse` + +Error response structure with additional metadata. + +**Interface Definition:** + +```ts +interface ApiErrorResponse extends Omit, 'data'> { + error: true; + status: number; + path: string; + stack?: string[]; +} +``` + +**Properties:** + +- `error`: Always `true` for error responses. +- `status`: HTTP status code. +- `path`: Path to the resource that caused the error. +- `stack`: Stack trace of the error (if available). +- Inherits `message`, `requestId`, `timestamp` from `ApiResponse`. + +**Examples:** + +```ts +import { ApiErrorResponse } from '@catbee/utils/types'; + +const error: ApiErrorResponse = { + error: true, + status: 404, + message: 'User not found', + path: '/api/users/123', + requestId: 'req-789', + timestamp: '2026-01-02T10:00:00Z', + stack: ['Error: User not found', 'at getUserById...'] +}; +``` + +--- + +### `Pagination` + +Generic pagination structure used for paged lists. + +**Interface Definition:** + +```ts +interface Pagination { + content: T[]; + pagination: { + totalRecords: number; + totalPages: number; + page: number; + limit: number; + sortBy: string; + sortOrder: 'asc' | 'desc'; + }; +} +``` + +**Properties:** + +- `content`: List of records for the current page. +- `pagination`: Metadata about the pagination state: + - `totalRecords`: Total number of records across all pages. + - `totalPages`: Total number of pages available. + - `page`: Current page number (1-based index). + - `limit`: Number of records per page. + - `sortBy`: Field by which the data is sorted. + - `sortOrder`: Sort order (ascending or descending). + +**Examples:** + +```ts +import { Pagination } from '@catbee/utils/types'; + +const users: Pagination<{ id: number; name: string }> = { + content: [ + { id: 1, name: 'Alice' }, + { id: 2, name: 'Bob' } + ], + pagination: { + totalRecords: 100, + totalPages: 10, + page: 1, + limit: 10, + sortBy: 'name', + sortOrder: 'asc' + } +}; +``` + +--- + +### `PaginationResponse` + +Alias for paginated API response. Allows semantic naming like `PaginationResponse`. + +**Type Definition:** + +```ts +type PaginationResponse = Pagination; +``` + +**Examples:** + +```ts +import { PaginationResponse } from '@catbee/utils/types'; + +const response: PaginationResponse = { + content: users, + pagination: { + totalRecords: 50, + totalPages: 5, + page: 1, + limit: 10, + sortBy: 'createdAt', + sortOrder: 'desc' + } +}; +``` + +--- + +### `BatchResponse` + +Response structure for batch operations when multiple operations are performed in a single request. + +**Interface Definition:** + +```ts +interface BatchResponse { + success: boolean; + total: number; + successful: number; + failed: number; + results: Array<{ + id: string | number; + success: boolean; + data?: T; + error?: { + message: string; + code?: string; + }; + }>; +} +``` + +**Properties:** + +- `success`: Overall success/failure indicator. +- `total`: Total number of operations. +- `successful`: Number of successful operations. +- `failed`: Number of failed operations. +- `results`: Array of individual operation results. + +**Examples:** + +```ts +import { BatchResponse } from '@catbee/utils/types'; + +const batch: BatchResponse<{ id: number }> = { + success: true, + total: 3, + successful: 2, + failed: 1, + results: [ + { id: 1, success: true, data: { id: 1 } }, + { id: 2, success: true, data: { id: 2 } }, + { id: 3, success: false, error: { message: 'Validation failed' } } + ] +}; +``` + +--- + +### `AsyncOperationResponse` + +Response structure for asynchronous operations that will complete in the future. + +**Interface Definition:** + +```ts +interface AsyncOperationResponse { + async: true; + jobId: string; + estimatedTime?: number; + statusUrl: string; +} +``` + +**Properties:** + +- `async`: Always `true` for async operations. +- `jobId`: Job or task ID to check status later. +- `estimatedTime`: Estimated completion time in seconds (if known). +- `statusUrl`: URL to check status. + +**Examples:** + +```ts +import { AsyncOperationResponse } from '@catbee/utils/types'; + +const asyncOp: AsyncOperationResponse = { + async: true, + jobId: 'job-abc-123', + estimatedTime: 300, + statusUrl: '/api/jobs/job-abc-123/status' +}; +``` + +--- + +### `StreamResponse` + +Response structure for streaming operations when data is returned as a stream. + +**Interface Definition:** + +```ts +interface StreamResponse { + streamId: string; + streamType: string; + totalSize?: number; + chunkSize: number; +} +``` + +**Properties:** + +- `streamId`: Stream identifier. +- `streamType`: Stream type (e.g., 'json', 'binary'). +- `totalSize`: Total size in bytes (if known). +- `chunkSize`: Chunk size in bytes. + +**Examples:** + +```ts +import { StreamResponse } from '@catbee/utils/types'; + +const stream: StreamResponse = { + streamId: 'stream-xyz-789', + streamType: 'json', + totalSize: 1024000, + chunkSize: 8192 +}; +``` + +--- + +### `PaginationParams` + +Pagination parameters for API requests. + +**Interface Definition:** + +```ts +interface PaginationParams { + page: number; + limit: number; + sortBy: string; + sortOrder: SortDirection; + search?: string; +} +``` + +**Properties:** + +- `page`: Current page number (1-based index). +- `limit`: Number of records per page. +- `sortBy`: Field by which the data is sorted. +- `sortOrder`: Sort order (ascending or descending). +- `search`: Optional search query. + +**Examples:** + +```ts +import { PaginationParams } from '@catbee/utils/types'; + +const params: PaginationParams = { + page: 1, + limit: 20, + sortBy: 'createdAt', + sortOrder: SortDirection.DESC, + search: 'john' +}; +``` + +--- + +### `WithPagination` + +Type that combines pagination parameters with additional data. + +**Type Definition:** + +```ts +type WithPagination = PaginationParams & T; +``` + +**Examples:** + +```ts +import { WithPagination } from '@catbee/utils/types'; + +interface UserFilters { + role: string; + active: boolean; +} + +type UserQuery = WithPagination; + +const query: UserQuery = { + page: 1, + limit: 20, + sortBy: 'name', + sortOrder: SortDirection.ASC, + role: 'admin', + active: true +}; +``` + +--- + +### `SortDirection` + +Sort direction enumeration. + +**Enum Definition:** + +```ts +enum SortDirection { + ASC = 'asc', + DESC = 'desc' +} +``` + +**Examples:** + +```ts +import { SortDirection } from '@catbee/utils/types'; + +const ascending = SortDirection.ASC; // 'asc' +const descending = SortDirection.DESC; // 'desc' + +function sortData(direction: SortDirection) { + // ... +} + +sortData(SortDirection.DESC); +``` diff --git a/docs/@catbee/utils/modules/url.md b/docs/@catbee/utils/modules/url.md index 20d94a7..275b4c5 100644 --- a/docs/@catbee/utils/modules/url.md +++ b/docs/@catbee/utils/modules/url.md @@ -1,20 +1,249 @@ -# URL Utilities +--- +slug: ../url +--- + +# URL Helpers for parsing and manipulating URLs. Includes functions for appending and extracting query parameters, validating URLs, joining and normalizing paths, building URLs, and parsing typed query params. All methods are fully typed. ## API Summary -- [**`appendQueryParams(url: string, params: Record): string`**](#appendqueryparams) - appends query parameters to a URL. -- [**`parseQueryString(query: string): Record`**](#parsequerystring) - parses a query string into a key-value object. -- [**`isValidUrl(url: string, requireHttps?: boolean): boolean`**](#isvalidurl) - checks if a string is a valid URL, optionally requiring HTTPS. -- [**`getDomain(url: string, removeSubdomains?: boolean): string`**](#getdomain) - extracts the domain name from a URL, optionally removing subdomains. -- [**`joinPaths(...segments: string[]): string`**](#joinpaths) - joins URL path segments, handling slashes. -- [**`normalizeUrl(url: string, base?: string): string`**](#normalizeurl) - normalizes a URL by resolving relative paths and protocol. -- [**`createUrlBuilder(baseUrl: string): (path: string, query?: Record) => string`**](#createurlbuilder) - creates a URL builder for constructing URLs with a base URL. -- [**`extractQueryParams(url: string, paramNames: string[]): Record`**](#extractqueryparams) - extracts specific query parameters from a URL. -- [**`removeQueryParams(url: string, paramsToRemove: string[]): string`**](#removequeryparams) - removes specified query parameters from a URL. -- [**`getExtension(url: string): string`**](#getextension) - gets the file extension from a URL path. -- [**`parseTypedQueryParams(url: string, converters?: Record T>): Record`**](#parsetypedqueryparams) - parses URL query parameters into a strongly-typed object. +### Classes + +- [**`UrlBuilder`**](#urlbuilder) - Fluent URL builder class for constructing and manipulating URLs. + +### Functions + +- [**`appendQueryParams(url, params): string`**](#appendqueryparams) - Appends query parameters to a URL. +- [**`parseQueryString(query): Record`**](#parsequerystring) - Parses a query string into a key-value object. +- [**`isValidUrl(url, requireHttps = false): boolean`**](#isvalidurl) - Checks if a string is a valid URL, optionally requiring HTTPS. +- [**`getDomain(url, removeSubdomains = false): string`**](#getdomain) - Extracts the domain name from a URL, optionally removing subdomains. +- [**`joinPaths(...segments): string`**](#joinpaths) - Joins URL path segments, handling slashes. +- [**`normalizeUrl(url, base?): string`**](#normalizeurl) - Normalizes a URL by resolving relative paths and protocol. +- [**`updateQueryParam(url, key, value): string`**](#updatequeryparam) - Updates or sets a single query parameter in a URL. +- [**`createUrlBuilder(baseUrl): Object`**](#createurlbuilder) - Creates a URL builder for constructing URLs with a base URL. +- [**`extractQueryParams(url, paramNames): Record`**](#extractqueryparams) - Extracts specific query parameters from a URL. +- [**`removeQueryParams(url, paramsToRemove): string`**](#removequeryparams) - Removes specified query parameters from a URL. +- [**`getExtension(url): string`**](#getextension) - Gets the file extension from a URL path. +- [**`parseTypedQueryParams(url, converters?): Partial`**](#parsetypedqueryparams) - Parses URL query parameters into a strongly-typed object. +- [**`getSubdomain(url): string`**](#getsubdomain) - Extracts the subdomain from a URL. +- [**`isRelativeUrl(url): boolean`**](#isrelativeurl) - Checks if a URL is relative (not absolute). +- [**`toAbsoluteUrl(relativeUrl, baseUrl): string`**](#toabsoluteurl) - Converts a relative URL to an absolute URL using a base URL. +- [**`sanitizeUrl(url, allowedProtocols = ['http', 'https']): string | null`**](#sanitizeurl) - Sanitizes a URL by removing dangerous protocols and normalizing. + +--- + +## Class Documentation + +### `UrlBuilder` + +Fluent URL builder class for constructing and manipulating URLs. All methods return a new instance, making it immutable. + +**Class Overview:** + +```ts +class UrlBuilder { + constructor(baseUrl?: string); + static from(url: string): UrlBuilder; + static http(host: string, path?: string): UrlBuilder; + static https(host: string, path?: string): UrlBuilder; + + clone(): UrlBuilder; + + // Protocol Methods + protocol(protocol: string): UrlBuilder; + http(): UrlBuilder; + https(): UrlBuilder; + + // Host/Domain Methods + host(hostname: string): UrlBuilder; + hostname(hostname: string): UrlBuilder; + port(port: number | string): UrlBuilder; + removePort(): UrlBuilder; + subdomain(subdomain: string): UrlBuilder; + + // Path Methods + path(path: string): UrlBuilder; + appendPath(...segments: string[]): UrlBuilder; + prependPath(...segments: string[]): UrlBuilder; + replacePathSegment(index: number, segment: string): UrlBuilder; + + // Query Parameter Methods + queryParam(key: string, value: string | number | boolean | null | undefined): UrlBuilder; + addQueryParams(params: Record): UrlBuilder; + setQueryParams(params: Record): UrlBuilder; + removeQueryParam(key: string): UrlBuilder; + removeQueryParams(keys: string[]): UrlBuilder; + clearQueryParams(): UrlBuilder; + appendQueryParam(key: string, value: string | number): UrlBuilder; + + // Hash/Fragment Methods + hash(hash: string): UrlBuilder; + removeHash(): UrlBuilder; + + // Username/Password Methods + username(username: string): UrlBuilder; + password(password: string): UrlBuilder; + auth(username: string, password: string): UrlBuilder; + removeAuth(): UrlBuilder; + + // Getter Methods + getProtocol(): string; + getHost(): string; + getHostname(): string; + getPort(): string; + getPath(): string; + getPathSegments(): string[]; + getQueryParam(key: string): string | null; + getQueryParams(): Record; + getQueryParamAll(key: string): string[]; + getHash(): string; + getSearch(): string; + getOrigin(): string; + getUsername(): string; + getPassword(): string; + + // Validation Methods + isValid(): boolean; + isHttps(): boolean; + isHttp(): boolean; + hasQueryParam(key: string): boolean; + hasHash(): boolean; + hasAuth(): boolean; + + // Transformation Methods + normalize(): UrlBuilder; + sanitize(allowedProtocols?: string[]): UrlBuilder | null; + lowercaseHost(): UrlBuilder; + + // Conversion Methods + build(): string; + toString(): string; + toURL(): URL; + toJSON(): string; + href(): string; + toObject(): { + protocol: string; + hostname: string; + port: string; + pathname: string; + search: string; + hash: string; + username: string; + password: string; + origin: string; + href: string; + queryParams: Record; + }; +} +``` + +**Examples:** + +```ts +import { UrlBuilder } from '@catbee/utils/url'; + +// Build from scratch +const url = new UrlBuilder() + .protocol('https') + .host('api.example.com') + .path('/users') + .queryParam('page', 1) + .queryParam('limit', 10) + .build(); +// -> 'https://api.example.com/users?page=1&limit=10' + +// Modify existing URL +const modified = UrlBuilder.from('https://example.com/old') + .path('/new') + .addQueryParams({ active: true, sort: 'name' }) + .toString(); +// -> 'https://example.com/new?active=true&sort=name' + +// Use static factory methods +const apiUrl = UrlBuilder.https('api.example.com', '/v1/users').build(); +const httpUrl = UrlBuilder.http('localhost', '/api').port(3000).build(); + +// Chain operations +const api = new UrlBuilder() + .https() + .host('api.example.com') + .appendPath('v1', 'users', '123') + .hash('profile') + .build(); +// -> 'https://api.example.com/v1/users/123#profile' + +// Work with query parameters +const searchUrl = new UrlBuilder('https://example.com/search') + .queryParam('q', 'typescript') + .queryParam('lang', 'en') + .queryParam('page', 1) + .build(); +// -> 'https://example.com/search?q=typescript&lang=en&page=1' + +// Modify paths dynamically +const resourceUrl = UrlBuilder.from('https://api.example.com/v1') + .appendPath('users', 'john', 'posts') + .queryParam('status', 'published') + .build(); +// -> 'https://api.example.com/v1/users/john/posts?status=published' + +// Handle authentication +const authUrl = new UrlBuilder('https://example.com') + .auth('user', 'pass123') + .path('/secure') + .build(); +// -> 'https://user:pass123@example.com/secure' + +// Use getters to inspect URL components +const builder = UrlBuilder.from('https://api.example.com:8080/users?page=1#top'); +console.log(builder.getProtocol()); // 'https:' +console.log(builder.getHost()); // 'api.example.com' +console.log(builder.getPort()); // '8080' +console.log(builder.getPath()); // '/users' +console.log(builder.getQueryParams()); // { page: '1' } +console.log(builder.getHash()); // '#top' + +// Validation +const url1 = new UrlBuilder('https://example.com'); +console.log(url1.isValid()); // true +console.log(url1.isHttps()); // true +console.log(url1.hasQueryParam('page')); // false + +// Complex URL manipulation +const complexUrl = new UrlBuilder() + .https() + .subdomain('api') + .host('example.com') + .port(443) + .appendPath('v2', 'products') + .addQueryParams({ + category: 'electronics', + sort: 'price', + order: 'asc' + }) + .hash('filters') + .build(); +// -> 'https://api.example.com:443/v2/products?category=electronics&sort=price&order=asc#filters' + +// Export as object +const urlObj = UrlBuilder.from('https://api.example.com/users?active=true').toObject(); +/* +{ + protocol: 'https:', + hostname: 'api.example.com', + port: '', + pathname: '/users', + search: '?active=true', + hash: '', + username: '', + password: '', + origin: 'https://api.example.com', + href: 'https://api.example.com/users?active=true', + queryParams: { active: 'true' } +} +*/ +``` --- @@ -42,7 +271,7 @@ function appendQueryParams(url: string, params: Record) **Example:** ```ts -import { appendQueryParams } from '@catbee/utils'; +import { appendQueryParams } from '@catbee/utils/url'; appendQueryParams('https://example.com', { page: 1, limit: 10 }); // → 'https://example.com/?page=1&limit=10' @@ -71,7 +300,7 @@ function parseQueryString(query: string): Record; **Example:** ```ts -import { parseQueryString } from '@catbee/utils'; +import { parseQueryString } from '@catbee/utils/url'; parseQueryString('?page=1&limit=10'); // → { page: '1', limit: '10' } @@ -86,7 +315,7 @@ Validates if a string is a valid URL, optionally requiring HTTPS. **Method Signature:** ```ts -function isValidUrl(url: string, requireHttps?: boolean): boolean; +function isValidUrl(url: string, requireHttps: boolean = false): boolean; ``` **Parameters:** @@ -101,7 +330,7 @@ function isValidUrl(url: string, requireHttps?: boolean): boolean; **Example:** ```ts -import { isValidUrl } from '@catbee/utils'; +import { isValidUrl } from '@catbee/utils/url'; isValidUrl('https://example.com'); // true isValidUrl('ftp://example.com'); // false (if requireHttps is true) @@ -116,13 +345,13 @@ Extracts the domain name from a URL, optionally removing subdomains. **Method Signature:** ```ts -function getDomain(url: string, removeSubdomains?: boolean): string; +function getDomain(url: string, removeSubdomains: boolean = false): string; ``` **Parameters:** - `url`: The URL to extract the domain from. -- `removeSubdomains`: Whether to remove subdomains (default is false). +- `removeSubdomains`: Whether to remove subdomains (default: false). **Returns:** @@ -131,7 +360,7 @@ function getDomain(url: string, removeSubdomains?: boolean): string; **Example:** ```ts -import { getDomain } from '@catbee/utils'; +import { getDomain } from '@catbee/utils/url'; getDomain('https://api.example.com/path'); // 'api.example.com' getDomain('https://api.example.com/path', true); // 'example.com' @@ -160,7 +389,7 @@ function joinPaths(...segments: string[]): string; **Example:** ```ts -import { joinPaths } from '@catbee/utils'; +import { joinPaths } from '@catbee/utils/url'; joinPaths('https://example.com/', '/api/', '/users'); // → 'https://example.com/api/users' @@ -190,7 +419,7 @@ function normalizeUrl(url: string, base?: string): string; **Example:** ```ts -import { normalizeUrl } from '@catbee/utils'; +import { normalizeUrl } from '@catbee/utils/url'; normalizeUrl('HTTP://Example.COM/foo/../bar'); // → 'http://example.com/bar' @@ -198,6 +427,37 @@ normalizeUrl('HTTP://Example.COM/foo/../bar'); --- +### `updateQueryParam()` + +Updates or sets a single query parameter in a URL. + +**Method Signature:** + +```ts +function updateQueryParam(url: string, key: string, value: string | number): string; +``` + +**Parameters:** + +- `url`: The source URL. +- `key`: Parameter key. +- `value`: Parameter value. + +**Returns:** + +- The URL with updated parameter. + +**Example:** + +```ts +import { updateQueryParam } from '@catbee/utils/url'; + +updateQueryParam('https://example.com?page=1', 'page', 2); +// → 'https://example.com?page=2' +``` + +--- + ### `createUrlBuilder()` Creates a URL builder for constructing URLs with a base URL. @@ -205,7 +465,10 @@ Creates a URL builder for constructing URLs with a base URL. **Method Signature:** ```ts -function createUrlBuilder(baseUrl: string): (path: string, query?: Record) => string; +function createUrlBuilder(baseUrl: string): { + path(path: string, params?: Record): string; + query(params: Record): string; +}; ``` **Parameters:** @@ -214,18 +477,22 @@ function createUrlBuilder(baseUrl: string): (path: string, query?: Record(url: string, converters?: Record any>): Partial; +function parseTypedQueryParams>( + url: string, + converters?: Record any> +): Partial; ``` **Parameters:** @@ -339,7 +609,7 @@ function parseTypedQueryParams(url: string, converters?: Record('https://example.com?page=2&q=test', { page: Number, @@ -347,3 +617,128 @@ parseTypedQueryParams<{ page: number; q: string }>('https://example.com?page=2&q }); // → { page: 2, q: 'test' } ``` + +--- + +### `getSubdomain()` + +Extracts the subdomain from a URL. + +**Method Signature:** + +```ts +function getSubdomain(url: string): string; +``` + +**Parameters:** + +- `url`: The URL to extract the subdomain from. + +**Returns:** + +- The subdomain or empty string if none exists. + +**Example:** + +```ts +import { getSubdomain } from '@catbee/utils/url'; + +getSubdomain('https://api.example.com'); // 'api' +getSubdomain('https://www.blog.example.com'); // 'www.blog' +getSubdomain('https://example.com'); // '' +``` + +--- + +### `isRelativeUrl()` + +Checks if a URL is relative (not absolute). + +**Method Signature:** + +```ts +function isRelativeUrl(url: string): boolean; +``` + +**Parameters:** + +- `url`: The URL to check. + +**Returns:** + +- `true` if the URL is relative, otherwise `false`. + +**Example:** + +```ts +import { isRelativeUrl } from '@catbee/utils/url'; + +isRelativeUrl('/path/to/page'); // true +isRelativeUrl('./relative'); // true +isRelativeUrl('../parent'); // true +isRelativeUrl('https://example.com/page'); // false +``` + +--- + +### `toAbsoluteUrl()` + +Converts a relative URL to an absolute URL using a base URL. + +**Method Signature:** + +```ts +function toAbsoluteUrl(relativeUrl: string, baseUrl: string): string; +``` + +**Parameters:** + +- `relativeUrl`: The relative URL to convert. +- `baseUrl`: The base URL to resolve against. + +**Returns:** + +- The absolute URL. + +**Example:** + +```ts +import { toAbsoluteUrl } from '@catbee/utils/url'; + +toAbsoluteUrl('/api/users', 'https://example.com'); +// → 'https://example.com/api/users' + +toAbsoluteUrl('../parent', 'https://example.com/path/to/page'); +// → 'https://example.com/path/parent' +``` + +--- + +### `sanitizeUrl()` + +Sanitizes a URL by removing dangerous protocols and normalizing. + +**Method Signature:** + +```ts +function sanitizeUrl(url: string, allowedProtocols: string[] = ['http', 'https']): string | null; +``` + +**Parameters:** + +- `url`: The URL to sanitize. +- `allowedProtocols`: Array of allowed protocols (default: `['http', 'https']`). + +**Returns:** + +- The sanitized URL or `null` if the URL uses a disallowed protocol. + +**Example:** + +```ts +import { sanitizeUrl } from '@catbee/utils/url'; + +sanitizeUrl('javascript:alert(1)'); // null (dangerous protocol) +sanitizeUrl('https://example.com'); // 'https://example.com/' +sanitizeUrl('ftp://example.com', ['ftp', 'http', 'https']); // 'ftp://example.com/' +``` diff --git a/docs/@catbee/utils/modules/validation.md b/docs/@catbee/utils/modules/validation.md index 4babab8..9f3d539 100644 --- a/docs/@catbee/utils/modules/validation.md +++ b/docs/@catbee/utils/modules/validation.md @@ -1,9 +1,10 @@ --- id: validation pagination_next: null +slug: ../validation --- -# Validation Utilities +# Validation Comprehensive suite of validators for strings, numbers, arrays, objects, dates, patterns, and more. Includes type guards, range checks, format checks, and multi-validator helpers. All methods are fully typed. @@ -12,7 +13,7 @@ Comprehensive suite of validators for strings, numbers, arrays, objects, dates, - [**`isPort(str: string | number): boolean`**](#isport) - Checks if a value is a valid port number (1-65535). - [**`isEmail(str: string): boolean`**](#isemail) - Checks if a string is a valid email address. - [**`isUUID(str: string): boolean`**](#isuuid) - Checks if a string is a valid UUID (versions 1-5). -- [**`isURL(str: string): boolean`**](#isurl) - Checks if a string is a valid URL. +- [**`isURL(str: string, allowedProtocols = ['http', 'https', 'ws', 'wss']): boolean`**](#isurl) - Checks if a string is a valid URL. - [**`isPhone(str: string): boolean`**](#isphone) - Checks if a string is a valid international phone number. - [**`isAlphanumeric(str: string): boolean`**](#isalphanumeric) - Checks if a string contains only letters and numbers. - [**`isNumeric(value: string | number): boolean`**](#isnumeric) - Checks if a value is numeric. @@ -26,9 +27,9 @@ Comprehensive suite of validators for strings, numbers, arrays, objects, dates, - [**`isIPv6(str: string): boolean`**](#isipv6) - Checks if a string is a valid IPv6 address. - [**`isCreditCard(str: string): boolean`**](#iscreditcard) - Checks if a string is a valid credit card number. - [**`isValidJSON(str: string): boolean`**](#isvalidjson) - Checks if a string is valid JSON. -- [**`isArray(value: unknown, itemGuard?: (item: unknown) => item is T): boolean`**](#isarray) - Checks if a value is an array. -- [**`isObject(value: unknown): boolean`**](#isobject) - Checks if a value is an object. -- [**`hasRequiredProps(obj: object, requiredProps: string[]): boolean`**](#hasrequiredprops) - Checks if an object has all required properties. +- [**`isArray(value: unknown, itemGuard?: (item: unknown) => item is T): value is T[]`**](#isarray) - Checks if a value is an array. +- [**`isBase64(str: string): boolean`**](#isbase64) - Checks if a string is a valid base64 encoded string. +- [**`hasRequiredProps(obj: Record, requiredProps: string[]): boolean`**](#hasrequiredprops) - Checks if an object has all required properties. - [**`isDateInRange(date: Date, minDate?: Date, maxDate?: Date): boolean`**](#isdateinrange) - Checks if a date is within a range. - [**`matchesPattern(str: string, pattern: RegExp): boolean`**](#matchespattern) - Checks if a string matches a regex pattern. - [**`validateAll(value: unknown, validators: Array<(value: unknown) => boolean>): boolean`**](#validateall) - Validates a value against multiple validators. @@ -58,7 +59,7 @@ function isPort(value: string | number): boolean; **Example:** ```ts -import { isPort } from '@catbee/utils'; +import { isPort } from '@catbee/utils/validation'; isPort(8080); // true isPort('65536'); // false @@ -87,7 +88,7 @@ function isEmail(value: string): boolean; **Example:** ```ts -import { isEmail } from '@catbee/utils'; +import { isEmail } from '@catbee/utils/validation'; isEmail('user@example.com'); // true ``` @@ -101,12 +102,13 @@ Checks if a string is a valid UUID (versions 1-5). **Method Signature:** ```ts -function isUUID(value: string): boolean; +function isURL(str: string, allowedProtocols: string[] = ['http', 'https', 'ws', 'wss']): boolean; ``` **Parameters:** -- `value`: The value to check (string). +- `str`: The value to check (string). +- `allowedProtocols`: Array of allowed URL protocols (default: `['http', 'https', 'ws', 'wss']`). **Returns:** @@ -115,7 +117,7 @@ function isUUID(value: string): boolean; **Example:** ```ts -import { isUUID } from '@catbee/utils'; +import { isUUID } from '@catbee/utils/validation'; isUUID('550e8400-e29b-41d4-a716-446655440000'); // true ``` @@ -129,23 +131,26 @@ Checks if a string is a valid URL. **Method Signature:** ```ts -function isURL(value: string): boolean; +function isURL(str: string, allowedProtocols: string[] = ['http', 'https', 'ws', 'wss']): boolean; ``` **Parameters:** -- `value`: The value to check (string). +- `str`: The value to check (string). +- `allowedProtocols`: Array of allowed URL protocols (default: `['http', 'https', 'ws', 'wss']`). **Returns:** -- `true` if the value is a valid URL, otherwise `false`. +- `true` if the value is a valid URL with an allowed protocol, otherwise `false`. **Example:** ```ts -import { isURL } from '@catbee/utils'; +import { isURL } from '@catbee/utils/validation'; isURL('https://example.com'); // true +isURL('ftp://example.com'); // false (ftp not in default allowed protocols) +isURL('ftp://example.com', ['ftp']); // true (ftp is allowed) ``` --- @@ -171,7 +176,7 @@ function isPhone(value: string): boolean; **Example:** ```ts -import { isPhone } from '@catbee/utils'; +import { isPhone } from '@catbee/utils/validation'; isPhone('+1-800-555-1234'); // true ``` @@ -199,7 +204,7 @@ function isAlphanumeric(value: string): boolean; **Example:** ```ts -import { isAlphanumeric } from '@catbee/utils'; +import { isAlphanumeric } from '@catbee/utils/validation'; isAlphanumeric('abc123'); // true ``` @@ -227,7 +232,7 @@ function isNumeric(value: string | number): boolean; **Example:** ```ts -import { isNumeric } from '@catbee/utils'; +import { isNumeric } from '@catbee/utils/validation'; isNumeric('42'); // true isNumeric('abc'); // false @@ -256,7 +261,7 @@ function isHexColor(value: string): boolean; **Example:** ```ts -import { isHexColor } from '@catbee/utils'; +import { isHexColor } from '@catbee/utils/validation'; isHexColor('#fff'); // true isHexColor('#123abc'); // true @@ -285,7 +290,7 @@ function isISODate(value: string): boolean; **Example:** ```ts -import { isISODate } from '@catbee/utils'; +import { isISODate } from '@catbee/utils/validation'; isISODate('2023-01-01T12:00:00Z'); // true ``` @@ -315,7 +320,7 @@ function isLengthBetween(str: string, min: number, max: number): boolean; **Example:** ```ts -import { isLengthBetween } from '@catbee/utils'; +import { isLengthBetween } from '@catbee/utils/validation'; isLengthBetween('abc', 2, 5); // true ``` @@ -345,7 +350,7 @@ function isNumberBetween(value: number, min: number, max: number): boolean; **Example:** ```ts -import { isNumberBetween } from '@catbee/utils'; +import { isNumberBetween } from '@catbee/utils/validation'; isNumberBetween(5, 1, 10); // true ``` @@ -373,7 +378,7 @@ function isAlpha(value: string): boolean; **Example:** ```ts -import { isAlpha } from '@catbee/utils'; +import { isAlpha } from '@catbee/utils/validation'; isAlpha('abcDEF'); // true ``` @@ -401,7 +406,7 @@ function isStrongPassword(value: string): boolean; **Example:** ```ts -import { isStrongPassword } from '@catbee/utils'; +import { isStrongPassword } from '@catbee/utils/validation'; isStrongPassword('Abc123!@#'); // true ``` @@ -429,7 +434,7 @@ function isIPv4(value: string): boolean; **Example:** ```ts -import { isIPv4 } from '@catbee/utils'; +import { isIPv4 } from '@catbee/utils/validation'; isIPv4('192.168.1.1'); // true ``` @@ -457,7 +462,7 @@ function isIPv6(value: string): boolean; **Example:** ```ts -import { isIPv6 } from '@catbee/utils'; +import { isIPv6 } from '@catbee/utils/validation'; isIPv6('2001:0db8:85a3:0000:0000:8a2e:0370:7334'); // true ``` @@ -485,7 +490,7 @@ function isCreditCard(value: string): boolean; **Example:** ```ts -import { isCreditCard } from '@catbee/utils'; +import { isCreditCard } from '@catbee/utils/validation'; isCreditCard('4111111111111111'); // true ``` @@ -513,7 +518,7 @@ function isValidJSON(value: string): boolean; **Example:** ```ts -import { isValidJSON } from '@catbee/utils'; +import { isValidJSON } from '@catbee/utils/validation'; isValidJSON('{"a":1}'); // true ``` @@ -542,37 +547,38 @@ function isArray(value: unknown, itemGuard?: (item: unknown) => item is T): v **Example:** ```ts -import { isArray } from '@catbee/utils'; +import { isArray } from '@catbee/utils/validation'; isArray([1, 2, 3], (x): x is number => typeof x === 'number'); // true ``` --- -### `isObject()` +### `isBase64()` -Checks if a value is a non-null object. +Checks if a string is a valid base64 encoded string. **Method Signature:** ```ts -function isObject(value: unknown): value is Record; +function isBase64(str: string): boolean; ``` **Parameters:** -- `value`: The value to check (unknown). +- `str`: The value to check (string). **Returns:** -- `true` if the value is a non-null object, otherwise `false`. +- `true` if the value is a valid base64 encoded string, otherwise `false`. **Example:** ```ts -import { isObject } from '@catbee/utils'; +import { isBase64 } from '@catbee/utils/validation'; -isObject({ a: 1 }); // true +isBase64('SGVsbG8gV29ybGQ='); // true +isBase64('Hello World'); // false ``` --- @@ -599,7 +605,7 @@ function hasRequiredProps(obj: Record, requiredProps: string[]) **Example:** ```ts -import { hasRequiredProps } from '@catbee/utils'; +import { hasRequiredProps } from '@catbee/utils/validation'; hasRequiredProps({ a: 1, b: 2 }, ['a', 'b']); // true ``` @@ -629,7 +635,7 @@ function isDateInRange(date: Date, minDate?: Date, maxDate?: Date): boolean; **Example:** ```ts -import { isDateInRange } from '@catbee/utils'; +import { isDateInRange } from '@catbee/utils/validation'; isDateInRange(new Date(), new Date('2020-01-01'), new Date('2030-01-01')); // true ``` @@ -658,7 +664,7 @@ function matchesPattern(str: string, pattern: RegExp): boolean; **Example:** ```ts -import { matchesPattern } from '@catbee/utils'; +import { matchesPattern } from '@catbee/utils/validation'; matchesPattern('abc123', /^[a-z]+\d+$/); // true ``` @@ -687,6 +693,6 @@ function validateAll(value: unknown, validators: Array<(value: unknown) => boole **Example:** ```ts -import { validateAll, isAlpha } from '@catbee/utils'; +import { validateAll, isAlpha } from '@catbee/utils/validation'; validateAll('abc', [isAlpha, str => str.length > 2]); // true ``` diff --git a/docs/@catbee/utils/utils.md b/docs/@catbee/utils/utils.md index 5827eec..91fefb2 100644 --- a/docs/@catbee/utils/utils.md +++ b/docs/@catbee/utils/utils.md @@ -12,52 +12,52 @@ A modular, production-grade utility library for Node.js and TypeScript. - [Introduction](./intro) - Overview, features, and installation - [Configuration](./config) - Configuration options and setup +### Express Server + +- [Express Server](./server) - Express.js server utilities and helpers + ### Utility Modules - [Utilities Overview](./modules) - Overview of all available utility modules #### Core Utilities -- [Array](./modules/array) - Array manipulation and utilities -- [Async](./modules/async) - Asynchronous operations and helpers -- [Object](./modules/object) - Object manipulation and utilities -- [String](./modules/string) - String processing and formatting -- [Type](./modules/type) - Type checking and validation utilities -- [Date](./modules/date) - Date manipulation and formatting -- [ID](./modules/id) - Unique identifier generation +- [Array](./array) - Array manipulation and utilities +- [Async](./async) - Asynchronous operations and helpers +- [Object](./object) - Object manipulation and utilities +- [String](./string) - String processing and formatting +- [Type](./type) - Type checking and validation utilities +- [Date](./date) - Date manipulation and formatting +- [ID](./id) - Unique identifier generation #### System & Performance -- [Cache](./modules/cache) - Caching mechanisms and strategies -- [Performance](./modules/performance) - Performance monitoring and optimization -- [Environment](./modules/environment) - Environment variable management -- [Context Store](./modules/context-store) - Context storage and management -- [Logger](./modules/logger) - Logging utilities and formatters +- [Cache](./cache) - Caching mechanisms and strategies +- [Performance](./performance) - Performance monitoring and optimization +- [Environment](./env) - Environment variable management +- [Context Store](./context-store) - Context storage and management +- [Logger](./logger) - Logging utilities and formatters #### File & Directory -- [File System](./modules/file-system) - File system operations -- [Directory](./modules/directory) - Directory management utilities -- [Stream](./modules/stream) - Stream processing utilities +- [File System](./fs) - File system operations +- [Directory](./directory) - Directory management utilities +- [Stream](./stream) - Stream processing utilities #### Web & Network -- [Request](./modules/request) - HTTP request utilities -- [Response](./modules/response) - HTTP response utilities -- [URL](./modules/url) - URL parsing and manipulation -- [HTTP Status Codes](./modules/http-status-codes) - HTTP status code constants +- [Request](./request) - HTTP request utilities +- [Response](./response) - HTTP response utilities +- [URL](./url) - URL parsing and manipulation +- [HTTP Status Codes](./http-status-codes) - HTTP status code constants #### Security & Validation -- [Crypto](./modules/crypto) - Cryptographic operations -- [Validation](./modules/validation) - Data validation utilities -- [Exception](./modules/exception) - Error handling and exceptions +- [Crypto](./crypto) - Cryptographic operations +- [Validation](./validation) - Data validation utilities +- [Exception](./exception) - Error handling and exceptions #### Advanced -- [Decorators](./modules/decorators) - TypeScript decorators -- [Middleware](./modules/middleware) - Middleware utilities - -### Express Server - -- [Express Server](./express-server) - Express.js server utilities and helpers +- [Decorator](./decorator) - TypeScript decorators +- [Middleware](./middleware) - Middleware utilities diff --git a/package-lock.json b/package-lock.json index 8ef9736..1b8f642 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,19 +20,19 @@ "prism-react-renderer": "^2.4.1", "react": "^19.2.3", "react-dom": "^19.2.3", - "sass": "^1.97.0" + "sass": "^1.97.1" }, "devDependencies": { "@catbee/utils": "^1.1.0", - "@commitlint/config-conventional": "^20.2.0", + "@commitlint/config-conventional": "^20.3.0", "@docusaurus/eslint-plugin": "^3.9.2", "@docusaurus/module-type-aliases": "^3.9.2", "@docusaurus/tsconfig": "^3.9.2", "@docusaurus/types": "^3.9.2", - "@ng-catbee/monaco-editor": "^21.0.1", + "@ng-catbee/monaco-editor": "^21.0.3", "@types/eslint__js": "^9.14.0", "@types/eslint-plugin-jsx-a11y": "^6.10.1", - "@typescript-eslint/parser": "^8.50.0", + "@typescript-eslint/parser": "^8.51.0", "eslint": "^9.39.2", "eslint-config-prettier": "^10.1.8", "eslint-plugin-jsx-a11y": "^6.10.2", @@ -44,10 +44,10 @@ "prettier": "^3.7.4", "rimraf": "^6.1.2", "typescript": "~5.9.3", - "typescript-eslint": "^8.50.0" + "typescript-eslint": "^8.51.0" }, "engines": { - "node": ">=18.0" + "node": ">=18.20.8" } }, "node_modules/@ai-sdk/gateway": { @@ -2100,9 +2100,9 @@ } }, "node_modules/@commitlint/config-conventional": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-20.2.0.tgz", - "integrity": "sha512-MsRac+yNIbTB4Q/psstKK4/ciVzACHicSwz+04Sxve+4DW+PiJeTjU0JnS4m/oOnulrXYN+yBPlKaBSGemRfgQ==", + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-20.3.0.tgz", + "integrity": "sha512-g1OXVl6E2v0xF1Ru2RpxQ+Vfy7XUcUsCmLKzGUrhFLS4hSNykje0QSy6djBtzOiOBQCepBrmIlqx/gRlzrSh5A==", "dev": true, "license": "MIT", "dependencies": { @@ -4970,9 +4970,9 @@ } }, "node_modules/@ng-catbee/monaco-editor": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/@ng-catbee/monaco-editor/-/monaco-editor-21.0.1.tgz", - "integrity": "sha512-FopImXtd1ittnHmubQvDBtC4FNTsqM55il9r63uElyzjBWCz4DIGEaNcqVj5XiGcn1f0k394uHAOxTveFNqj3g==", + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@ng-catbee/monaco-editor/-/monaco-editor-21.0.3.tgz", + "integrity": "sha512-PXe/xvA43B2Wh89VN1hGuWfdNccmxPKnkEr9KX1uNw1AzV3RAz3ZHNW71VQ18RuS91mBBCzXUESAL44ydhtSpg==", "dev": true, "license": "MIT", "dependencies": { @@ -6565,20 +6565,20 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.50.0.tgz", - "integrity": "sha512-O7QnmOXYKVtPrfYzMolrCTfkezCJS9+ljLdKW/+DCvRsc3UAz+sbH6Xcsv7p30+0OwUbeWfUDAQE0vpabZ3QLg==", + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.51.0.tgz", + "integrity": "sha512-XtssGWJvypyM2ytBnSnKtHYOGT+4ZwTnBVl36TA4nRO2f4PRNGz5/1OszHzcZCvcBMh+qb7I06uoCmLTRdR9og==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.50.0", - "@typescript-eslint/type-utils": "8.50.0", - "@typescript-eslint/utils": "8.50.0", - "@typescript-eslint/visitor-keys": "8.50.0", + "@typescript-eslint/scope-manager": "8.51.0", + "@typescript-eslint/type-utils": "8.51.0", + "@typescript-eslint/utils": "8.51.0", + "@typescript-eslint/visitor-keys": "8.51.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" + "ts-api-utils": "^2.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6588,7 +6588,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.50.0", + "@typescript-eslint/parser": "^8.51.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } @@ -6604,17 +6604,17 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.50.0.tgz", - "integrity": "sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==", + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.51.0.tgz", + "integrity": "sha512-3xP4XzzDNQOIqBMWogftkwxhg5oMKApqY0BAflmLZiFYHqyhSOxv/cd/zPQLTcCXr4AkaKb25joocY0BD1WC6A==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.50.0", - "@typescript-eslint/types": "8.50.0", - "@typescript-eslint/typescript-estree": "8.50.0", - "@typescript-eslint/visitor-keys": "8.50.0", + "@typescript-eslint/scope-manager": "8.51.0", + "@typescript-eslint/types": "8.51.0", + "@typescript-eslint/typescript-estree": "8.51.0", + "@typescript-eslint/visitor-keys": "8.51.0", "debug": "^4.3.4" }, "engines": { @@ -6630,14 +6630,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.50.0.tgz", - "integrity": "sha512-Cg/nQcL1BcoTijEWyx4mkVC56r8dj44bFDvBdygifuS20f3OZCHmFbjF34DPSi07kwlFvqfv/xOLnJ5DquxSGQ==", + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.51.0.tgz", + "integrity": "sha512-Luv/GafO07Z7HpiI7qeEW5NW8HUtZI/fo/kE0YbtQEFpJRUuR0ajcWfCE5bnMvL7QQFrmT/odMe8QZww8X2nfQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.50.0", - "@typescript-eslint/types": "^8.50.0", + "@typescript-eslint/tsconfig-utils": "^8.51.0", + "@typescript-eslint/types": "^8.51.0", "debug": "^4.3.4" }, "engines": { @@ -6652,14 +6652,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.50.0.tgz", - "integrity": "sha512-xCwfuCZjhIqy7+HKxBLrDVT5q/iq7XBVBXLn57RTIIpelLtEIZHXAF/Upa3+gaCpeV1NNS5Z9A+ID6jn50VD4A==", + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.51.0.tgz", + "integrity": "sha512-JhhJDVwsSx4hiOEQPeajGhCWgBMBwVkxC/Pet53EpBVs7zHHtayKefw1jtPaNRXpI9RA2uocdmpdfE7T+NrizA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.50.0", - "@typescript-eslint/visitor-keys": "8.50.0" + "@typescript-eslint/types": "8.51.0", + "@typescript-eslint/visitor-keys": "8.51.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6670,9 +6670,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.50.0.tgz", - "integrity": "sha512-vxd3G/ybKTSlm31MOA96gqvrRGv9RJ7LGtZCn2Vrc5htA0zCDvcMqUkifcjrWNNKXHUU3WCkYOzzVSFBd0wa2w==", + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.51.0.tgz", + "integrity": "sha512-Qi5bSy/vuHeWyir2C8u/uqGMIlIDu8fuiYWv48ZGlZ/k+PRPHtaAu7erpc7p5bzw2WNNSniuxoMSO4Ar6V9OXw==", "dev": true, "license": "MIT", "engines": { @@ -6687,17 +6687,17 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.50.0.tgz", - "integrity": "sha512-7OciHT2lKCewR0mFoBrvZJ4AXTMe/sYOe87289WAViOocEmDjjv8MvIOT2XESuKj9jp8u3SZYUSh89QA4S1kQw==", + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.51.0.tgz", + "integrity": "sha512-0XVtYzxnobc9K0VU7wRWg1yiUrw4oQzexCG2V2IDxxCxhqBMSMbjB+6o91A+Uc0GWtgjCa3Y8bi7hwI0Tu4n5Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.50.0", - "@typescript-eslint/typescript-estree": "8.50.0", - "@typescript-eslint/utils": "8.50.0", + "@typescript-eslint/types": "8.51.0", + "@typescript-eslint/typescript-estree": "8.51.0", + "@typescript-eslint/utils": "8.51.0", "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" + "ts-api-utils": "^2.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6712,9 +6712,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.50.0.tgz", - "integrity": "sha512-iX1mgmGrXdANhhITbpp2QQM2fGehBse9LbTf0sidWK6yg/NE+uhV5dfU1g6EYPlcReYmkE9QLPq/2irKAmtS9w==", + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.51.0.tgz", + "integrity": "sha512-TizAvWYFM6sSscmEakjY3sPqGwxZRSywSsPEiuZF6d5GmGD9Gvlsv0f6N8FvAAA0CD06l3rIcWNbsN1e5F/9Ag==", "dev": true, "license": "MIT", "engines": { @@ -6726,21 +6726,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.50.0.tgz", - "integrity": "sha512-W7SVAGBR/IX7zm1t70Yujpbk+zdPq/u4soeFSknWFdXIFuWsBGBOUu/Tn/I6KHSKvSh91OiMuaSnYp3mtPt5IQ==", + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.51.0.tgz", + "integrity": "sha512-1qNjGqFRmlq0VW5iVlcyHBbCjPB7y6SxpBkrbhNWMy/65ZoncXCEPJxkRZL8McrseNH6lFhaxCIaX+vBuFnRng==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.50.0", - "@typescript-eslint/tsconfig-utils": "8.50.0", - "@typescript-eslint/types": "8.50.0", - "@typescript-eslint/visitor-keys": "8.50.0", + "@typescript-eslint/project-service": "8.51.0", + "@typescript-eslint/tsconfig-utils": "8.51.0", + "@typescript-eslint/types": "8.51.0", + "@typescript-eslint/visitor-keys": "8.51.0", "debug": "^4.3.4", "minimatch": "^9.0.4", "semver": "^7.6.0", "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.1.0" + "ts-api-utils": "^2.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6754,16 +6754,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.50.0.tgz", - "integrity": "sha512-87KgUXET09CRjGCi2Ejxy3PULXna63/bMYv72tCAlDJC3Yqwln0HiFJ3VJMst2+mEtNtZu5oFvX4qJGjKsnAgg==", + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.51.0.tgz", + "integrity": "sha512-11rZYxSe0zabiKaCP2QAwRf/dnmgFgvTmeDTtZvUvXG3UuAdg/GU02NExmmIXzz3vLGgMdtrIosI84jITQOxUA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.50.0", - "@typescript-eslint/types": "8.50.0", - "@typescript-eslint/typescript-estree": "8.50.0" + "@typescript-eslint/scope-manager": "8.51.0", + "@typescript-eslint/types": "8.51.0", + "@typescript-eslint/typescript-estree": "8.51.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6778,13 +6778,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.50.0.tgz", - "integrity": "sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q==", + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.51.0.tgz", + "integrity": "sha512-mM/JRQOzhVN1ykejrvwnBRV3+7yTKK8tVANVN3o1O0t0v7o+jqdVu9crPy5Y9dov15TJk/FTIgoUGHrTOVL3Zg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.50.0", + "@typescript-eslint/types": "8.51.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -20264,9 +20264,9 @@ "license": "MIT" }, "node_modules/sass": { - "version": "1.97.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.0.tgz", - "integrity": "sha512-KR0igP1z4avUJetEuIeOdDlwaUDvkH8wSx7FdSjyYBS3dpyX3TzHfAMO0G1Q4/3cdjcmi3r7idh+KCmKqS+KeQ==", + "version": "1.97.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.1.tgz", + "integrity": "sha512-uf6HoO8fy6ClsrShvMgaKUn14f2EHQLQRtpsZZLeU/Mv0Q1K5P0+x2uvH6Cub39TVVbWNSrraUhDAoFph6vh0A==", "license": "MIT", "peer": true, "dependencies": { @@ -21937,9 +21937,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.3.0.tgz", + "integrity": "sha512-6eg3Y9SF7SsAvGzRHQvvc1skDAhwI4YQ32ui1scxD1Ccr0G5qIIbUBT3pFTKX8kmWIQClHobtUdNuaBgwdfdWg==", "dev": true, "license": "MIT", "engines": { @@ -22135,16 +22135,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.50.0.tgz", - "integrity": "sha512-Q1/6yNUmCpH94fbgMUMg2/BSAr/6U7GBk61kZTv1/asghQOWOjTlp9K8mixS5NcJmm2creY+UFfGeW/+OcA64A==", + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.51.0.tgz", + "integrity": "sha512-jh8ZuM5oEh2PSdyQG9YAEM1TCGuWenLSuSUhf/irbVUNW9O5FhbFVONviN2TgMTBnUmyHv7E56rYnfLZK6TkiA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.50.0", - "@typescript-eslint/parser": "8.50.0", - "@typescript-eslint/typescript-estree": "8.50.0", - "@typescript-eslint/utils": "8.50.0" + "@typescript-eslint/eslint-plugin": "8.51.0", + "@typescript-eslint/parser": "8.51.0", + "@typescript-eslint/typescript-estree": "8.51.0", + "@typescript-eslint/utils": "8.51.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/package.json b/package.json index 969611d..70a1513 100644 --- a/package.json +++ b/package.json @@ -39,19 +39,19 @@ "prism-react-renderer": "^2.4.1", "react": "^19.2.3", "react-dom": "^19.2.3", - "sass": "^1.97.0" + "sass": "^1.97.1" }, "devDependencies": { "@catbee/utils": "^1.1.0", - "@commitlint/config-conventional": "^20.2.0", + "@commitlint/config-conventional": "^20.3.0", "@docusaurus/eslint-plugin": "^3.9.2", "@docusaurus/module-type-aliases": "^3.9.2", "@docusaurus/tsconfig": "^3.9.2", "@docusaurus/types": "^3.9.2", - "@ng-catbee/monaco-editor": "^21.0.1", + "@ng-catbee/monaco-editor": "^21.0.3", "@types/eslint__js": "^9.14.0", "@types/eslint-plugin-jsx-a11y": "^6.10.1", - "@typescript-eslint/parser": "^8.50.0", + "@typescript-eslint/parser": "^8.51.0", "eslint": "^9.39.2", "eslint-config-prettier": "^10.1.8", "eslint-plugin-jsx-a11y": "^6.10.2", @@ -63,7 +63,7 @@ "prettier": "^3.7.4", "rimraf": "^6.1.2", "typescript": "~5.9.3", - "typescript-eslint": "^8.50.0" + "typescript-eslint": "^8.51.0" }, "browserslist": { "production": [ @@ -78,6 +78,6 @@ ] }, "engines": { - "node": ">=18.0" + "node": ">=18.20.8" } } diff --git a/src/package.config.ts b/src/package.config.ts index 941fb34..3e51ab6 100644 --- a/src/package.config.ts +++ b/src/package.config.ts @@ -1,5 +1,5 @@ export const packageVersionConfig: Record = { - '@catbee/utils': '1.x.x', + '@catbee/utils': '2.x.x', '@ng-catbee/cookie': '21', '@ng-catbee/indexed-db': '21', '@ng-catbee/jwt': '21',