@catbee/utils 0.0.8-rc.4 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.mjs CHANGED
@@ -9,6 +9,7 @@ import { rateLimit as rateLimit$1 } from 'express-rate-limit';
9
9
  import os, { tmpdir } from 'os';
10
10
  import fsp from 'fs/promises';
11
11
  import { pipeline } from 'stream/promises';
12
+ import { Readable, Transform } from 'stream';
12
13
  import { URL as URL$1, URLSearchParams } from 'url';
13
14
  import express from 'express';
14
15
  import https from 'https';
@@ -1648,7 +1649,7 @@ function addSensitiveFields(fields) {
1648
1649
  __name(addSensitiveFields, "addSensitiveFields");
1649
1650
  var _globalThis = typeof globalThis === "object" ? globalThis : global;
1650
1651
  var _global = _globalThis;
1651
- function setupLogger() {
1652
+ function setupLogger(isGlobal = true) {
1652
1653
  const logParams = {
1653
1654
  name: config.logger.name || "@catbee/utils",
1654
1655
  level: config.logger.level || "info",
@@ -1662,14 +1663,14 @@ function setupLogger() {
1662
1663
  ],
1663
1664
  censor: /* @__PURE__ */ __name((value, path3) => redact(value, path3), "censor")
1664
1665
  },
1665
- // serializers: {
1666
- // req: pino.stdSerializers.req,
1667
- // request: pino.stdSerializers.req,
1668
- // res: pino.stdSerializers.res,
1669
- // response: pino.stdSerializers.res,
1670
- // err: pino.stdSerializers.err,
1671
- // error: pino.stdSerializers.err
1672
- // },
1666
+ serializers: {
1667
+ req: pino.stdSerializers.req,
1668
+ request: pino.stdSerializers.req,
1669
+ res: pino.stdSerializers.res,
1670
+ response: pino.stdSerializers.res,
1671
+ err: pino.stdSerializers.err,
1672
+ error: pino.stdSerializers.err
1673
+ },
1673
1674
  timestamp: stdTimeFunctions.isoTime
1674
1675
  };
1675
1676
  const logger2 = config.logger.pretty && Env.isDev() ? pino(logParams, pino.transport({
@@ -1682,11 +1683,17 @@ function setupLogger() {
1682
1683
  levelFirst: true
1683
1684
  }
1684
1685
  })) : pino(logParams);
1685
- _global[GLOBAL_LOGGER_KEY] = logger2;
1686
- _global[GLOBAL_LOGGER_KEY]?.debug("Logger initialized");
1686
+ if (isGlobal) {
1687
+ _global[GLOBAL_LOGGER_KEY] = logger2;
1688
+ _global[GLOBAL_LOGGER_KEY]?.debug("Logger initialized");
1689
+ }
1690
+ return logger2;
1687
1691
  }
1688
1692
  __name(setupLogger, "setupLogger");
1689
- function getLogger() {
1693
+ function getLogger(newInstance = false) {
1694
+ if (newInstance) {
1695
+ return setupLogger(false);
1696
+ }
1690
1697
  const logger2 = ContextStore.get(StoreKeys.LOGGER);
1691
1698
  if (logger2) return logger2;
1692
1699
  if (!_global[GLOBAL_LOGGER_KEY]) {
@@ -1695,7 +1702,7 @@ function getLogger() {
1695
1702
  return _global[GLOBAL_LOGGER_KEY];
1696
1703
  }
1697
1704
  __name(getLogger, "getLogger");
1698
- var logger = getLogger();
1705
+ var logger = getLogger(false);
1699
1706
  function createChildLogger(bindings, parentLogger) {
1700
1707
  const logger2 = parentLogger || getLogger();
1701
1708
  return logger2.child(bindings);
@@ -2486,6 +2493,234 @@ function verifySignedToken(token, secret) {
2486
2493
  }
2487
2494
  __name(verifySignedToken, "verifySignedToken");
2488
2495
 
2496
+ // src/utils/date.utils.ts
2497
+ function formatDate(date, options = {}) {
2498
+ const { format = "yyyy-MM-dd", locale, timeZone } = options;
2499
+ const d = date instanceof Date ? date : new Date(date);
2500
+ if (format === "yyyy-MM-dd") {
2501
+ const yyyy = d.getFullYear();
2502
+ const mm = String(d.getMonth() + 1).padStart(2, "0");
2503
+ const dd = String(d.getDate()).padStart(2, "0");
2504
+ return `${yyyy}-${mm}-${dd}`;
2505
+ }
2506
+ if (format === "yyyy-MM-dd HH:mm:ss") {
2507
+ const yyyy = d.getFullYear();
2508
+ const mm = String(d.getMonth() + 1).padStart(2, "0");
2509
+ const dd = String(d.getDate()).padStart(2, "0");
2510
+ const HH = String(d.getHours()).padStart(2, "0");
2511
+ const MM = String(d.getMinutes()).padStart(2, "0");
2512
+ const SS = String(d.getSeconds()).padStart(2, "0");
2513
+ return `${yyyy}-${mm}-${dd} ${HH}:${MM}:${SS}`;
2514
+ }
2515
+ if (format === "relative") {
2516
+ return formatRelativeTime(date);
2517
+ }
2518
+ return new Intl.DateTimeFormat(locale, {
2519
+ timeZone,
2520
+ year: format.includes("yyyy") || format.includes("y") ? "numeric" : void 0,
2521
+ month: format.includes("MM") || format.includes("M") ? "numeric" : void 0,
2522
+ day: format.includes("dd") || format.includes("d") ? "numeric" : void 0,
2523
+ hour: format.includes("HH") || format.includes("H") ? "numeric" : void 0,
2524
+ minute: format.includes("mm") || format.includes("m") ? "numeric" : void 0,
2525
+ second: format.includes("ss") || format.includes("s") ? "numeric" : void 0,
2526
+ hour12: !format.includes("HH") && !format.includes("H")
2527
+ }).format(d);
2528
+ }
2529
+ __name(formatDate, "formatDate");
2530
+ function formatRelativeTime(date, now = /* @__PURE__ */ new Date(), locale) {
2531
+ const d1 = date instanceof Date ? date : new Date(date);
2532
+ const d2 = now instanceof Date ? now : new Date(now);
2533
+ const diffMs = d1.getTime() - d2.getTime();
2534
+ const diffSecs = Math.round(diffMs / 1e3);
2535
+ const diffMins = Math.round(diffSecs / 60);
2536
+ const diffHours = Math.round(diffMins / 60);
2537
+ const diffDays = Math.round(diffHours / 24);
2538
+ const diffMonths = Math.round(diffDays / 30);
2539
+ const diffYears = Math.round(diffDays / 365);
2540
+ const formatter = new Intl.RelativeTimeFormat(locale, {
2541
+ numeric: "auto"
2542
+ });
2543
+ if (Math.abs(diffSecs) < 60) return formatter.format(diffSecs, "second");
2544
+ if (Math.abs(diffMins) < 60) return formatter.format(diffMins, "minute");
2545
+ if (Math.abs(diffHours) < 24) return formatter.format(diffHours, "hour");
2546
+ if (Math.abs(diffDays) < 30) return formatter.format(diffDays, "day");
2547
+ if (Math.abs(diffMonths) < 12) return formatter.format(diffMonths, "month");
2548
+ return formatter.format(diffYears, "year");
2549
+ }
2550
+ __name(formatRelativeTime, "formatRelativeTime");
2551
+ function parseDate(input, fallback) {
2552
+ if (typeof input === "number") {
2553
+ return new Date(input);
2554
+ }
2555
+ try {
2556
+ const date = new Date(input);
2557
+ if (isNaN(date.getTime())) {
2558
+ return fallback || null;
2559
+ }
2560
+ return date;
2561
+ } catch {
2562
+ return fallback || null;
2563
+ }
2564
+ }
2565
+ __name(parseDate, "parseDate");
2566
+ function dateDiff(date1, date2 = /* @__PURE__ */ new Date(), unit = "days") {
2567
+ const d1 = date1 instanceof Date ? date1 : new Date(date1);
2568
+ const d2 = date2 instanceof Date ? date2 : new Date(date2);
2569
+ const diffMs = d1.getTime() - d2.getTime();
2570
+ switch (unit) {
2571
+ case "milliseconds":
2572
+ return diffMs;
2573
+ case "seconds":
2574
+ return diffMs / 1e3;
2575
+ case "minutes":
2576
+ return diffMs / (1e3 * 60);
2577
+ case "hours":
2578
+ return diffMs / (1e3 * 60 * 60);
2579
+ case "days":
2580
+ return diffMs / (1e3 * 60 * 60 * 24);
2581
+ case "months":
2582
+ return (d1.getFullYear() - d2.getFullYear()) * 12 + d1.getMonth() - d2.getMonth();
2583
+ case "years":
2584
+ return d1.getFullYear() - d2.getFullYear();
2585
+ default:
2586
+ throw new Error(`Unsupported unit: ${unit}`);
2587
+ }
2588
+ }
2589
+ __name(dateDiff, "dateDiff");
2590
+ function addToDate(date, amount, unit) {
2591
+ const d = date instanceof Date ? new Date(date) : new Date(date);
2592
+ switch (unit) {
2593
+ case "milliseconds":
2594
+ d.setMilliseconds(d.getMilliseconds() + amount);
2595
+ break;
2596
+ case "seconds":
2597
+ d.setSeconds(d.getSeconds() + amount);
2598
+ break;
2599
+ case "minutes":
2600
+ d.setMinutes(d.getMinutes() + amount);
2601
+ break;
2602
+ case "hours":
2603
+ d.setHours(d.getHours() + amount);
2604
+ break;
2605
+ case "days":
2606
+ d.setDate(d.getDate() + amount);
2607
+ break;
2608
+ case "months": {
2609
+ const origDate = d.getDate();
2610
+ const origMonth = d.getMonth();
2611
+ d.setDate(1);
2612
+ d.setMonth(origMonth + amount);
2613
+ const lastDay = new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate();
2614
+ d.setDate(Math.min(origDate, lastDay));
2615
+ break;
2616
+ }
2617
+ case "years":
2618
+ d.setFullYear(d.getFullYear() + amount);
2619
+ break;
2620
+ default:
2621
+ throw new Error(`Unsupported unit: ${unit}`);
2622
+ }
2623
+ return d;
2624
+ }
2625
+ __name(addToDate, "addToDate");
2626
+ function startOf(date, unit) {
2627
+ const d = date instanceof Date ? new Date(date) : new Date(date);
2628
+ switch (unit) {
2629
+ case "second":
2630
+ d.setMilliseconds(0);
2631
+ break;
2632
+ case "minute":
2633
+ d.setSeconds(0, 0);
2634
+ break;
2635
+ case "hour":
2636
+ d.setMinutes(0, 0, 0);
2637
+ break;
2638
+ case "day":
2639
+ d.setHours(0, 0, 0, 0);
2640
+ break;
2641
+ case "week":
2642
+ d.setHours(0, 0, 0, 0);
2643
+ d.setDate(d.getDate() - d.getDay());
2644
+ break;
2645
+ case "month":
2646
+ d.setDate(1);
2647
+ d.setHours(0, 0, 0, 0);
2648
+ break;
2649
+ case "quarter":
2650
+ d.setMonth(Math.floor(d.getMonth() / 3) * 3, 1);
2651
+ d.setHours(0, 0, 0, 0);
2652
+ break;
2653
+ case "year":
2654
+ d.setMonth(0, 1);
2655
+ d.setHours(0, 0, 0, 0);
2656
+ break;
2657
+ default:
2658
+ throw new Error(`Unsupported unit: ${unit}`);
2659
+ }
2660
+ return d;
2661
+ }
2662
+ __name(startOf, "startOf");
2663
+ function endOf(date, unit) {
2664
+ const d = date instanceof Date ? new Date(date) : new Date(date);
2665
+ switch (unit) {
2666
+ case "second":
2667
+ d.setMilliseconds(999);
2668
+ break;
2669
+ case "minute":
2670
+ d.setSeconds(59, 999);
2671
+ break;
2672
+ case "hour":
2673
+ d.setMinutes(59, 59, 999);
2674
+ break;
2675
+ case "day":
2676
+ d.setHours(23, 59, 59, 999);
2677
+ break;
2678
+ case "week":
2679
+ d.setDate(d.getDate() - d.getDay() + 6);
2680
+ d.setHours(23, 59, 59, 999);
2681
+ break;
2682
+ case "month":
2683
+ d.setMonth(d.getMonth() + 1, 0);
2684
+ d.setHours(23, 59, 59, 999);
2685
+ break;
2686
+ case "quarter":
2687
+ d.setMonth(Math.floor(d.getMonth() / 3) * 3 + 3, 0);
2688
+ d.setHours(23, 59, 59, 999);
2689
+ break;
2690
+ case "year":
2691
+ d.setMonth(11, 31);
2692
+ d.setHours(23, 59, 59, 999);
2693
+ break;
2694
+ default:
2695
+ throw new Error(`Unsupported unit: ${unit}`);
2696
+ }
2697
+ return d;
2698
+ }
2699
+ __name(endOf, "endOf");
2700
+ function isBetween(date, start, end, inclusive = true) {
2701
+ const d = date instanceof Date ? date.getTime() : date;
2702
+ const s = start instanceof Date ? start.getTime() : start;
2703
+ const e = end instanceof Date ? end.getTime() : end;
2704
+ return inclusive ? d >= s && d <= e : d > s && d < e;
2705
+ }
2706
+ __name(isBetween, "isBetween");
2707
+ function isLeapYear(year) {
2708
+ const y = year instanceof Date ? year.getFullYear() : year;
2709
+ return y % 4 === 0 && y % 100 !== 0 || y % 400 === 0;
2710
+ }
2711
+ __name(isLeapYear, "isLeapYear");
2712
+ function daysInMonth(year, month) {
2713
+ if (year instanceof Date) {
2714
+ month = year.getMonth();
2715
+ year = year.getFullYear();
2716
+ }
2717
+ if (month === void 0) {
2718
+ throw new Error("Month is required when year is a number");
2719
+ }
2720
+ return new Date(year, month + 1, 0).getDate();
2721
+ }
2722
+ __name(daysInMonth, "daysInMonth");
2723
+
2489
2724
  // src/utils/response.utils.ts
2490
2725
  var SuccessResponse = class {
2491
2726
  static {
@@ -2739,1188 +2974,1500 @@ var HttpStatusCodes = /* @__PURE__ */ (function(HttpStatusCodes2) {
2739
2974
  HttpStatusCodes2[HttpStatusCodes2["NETWORK_AUTHENTICATION_REQUIRED"] = 511] = "NETWORK_AUTHENTICATION_REQUIRED";
2740
2975
  return HttpStatusCodes2;
2741
2976
  })({});
2742
- var ROUTES_KEY = Symbol("routes");
2743
- var MIDDLEWARE_KEY = Symbol("middlewares");
2744
- var PARAMS_KEY = Symbol("params");
2745
- var HTTP_CODE_KEY = Symbol("httpCode");
2746
- var HEADER_KEY = Symbol("headers");
2747
- var BEFORE_KEY = Symbol("before");
2748
- var AFTER_KEY = Symbol("after");
2749
- var ROLES_KEY = Symbol("roles");
2750
- var REDIRECT_KEY = Symbol("redirect");
2751
- var CACHE_KEY = Symbol("cache");
2752
- var RATE_LIMIT_KEY = Symbol("rateLimit");
2753
- var CONTENT_TYPE_KEY = Symbol("contentType");
2754
- var VERSION_KEY = Symbol("version");
2755
- var TIMEOUT_KEY = Symbol("timeout");
2756
- var LOG_KEY = Symbol("log");
2757
- var RateLimiterCache = class RateLimiterCache2 {
2977
+
2978
+ // src/utils/exception.utils.ts
2979
+ var HttpError = class extends ErrorResponse {
2758
2980
  static {
2759
- __name(this, "RateLimiterCache");
2981
+ __name(this, "HttpError");
2760
2982
  }
2761
- cache;
2762
- constructor(maxSize = 100, ttlMs = 5 * 60 * 1e3) {
2763
- this.cache = new TTLCache({
2764
- maxSize,
2765
- ttlMs
2766
- });
2983
+ /**
2984
+ * Creates a new HTTP error instance.
2985
+ * @param status - A valid HTTP status code (e.g., 400, 500).
2986
+ * @param message - The error message to return to the client.
2987
+ */
2988
+ constructor(status, message) {
2989
+ super(message);
2990
+ this.status = status;
2767
2991
  }
2768
- generateKey(options) {
2769
- return `${options.max}:${options.windowMs}:${options.standardHeaders}:${options.legacyHeaders}`;
2992
+ };
2993
+ var InternalServerErrorException = class extends ErrorResponse {
2994
+ static {
2995
+ __name(this, "InternalServerErrorException");
2770
2996
  }
2771
- get(options) {
2772
- const key = this.generateKey(options);
2773
- const cached = this.cache.get(key);
2774
- if (cached) {
2775
- return cached.limiter;
2776
- }
2777
- const limiter = rateLimit$1({
2778
- ...options,
2779
- handler: /* @__PURE__ */ __name((req, res) => {
2780
- const errorResponse = createFinalErrorResponse(req, HttpStatusCodes.TOO_MANY_REQUESTS, "Too Many Requests");
2781
- res.status(HttpStatusCodes.TOO_MANY_REQUESTS).json(errorResponse);
2782
- }, "handler")
2783
- });
2784
- this.cache.set(key, {
2785
- limiter,
2786
- config: key
2787
- });
2788
- return limiter;
2997
+ /**
2998
+ * @param message - Optional custom message. Defaults to "Internal server error".
2999
+ */
3000
+ constructor(message = "Internal server error") {
3001
+ super(message);
3002
+ this.status = HttpStatusCodes.INTERNAL_SERVER_ERROR;
2789
3003
  }
2790
- clear() {
2791
- this.cache.clear();
3004
+ };
3005
+ var UnauthorizedException = class extends ErrorResponse {
3006
+ static {
3007
+ __name(this, "UnauthorizedException");
2792
3008
  }
2793
- size() {
2794
- return this.cache.size();
3009
+ /**
3010
+ * @param message - Optional custom message. Defaults to "Unauthorized".
3011
+ */
3012
+ constructor(message = "Unauthorized") {
3013
+ super(message);
3014
+ this.status = HttpStatusCodes.UNAUTHORIZED;
2795
3015
  }
2796
- destroy() {
2797
- this.cache.destroy();
3016
+ };
3017
+ var BadRequestException = class extends ErrorResponse {
3018
+ static {
3019
+ __name(this, "BadRequestException");
3020
+ }
3021
+ /**
3022
+ * @param message - Optional custom message. Defaults to "Bad request".
3023
+ */
3024
+ constructor(message = "Bad request") {
3025
+ super(message);
3026
+ this.status = HttpStatusCodes.BAD_REQUEST;
2798
3027
  }
2799
3028
  };
2800
- var rateLimiterCache = new RateLimiterCache();
2801
- var DIContainer = class {
3029
+ var NotFoundException = class extends ErrorResponse {
2802
3030
  static {
2803
- __name(this, "DIContainer");
3031
+ __name(this, "NotFoundException");
2804
3032
  }
2805
- instances = /* @__PURE__ */ new Map();
2806
- constructing = /* @__PURE__ */ new Map();
2807
- register(target) {
2808
- if (!this.instances.has(target) && !this.constructing.has(target)) ;
3033
+ /**
3034
+ * @param message - Optional custom message. Defaults to "Resource not found".
3035
+ */
3036
+ constructor(message = "Resource not found") {
3037
+ super(message);
3038
+ this.status = HttpStatusCodes.NOT_FOUND;
2809
3039
  }
2810
- get(target) {
2811
- if (this.instances.has(target)) {
2812
- return this.instances.get(target);
2813
- }
2814
- if (this.constructing.has(target)) {
2815
- return this.constructing.get(target);
2816
- }
2817
- let proxy = {};
2818
- this.constructing.set(target, proxy);
2819
- const paramTypes = Reflect.getMetadata("design:paramtypes", target) || [];
2820
- const dependencies = paramTypes.map((dep) => this.get(dep));
2821
- const instance = new target(...dependencies);
2822
- Object.assign(proxy, instance);
2823
- this.instances.set(target, proxy);
2824
- this.constructing.delete(target);
2825
- Object.setPrototypeOf(proxy, target.prototype);
2826
- return proxy;
3040
+ };
3041
+ var ForbiddenException = class extends ErrorResponse {
3042
+ static {
3043
+ __name(this, "ForbiddenException");
2827
3044
  }
2828
- clear() {
2829
- this.instances.clear();
2830
- this.constructing.clear();
3045
+ /**
3046
+ * @param message - Optional custom message. Defaults to "Forbidden".
3047
+ */
3048
+ constructor(message = "Forbidden") {
3049
+ super(message);
3050
+ this.status = HttpStatusCodes.FORBIDDEN;
2831
3051
  }
2832
3052
  };
2833
- var diContainer = new DIContainer();
2834
- function normalizeHeaderValue(value) {
2835
- if (typeof value === "undefined") return void 0;
2836
- if (typeof value === "string") return value;
2837
- if (Array.isArray(value) && value.every((item) => typeof item === "string")) {
2838
- return value;
3053
+ var ConflictException = class extends ErrorResponse {
3054
+ static {
3055
+ __name(this, "ConflictException");
2839
3056
  }
2840
- return String(value);
2841
- }
2842
- __name(normalizeHeaderValue, "normalizeHeaderValue");
2843
- function Injectable() {
2844
- return (target) => {
2845
- Reflect.defineMetadata("injectable", true, target);
2846
- diContainer.register(target);
2847
- };
2848
- }
2849
- __name(Injectable, "Injectable");
2850
- function Inject(targetClass) {
2851
- return (target, propertyKey) => {
2852
- Object.defineProperty(target, propertyKey, {
2853
- get: /* @__PURE__ */ __name(function() {
2854
- return diContainer.get(targetClass);
2855
- }, "get"),
2856
- enumerable: true,
2857
- configurable: true
2858
- });
2859
- };
2860
- }
2861
- __name(Inject, "Inject");
2862
- function createRouteDecorator(method) {
2863
- return (path3) => {
2864
- return (target, propertyKey, _descriptor) => {
2865
- const routes = Reflect.getMetadata(ROUTES_KEY, target.constructor) || [];
2866
- routes.push({
2867
- path: path3,
2868
- method,
2869
- handlerName: propertyKey
2870
- });
2871
- Reflect.defineMetadata(ROUTES_KEY, routes, target.constructor);
2872
- };
2873
- };
2874
- }
2875
- __name(createRouteDecorator, "createRouteDecorator");
2876
- var Get = createRouteDecorator("get");
2877
- var Post = createRouteDecorator("post");
2878
- var Put = createRouteDecorator("put");
2879
- var Patch = createRouteDecorator("patch");
2880
- var Delete = createRouteDecorator("delete");
2881
- var Options = createRouteDecorator("options");
2882
- var Head = createRouteDecorator("head");
2883
- var Trace = createRouteDecorator("trace");
2884
- var Connect = createRouteDecorator("connect");
2885
- function Controller(basePath) {
2886
- return (target) => {
2887
- Reflect.defineMetadata("basePath", basePath, target);
2888
- };
2889
- }
2890
- __name(Controller, "Controller");
2891
- function Use(...middlewares) {
2892
- return (target, propertyKey, _descriptor) => {
2893
- const existing = Reflect.getMetadata(MIDDLEWARE_KEY, target, propertyKey) || [];
2894
- Reflect.defineMetadata(MIDDLEWARE_KEY, [
2895
- ...existing,
2896
- ...middlewares
2897
- ], target, propertyKey);
2898
- };
2899
- }
2900
- __name(Use, "Use");
2901
- function createParamDecorator(type, key) {
2902
- return (paramKey) => {
2903
- return (target, propertyKey, parameterIndex) => {
2904
- const params = Reflect.getMetadata(PARAMS_KEY, target, propertyKey) || [];
2905
- params.push({
2906
- index: parameterIndex,
2907
- type,
2908
- key: paramKey || key
2909
- });
2910
- params.sort((a, b) => a.index - b.index);
2911
- Reflect.defineMetadata(PARAMS_KEY, params, target, propertyKey);
2912
- };
2913
- };
2914
- }
2915
- __name(createParamDecorator, "createParamDecorator");
2916
- var Query = createParamDecorator("query");
2917
- var Param = createParamDecorator("param");
2918
- var Body = createParamDecorator("body");
2919
- var Req = createParamDecorator("req");
2920
- var Res = createParamDecorator("res");
2921
- function HttpCode(status) {
2922
- return (target, propertyKey, _descriptor) => {
2923
- Reflect.defineMetadata(HTTP_CODE_KEY, status, target, propertyKey);
2924
- };
2925
- }
2926
- __name(HttpCode, "HttpCode");
2927
- function Header(name, value) {
2928
- return Headers(name, value);
2929
- }
2930
- __name(Header, "Header");
2931
- function Headers(headers, value) {
2932
- return (target, propertyKey) => {
2933
- if (typeof propertyKey === "undefined") {
2934
- const existing = Reflect.getMetadata(HEADER_KEY, target) || {};
2935
- const newHeaders = typeof headers === "string" ? {
2936
- [headers]: value
2937
- } : headers;
2938
- Reflect.defineMetadata(HEADER_KEY, {
2939
- ...existing,
2940
- ...newHeaders
2941
- }, target);
2942
- } else {
2943
- const existing = Reflect.getMetadata(HEADER_KEY, target, propertyKey) || {};
2944
- const newHeaders = typeof headers === "string" ? {
2945
- [headers]: value
2946
- } : headers;
2947
- Reflect.defineMetadata(HEADER_KEY, {
2948
- ...existing,
2949
- ...newHeaders
2950
- }, target, propertyKey);
2951
- }
2952
- };
3057
+ /**
3058
+ * @param message - Optional custom message. Defaults to "Conflict".
3059
+ */
3060
+ constructor(message = "Conflict") {
3061
+ super(message);
3062
+ this.status = HttpStatusCodes.CONFLICT;
3063
+ }
3064
+ };
3065
+ var BadGatewayException = class extends ErrorResponse {
3066
+ static {
3067
+ __name(this, "BadGatewayException");
3068
+ }
3069
+ /**
3070
+ * @param message - Optional custom message. Defaults to "Bad Gateway".
3071
+ */
3072
+ constructor(message = "Bad Gateway") {
3073
+ super(message);
3074
+ this.status = HttpStatusCodes.BAD_GATEWAY;
3075
+ }
3076
+ };
3077
+ var TooManyRequestsException = class extends ErrorResponse {
3078
+ static {
3079
+ __name(this, "TooManyRequestsException");
3080
+ }
3081
+ /**
3082
+ * @param message - Optional custom message. Defaults to "Too many requests".
3083
+ */
3084
+ constructor(message = "Too many requests") {
3085
+ super(message);
3086
+ this.status = HttpStatusCodes.TOO_MANY_REQUESTS;
3087
+ }
3088
+ };
3089
+ var ServiceUnavailableException = class extends ErrorResponse {
3090
+ static {
3091
+ __name(this, "ServiceUnavailableException");
3092
+ }
3093
+ /**
3094
+ * @param message - Optional custom message. Defaults to "Service Unavailable".
3095
+ */
3096
+ constructor(message = "Service Unavailable") {
3097
+ super(message);
3098
+ this.status = HttpStatusCodes.SERVICE_UNAVAILABLE;
3099
+ }
3100
+ };
3101
+ var GatewayTimeoutException = class extends ErrorResponse {
3102
+ static {
3103
+ __name(this, "GatewayTimeoutException");
3104
+ }
3105
+ /**
3106
+ * @param message - Optional custom message. Defaults to "Gateway Timeout".
3107
+ */
3108
+ constructor(message = "Gateway Timeout") {
3109
+ super(message);
3110
+ this.status = HttpStatusCodes.GATEWAY_TIMEOUT;
3111
+ }
3112
+ };
3113
+ var UnprocessableEntityException = class extends ErrorResponse {
3114
+ static {
3115
+ __name(this, "UnprocessableEntityException");
3116
+ }
3117
+ details;
3118
+ /**
3119
+ * @param message - Optional custom message. Defaults to "Unprocessable Entity".
3120
+ * @param details - Optional validation details or additional error context.
3121
+ */
3122
+ constructor(message = "Unprocessable Entity", details) {
3123
+ super(message), this.details = details;
3124
+ this.status = HttpStatusCodes.UNPROCESSABLE_ENTITY;
3125
+ }
3126
+ };
3127
+ var MethodNotAllowedException = class extends ErrorResponse {
3128
+ static {
3129
+ __name(this, "MethodNotAllowedException");
3130
+ }
3131
+ allowedMethods;
3132
+ /**
3133
+ * @param message - Optional custom message. Defaults to "Method Not Allowed".
3134
+ * @param allowedMethods - Optional array of allowed HTTP methods.
3135
+ */
3136
+ constructor(message = "Method Not Allowed", allowedMethods) {
3137
+ super(message), this.allowedMethods = allowedMethods;
3138
+ this.status = HttpStatusCodes.METHOD_NOT_ALLOWED;
3139
+ }
3140
+ };
3141
+ var NotAcceptableException = class extends ErrorResponse {
3142
+ static {
3143
+ __name(this, "NotAcceptableException");
3144
+ }
3145
+ /**
3146
+ * @param message - Optional custom message. Defaults to "Not Acceptable".
3147
+ */
3148
+ constructor(message = "Not Acceptable") {
3149
+ super(message);
3150
+ this.status = HttpStatusCodes.NOT_ACCEPTABLE;
3151
+ }
3152
+ };
3153
+ var RequestTimeoutException = class extends ErrorResponse {
3154
+ static {
3155
+ __name(this, "RequestTimeoutException");
3156
+ }
3157
+ /**
3158
+ * @param message - Optional custom message. Defaults to "Request Timeout".
3159
+ */
3160
+ constructor(message = "Request Timeout") {
3161
+ super(message);
3162
+ this.status = HttpStatusCodes.REQUEST_TIMEOUT;
3163
+ }
3164
+ };
3165
+ var UnsupportedMediaTypeException = class extends ErrorResponse {
3166
+ static {
3167
+ __name(this, "UnsupportedMediaTypeException");
3168
+ }
3169
+ /**
3170
+ * @param message - Optional custom message. Defaults to "Unsupported Media Type".
3171
+ */
3172
+ constructor(message = "Unsupported Media Type") {
3173
+ super(message);
3174
+ this.status = HttpStatusCodes.UNSUPPORTED_MEDIA_TYPE;
3175
+ }
3176
+ };
3177
+ var PayloadTooLargeException = class extends ErrorResponse {
3178
+ static {
3179
+ __name(this, "PayloadTooLargeException");
3180
+ }
3181
+ /**
3182
+ * @param message - Optional custom message. Defaults to "Payload Too Large".
3183
+ */
3184
+ constructor(message = "Payload Too Large") {
3185
+ super(message);
3186
+ this.status = HttpStatusCodes.PAYLOAD_TOO_LARGE;
3187
+ }
3188
+ };
3189
+ var InsufficientStorageException = class extends ErrorResponse {
3190
+ static {
3191
+ __name(this, "InsufficientStorageException");
3192
+ }
3193
+ /**
3194
+ * @param message - Optional custom message. Defaults to "Insufficient Storage".
3195
+ */
3196
+ constructor(message = "Insufficient Storage") {
3197
+ super(message);
3198
+ this.status = HttpStatusCodes.INSUFFICIENT_STORAGE;
3199
+ }
3200
+ };
3201
+ function isHttpError(error) {
3202
+ return error instanceof ErrorResponse;
2953
3203
  }
2954
- __name(Headers, "Headers");
2955
- function Before(fn) {
2956
- return (target, propertyKey, _descriptor) => {
2957
- const hooks = Reflect.getMetadata(BEFORE_KEY, target, propertyKey) || [];
2958
- hooks.push(fn);
2959
- Reflect.defineMetadata(BEFORE_KEY, hooks, target, propertyKey);
2960
- };
3204
+ __name(isHttpError, "isHttpError");
3205
+ function createHttpError(status, message) {
3206
+ switch (status) {
3207
+ case HttpStatusCodes.BAD_REQUEST:
3208
+ return new BadRequestException(message);
3209
+ case HttpStatusCodes.UNAUTHORIZED:
3210
+ return new UnauthorizedException(message);
3211
+ case HttpStatusCodes.FORBIDDEN:
3212
+ return new ForbiddenException(message);
3213
+ case HttpStatusCodes.NOT_FOUND:
3214
+ return new NotFoundException(message);
3215
+ case HttpStatusCodes.METHOD_NOT_ALLOWED:
3216
+ return new MethodNotAllowedException(message);
3217
+ case HttpStatusCodes.NOT_ACCEPTABLE:
3218
+ return new NotAcceptableException(message);
3219
+ case HttpStatusCodes.REQUEST_TIMEOUT:
3220
+ return new RequestTimeoutException(message);
3221
+ case HttpStatusCodes.CONFLICT:
3222
+ return new ConflictException(message);
3223
+ case HttpStatusCodes.GONE:
3224
+ return new HttpError(HttpStatusCodes.GONE, message || "Gone");
3225
+ case HttpStatusCodes.PAYLOAD_TOO_LARGE:
3226
+ return new PayloadTooLargeException(message);
3227
+ case HttpStatusCodes.UNSUPPORTED_MEDIA_TYPE:
3228
+ return new UnsupportedMediaTypeException(message);
3229
+ case HttpStatusCodes.UNPROCESSABLE_ENTITY:
3230
+ return new UnprocessableEntityException(message);
3231
+ case HttpStatusCodes.TOO_MANY_REQUESTS:
3232
+ return new TooManyRequestsException(message);
3233
+ case HttpStatusCodes.INTERNAL_SERVER_ERROR:
3234
+ return new InternalServerErrorException(message);
3235
+ case HttpStatusCodes.BAD_GATEWAY:
3236
+ return new BadGatewayException(message);
3237
+ case HttpStatusCodes.SERVICE_UNAVAILABLE:
3238
+ return new ServiceUnavailableException(message);
3239
+ case HttpStatusCodes.GATEWAY_TIMEOUT:
3240
+ return new GatewayTimeoutException(message);
3241
+ case HttpStatusCodes.INSUFFICIENT_STORAGE:
3242
+ return new InsufficientStorageException(message);
3243
+ default:
3244
+ return new HttpError(status, message || `HTTP Error ${status}`);
3245
+ }
2961
3246
  }
2962
- __name(Before, "Before");
2963
- function After(fn) {
2964
- return (target, propertyKey, _descriptor) => {
2965
- const hooks = Reflect.getMetadata(AFTER_KEY, target, propertyKey) || [];
2966
- hooks.push(fn);
2967
- Reflect.defineMetadata(AFTER_KEY, hooks, target, propertyKey);
2968
- };
3247
+ __name(createHttpError, "createHttpError");
3248
+ function hasErrorShape(error) {
3249
+ return typeof error === "object" && error !== null && "message" in error && typeof error.message === "string";
2969
3250
  }
2970
- __name(After, "After");
2971
- function Roles(...roles) {
2972
- return (target, propertyKey, _descriptor) => {
2973
- if (typeof propertyKey === "undefined") {
2974
- Reflect.defineMetadata(ROLES_KEY, roles, target);
2975
- } else {
2976
- Reflect.defineMetadata(ROLES_KEY, roles, target, propertyKey);
3251
+ __name(hasErrorShape, "hasErrorShape");
3252
+ function getErrorMessage(error) {
3253
+ if (typeof error === "string") {
3254
+ return error;
3255
+ }
3256
+ if (error instanceof Error) {
3257
+ return error.message;
3258
+ }
3259
+ if (hasErrorShape(error)) {
3260
+ return error.message;
3261
+ }
3262
+ return "Unknown error occurred";
3263
+ }
3264
+ __name(getErrorMessage, "getErrorMessage");
3265
+ function withErrorHandling(handler) {
3266
+ return async (...args) => {
3267
+ try {
3268
+ return await handler(...args);
3269
+ } catch (err) {
3270
+ if (isHttpError(err)) {
3271
+ throw err;
3272
+ }
3273
+ getLogger().error({
3274
+ err
3275
+ }, "Caught error in handler");
3276
+ let status = HttpStatusCodes.INTERNAL_SERVER_ERROR;
3277
+ if (hasErrorShape(err) && typeof err.status === "number") {
3278
+ status = err.status;
3279
+ }
3280
+ throw createHttpError(status, getErrorMessage(err));
2977
3281
  }
2978
3282
  };
2979
3283
  }
2980
- __name(Roles, "Roles");
2981
- function Redirect(url, statusCode = 302) {
2982
- return (target, propertyKey, descriptor) => {
2983
- Reflect.defineMetadata(REDIRECT_KEY, {
2984
- url,
2985
- statusCode
2986
- }, target, propertyKey);
2987
- return descriptor;
2988
- };
2989
- }
2990
- __name(Redirect, "Redirect");
2991
- function Cache(ttlSeconds) {
2992
- return (target, propertyKey, _descriptor) => {
2993
- if (typeof propertyKey === "undefined") {
2994
- Reflect.defineMetadata(CACHE_KEY, {
2995
- ttlSeconds
2996
- }, target);
2997
- } else {
2998
- Reflect.defineMetadata(CACHE_KEY, {
2999
- ttlSeconds
3000
- }, target, propertyKey);
3001
- }
3002
- };
3003
- }
3004
- __name(Cache, "Cache");
3005
- function RateLimit(options) {
3006
- const opts = {
3007
- standardHeaders: true,
3008
- legacyHeaders: false,
3009
- ...options
3010
- };
3011
- return (target, propertyKey, _descriptor) => {
3012
- if (typeof propertyKey === "undefined") {
3013
- Reflect.defineMetadata(RATE_LIMIT_KEY, opts, target);
3014
- } else {
3015
- Reflect.defineMetadata(RATE_LIMIT_KEY, opts, target, propertyKey);
3016
- }
3017
- };
3018
- }
3019
- __name(RateLimit, "RateLimit");
3020
- function ContentType(type) {
3021
- return (target, propertyKey, _descriptor) => {
3022
- Reflect.defineMetadata(CONTENT_TYPE_KEY, {
3023
- type
3024
- }, target, propertyKey);
3025
- };
3026
- }
3027
- __name(ContentType, "ContentType");
3028
- function Version(version, options) {
3029
- const opts = {
3030
- addPrefix: true,
3031
- addHeader: true,
3032
- headerName: "X-API-Version",
3033
- ...options
3034
- };
3035
- return (target, propertyKey, _descriptor) => {
3036
- if (typeof propertyKey === "undefined") {
3037
- Reflect.defineMetadata(VERSION_KEY, {
3038
- version,
3039
- options: opts
3040
- }, target);
3041
- } else {
3042
- Reflect.defineMetadata(VERSION_KEY, {
3043
- version,
3044
- options: opts
3045
- }, target, propertyKey);
3046
- }
3047
- };
3048
- }
3049
- __name(Version, "Version");
3050
- function Timeout(ms) {
3051
- return (target, propertyKey, _descriptor) => {
3052
- if (typeof propertyKey === "undefined") {
3053
- Reflect.defineMetadata(TIMEOUT_KEY, {
3054
- ms
3055
- }, target);
3056
- } else {
3057
- Reflect.defineMetadata(TIMEOUT_KEY, {
3058
- ms
3059
- }, target, propertyKey);
3060
- }
3061
- };
3062
- }
3063
- __name(Timeout, "Timeout");
3064
- function Log(options) {
3065
- return (target, propertyKey, _descriptor) => {
3066
- const config2 = {
3067
- logEntry: true,
3068
- logExit: true,
3069
- logBody: false,
3070
- logParams: false,
3071
- logResponse: false,
3072
- ...options
3073
- };
3074
- if (typeof propertyKey === "undefined") {
3075
- Reflect.defineMetadata(LOG_KEY, config2, target);
3076
- } else {
3077
- Reflect.defineMetadata(LOG_KEY, config2, target, propertyKey);
3078
- }
3079
- };
3080
- }
3081
- __name(Log, "Log");
3082
- function registerControllers(router, controllers) {
3083
- controllers.forEach((ControllerClass) => {
3084
- const instance = diContainer.get(ControllerClass);
3085
- const basePath = Reflect.getMetadata("basePath", ControllerClass) || "";
3086
- const routes = Reflect.getMetadata(ROUTES_KEY, ControllerClass) || [];
3087
- const controllerRateLimit = Reflect.getMetadata(RATE_LIMIT_KEY, ControllerClass);
3088
- const controllerCache = Reflect.getMetadata(CACHE_KEY, ControllerClass);
3089
- const controllerTimeout = Reflect.getMetadata(TIMEOUT_KEY, ControllerClass);
3090
- const controllerVersion = Reflect.getMetadata(VERSION_KEY, ControllerClass);
3091
- const controllerRoles = Reflect.getMetadata(ROLES_KEY, ControllerClass);
3092
- const controllerLogConfig = Reflect.getMetadata(LOG_KEY, ControllerClass);
3093
- const controllerHeaders = Reflect.getMetadata(HEADER_KEY, ControllerClass) || {};
3094
- routes.forEach(({ path: path3, method, handlerName }) => {
3095
- const middlewares = Reflect.getMetadata(MIDDLEWARE_KEY, instance, handlerName) || [];
3096
- const params = Reflect.getMetadata(PARAMS_KEY, instance, handlerName) || [];
3097
- const httpCode = Reflect.getMetadata(HTTP_CODE_KEY, instance, handlerName);
3098
- const methodHeaders = Reflect.getMetadata(HEADER_KEY, instance, handlerName) || {};
3099
- const headers = {
3100
- ...controllerHeaders,
3101
- ...methodHeaders
3102
- };
3103
- const beforeHooks = Reflect.getMetadata(BEFORE_KEY, instance, handlerName) || [];
3104
- const afterHooks = Reflect.getMetadata(AFTER_KEY, instance, handlerName) || [];
3105
- const redirect = Reflect.getMetadata(REDIRECT_KEY, instance, handlerName);
3106
- const contentType = Reflect.getMetadata(CONTENT_TYPE_KEY, instance, handlerName);
3107
- const roles = Reflect.getMetadata(ROLES_KEY, instance, handlerName) || controllerRoles || [];
3108
- const cache = Reflect.getMetadata(CACHE_KEY, instance, handlerName) || controllerCache;
3109
- const rateLimitOptions = Reflect.getMetadata(RATE_LIMIT_KEY, instance, handlerName) || controllerRateLimit;
3110
- const version = Reflect.getMetadata(VERSION_KEY, instance, handlerName) || controllerVersion;
3111
- const timeout2 = Reflect.getMetadata(TIMEOUT_KEY, instance, handlerName) || controllerTimeout;
3112
- const logConfig = Reflect.getMetadata(LOG_KEY, instance, handlerName) || controllerLogConfig;
3113
- let rateLimiter = null;
3114
- if (rateLimitOptions) {
3115
- try {
3116
- rateLimiter = rateLimiterCache.get(rateLimitOptions);
3117
- } catch (err) {
3118
- getLogger().warn({
3119
- err
3120
- }, "express-rate-limit not available, skipping rate limiting for this route");
3121
- }
3122
- }
3123
- let finalPath = path3;
3124
- if (version?.options?.addPrefix) {
3125
- finalPath = `/${version.version}${path3}`;
3126
- }
3127
- const handler = /* @__PURE__ */ __name(async (req, res, next) => {
3128
- req["startTime"] = Date.now();
3129
- let timeoutId;
3130
- let timedOut = false;
3131
- try {
3132
- if (timeout2) {
3133
- timeoutId = setTimeout(() => {
3134
- if (!res.headersSent && !timedOut) {
3135
- timedOut = true;
3136
- const errorResponse = createFinalErrorResponse(req, HttpStatusCodes.REQUEST_TIMEOUT, "Request timed out");
3137
- res.status(HttpStatusCodes.REQUEST_TIMEOUT).json(errorResponse);
3138
- }
3139
- }, timeout2.ms);
3140
- }
3141
- if (timedOut) return;
3142
- if (rateLimiter) {
3143
- await new Promise((resolve2, reject) => {
3144
- rateLimiter(req, res, (err) => {
3145
- if (err) reject(err);
3146
- else resolve2();
3147
- });
3148
- });
3149
- }
3150
- if (!res.headersSent && contentType) {
3151
- res.setHeader("Content-Type", contentType.type);
3152
- }
3153
- if (version?.options?.addHeader && version?.options?.headerName && version?.version) {
3154
- if (!res.headersSent) {
3155
- res.setHeader(version.options.headerName, version.version);
3156
- }
3157
- }
3158
- if (cache && !res.headersSent) {
3159
- res.setHeader("Cache-Control", `public, max-age=${cache.ttlSeconds}`);
3160
- }
3161
- if (logConfig?.logEntry) {
3162
- const logger2 = getLogger();
3163
- const logData = {
3164
- method: req.method,
3165
- url: req.originalUrl || req.url,
3166
- userAgent: req.get("User-Agent")
3167
- };
3168
- if (logConfig.logParams) {
3169
- logData.params = req.params;
3170
- logData.query = req.query;
3171
- }
3172
- if (logConfig.logBody) logData.body = req.body;
3173
- logger2.info({
3174
- entry: logData
3175
- }, "Route Entry:");
3176
- }
3177
- if (roles.length && !req?.user?.roles?.some((role) => roles.includes(role))) {
3178
- const errorResponse = createFinalErrorResponse(req, HttpStatusCodes.FORBIDDEN, "Forbidden Insufficient Roles");
3179
- res.status(HttpStatusCodes.FORBIDDEN).json(errorResponse);
3180
- if (timeoutId) {
3181
- clearTimeout(timeoutId);
3182
- }
3183
- return;
3184
- }
3185
- if (redirect && redirect.url) {
3186
- if (timeoutId) {
3187
- clearTimeout(timeoutId);
3188
- }
3189
- return res.redirect(redirect.statusCode, redirect.url);
3190
- }
3191
- for (const fn of beforeHooks) await fn(req, res);
3192
- const args = [];
3193
- if (params.length) {
3194
- params.forEach(({ index, type, key }) => {
3195
- switch (type) {
3196
- case "query":
3197
- args[index] = key ? req.query[key] : req.query;
3198
- break;
3199
- case "param":
3200
- args[index] = key ? req.params[key] : req.params;
3201
- break;
3202
- case "body":
3203
- args[index] = key ? req.body?.[key] : req.body;
3204
- break;
3205
- case "req":
3206
- args[index] = req;
3207
- break;
3208
- case "res":
3209
- args[index] = res;
3210
- break;
3211
- }
3212
- });
3213
- }
3214
- const result = instance[handlerName](...args);
3215
- const awaited = result instanceof Promise ? await result : result;
3216
- if (timeoutId) {
3217
- clearTimeout(timeoutId);
3218
- }
3219
- if (timedOut) return;
3220
- if (redirect && awaited && typeof awaited === "object" && "url" in awaited) {
3221
- const redirectUrl = awaited.url;
3222
- const redirectStatus = awaited.statusCode || redirect.statusCode;
3223
- return res.redirect(redirectStatus, redirectUrl);
3224
- }
3225
- if (!res.headersSent && typeof awaited !== "undefined") {
3226
- if (httpCode) res.status(httpCode);
3227
- for (const [k, v] of Object.entries(headers)) {
3228
- const normalized = normalizeHeaderValue(v);
3229
- if (typeof normalized !== "undefined") {
3230
- res.set(k, normalized);
3231
- }
3232
- }
3233
- res.json(awaited);
3234
- }
3235
- if (logConfig?.logExit) {
3236
- const logger2 = getLogger();
3237
- const logData = {
3238
- method: req.method,
3239
- url: req.originalUrl || req.url,
3240
- statusCode: res.statusCode,
3241
- duration: `${Date.now() - req.startTime}ms`
3242
- };
3243
- if (logConfig.logResponse) logData.response = awaited;
3244
- logger2.info({
3245
- exit: logData
3246
- }, "Route Exit:");
3247
- }
3248
- for (const fn of afterHooks) await fn(req, res, awaited);
3249
- } catch (err) {
3250
- if (timeoutId) {
3251
- clearTimeout(timeoutId);
3252
- }
3253
- next(err);
3254
- }
3255
- }, "handler");
3256
- router[method](basePath + finalPath, ...middlewares, handler);
3284
+ __name(withErrorHandling, "withErrorHandling");
3285
+
3286
+ // src/utils/decorators.utils.ts
3287
+ var ROUTES_KEY = Symbol("routes");
3288
+ var MIDDLEWARE_KEY = Symbol("middlewares");
3289
+ var PARAMS_KEY = Symbol("params");
3290
+ var HTTP_CODE_KEY = Symbol("httpCode");
3291
+ var HEADER_KEY = Symbol("headers");
3292
+ var BEFORE_KEY = Symbol("before");
3293
+ var AFTER_KEY = Symbol("after");
3294
+ var ROLES_KEY = Symbol("roles");
3295
+ var REDIRECT_KEY = Symbol("redirect");
3296
+ var CACHE_KEY = Symbol("cache");
3297
+ var RATE_LIMIT_KEY = Symbol("rateLimit");
3298
+ var CONTENT_TYPE_KEY = Symbol("contentType");
3299
+ var VERSION_KEY = Symbol("version");
3300
+ var TIMEOUT_KEY = Symbol("timeout");
3301
+ var LOG_KEY = Symbol("log");
3302
+ var RateLimiterCache = class RateLimiterCache2 {
3303
+ static {
3304
+ __name(this, "RateLimiterCache");
3305
+ }
3306
+ cache;
3307
+ constructor(maxSize = 100, ttlMs = 5 * 60 * 1e3) {
3308
+ this.cache = new TTLCache({
3309
+ maxSize,
3310
+ ttlMs
3311
+ });
3312
+ }
3313
+ generateKey(options) {
3314
+ return `${options.max}:${options.windowMs}:${options.standardHeaders}:${options.legacyHeaders}`;
3315
+ }
3316
+ get(options) {
3317
+ const key = this.generateKey(options);
3318
+ const cached = this.cache.get(key);
3319
+ if (cached) {
3320
+ return cached.limiter;
3321
+ }
3322
+ const limiter = rateLimit$1({
3323
+ ...options,
3324
+ handler: /* @__PURE__ */ __name((req, res) => {
3325
+ const errorResponse = createFinalErrorResponse(req, HttpStatusCodes.TOO_MANY_REQUESTS, "Too Many Requests");
3326
+ res.status(HttpStatusCodes.TOO_MANY_REQUESTS).json(errorResponse);
3327
+ }, "handler")
3257
3328
  });
3258
- });
3259
- }
3260
- __name(registerControllers, "registerControllers");
3261
- async function ensureDir(dirPath) {
3262
- await fsp.mkdir(dirPath, {
3263
- recursive: true
3264
- });
3265
- }
3266
- __name(ensureDir, "ensureDir");
3267
- async function listFiles(dirPath, recursive = false) {
3268
- const entries = await fsp.readdir(dirPath, {
3269
- withFileTypes: true
3270
- });
3271
- const files = [];
3272
- for (const entry of entries) {
3273
- const fullPath = path.join(dirPath, entry.name);
3274
- if (entry.isFile()) {
3275
- files.push(fullPath);
3276
- } else if (recursive && entry.isDirectory()) {
3277
- const nestedFiles = await listFiles(fullPath, true);
3278
- files.push(...nestedFiles);
3329
+ this.cache.set(key, {
3330
+ limiter,
3331
+ config: key
3332
+ });
3333
+ return limiter;
3334
+ }
3335
+ clear() {
3336
+ this.cache.clear();
3337
+ }
3338
+ size() {
3339
+ return this.cache.size();
3340
+ }
3341
+ destroy() {
3342
+ this.cache.destroy();
3343
+ }
3344
+ };
3345
+ var rateLimiterCache = new RateLimiterCache();
3346
+ var DIContainer = class {
3347
+ static {
3348
+ __name(this, "DIContainer");
3349
+ }
3350
+ instances = /* @__PURE__ */ new Map();
3351
+ constructing = /* @__PURE__ */ new Map();
3352
+ propertyInjections = /* @__PURE__ */ new Map();
3353
+ register(target) {
3354
+ if (!this.instances.has(target) && !this.constructing.has(target)) ;
3355
+ }
3356
+ /**
3357
+ * Register a property injection to be resolved when the target class is instantiated
3358
+ */
3359
+ registerPropertyInjection(target, propertyKey, injectClass) {
3360
+ const targetClass = target.constructor;
3361
+ if (!this.propertyInjections.has(targetClass)) {
3362
+ this.propertyInjections.set(targetClass, []);
3279
3363
  }
3364
+ this.propertyInjections.get(targetClass).push({
3365
+ targetClass: injectClass,
3366
+ propertyKey
3367
+ });
3280
3368
  }
3281
- return files;
3282
- }
3283
- __name(listFiles, "listFiles");
3284
- async function deleteDirRecursive(dirPath) {
3285
- await fsp.rm(dirPath, {
3286
- recursive: true,
3287
- force: true
3288
- });
3289
- }
3290
- __name(deleteDirRecursive, "deleteDirRecursive");
3291
- async function isDirectory(pathStr) {
3292
- try {
3293
- const stat = await fsp.stat(pathStr);
3294
- return stat.isDirectory();
3295
- } catch {
3296
- return false;
3369
+ get(target) {
3370
+ if (this.instances.has(target)) {
3371
+ return this.instances.get(target);
3372
+ }
3373
+ if (this.constructing.has(target)) {
3374
+ return this.constructing.get(target);
3375
+ }
3376
+ let proxy = {};
3377
+ this.constructing.set(target, proxy);
3378
+ const paramTypes = Reflect.getMetadata("design:paramtypes", target) || [];
3379
+ const dependencies = paramTypes.map((dep) => this.get(dep));
3380
+ const instance = new target(...dependencies);
3381
+ this.applyPropertyInjections(target, instance);
3382
+ Object.assign(proxy, instance);
3383
+ this.instances.set(target, proxy);
3384
+ this.constructing.delete(target);
3385
+ Object.setPrototypeOf(proxy, target.prototype);
3386
+ return proxy;
3297
3387
  }
3298
- }
3299
- __name(isDirectory, "isDirectory");
3300
- async function copyDir(src, dest) {
3301
- await ensureDir(dest);
3302
- const entries = await fsp.readdir(src, {
3303
- withFileTypes: true
3304
- });
3305
- for (const entry of entries) {
3306
- const srcPath = path.join(src, entry.name);
3307
- const destPath = path.join(dest, entry.name);
3308
- if (entry.isDirectory()) {
3309
- await copyDir(srcPath, destPath);
3310
- } else {
3311
- await fsp.copyFile(srcPath, destPath);
3388
+ applyPropertyInjections(targetClass, instance) {
3389
+ const injections = this.propertyInjections.get(targetClass) || [];
3390
+ for (const injection of injections) {
3391
+ const dependency = this.get(injection.targetClass);
3392
+ instance[injection.propertyKey] = dependency;
3312
3393
  }
3313
3394
  }
3395
+ clear() {
3396
+ this.instances.clear();
3397
+ this.constructing.clear();
3398
+ this.propertyInjections.clear();
3399
+ }
3400
+ };
3401
+ var diContainer = new DIContainer();
3402
+ function normalizeHeaderValue(value) {
3403
+ if (typeof value === "undefined") return void 0;
3404
+ if (typeof value === "string") return value;
3405
+ if (Array.isArray(value) && value.every((item) => typeof item === "string")) {
3406
+ return value;
3407
+ }
3408
+ return String(value);
3314
3409
  }
3315
- __name(copyDir, "copyDir");
3316
- async function moveDir(src, dest) {
3317
- await copyDir(src, dest);
3318
- await deleteDirRecursive(src);
3410
+ __name(normalizeHeaderValue, "normalizeHeaderValue");
3411
+ function Injectable() {
3412
+ return (target) => {
3413
+ Reflect.defineMetadata("injectable", true, target);
3414
+ diContainer.register(target);
3415
+ };
3319
3416
  }
3320
- __name(moveDir, "moveDir");
3321
- async function emptyDir(dirPath) {
3322
- const entries = await fsp.readdir(dirPath, {
3323
- withFileTypes: true
3324
- });
3325
- for (const entry of entries) {
3326
- const entryPath = path.join(dirPath, entry.name);
3327
- if (entry.isDirectory()) {
3328
- await fsp.rm(entryPath, {
3329
- recursive: true,
3330
- force: true
3417
+ __name(Injectable, "Injectable");
3418
+ function Inject(targetClass) {
3419
+ return (target, propertyKey) => {
3420
+ diContainer.registerPropertyInjection(target, propertyKey, targetClass);
3421
+ Object.defineProperty(target, propertyKey, {
3422
+ configurable: true,
3423
+ get: /* @__PURE__ */ __name(function() {
3424
+ const value = Object.getOwnPropertyDescriptor(this, propertyKey)?.value;
3425
+ if (value !== void 0) return value;
3426
+ const injectedValue = diContainer.get(targetClass);
3427
+ Object.defineProperty(this, propertyKey, {
3428
+ value: injectedValue,
3429
+ writable: true,
3430
+ configurable: true
3431
+ });
3432
+ return injectedValue;
3433
+ }, "get"),
3434
+ set: /* @__PURE__ */ __name(function(value) {
3435
+ Object.defineProperty(this, propertyKey, {
3436
+ value,
3437
+ writable: true,
3438
+ configurable: true
3439
+ });
3440
+ }, "set")
3441
+ });
3442
+ };
3443
+ }
3444
+ __name(Inject, "Inject");
3445
+ function inject(targetClass) {
3446
+ return diContainer.get(targetClass);
3447
+ }
3448
+ __name(inject, "inject");
3449
+ function createRouteDecorator(method) {
3450
+ return (path3) => {
3451
+ return (target, propertyKey, _descriptor) => {
3452
+ const routes = Reflect.getMetadata(ROUTES_KEY, target.constructor) || [];
3453
+ routes.push({
3454
+ path: path3,
3455
+ method,
3456
+ handlerName: propertyKey
3331
3457
  });
3332
- } else {
3333
- await fsp.unlink(entryPath);
3334
- }
3335
- }
3458
+ Reflect.defineMetadata(ROUTES_KEY, routes, target.constructor);
3459
+ };
3460
+ };
3336
3461
  }
3337
- __name(emptyDir, "emptyDir");
3338
- async function getDirSize(dirPath) {
3339
- let total = 0;
3340
- const entries = await fsp.readdir(dirPath, {
3341
- withFileTypes: true
3342
- });
3343
- for (const entry of entries) {
3344
- const entryPath = path.join(dirPath, entry.name);
3345
- if (entry.isDirectory()) {
3346
- total += await getDirSize(entryPath);
3462
+ __name(createRouteDecorator, "createRouteDecorator");
3463
+ var Get = createRouteDecorator("get");
3464
+ var Post = createRouteDecorator("post");
3465
+ var Put = createRouteDecorator("put");
3466
+ var Patch = createRouteDecorator("patch");
3467
+ var Delete = createRouteDecorator("delete");
3468
+ var Options = createRouteDecorator("options");
3469
+ var Head = createRouteDecorator("head");
3470
+ var Trace = createRouteDecorator("trace");
3471
+ var Connect = createRouteDecorator("connect");
3472
+ function Controller(basePath) {
3473
+ return (target) => {
3474
+ Reflect.defineMetadata("basePath", basePath, target);
3475
+ };
3476
+ }
3477
+ __name(Controller, "Controller");
3478
+ function Use(...middlewares) {
3479
+ return (target, propertyKey, _descriptor) => {
3480
+ if (typeof propertyKey === "undefined") {
3481
+ const existing = Reflect.getMetadata(MIDDLEWARE_KEY, target) || [];
3482
+ Reflect.defineMetadata(MIDDLEWARE_KEY, [
3483
+ ...existing,
3484
+ ...middlewares
3485
+ ], target);
3347
3486
  } else {
3348
- const stat = await fsp.stat(entryPath);
3349
- total += stat.size;
3487
+ const existing = Reflect.getMetadata(MIDDLEWARE_KEY, target, propertyKey) || [];
3488
+ Reflect.defineMetadata(MIDDLEWARE_KEY, [
3489
+ ...existing,
3490
+ ...middlewares
3491
+ ], target, propertyKey);
3350
3492
  }
3351
- }
3352
- return total;
3493
+ };
3353
3494
  }
3354
- __name(getDirSize, "getDirSize");
3355
- function watchDir(dirPath, callback) {
3356
- const watcher = fs.watch(dirPath, (eventType, filename) => {
3357
- callback(eventType, filename);
3495
+ __name(Use, "Use");
3496
+ function defineParamMetadata(target, propertyKey, parameterIndex, type, key) {
3497
+ const params = Reflect.getMetadata(PARAMS_KEY, target, propertyKey) || [];
3498
+ params.push({
3499
+ index: parameterIndex,
3500
+ type,
3501
+ key
3358
3502
  });
3359
- return () => watcher.close();
3503
+ params.sort((a, b) => a.index - b.index);
3504
+ Reflect.defineMetadata(PARAMS_KEY, params, target, propertyKey);
3360
3505
  }
3361
- __name(watchDir, "watchDir");
3362
- async function findFilesByPattern(pattern, options = {}) {
3363
- const matchPattern = /* @__PURE__ */ __name((name, pattern2) => {
3364
- let escaped = pattern2.replaceAll(/[.+^${}()|[\]\\]/g, "\\$&");
3365
- escaped = escaped.replaceAll("*", ".*").replaceAll("?", ".");
3366
- const regex = new RegExp(`^${escaped}$`);
3367
- return regex.test(name);
3368
- }, "matchPattern");
3369
- const { cwd = process.cwd(), dot = false } = options;
3370
- const segments = pattern.split("/");
3371
- async function walk(dir, segIndex) {
3372
- if (segIndex >= segments.length) return [];
3373
- const segment = segments[segIndex];
3374
- const entries = await fs.promises.readdir(dir, {
3375
- withFileTypes: true
3376
- });
3377
- let matchedFiles = [];
3378
- for (const entry of entries) {
3379
- const entryName = entry.name;
3380
- if (!dot && entryName.startsWith(".")) continue;
3381
- const fullPath = path.join(dir, entryName);
3382
- const isLastSegment = segIndex === segments.length - 1;
3383
- if (isLastSegment) {
3384
- if (matchPattern(entryName, segment) && entry.isFile()) {
3385
- matchedFiles.push(fullPath);
3386
- }
3387
- } else if (entry.isDirectory()) {
3388
- if (matchPattern(entryName, segment)) {
3389
- matchedFiles.push(...await walk(fullPath, segIndex + 1));
3390
- }
3506
+ __name(defineParamMetadata, "defineParamMetadata");
3507
+ function createParamDecorator(type, key) {
3508
+ return (paramKey) => {
3509
+ return (target, propertyKey, parameterIndex) => {
3510
+ defineParamMetadata(target, propertyKey, parameterIndex, type, paramKey ?? key);
3511
+ };
3512
+ };
3513
+ }
3514
+ __name(createParamDecorator, "createParamDecorator");
3515
+ function createParamDecoratorWithoutParam(type) {
3516
+ return () => {
3517
+ return (target, propertyKey, parameterIndex) => {
3518
+ defineParamMetadata(target, propertyKey, parameterIndex, type, void 0);
3519
+ };
3520
+ };
3521
+ }
3522
+ __name(createParamDecoratorWithoutParam, "createParamDecoratorWithoutParam");
3523
+ function createApiUrlParamDecorator(type, key) {
3524
+ return (paramKey, options) => {
3525
+ return (target, propertyKey, parameterIndex) => {
3526
+ const params = Reflect.getMetadata(PARAMS_KEY, target, propertyKey) || [];
3527
+ let paramOptions = void 0;
3528
+ if (typeof options === "object") {
3529
+ paramOptions = {};
3530
+ paramOptions.type = "string";
3531
+ paramOptions.dataType = "single";
3532
+ paramOptions.delimiter = ",";
3533
+ paramOptions.required = false;
3534
+ paramOptions.throwError = true;
3535
+ paramOptions = {
3536
+ ...paramOptions,
3537
+ ...options
3538
+ };
3391
3539
  }
3392
- }
3393
- return matchedFiles;
3394
- }
3395
- __name(walk, "walk");
3396
- return walk(cwd, 0);
3540
+ params.push({
3541
+ index: parameterIndex,
3542
+ type,
3543
+ key: paramKey || key,
3544
+ options: paramOptions
3545
+ });
3546
+ params.sort((a, b) => a.index - b.index);
3547
+ Reflect.defineMetadata(PARAMS_KEY, params, target, propertyKey);
3548
+ };
3549
+ };
3397
3550
  }
3398
- __name(findFilesByPattern, "findFilesByPattern");
3399
- async function getSubdirectories(dirPath, recursive = false) {
3400
- const entries = await fsp.readdir(dirPath, {
3401
- withFileTypes: true
3402
- });
3403
- const dirs = entries.filter((entry) => entry.isDirectory()).map((entry) => path.join(dirPath, entry.name));
3404
- if (recursive && dirs.length > 0) {
3405
- const nestedDirs = await Promise.all(dirs.map((dir) => getSubdirectories(dir, true)));
3406
- return [
3407
- ...dirs,
3408
- ...nestedDirs.flat()
3409
- ];
3410
- }
3411
- return dirs;
3551
+ __name(createApiUrlParamDecorator, "createApiUrlParamDecorator");
3552
+ var Query = createApiUrlParamDecorator("query");
3553
+ var Param = createApiUrlParamDecorator("param");
3554
+ var Body = createParamDecorator("body");
3555
+ var ReqLogger = createParamDecoratorWithoutParam("logger");
3556
+ var ReqId = createParamDecoratorWithoutParam("reqId");
3557
+ var ReqHeader = createParamDecorator("reqHeader");
3558
+ var ReqCookie = createParamDecorator("cookie");
3559
+ var Req = createParamDecoratorWithoutParam("req");
3560
+ var Res = createParamDecoratorWithoutParam("res");
3561
+ function HttpCode(status) {
3562
+ return (target, propertyKey, _descriptor) => {
3563
+ Reflect.defineMetadata(HTTP_CODE_KEY, status, target, propertyKey);
3564
+ };
3412
3565
  }
3413
- __name(getSubdirectories, "getSubdirectories");
3414
- async function ensureEmptyDir(dirPath) {
3415
- if (await isDirectory(dirPath)) {
3416
- await emptyDir(dirPath);
3417
- } else {
3418
- try {
3419
- await fsp.unlink(dirPath);
3420
- } catch (error) {
3421
- if (error.code !== "ENOENT") {
3422
- throw error;
3423
- }
3566
+ __name(HttpCode, "HttpCode");
3567
+ function Header(name, value) {
3568
+ return Headers(name, value);
3569
+ }
3570
+ __name(Header, "Header");
3571
+ function Headers(headers, value) {
3572
+ return (target, propertyKey) => {
3573
+ if (typeof propertyKey === "undefined") {
3574
+ const existing = Reflect.getMetadata(HEADER_KEY, target) || {};
3575
+ const newHeaders = typeof headers === "string" ? {
3576
+ [headers]: value
3577
+ } : headers;
3578
+ Reflect.defineMetadata(HEADER_KEY, {
3579
+ ...existing,
3580
+ ...newHeaders
3581
+ }, target);
3582
+ } else {
3583
+ const existing = Reflect.getMetadata(HEADER_KEY, target, propertyKey) || {};
3584
+ const newHeaders = typeof headers === "string" ? {
3585
+ [headers]: value
3586
+ } : headers;
3587
+ Reflect.defineMetadata(HEADER_KEY, {
3588
+ ...existing,
3589
+ ...newHeaders
3590
+ }, target, propertyKey);
3424
3591
  }
3425
- await ensureDir(dirPath);
3426
- }
3592
+ };
3427
3593
  }
3428
- __name(ensureEmptyDir, "ensureEmptyDir");
3429
- async function createTempDir(options = {}) {
3430
- const prefix = options.prefix || "tmp-";
3431
- const parentDir = options.parentDir || os.tmpdir();
3432
- const dirName = `${prefix}${randomBytes(6).toString("hex")}`;
3433
- const tempDirPath = path.join(parentDir, dirName);
3434
- await ensureDir(tempDirPath);
3435
- const cleanup = /* @__PURE__ */ __name(async () => {
3436
- try {
3437
- await deleteDirRecursive(tempDirPath);
3438
- } catch {
3594
+ __name(Headers, "Headers");
3595
+ function Before(fn) {
3596
+ return (target, propertyKey, _descriptor) => {
3597
+ if (typeof propertyKey === "undefined") {
3598
+ const hooks = Reflect.getMetadata(BEFORE_KEY, target) || [];
3599
+ hooks.push(fn);
3600
+ Reflect.defineMetadata(BEFORE_KEY, hooks, target);
3601
+ } else {
3602
+ const hooks = Reflect.getMetadata(BEFORE_KEY, target, propertyKey) || [];
3603
+ hooks.push(fn);
3604
+ Reflect.defineMetadata(BEFORE_KEY, hooks, target, propertyKey);
3439
3605
  }
3440
- }, "cleanup");
3441
- if (options.cleanup) {
3442
- process.once("exit", () => {
3443
- try {
3444
- fs.rmSync(tempDirPath, {
3445
- recursive: true,
3446
- force: true
3447
- });
3448
- } catch {
3449
- }
3450
- });
3451
- const signals = [
3452
- "SIGINT",
3453
- "SIGTERM",
3454
- "SIGUSR1",
3455
- "SIGUSR2"
3456
- ];
3457
- for (const signal of signals) {
3458
- process.once(signal, () => {
3459
- cleanup().then(() => process.exit());
3460
- });
3606
+ };
3607
+ }
3608
+ __name(Before, "Before");
3609
+ function After(fn) {
3610
+ return (target, propertyKey, _descriptor) => {
3611
+ if (typeof propertyKey === "undefined") {
3612
+ const hooks = Reflect.getMetadata(AFTER_KEY, target) || [];
3613
+ hooks.push(fn);
3614
+ Reflect.defineMetadata(AFTER_KEY, hooks, target);
3615
+ } else {
3616
+ const hooks = Reflect.getMetadata(AFTER_KEY, target, propertyKey) || [];
3617
+ hooks.push(fn);
3618
+ Reflect.defineMetadata(AFTER_KEY, hooks, target, propertyKey);
3461
3619
  }
3462
- }
3463
- return {
3464
- path: tempDirPath,
3465
- cleanup
3466
3620
  };
3467
3621
  }
3468
- __name(createTempDir, "createTempDir");
3469
- async function findNewestFile(dirPath, recursive = false) {
3470
- const files = await listFiles(dirPath, recursive);
3471
- if (files.length === 0) return null;
3472
- const stats = await Promise.all(files.map(async (file) => ({
3473
- path: file,
3474
- mtime: (await fsp.stat(file)).mtime
3475
- })));
3476
- return stats.sort((a, b) => b.mtime.getTime() - a.mtime.getTime())[0].path;
3622
+ __name(After, "After");
3623
+ function Roles(...roles) {
3624
+ return (target, propertyKey, _descriptor) => {
3625
+ if (typeof propertyKey === "undefined") {
3626
+ Reflect.defineMetadata(ROLES_KEY, roles, target);
3627
+ } else {
3628
+ Reflect.defineMetadata(ROLES_KEY, roles, target, propertyKey);
3629
+ }
3630
+ };
3477
3631
  }
3478
- __name(findNewestFile, "findNewestFile");
3479
- async function findOldestFile(dirPath, recursive = false) {
3480
- const files = await listFiles(dirPath, recursive);
3481
- if (files.length === 0) return null;
3482
- const stats = await Promise.all(files.map(async (file) => ({
3483
- path: file,
3484
- mtime: (await fsp.stat(file)).mtime
3485
- })));
3486
- return stats.sort((a, b) => a.mtime.getTime() - b.mtime.getTime())[0].path;
3632
+ __name(Roles, "Roles");
3633
+ function Redirect(url, statusCode = 302) {
3634
+ return (target, propertyKey, descriptor) => {
3635
+ Reflect.defineMetadata(REDIRECT_KEY, {
3636
+ url,
3637
+ statusCode
3638
+ }, target, propertyKey);
3639
+ return descriptor;
3640
+ };
3487
3641
  }
3488
- __name(findOldestFile, "findOldestFile");
3489
- async function findInDir(dirPath, predicate, recursive = false) {
3490
- const entries = await fsp.readdir(dirPath, {
3491
- withFileTypes: true
3492
- });
3493
- const results = [];
3494
- for (const entry of entries) {
3495
- const entryPath = path.join(dirPath, entry.name);
3496
- const stat = await fsp.stat(entryPath);
3497
- if (await predicate(entryPath, stat)) {
3498
- results.push(entryPath);
3642
+ __name(Redirect, "Redirect");
3643
+ function Cache(ttlSeconds) {
3644
+ return (target, propertyKey, _descriptor) => {
3645
+ if (typeof propertyKey === "undefined") {
3646
+ Reflect.defineMetadata(CACHE_KEY, {
3647
+ ttlSeconds
3648
+ }, target);
3649
+ } else {
3650
+ Reflect.defineMetadata(CACHE_KEY, {
3651
+ ttlSeconds
3652
+ }, target, propertyKey);
3499
3653
  }
3500
- if (recursive && entry.isDirectory()) {
3501
- const nestedResults = await findInDir(entryPath, predicate, true);
3502
- results.push(...nestedResults);
3654
+ };
3655
+ }
3656
+ __name(Cache, "Cache");
3657
+ function RateLimit(options) {
3658
+ const opts = {
3659
+ standardHeaders: true,
3660
+ legacyHeaders: false,
3661
+ ...options
3662
+ };
3663
+ return (target, propertyKey, _descriptor) => {
3664
+ if (typeof propertyKey === "undefined") {
3665
+ Reflect.defineMetadata(RATE_LIMIT_KEY, opts, target);
3666
+ } else {
3667
+ Reflect.defineMetadata(RATE_LIMIT_KEY, opts, target, propertyKey);
3503
3668
  }
3504
- }
3505
- return results;
3669
+ };
3506
3670
  }
3507
- __name(findInDir, "findInDir");
3508
- async function watchDirRecursive(dirPath, callback, includeSubdirs = true) {
3509
- const normalizedBasePath = path.normalize(dirPath);
3510
- const watchers = [];
3511
- const addWatcher = /* @__PURE__ */ __name((dir) => {
3512
- try {
3513
- const watcher = fs.watch(dir, (eventType, filename) => {
3514
- if (!filename) return;
3515
- const fullPath = path.join(dir, filename);
3516
- const relativePath = path.relative(normalizedBasePath, fullPath);
3517
- callback(eventType, relativePath);
3518
- if (eventType === "rename" && includeSubdirs) {
3519
- setTimeout(async () => {
3520
- try {
3521
- if (await isDirectory(fullPath)) {
3522
- addWatcher(fullPath);
3523
- }
3524
- } catch {
3525
- }
3526
- }, 100);
3527
- }
3528
- });
3529
- watchers.push(watcher);
3530
- } catch {
3671
+ __name(RateLimit, "RateLimit");
3672
+ function ContentType(type) {
3673
+ return (target, propertyKey, _descriptor) => {
3674
+ if (typeof propertyKey === "undefined") {
3675
+ Reflect.defineMetadata(CONTENT_TYPE_KEY, {
3676
+ type
3677
+ }, target);
3678
+ } else {
3679
+ Reflect.defineMetadata(CONTENT_TYPE_KEY, {
3680
+ type
3681
+ }, target, propertyKey);
3531
3682
  }
3532
- }, "addWatcher");
3533
- addWatcher(normalizedBasePath);
3534
- if (includeSubdirs) {
3535
- const subdirs = await getSubdirectories(normalizedBasePath, true);
3536
- for (const subdir of subdirs) {
3537
- addWatcher(subdir);
3683
+ };
3684
+ }
3685
+ __name(ContentType, "ContentType");
3686
+ function Version(version, options) {
3687
+ const opts = {
3688
+ addPrefix: true,
3689
+ addHeader: true,
3690
+ headerName: "X-API-Version",
3691
+ ...options
3692
+ };
3693
+ return (target, propertyKey, _descriptor) => {
3694
+ if (typeof propertyKey === "undefined") {
3695
+ Reflect.defineMetadata(VERSION_KEY, {
3696
+ version,
3697
+ options: opts
3698
+ }, target);
3699
+ } else {
3700
+ Reflect.defineMetadata(VERSION_KEY, {
3701
+ version,
3702
+ options: opts
3703
+ }, target, propertyKey);
3538
3704
  }
3539
- }
3540
- return () => {
3541
- for (const watcher of watchers) {
3542
- watcher.close();
3705
+ };
3706
+ }
3707
+ __name(Version, "Version");
3708
+ function Timeout(ms) {
3709
+ return (target, propertyKey, _descriptor) => {
3710
+ if (typeof propertyKey === "undefined") {
3711
+ Reflect.defineMetadata(TIMEOUT_KEY, {
3712
+ ms
3713
+ }, target);
3714
+ } else {
3715
+ Reflect.defineMetadata(TIMEOUT_KEY, {
3716
+ ms
3717
+ }, target, propertyKey);
3543
3718
  }
3544
3719
  };
3545
3720
  }
3546
- __name(watchDirRecursive, "watchDirRecursive");
3547
- async function getDirStats(dirPath) {
3548
- let fileCount = 0;
3549
- let dirCount = 0;
3550
- let totalSize = 0;
3551
- async function processDir(currentPath) {
3552
- const entries = await fsp.readdir(currentPath, {
3553
- withFileTypes: true
3554
- });
3555
- for (const entry of entries) {
3556
- const entryPath = path.join(currentPath, entry.name);
3557
- if (entry.isDirectory()) {
3558
- dirCount++;
3559
- await processDir(entryPath);
3560
- } else if (entry.isFile()) {
3561
- fileCount++;
3562
- const stat = await fsp.stat(entryPath);
3563
- totalSize += stat.size;
3564
- }
3721
+ __name(Timeout, "Timeout");
3722
+ function Log(options) {
3723
+ return (target, propertyKey, _descriptor) => {
3724
+ const config2 = {
3725
+ logEntry: true,
3726
+ logExit: true,
3727
+ logBody: false,
3728
+ logParams: false,
3729
+ logResponse: false,
3730
+ ...options
3731
+ };
3732
+ if (typeof propertyKey === "undefined") {
3733
+ Reflect.defineMetadata(LOG_KEY, config2, target);
3734
+ } else {
3735
+ Reflect.defineMetadata(LOG_KEY, config2, target, propertyKey);
3565
3736
  }
3566
- }
3567
- __name(processDir, "processDir");
3568
- await processDir(dirPath);
3569
- return {
3570
- fileCount,
3571
- dirCount,
3572
- totalSize
3573
3737
  };
3574
3738
  }
3575
- __name(getDirStats, "getDirStats");
3576
- async function walkDir(dirPath, options) {
3577
- const traversalOrder = options.traversalOrder || "pre";
3578
- const entries = await fsp.readdir(dirPath, {
3579
- withFileTypes: true
3739
+ __name(Log, "Log");
3740
+ function registerControllers(router, controllers) {
3741
+ controllers.forEach((ControllerClass) => {
3742
+ if (!Reflect.getMetadata("injectable", ControllerClass)) {
3743
+ Reflect.defineMetadata("injectable", true, ControllerClass);
3744
+ diContainer.register(ControllerClass);
3745
+ }
3746
+ const instance = diContainer.get(ControllerClass);
3747
+ let basePath = Reflect.getMetadata("basePath", ControllerClass) || "";
3748
+ const routes = Reflect.getMetadata(ROUTES_KEY, ControllerClass) || [];
3749
+ const controllerRateLimit = Reflect.getMetadata(RATE_LIMIT_KEY, ControllerClass);
3750
+ const controllerCache = Reflect.getMetadata(CACHE_KEY, ControllerClass);
3751
+ const controllerTimeout = Reflect.getMetadata(TIMEOUT_KEY, ControllerClass);
3752
+ const controllerVersion = Reflect.getMetadata(VERSION_KEY, ControllerClass);
3753
+ const controllerRoles = Reflect.getMetadata(ROLES_KEY, ControllerClass);
3754
+ const controllerLogConfig = Reflect.getMetadata(LOG_KEY, ControllerClass);
3755
+ const controllerHeaders = Reflect.getMetadata(HEADER_KEY, ControllerClass) || {};
3756
+ const controllerMiddlewares = Reflect.getMetadata(MIDDLEWARE_KEY, ControllerClass) || [];
3757
+ const controllerBeforeHooks = Reflect.getMetadata(BEFORE_KEY, ControllerClass) || [];
3758
+ const controllerAfterHooks = Reflect.getMetadata(AFTER_KEY, ControllerClass) || [];
3759
+ const controllerContentType = Reflect.getMetadata(CONTENT_TYPE_KEY, ControllerClass);
3760
+ if (controllerVersion?.options?.addPrefix) {
3761
+ basePath = `/${controllerVersion.version}${basePath || ""}`;
3762
+ }
3763
+ routes.forEach(({ path: path3, method, handlerName }) => {
3764
+ const methodMiddlewares = Reflect.getMetadata(MIDDLEWARE_KEY, instance, handlerName) || [];
3765
+ const params = Reflect.getMetadata(PARAMS_KEY, instance, handlerName) || [];
3766
+ const httpCode = Reflect.getMetadata(HTTP_CODE_KEY, instance, handlerName);
3767
+ const methodHeaders = Reflect.getMetadata(HEADER_KEY, instance, handlerName) || {};
3768
+ const headers = {
3769
+ ...controllerHeaders,
3770
+ ...methodHeaders
3771
+ };
3772
+ const middlewares = [
3773
+ ...controllerMiddlewares,
3774
+ ...methodMiddlewares
3775
+ ];
3776
+ const methodBeforeHooks = Reflect.getMetadata(BEFORE_KEY, instance, handlerName) || [];
3777
+ const methodAfterHooks = Reflect.getMetadata(AFTER_KEY, instance, handlerName) || [];
3778
+ const beforeHooks = [
3779
+ ...controllerBeforeHooks,
3780
+ ...methodBeforeHooks
3781
+ ];
3782
+ const afterHooks = [
3783
+ ...methodAfterHooks,
3784
+ ...controllerAfterHooks
3785
+ ];
3786
+ const redirect = Reflect.getMetadata(REDIRECT_KEY, instance, handlerName);
3787
+ const methodContentType = Reflect.getMetadata(CONTENT_TYPE_KEY, instance, handlerName);
3788
+ const contentType = methodContentType || controllerContentType;
3789
+ const roles = Reflect.getMetadata(ROLES_KEY, instance, handlerName) || controllerRoles || [];
3790
+ const cache = Reflect.getMetadata(CACHE_KEY, instance, handlerName) || controllerCache;
3791
+ const rateLimitOptions = Reflect.getMetadata(RATE_LIMIT_KEY, instance, handlerName) || controllerRateLimit;
3792
+ const methodVersion = Reflect.getMetadata(VERSION_KEY, instance, handlerName);
3793
+ const version = methodVersion || controllerVersion;
3794
+ const timeout2 = Reflect.getMetadata(TIMEOUT_KEY, instance, handlerName) || controllerTimeout;
3795
+ const logConfig = Reflect.getMetadata(LOG_KEY, instance, handlerName) || controllerLogConfig;
3796
+ let rateLimiter = null;
3797
+ if (rateLimitOptions) {
3798
+ try {
3799
+ rateLimiter = rateLimiterCache.get(rateLimitOptions);
3800
+ } catch (err) {
3801
+ getLogger().warn({
3802
+ err
3803
+ }, "express-rate-limit not available, skipping rate limiting for this route");
3804
+ }
3805
+ }
3806
+ let finalPath = path3;
3807
+ if (methodVersion?.options?.addPrefix && methodVersion.version !== controllerVersion?.version) {
3808
+ finalPath = `/${methodVersion.version}${path3}`;
3809
+ }
3810
+ const handler = /* @__PURE__ */ __name(async (req, res, next) => {
3811
+ req["startTime"] = Date.now();
3812
+ let timeoutId;
3813
+ let timedOut = false;
3814
+ try {
3815
+ if (timeout2) {
3816
+ timeoutId = setTimeout(() => {
3817
+ if (!res.headersSent && !timedOut) {
3818
+ timedOut = true;
3819
+ const errorResponse = createFinalErrorResponse(req, HttpStatusCodes.REQUEST_TIMEOUT, "Request timed out");
3820
+ res.status(HttpStatusCodes.REQUEST_TIMEOUT).json(errorResponse);
3821
+ }
3822
+ }, timeout2.ms);
3823
+ }
3824
+ if (timedOut) return;
3825
+ if (rateLimiter) {
3826
+ await new Promise((resolve2, reject) => {
3827
+ rateLimiter(req, res, (err) => {
3828
+ if (err) reject(err);
3829
+ else resolve2();
3830
+ });
3831
+ });
3832
+ }
3833
+ if (!res.headersSent && contentType) {
3834
+ res.setHeader("Content-Type", contentType.type);
3835
+ }
3836
+ if (version?.options?.addHeader && version?.options?.headerName && version?.version) {
3837
+ if (!res.headersSent) {
3838
+ res.setHeader(version.options.headerName, version.version);
3839
+ }
3840
+ }
3841
+ if (cache && !res.headersSent) {
3842
+ res.setHeader("Cache-Control", `public, max-age=${cache.ttlSeconds}`);
3843
+ }
3844
+ if (logConfig?.logEntry) {
3845
+ const logger2 = getLogger();
3846
+ const logData = {
3847
+ method: req.method,
3848
+ url: req.originalUrl || req.url,
3849
+ userAgent: req.get("User-Agent")
3850
+ };
3851
+ if (logConfig.logParams) {
3852
+ logData.params = req.params;
3853
+ logData.query = req.query;
3854
+ }
3855
+ if (logConfig.logBody) logData.body = req.body;
3856
+ logger2.info({
3857
+ entry: logData
3858
+ }, "Route Entry:");
3859
+ }
3860
+ if (roles.length && !req?.user?.roles?.some((role) => roles.includes(role))) {
3861
+ const errorResponse = createFinalErrorResponse(req, HttpStatusCodes.FORBIDDEN, "Forbidden Insufficient Roles");
3862
+ res.status(HttpStatusCodes.FORBIDDEN).json(errorResponse);
3863
+ if (timeoutId) {
3864
+ clearTimeout(timeoutId);
3865
+ }
3866
+ return;
3867
+ }
3868
+ if (redirect && redirect.url) {
3869
+ if (timeoutId) {
3870
+ clearTimeout(timeoutId);
3871
+ }
3872
+ return res.redirect(redirect.statusCode, redirect.url);
3873
+ }
3874
+ for (const fn of beforeHooks) await fn(req, res);
3875
+ const args = [];
3876
+ if (params.length) {
3877
+ params.forEach(({ index, type, key, options }) => {
3878
+ let rawValue;
3879
+ switch (type) {
3880
+ case "query":
3881
+ rawValue = key ? req.query[key] : req.query;
3882
+ args[index] = options ? applyParamOptions(rawValue, options, key) : rawValue;
3883
+ break;
3884
+ case "param":
3885
+ rawValue = key ? req.params[key] : req.params;
3886
+ args[index] = options ? applyParamOptions(rawValue, options, key) : rawValue;
3887
+ break;
3888
+ case "body":
3889
+ args[index] = key ? req.body?.[key] : req.body;
3890
+ break;
3891
+ case "req":
3892
+ args[index] = req;
3893
+ break;
3894
+ case "res":
3895
+ args[index] = res;
3896
+ break;
3897
+ case "logger":
3898
+ args[index] = getLogger();
3899
+ break;
3900
+ case "reqHeader":
3901
+ args[index] = key ? req.headers[key.toLowerCase()] : req.headers;
3902
+ break;
3903
+ case "reqId":
3904
+ args[index] = req.headers["x-request-id"] || req?.id || void 0;
3905
+ break;
3906
+ case "cookie":
3907
+ args[index] = key ? req.cookies?.[key] : req.cookies;
3908
+ break;
3909
+ }
3910
+ });
3911
+ }
3912
+ const result = instance[handlerName](...args);
3913
+ const awaited = result instanceof Promise ? await result : result;
3914
+ if (timeoutId) {
3915
+ clearTimeout(timeoutId);
3916
+ }
3917
+ if (timedOut) return;
3918
+ if (redirect && awaited && typeof awaited === "object" && "url" in awaited) {
3919
+ const redirectUrl = awaited.url;
3920
+ const redirectStatus = awaited.statusCode || redirect.statusCode;
3921
+ return res.redirect(redirectStatus, redirectUrl);
3922
+ }
3923
+ if (!res.headersSent && typeof awaited !== "undefined") {
3924
+ if (httpCode) res.status(httpCode);
3925
+ for (const [k, v] of Object.entries(headers)) {
3926
+ const normalized = normalizeHeaderValue(v);
3927
+ if (typeof normalized !== "undefined") {
3928
+ res.set(k, normalized);
3929
+ }
3930
+ }
3931
+ res.json(awaited);
3932
+ }
3933
+ if (logConfig?.logExit) {
3934
+ const logger2 = getLogger();
3935
+ const logData = {
3936
+ method: req.method,
3937
+ url: req.originalUrl || req.url,
3938
+ statusCode: res.statusCode,
3939
+ duration: `${Date.now() - req.startTime}ms`
3940
+ };
3941
+ if (logConfig.logResponse) logData.response = awaited;
3942
+ logger2.info({
3943
+ exit: logData
3944
+ }, "Route Exit:");
3945
+ }
3946
+ for (const fn of afterHooks) await fn(req, res, awaited);
3947
+ } catch (err) {
3948
+ if (timeoutId) {
3949
+ clearTimeout(timeoutId);
3950
+ }
3951
+ next(err);
3952
+ }
3953
+ }, "handler");
3954
+ router[method](basePath + finalPath, ...middlewares, handler);
3955
+ });
3580
3956
  });
3581
- for (const entry of entries) {
3582
- const entryPath = path.join(dirPath, entry.name);
3583
- const stats = await fsp.stat(entryPath);
3584
- const entryInfo = {
3585
- path: entryPath,
3586
- name: entry.name,
3587
- isDirectory: entry.isDirectory(),
3588
- stats
3589
- };
3590
- if (entry.isDirectory()) {
3591
- if (traversalOrder === "pre") {
3592
- const shouldContinue = await options.visitorFn(entryInfo);
3593
- if (shouldContinue !== false) {
3594
- await walkDir(entryPath, options);
3957
+ }
3958
+ __name(registerControllers, "registerControllers");
3959
+ function applyParamOptions(rawValue, options, key) {
3960
+ if (!options) return rawValue;
3961
+ let value = rawValue;
3962
+ const paramName = key ? `: ${key}` : "";
3963
+ if (value === void 0 || value === null || value === "") {
3964
+ if ("default" in options) {
3965
+ value = options.default;
3966
+ }
3967
+ }
3968
+ if (options.required && (value === void 0 || value === null || value === "")) {
3969
+ throw new BadRequestException(`Required parameter missing${paramName}`);
3970
+ }
3971
+ if (value !== void 0 && value !== null) {
3972
+ if (options.dataType === "array") {
3973
+ const delimiter = options.delimiter || ",";
3974
+ if (typeof value === "string") {
3975
+ value = value.split(delimiter).map((v) => v.trim());
3976
+ } else if (!Array.isArray(value)) {
3977
+ value = [
3978
+ value
3979
+ ];
3980
+ }
3981
+ if (options.type === "string" && options.pattern) {
3982
+ const hasInvalidPattern = value.some((v) => !options.pattern.test(v));
3983
+ if (hasInvalidPattern) {
3984
+ throw new BadRequestException(`Parameter '${key}' array contains values that do not match pattern: ${options.patternName || options.pattern}`);
3985
+ }
3986
+ }
3987
+ if (options.type === "number" && options.pattern) {
3988
+ const hasInvalidPattern = value.some((v) => !options.pattern.test(String(v)));
3989
+ if (hasInvalidPattern) {
3990
+ throw new BadRequestException(`Parameter '${key}' array contains values that do not match pattern: ${options.patternName || options.pattern}`);
3991
+ }
3992
+ }
3993
+ if (options.type === "number" && options.throwError !== false) {
3994
+ const hasInvalidNumber = value.some((v) => isNaN(Number(v)));
3995
+ if (hasInvalidNumber) {
3996
+ throw new BadRequestException(`Type error: expected number array${paramName}`);
3997
+ }
3998
+ if (options.min !== void 0) {
3999
+ const hasBelowMin = value.some((v) => Number(v) < options.min);
4000
+ if (hasBelowMin) {
4001
+ throw new BadRequestException(`Validation error: number array values must be >= ${options.min}${paramName}`);
4002
+ }
4003
+ }
4004
+ if (options.max !== void 0) {
4005
+ const hasAboveMax = value.some((v) => Number(v) > options.max);
4006
+ if (hasAboveMax) {
4007
+ throw new BadRequestException(`Validation error: number array values must be <= ${options.max}${paramName}`);
4008
+ }
4009
+ }
4010
+ }
4011
+ if (options.type === "boolean" && options.throwError !== false) {
4012
+ const hasInvalidBoolean = value.some((v) => !isValidBooleanInput(v));
4013
+ if (hasInvalidBoolean) {
4014
+ throw new BadRequestException(`Type error: expected boolean array${paramName}`);
4015
+ }
4016
+ }
4017
+ if (options.type) {
4018
+ value = value.map((v) => convertType(v, options.type || "string"));
4019
+ }
4020
+ } else if (options.dataType === "object") {
4021
+ if (typeof value === "string") {
4022
+ try {
4023
+ value = JSON.parse(value);
4024
+ } catch (_e) {
4025
+ if (options.throwError !== false) {
4026
+ throw new BadRequestException(`Invalid JSON object${paramName}`);
4027
+ }
4028
+ }
4029
+ }
4030
+ } else if (options.type) {
4031
+ const originalValue = value;
4032
+ value = convertType(value, options.type);
4033
+ if (options.throwError === false) {
4034
+ if (options.type === "number" && typeof value === "number") {
4035
+ if (options.min !== void 0 && value < options.min) {
4036
+ value = void 0;
4037
+ }
4038
+ if (options.max !== void 0 && value > options.max) {
4039
+ value = void 0;
4040
+ }
4041
+ }
4042
+ }
4043
+ if (options.type === "string" && options.pattern && typeof value === "string" && !options.pattern.test(value)) {
4044
+ throw new BadRequestException(`Parameter '${key}' does not match pattern: ${options.patternName || options.pattern}`);
4045
+ } else if (options.type === "number" && options.pattern && typeof value === "number" && !options.pattern.test(String(value))) {
4046
+ throw new BadRequestException(`Parameter '${key}' does not match pattern: ${options.patternName || options.pattern}`);
4047
+ }
4048
+ if (options.throwError !== false) {
4049
+ if (options.type === "number" && typeof value === "number" && isNaN(value)) {
4050
+ throw new BadRequestException(`Type error: expected number${paramName}`);
4051
+ }
4052
+ if (options.type === "number" && typeof value === "number") {
4053
+ if (options.min !== void 0 && value < options.min) {
4054
+ throw new BadRequestException(`Validation error: number must be >= ${options.min}${paramName}`);
4055
+ }
4056
+ if (options.max !== void 0 && value > options.max) {
4057
+ throw new BadRequestException(`Validation error: number must be <= ${options.max}${paramName}`);
4058
+ }
4059
+ }
4060
+ if (options.type === "boolean" && !isValidBooleanInput(originalValue)) {
4061
+ throw new BadRequestException(`Type error: expected boolean${paramName}`);
3595
4062
  }
3596
- } else {
3597
- await walkDir(entryPath, options);
3598
- await options.visitorFn(entryInfo);
3599
4063
  }
3600
- } else {
3601
- await options.visitorFn(entryInfo);
3602
4064
  }
3603
4065
  }
3604
- if (traversalOrder === "post") {
3605
- const stats = await fsp.stat(dirPath);
3606
- const entryInfo = {
3607
- path: dirPath,
3608
- name: path.basename(dirPath),
3609
- isDirectory: true,
3610
- stats
3611
- };
3612
- await options.visitorFn(entryInfo);
3613
- }
3614
- }
3615
- __name(walkDir, "walkDir");
3616
-
3617
- // src/utils/exception.utils.ts
3618
- var HttpError = class extends ErrorResponse {
3619
- static {
3620
- __name(this, "HttpError");
3621
- }
3622
- /**
3623
- * Creates a new HTTP error instance.
3624
- * @param status - A valid HTTP status code (e.g., 400, 500).
3625
- * @param message - The error message to return to the client.
3626
- */
3627
- constructor(status, message) {
3628
- super(message);
3629
- this.status = status;
3630
- }
3631
- };
3632
- var InternalServerErrorException = class extends ErrorResponse {
3633
- static {
3634
- __name(this, "InternalServerErrorException");
3635
- }
3636
- /**
3637
- * @param message - Optional custom message. Defaults to "Internal server error".
3638
- */
3639
- constructor(message = "Internal server error") {
3640
- super(message);
3641
- this.status = HttpStatusCodes.INTERNAL_SERVER_ERROR;
3642
- }
3643
- };
3644
- var UnauthorizedException = class extends ErrorResponse {
3645
- static {
3646
- __name(this, "UnauthorizedException");
3647
- }
3648
- /**
3649
- * @param message - Optional custom message. Defaults to "Unauthorized".
3650
- */
3651
- constructor(message = "Unauthorized") {
3652
- super(message);
3653
- this.status = HttpStatusCodes.UNAUTHORIZED;
3654
- }
3655
- };
3656
- var BadRequestException = class extends ErrorResponse {
3657
- static {
3658
- __name(this, "BadRequestException");
3659
- }
3660
- /**
3661
- * @param message - Optional custom message. Defaults to "Bad request".
3662
- */
3663
- constructor(message = "Bad request") {
3664
- super(message);
3665
- this.status = HttpStatusCodes.BAD_REQUEST;
3666
- }
3667
- };
3668
- var NotFoundException = class extends ErrorResponse {
3669
- static {
3670
- __name(this, "NotFoundException");
3671
- }
3672
- /**
3673
- * @param message - Optional custom message. Defaults to "Resource not found".
3674
- */
3675
- constructor(message = "Resource not found") {
3676
- super(message);
3677
- this.status = HttpStatusCodes.NOT_FOUND;
3678
- }
3679
- };
3680
- var ForbiddenException = class extends ErrorResponse {
3681
- static {
3682
- __name(this, "ForbiddenException");
3683
- }
3684
- /**
3685
- * @param message - Optional custom message. Defaults to "Forbidden".
3686
- */
3687
- constructor(message = "Forbidden") {
3688
- super(message);
3689
- this.status = HttpStatusCodes.FORBIDDEN;
3690
- }
3691
- };
3692
- var ConflictException = class extends ErrorResponse {
3693
- static {
3694
- __name(this, "ConflictException");
3695
- }
3696
- /**
3697
- * @param message - Optional custom message. Defaults to "Conflict".
3698
- */
3699
- constructor(message = "Conflict") {
3700
- super(message);
3701
- this.status = HttpStatusCodes.CONFLICT;
3702
- }
3703
- };
3704
- var BadGatewayException = class extends ErrorResponse {
3705
- static {
3706
- __name(this, "BadGatewayException");
3707
- }
3708
- /**
3709
- * @param message - Optional custom message. Defaults to "Bad Gateway".
3710
- */
3711
- constructor(message = "Bad Gateway") {
3712
- super(message);
3713
- this.status = HttpStatusCodes.BAD_GATEWAY;
3714
- }
3715
- };
3716
- var TooManyRequestsException = class extends ErrorResponse {
3717
- static {
3718
- __name(this, "TooManyRequestsException");
3719
- }
3720
- /**
3721
- * @param message - Optional custom message. Defaults to "Too many requests".
3722
- */
3723
- constructor(message = "Too many requests") {
3724
- super(message);
3725
- this.status = HttpStatusCodes.TOO_MANY_REQUESTS;
3726
- }
3727
- };
3728
- var ServiceUnavailableException = class extends ErrorResponse {
3729
- static {
3730
- __name(this, "ServiceUnavailableException");
3731
- }
3732
- /**
3733
- * @param message - Optional custom message. Defaults to "Service Unavailable".
3734
- */
3735
- constructor(message = "Service Unavailable") {
3736
- super(message);
3737
- this.status = HttpStatusCodes.SERVICE_UNAVAILABLE;
3738
- }
3739
- };
3740
- var GatewayTimeoutException = class extends ErrorResponse {
3741
- static {
3742
- __name(this, "GatewayTimeoutException");
3743
- }
3744
- /**
3745
- * @param message - Optional custom message. Defaults to "Gateway Timeout".
3746
- */
3747
- constructor(message = "Gateway Timeout") {
3748
- super(message);
3749
- this.status = HttpStatusCodes.GATEWAY_TIMEOUT;
3750
- }
3751
- };
3752
- var UnprocessableEntityException = class extends ErrorResponse {
3753
- static {
3754
- __name(this, "UnprocessableEntityException");
3755
- }
3756
- details;
3757
- /**
3758
- * @param message - Optional custom message. Defaults to "Unprocessable Entity".
3759
- * @param details - Optional validation details or additional error context.
3760
- */
3761
- constructor(message = "Unprocessable Entity", details) {
3762
- super(message), this.details = details;
3763
- this.status = HttpStatusCodes.UNPROCESSABLE_ENTITY;
3764
- }
3765
- };
3766
- var MethodNotAllowedException = class extends ErrorResponse {
3767
- static {
3768
- __name(this, "MethodNotAllowedException");
4066
+ if (options.transform && typeof options.transform === "function") {
4067
+ value = options.transform(value);
3769
4068
  }
3770
- allowedMethods;
3771
- /**
3772
- * @param message - Optional custom message. Defaults to "Method Not Allowed".
3773
- * @param allowedMethods - Optional array of allowed HTTP methods.
3774
- */
3775
- constructor(message = "Method Not Allowed", allowedMethods) {
3776
- super(message), this.allowedMethods = allowedMethods;
3777
- this.status = HttpStatusCodes.METHOD_NOT_ALLOWED;
4069
+ if (options.validate && typeof options.validate === "function") {
4070
+ if (!options.validate(value)) {
4071
+ if (options.throwError !== false) {
4072
+ throw new BadRequestException(`Parameter validation failed${paramName}`);
4073
+ }
4074
+ return void 0;
4075
+ }
3778
4076
  }
3779
- };
3780
- var NotAcceptableException = class extends ErrorResponse {
3781
- static {
3782
- __name(this, "NotAcceptableException");
4077
+ return value;
4078
+ }
4079
+ __name(applyParamOptions, "applyParamOptions");
4080
+ function isValidBooleanInput(value) {
4081
+ if (typeof value === "boolean") return true;
4082
+ if (typeof value === "number") return value === 0 || value === 1;
4083
+ if (typeof value === "string") {
4084
+ const normalizedValue = value.toLowerCase().trim();
4085
+ return [
4086
+ "true",
4087
+ "false",
4088
+ "0",
4089
+ "1",
4090
+ "yes",
4091
+ "no"
4092
+ ].includes(normalizedValue);
3783
4093
  }
3784
- /**
3785
- * @param message - Optional custom message. Defaults to "Not Acceptable".
3786
- */
3787
- constructor(message = "Not Acceptable") {
3788
- super(message);
3789
- this.status = HttpStatusCodes.NOT_ACCEPTABLE;
4094
+ return false;
4095
+ }
4096
+ __name(isValidBooleanInput, "isValidBooleanInput");
4097
+ function convertType(value, type) {
4098
+ if (value === void 0 || value === null) {
4099
+ return value;
3790
4100
  }
3791
- };
3792
- var RequestTimeoutException = class extends ErrorResponse {
3793
- static {
3794
- __name(this, "RequestTimeoutException");
4101
+ switch (type) {
4102
+ case "number":
4103
+ return Number(value);
4104
+ case "boolean":
4105
+ if (typeof value === "string") {
4106
+ const normalizedValue = value.toLowerCase().trim();
4107
+ return normalizedValue === "true" || normalizedValue === "1" || normalizedValue === "yes";
4108
+ }
4109
+ return value === true || value === 1;
4110
+ case "string":
4111
+ default:
4112
+ return String(value);
3795
4113
  }
3796
- /**
3797
- * @param message - Optional custom message. Defaults to "Request Timeout".
3798
- */
3799
- constructor(message = "Request Timeout") {
3800
- super(message);
3801
- this.status = HttpStatusCodes.REQUEST_TIMEOUT;
4114
+ }
4115
+ __name(convertType, "convertType");
4116
+ async function ensureDir(dirPath) {
4117
+ await fsp.mkdir(dirPath, {
4118
+ recursive: true
4119
+ });
4120
+ }
4121
+ __name(ensureDir, "ensureDir");
4122
+ async function listFiles(dirPath, recursive = false) {
4123
+ const entries = await fsp.readdir(dirPath, {
4124
+ withFileTypes: true
4125
+ });
4126
+ const files = [];
4127
+ for (const entry of entries) {
4128
+ const fullPath = path.join(dirPath, entry.name);
4129
+ if (entry.isFile()) {
4130
+ files.push(fullPath);
4131
+ } else if (recursive && entry.isDirectory()) {
4132
+ const nestedFiles = await listFiles(fullPath, true);
4133
+ files.push(...nestedFiles);
4134
+ }
3802
4135
  }
3803
- };
3804
- var UnsupportedMediaTypeException = class extends ErrorResponse {
3805
- static {
3806
- __name(this, "UnsupportedMediaTypeException");
4136
+ return files;
4137
+ }
4138
+ __name(listFiles, "listFiles");
4139
+ async function deleteDirRecursive(dirPath) {
4140
+ await fsp.rm(dirPath, {
4141
+ recursive: true,
4142
+ force: true
4143
+ });
4144
+ }
4145
+ __name(deleteDirRecursive, "deleteDirRecursive");
4146
+ async function isDirectory(pathStr) {
4147
+ try {
4148
+ const stat = await fsp.stat(pathStr);
4149
+ return stat.isDirectory();
4150
+ } catch {
4151
+ return false;
3807
4152
  }
3808
- /**
3809
- * @param message - Optional custom message. Defaults to "Unsupported Media Type".
3810
- */
3811
- constructor(message = "Unsupported Media Type") {
3812
- super(message);
3813
- this.status = HttpStatusCodes.UNSUPPORTED_MEDIA_TYPE;
4153
+ }
4154
+ __name(isDirectory, "isDirectory");
4155
+ async function copyDir(src, dest) {
4156
+ await ensureDir(dest);
4157
+ const entries = await fsp.readdir(src, {
4158
+ withFileTypes: true
4159
+ });
4160
+ for (const entry of entries) {
4161
+ const srcPath = path.join(src, entry.name);
4162
+ const destPath = path.join(dest, entry.name);
4163
+ if (entry.isDirectory()) {
4164
+ await copyDir(srcPath, destPath);
4165
+ } else {
4166
+ await fsp.copyFile(srcPath, destPath);
4167
+ }
3814
4168
  }
3815
- };
3816
- var PayloadTooLargeException = class extends ErrorResponse {
3817
- static {
3818
- __name(this, "PayloadTooLargeException");
4169
+ }
4170
+ __name(copyDir, "copyDir");
4171
+ async function moveDir(src, dest) {
4172
+ await copyDir(src, dest);
4173
+ await deleteDirRecursive(src);
4174
+ }
4175
+ __name(moveDir, "moveDir");
4176
+ async function emptyDir(dirPath) {
4177
+ const entries = await fsp.readdir(dirPath, {
4178
+ withFileTypes: true
4179
+ });
4180
+ for (const entry of entries) {
4181
+ const entryPath = path.join(dirPath, entry.name);
4182
+ if (entry.isDirectory()) {
4183
+ await fsp.rm(entryPath, {
4184
+ recursive: true,
4185
+ force: true
4186
+ });
4187
+ } else {
4188
+ await fsp.unlink(entryPath);
4189
+ }
3819
4190
  }
3820
- /**
3821
- * @param message - Optional custom message. Defaults to "Payload Too Large".
3822
- */
3823
- constructor(message = "Payload Too Large") {
3824
- super(message);
3825
- this.status = HttpStatusCodes.PAYLOAD_TOO_LARGE;
4191
+ }
4192
+ __name(emptyDir, "emptyDir");
4193
+ async function getDirSize(dirPath) {
4194
+ let total = 0;
4195
+ const entries = await fsp.readdir(dirPath, {
4196
+ withFileTypes: true
4197
+ });
4198
+ for (const entry of entries) {
4199
+ const entryPath = path.join(dirPath, entry.name);
4200
+ if (entry.isDirectory()) {
4201
+ total += await getDirSize(entryPath);
4202
+ } else {
4203
+ const stat = await fsp.stat(entryPath);
4204
+ total += stat.size;
4205
+ }
3826
4206
  }
3827
- };
3828
- var InsufficientStorageException = class extends ErrorResponse {
3829
- static {
3830
- __name(this, "InsufficientStorageException");
4207
+ return total;
4208
+ }
4209
+ __name(getDirSize, "getDirSize");
4210
+ function watchDir(dirPath, callback) {
4211
+ const watcher = fs.watch(dirPath, (eventType, filename) => {
4212
+ callback(eventType, filename);
4213
+ });
4214
+ return () => watcher.close();
4215
+ }
4216
+ __name(watchDir, "watchDir");
4217
+ async function findFilesByPattern(pattern, options = {}) {
4218
+ const matchPattern = /* @__PURE__ */ __name((name, pattern2) => {
4219
+ let escaped = pattern2.replaceAll(/[.+^${}()|[\]\\]/g, "\\$&");
4220
+ escaped = escaped.replaceAll("*", ".*").replaceAll("?", ".");
4221
+ const regex = new RegExp(`^${escaped}$`);
4222
+ return regex.test(name);
4223
+ }, "matchPattern");
4224
+ const { cwd = process.cwd(), dot = false } = options;
4225
+ const segments = pattern.split("/");
4226
+ async function walk(dir, segIndex) {
4227
+ if (segIndex >= segments.length) return [];
4228
+ const segment = segments[segIndex];
4229
+ const entries = await fs.promises.readdir(dir, {
4230
+ withFileTypes: true
4231
+ });
4232
+ let matchedFiles = [];
4233
+ for (const entry of entries) {
4234
+ const entryName = entry.name;
4235
+ if (!dot && entryName.startsWith(".")) continue;
4236
+ const fullPath = path.join(dir, entryName);
4237
+ const isLastSegment = segIndex === segments.length - 1;
4238
+ if (isLastSegment) {
4239
+ if (matchPattern(entryName, segment) && entry.isFile()) {
4240
+ matchedFiles.push(fullPath);
4241
+ }
4242
+ } else if (entry.isDirectory()) {
4243
+ if (matchPattern(entryName, segment)) {
4244
+ matchedFiles.push(...await walk(fullPath, segIndex + 1));
4245
+ }
4246
+ }
4247
+ }
4248
+ return matchedFiles;
3831
4249
  }
3832
- /**
3833
- * @param message - Optional custom message. Defaults to "Insufficient Storage".
3834
- */
3835
- constructor(message = "Insufficient Storage") {
3836
- super(message);
3837
- this.status = HttpStatusCodes.INSUFFICIENT_STORAGE;
4250
+ __name(walk, "walk");
4251
+ return walk(cwd, 0);
4252
+ }
4253
+ __name(findFilesByPattern, "findFilesByPattern");
4254
+ async function getSubdirectories(dirPath, recursive = false) {
4255
+ const entries = await fsp.readdir(dirPath, {
4256
+ withFileTypes: true
4257
+ });
4258
+ const dirs = entries.filter((entry) => entry.isDirectory()).map((entry) => path.join(dirPath, entry.name));
4259
+ if (recursive && dirs.length > 0) {
4260
+ const nestedDirs = await Promise.all(dirs.map((dir) => getSubdirectories(dir, true)));
4261
+ return [
4262
+ ...dirs,
4263
+ ...nestedDirs.flat()
4264
+ ];
3838
4265
  }
3839
- };
3840
- function isHttpError(error) {
3841
- return error instanceof ErrorResponse;
4266
+ return dirs;
3842
4267
  }
3843
- __name(isHttpError, "isHttpError");
3844
- function createHttpError(status, message) {
3845
- switch (status) {
3846
- case HttpStatusCodes.BAD_REQUEST:
3847
- return new BadRequestException(message);
3848
- case HttpStatusCodes.UNAUTHORIZED:
3849
- return new UnauthorizedException(message);
3850
- case HttpStatusCodes.FORBIDDEN:
3851
- return new ForbiddenException(message);
3852
- case HttpStatusCodes.NOT_FOUND:
3853
- return new NotFoundException(message);
3854
- case HttpStatusCodes.METHOD_NOT_ALLOWED:
3855
- return new MethodNotAllowedException(message);
3856
- case HttpStatusCodes.NOT_ACCEPTABLE:
3857
- return new NotAcceptableException(message);
3858
- case HttpStatusCodes.REQUEST_TIMEOUT:
3859
- return new RequestTimeoutException(message);
3860
- case HttpStatusCodes.CONFLICT:
3861
- return new ConflictException(message);
3862
- case HttpStatusCodes.GONE:
3863
- return new HttpError(HttpStatusCodes.GONE, message || "Gone");
3864
- case HttpStatusCodes.PAYLOAD_TOO_LARGE:
3865
- return new PayloadTooLargeException(message);
3866
- case HttpStatusCodes.UNSUPPORTED_MEDIA_TYPE:
3867
- return new UnsupportedMediaTypeException(message);
3868
- case HttpStatusCodes.UNPROCESSABLE_ENTITY:
3869
- return new UnprocessableEntityException(message);
3870
- case HttpStatusCodes.TOO_MANY_REQUESTS:
3871
- return new TooManyRequestsException(message);
3872
- case HttpStatusCodes.INTERNAL_SERVER_ERROR:
3873
- return new InternalServerErrorException(message);
3874
- case HttpStatusCodes.BAD_GATEWAY:
3875
- return new BadGatewayException(message);
3876
- case HttpStatusCodes.SERVICE_UNAVAILABLE:
3877
- return new ServiceUnavailableException(message);
3878
- case HttpStatusCodes.GATEWAY_TIMEOUT:
3879
- return new GatewayTimeoutException(message);
3880
- case HttpStatusCodes.INSUFFICIENT_STORAGE:
3881
- return new InsufficientStorageException(message);
3882
- default:
3883
- return new HttpError(status, message || `HTTP Error ${status}`);
4268
+ __name(getSubdirectories, "getSubdirectories");
4269
+ async function ensureEmptyDir(dirPath) {
4270
+ if (await isDirectory(dirPath)) {
4271
+ await emptyDir(dirPath);
4272
+ } else {
4273
+ try {
4274
+ await fsp.unlink(dirPath);
4275
+ } catch (error) {
4276
+ if (error.code !== "ENOENT") {
4277
+ throw error;
4278
+ }
4279
+ }
4280
+ await ensureDir(dirPath);
4281
+ }
4282
+ }
4283
+ __name(ensureEmptyDir, "ensureEmptyDir");
4284
+ async function createTempDir(options = {}) {
4285
+ const prefix = options.prefix || "tmp-";
4286
+ const parentDir = options.parentDir || os.tmpdir();
4287
+ const dirName = `${prefix}${randomBytes(6).toString("hex")}`;
4288
+ const tempDirPath = path.join(parentDir, dirName);
4289
+ await ensureDir(tempDirPath);
4290
+ const cleanup = /* @__PURE__ */ __name(async () => {
4291
+ try {
4292
+ await deleteDirRecursive(tempDirPath);
4293
+ } catch {
4294
+ }
4295
+ }, "cleanup");
4296
+ if (options.cleanup) {
4297
+ process.once("exit", () => {
4298
+ try {
4299
+ fs.rmSync(tempDirPath, {
4300
+ recursive: true,
4301
+ force: true
4302
+ });
4303
+ } catch {
4304
+ }
4305
+ });
4306
+ const signals = [
4307
+ "SIGINT",
4308
+ "SIGTERM",
4309
+ "SIGUSR1",
4310
+ "SIGUSR2"
4311
+ ];
4312
+ for (const signal of signals) {
4313
+ process.once(signal, () => {
4314
+ cleanup().then(() => process.exit());
4315
+ });
4316
+ }
3884
4317
  }
4318
+ return {
4319
+ path: tempDirPath,
4320
+ cleanup
4321
+ };
3885
4322
  }
3886
- __name(createHttpError, "createHttpError");
3887
- function hasErrorShape(error) {
3888
- return typeof error === "object" && error !== null && "message" in error && typeof error.message === "string";
4323
+ __name(createTempDir, "createTempDir");
4324
+ async function findNewestFile(dirPath, recursive = false) {
4325
+ const files = await listFiles(dirPath, recursive);
4326
+ if (files.length === 0) return null;
4327
+ const stats = await Promise.all(files.map(async (file) => ({
4328
+ path: file,
4329
+ mtime: (await fsp.stat(file)).mtime
4330
+ })));
4331
+ return stats.sort((a, b) => b.mtime.getTime() - a.mtime.getTime())[0].path;
3889
4332
  }
3890
- __name(hasErrorShape, "hasErrorShape");
3891
- function getErrorMessage(error) {
3892
- if (typeof error === "string") {
3893
- return error;
3894
- }
3895
- if (error instanceof Error) {
3896
- return error.message;
3897
- }
3898
- if (hasErrorShape(error)) {
3899
- return error.message;
4333
+ __name(findNewestFile, "findNewestFile");
4334
+ async function findOldestFile(dirPath, recursive = false) {
4335
+ const files = await listFiles(dirPath, recursive);
4336
+ if (files.length === 0) return null;
4337
+ const stats = await Promise.all(files.map(async (file) => ({
4338
+ path: file,
4339
+ mtime: (await fsp.stat(file)).mtime
4340
+ })));
4341
+ return stats.sort((a, b) => a.mtime.getTime() - b.mtime.getTime())[0].path;
4342
+ }
4343
+ __name(findOldestFile, "findOldestFile");
4344
+ async function findInDir(dirPath, predicate, recursive = false) {
4345
+ const entries = await fsp.readdir(dirPath, {
4346
+ withFileTypes: true
4347
+ });
4348
+ const results = [];
4349
+ for (const entry of entries) {
4350
+ const entryPath = path.join(dirPath, entry.name);
4351
+ const stat = await fsp.stat(entryPath);
4352
+ if (await predicate(entryPath, stat)) {
4353
+ results.push(entryPath);
4354
+ }
4355
+ if (recursive && entry.isDirectory()) {
4356
+ const nestedResults = await findInDir(entryPath, predicate, true);
4357
+ results.push(...nestedResults);
4358
+ }
3900
4359
  }
3901
- return "Unknown error occurred";
4360
+ return results;
3902
4361
  }
3903
- __name(getErrorMessage, "getErrorMessage");
3904
- function withErrorHandling(handler) {
3905
- return async (...args) => {
4362
+ __name(findInDir, "findInDir");
4363
+ async function watchDirRecursive(dirPath, callback, includeSubdirs = true) {
4364
+ const normalizedBasePath = path.normalize(dirPath);
4365
+ const watchers = [];
4366
+ const addWatcher = /* @__PURE__ */ __name((dir) => {
3906
4367
  try {
3907
- return await handler(...args);
3908
- } catch (err) {
3909
- if (isHttpError(err)) {
3910
- throw err;
3911
- }
3912
- getLogger().error({
3913
- err
3914
- }, "Caught error in handler");
3915
- let status = HttpStatusCodes.INTERNAL_SERVER_ERROR;
3916
- if (hasErrorShape(err) && typeof err.status === "number") {
3917
- status = err.status;
4368
+ const watcher = fs.watch(dir, (eventType, filename) => {
4369
+ if (!filename) return;
4370
+ const fullPath = path.join(dir, filename);
4371
+ const relativePath = path.relative(normalizedBasePath, fullPath);
4372
+ callback(eventType, relativePath);
4373
+ if (eventType === "rename" && includeSubdirs) {
4374
+ setTimeout(async () => {
4375
+ try {
4376
+ if (await isDirectory(fullPath)) {
4377
+ addWatcher(fullPath);
4378
+ }
4379
+ } catch {
4380
+ }
4381
+ }, 100);
4382
+ }
4383
+ });
4384
+ watchers.push(watcher);
4385
+ } catch {
4386
+ }
4387
+ }, "addWatcher");
4388
+ addWatcher(normalizedBasePath);
4389
+ if (includeSubdirs) {
4390
+ const subdirs = await getSubdirectories(normalizedBasePath, true);
4391
+ for (const subdir of subdirs) {
4392
+ addWatcher(subdir);
4393
+ }
4394
+ }
4395
+ return () => {
4396
+ for (const watcher of watchers) {
4397
+ watcher.close();
4398
+ }
4399
+ };
4400
+ }
4401
+ __name(watchDirRecursive, "watchDirRecursive");
4402
+ async function getDirStats(dirPath) {
4403
+ let fileCount = 0;
4404
+ let dirCount = 0;
4405
+ let totalSize = 0;
4406
+ async function processDir(currentPath) {
4407
+ const entries = await fsp.readdir(currentPath, {
4408
+ withFileTypes: true
4409
+ });
4410
+ for (const entry of entries) {
4411
+ const entryPath = path.join(currentPath, entry.name);
4412
+ if (entry.isDirectory()) {
4413
+ dirCount++;
4414
+ await processDir(entryPath);
4415
+ } else if (entry.isFile()) {
4416
+ fileCount++;
4417
+ const stat = await fsp.stat(entryPath);
4418
+ totalSize += stat.size;
3918
4419
  }
3919
- throw createHttpError(status, getErrorMessage(err));
3920
4420
  }
4421
+ }
4422
+ __name(processDir, "processDir");
4423
+ await processDir(dirPath);
4424
+ return {
4425
+ fileCount,
4426
+ dirCount,
4427
+ totalSize
3921
4428
  };
3922
4429
  }
3923
- __name(withErrorHandling, "withErrorHandling");
4430
+ __name(getDirStats, "getDirStats");
4431
+ async function walkDir(dirPath, options) {
4432
+ const traversalOrder = options.traversalOrder || "pre";
4433
+ const entries = await fsp.readdir(dirPath, {
4434
+ withFileTypes: true
4435
+ });
4436
+ for (const entry of entries) {
4437
+ const entryPath = path.join(dirPath, entry.name);
4438
+ const stats = await fsp.stat(entryPath);
4439
+ const entryInfo = {
4440
+ path: entryPath,
4441
+ name: entry.name,
4442
+ isDirectory: entry.isDirectory(),
4443
+ stats
4444
+ };
4445
+ if (entry.isDirectory()) {
4446
+ if (traversalOrder === "pre") {
4447
+ const shouldContinue = await options.visitorFn(entryInfo);
4448
+ if (shouldContinue !== false) {
4449
+ await walkDir(entryPath, options);
4450
+ }
4451
+ } else {
4452
+ await walkDir(entryPath, options);
4453
+ await options.visitorFn(entryInfo);
4454
+ }
4455
+ } else {
4456
+ await options.visitorFn(entryInfo);
4457
+ }
4458
+ }
4459
+ if (traversalOrder === "post") {
4460
+ const stats = await fsp.stat(dirPath);
4461
+ const entryInfo = {
4462
+ path: dirPath,
4463
+ name: path.basename(dirPath),
4464
+ isDirectory: true,
4465
+ stats
4466
+ };
4467
+ await options.visitorFn(entryInfo);
4468
+ }
4469
+ }
4470
+ __name(walkDir, "walkDir");
3924
4471
  async function fileExists(path3) {
3925
4472
  try {
3926
4473
  await fsp.access(path3);
@@ -4247,6 +4794,202 @@ function healthCheck(options) {
4247
4794
  }
4248
4795
  __name(healthCheck, "healthCheck");
4249
4796
 
4797
+ // src/utils/performance.utils.ts
4798
+ function timeSync(fn, options = {}) {
4799
+ const { label = fn.name || "anonymous function", log = false, logLevel = "debug" } = options;
4800
+ const startTime = performance.now();
4801
+ const result = fn();
4802
+ const endTime = performance.now();
4803
+ const durationMs = endTime - startTime;
4804
+ const durationSec = durationMs / 1e3;
4805
+ const timing = {
4806
+ durationMs,
4807
+ durationSec,
4808
+ startTime,
4809
+ endTime,
4810
+ label
4811
+ };
4812
+ if (log) {
4813
+ const logger2 = getLogger();
4814
+ const message = `${label} completed in ${durationMs.toFixed(2)}ms`;
4815
+ switch (logLevel) {
4816
+ case "trace":
4817
+ logger2.trace({
4818
+ timing
4819
+ }, message);
4820
+ break;
4821
+ case "debug":
4822
+ logger2.debug({
4823
+ timing
4824
+ }, message);
4825
+ break;
4826
+ case "info":
4827
+ logger2.info({
4828
+ timing
4829
+ }, message);
4830
+ break;
4831
+ case "warn":
4832
+ logger2.warn({
4833
+ timing
4834
+ }, message);
4835
+ break;
4836
+ case "error":
4837
+ logger2.error({
4838
+ timing
4839
+ }, message);
4840
+ break;
4841
+ default:
4842
+ logger2.debug({
4843
+ timing
4844
+ }, message);
4845
+ break;
4846
+ }
4847
+ }
4848
+ return {
4849
+ result,
4850
+ timing
4851
+ };
4852
+ }
4853
+ __name(timeSync, "timeSync");
4854
+ async function timeAsync(fn, options = {}) {
4855
+ const { label = fn.name || "anonymous async function", log = false, logLevel = "debug" } = options;
4856
+ const startTime = performance.now();
4857
+ const result = await fn();
4858
+ const endTime = performance.now();
4859
+ const durationMs = endTime - startTime;
4860
+ const durationSec = durationMs / 1e3;
4861
+ const timing = {
4862
+ durationMs,
4863
+ durationSec,
4864
+ startTime,
4865
+ endTime,
4866
+ label
4867
+ };
4868
+ if (log) {
4869
+ const logger2 = getLogger();
4870
+ const message = `${label} completed in ${durationMs.toFixed(2)}ms`;
4871
+ switch (logLevel) {
4872
+ case "trace":
4873
+ logger2.trace({
4874
+ timing
4875
+ }, message);
4876
+ break;
4877
+ case "debug":
4878
+ logger2.debug({
4879
+ timing
4880
+ }, message);
4881
+ break;
4882
+ case "info":
4883
+ logger2.info({
4884
+ timing
4885
+ }, message);
4886
+ break;
4887
+ case "warn":
4888
+ logger2.warn({
4889
+ timing
4890
+ }, message);
4891
+ break;
4892
+ case "error":
4893
+ logger2.error({
4894
+ timing
4895
+ }, message);
4896
+ break;
4897
+ default:
4898
+ logger2.debug({
4899
+ timing
4900
+ }, message);
4901
+ break;
4902
+ }
4903
+ }
4904
+ return {
4905
+ result,
4906
+ timing
4907
+ };
4908
+ }
4909
+ __name(timeAsync, "timeAsync");
4910
+ function timed(options = {}) {
4911
+ return function(target, propertyKeyOrContext, descriptor) {
4912
+ let propertyKey;
4913
+ let actualDescriptor;
4914
+ if (typeof propertyKeyOrContext === "object" && propertyKeyOrContext !== null && "name" in propertyKeyOrContext) {
4915
+ propertyKey = propertyKeyOrContext.name;
4916
+ actualDescriptor = descriptor ?? Object.getOwnPropertyDescriptor(target, propertyKey);
4917
+ } else {
4918
+ propertyKey = propertyKeyOrContext;
4919
+ actualDescriptor = descriptor ?? Object.getOwnPropertyDescriptor(target, propertyKey);
4920
+ }
4921
+ const originalMethod = actualDescriptor.value;
4922
+ actualDescriptor.value = function(...args) {
4923
+ const methodOptions = {
4924
+ ...options,
4925
+ label: options.label || `${target.constructor.name}.${String(propertyKey)}`
4926
+ };
4927
+ if (originalMethod.constructor.name === "AsyncFunction") {
4928
+ return timeAsync(() => originalMethod.apply(this, args), methodOptions).then(({ result }) => result);
4929
+ } else {
4930
+ return timeSync(() => originalMethod.apply(this, args), methodOptions).result;
4931
+ }
4932
+ };
4933
+ if (descriptor) {
4934
+ descriptor.value = actualDescriptor.value;
4935
+ } else {
4936
+ Object.defineProperty(target, propertyKey, actualDescriptor);
4937
+ }
4938
+ };
4939
+ }
4940
+ __name(timed, "timed");
4941
+ function memoize(fn, options = {}) {
4942
+ const { ttl, maxSize, autoCleanupMs, cacheKey = /* @__PURE__ */ __name((...args) => JSON.stringify(args), "cacheKey") } = options;
4943
+ const cache = new TTLCache({
4944
+ ttlMs: ttl,
4945
+ maxSize,
4946
+ autoCleanupMs
4947
+ });
4948
+ return function(...args) {
4949
+ const key = cacheKey(...args);
4950
+ const cachedValue = cache.get(key);
4951
+ if (cachedValue !== void 0) {
4952
+ return cachedValue;
4953
+ }
4954
+ const value = fn(...args);
4955
+ cache.set(key, value);
4956
+ return value;
4957
+ };
4958
+ }
4959
+ __name(memoize, "memoize");
4960
+ function trackMemoryUsage(fn, options = {}) {
4961
+ const { log = false, label = fn.name || "anonymous function" } = options;
4962
+ if (global.gc) {
4963
+ global.gc();
4964
+ }
4965
+ const beforeMemory = process.memoryUsage();
4966
+ const result = fn();
4967
+ const afterMemory = process.memoryUsage();
4968
+ const diff = {};
4969
+ for (const key in afterMemory) {
4970
+ if (Object.prototype.hasOwnProperty.call(afterMemory, key)) {
4971
+ diff[key] = afterMemory[key] - beforeMemory[key];
4972
+ }
4973
+ }
4974
+ if (log) {
4975
+ getLogger().info({
4976
+ label,
4977
+ memoryBefore: beforeMemory,
4978
+ memoryAfter: afterMemory,
4979
+ memoryDiff: diff
4980
+ }, `Memory usage for ${label}`);
4981
+ }
4982
+ return {
4983
+ result,
4984
+ memoryUsage: {
4985
+ before: beforeMemory,
4986
+ after: afterMemory,
4987
+ diff
4988
+ }
4989
+ };
4990
+ }
4991
+ __name(trackMemoryUsage, "trackMemoryUsage");
4992
+
4250
4993
  // src/utils/request.utils.ts
4251
4994
  var getPaginationParams = /* @__PURE__ */ __name((req) => {
4252
4995
  const query = req.query;
@@ -4405,6 +5148,120 @@ function extractFilterParams(query, allowedFilters) {
4405
5148
  return filters;
4406
5149
  }
4407
5150
  __name(extractFilterParams, "extractFilterParams");
5151
+ function bufferToStream(data) {
5152
+ const readable = new Readable();
5153
+ readable.push(data);
5154
+ readable.push(null);
5155
+ return readable;
5156
+ }
5157
+ __name(bufferToStream, "bufferToStream");
5158
+ async function streamToBuffer(stream) {
5159
+ return new Promise((resolve2, reject) => {
5160
+ const chunks = [];
5161
+ stream.on("data", (chunk2) => chunks.push(Buffer.isBuffer(chunk2) ? chunk2 : Buffer.from(chunk2)));
5162
+ stream.on("end", () => resolve2(Buffer.concat(chunks)));
5163
+ stream.on("error", reject);
5164
+ });
5165
+ }
5166
+ __name(streamToBuffer, "streamToBuffer");
5167
+ async function streamToString(stream, encoding = "utf8") {
5168
+ const buffer = await streamToBuffer(stream);
5169
+ return buffer.toString(encoding);
5170
+ }
5171
+ __name(streamToString, "streamToString");
5172
+ function createThrottleStream(bytesPerSecond) {
5173
+ let bytesSent = 0;
5174
+ let startTime = Date.now();
5175
+ return new Transform({
5176
+ transform(chunk2, _encoding, callback) {
5177
+ const elapsedSeconds = (Date.now() - startTime) / 1e3;
5178
+ const targetBytes = bytesPerSecond * elapsedSeconds;
5179
+ const chunkSize = chunk2.length;
5180
+ if (bytesSent + chunkSize <= targetBytes) {
5181
+ bytesSent += chunkSize;
5182
+ callback(null, chunk2);
5183
+ return;
5184
+ }
5185
+ const delay = ((bytesSent + chunkSize) / bytesPerSecond - elapsedSeconds) * 1e3;
5186
+ setTimeout(() => {
5187
+ bytesSent += chunkSize;
5188
+ callback(null, chunk2);
5189
+ }, delay);
5190
+ }
5191
+ });
5192
+ }
5193
+ __name(createThrottleStream, "createThrottleStream");
5194
+ function createBatchStream(size, options = {}) {
5195
+ const objectMode = options.objectMode !== false;
5196
+ let batch = [];
5197
+ let buffers = [];
5198
+ let bufferSize = 0;
5199
+ return new Transform({
5200
+ objectMode,
5201
+ transform(chunk2, _encoding, callback) {
5202
+ if (objectMode) {
5203
+ batch.push(chunk2);
5204
+ if (batch.length >= size) {
5205
+ this.push(batch);
5206
+ batch = [];
5207
+ }
5208
+ } else {
5209
+ const buffer = Buffer.isBuffer(chunk2) ? chunk2 : Buffer.from(chunk2);
5210
+ buffers.push(buffer);
5211
+ bufferSize += buffer.length;
5212
+ let combined = Buffer.concat(buffers, bufferSize);
5213
+ while (combined.length >= size) {
5214
+ this.push(combined.slice(0, size));
5215
+ combined = combined.slice(size);
5216
+ }
5217
+ buffers = combined.length > 0 ? [
5218
+ combined
5219
+ ] : [];
5220
+ bufferSize = combined.length;
5221
+ }
5222
+ callback();
5223
+ },
5224
+ flush(callback) {
5225
+ if (objectMode && batch.length > 0) {
5226
+ this.push(batch);
5227
+ } else if (!objectMode && buffers.length > 0 && bufferSize > 0) {
5228
+ let combined = Buffer.concat(buffers, bufferSize);
5229
+ while (combined.length >= size) {
5230
+ this.push(combined.slice(0, size));
5231
+ combined = combined.slice(size);
5232
+ }
5233
+ if (combined.length > 0) {
5234
+ this.push(combined);
5235
+ }
5236
+ }
5237
+ callback();
5238
+ }
5239
+ });
5240
+ }
5241
+ __name(createBatchStream, "createBatchStream");
5242
+ function createLineStream(options = {}) {
5243
+ const { encoding = "utf8", includeNewlines = false } = options;
5244
+ let buffer = "";
5245
+ return new Transform({
5246
+ objectMode: true,
5247
+ transform(chunk2, _encoding, callback) {
5248
+ const str = buffer + (Buffer.isBuffer(chunk2) ? chunk2.toString(encoding) : chunk2);
5249
+ const lines = str.split(/\r?\n/);
5250
+ buffer = lines.pop() || "";
5251
+ for (const line of lines) {
5252
+ this.push(includeNewlines ? line + "\n" : line);
5253
+ }
5254
+ callback();
5255
+ },
5256
+ flush(callback) {
5257
+ if (buffer) {
5258
+ this.push(buffer);
5259
+ }
5260
+ callback();
5261
+ }
5262
+ });
5263
+ }
5264
+ __name(createLineStream, "createLineStream");
4408
5265
 
4409
5266
  // src/utils/string.utils.ts
4410
5267
  function capitalize(str) {
@@ -4464,6 +5321,84 @@ function countOccurrences(str, substring, caseSensitive = true) {
4464
5321
  return str.split(substring).length - 1;
4465
5322
  }
4466
5323
  __name(countOccurrences, "countOccurrences");
5324
+
5325
+ // src/utils/type.utils.ts
5326
+ function isPrimitiveType(value, type) {
5327
+ if (type === "array") {
5328
+ return Array.isArray(value);
5329
+ }
5330
+ if (type === "null") {
5331
+ return value === null;
5332
+ }
5333
+ if (type === "undefined") {
5334
+ return value === void 0;
5335
+ }
5336
+ if (type === "object") {
5337
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5338
+ }
5339
+ return typeof value === type;
5340
+ }
5341
+ __name(isPrimitiveType, "isPrimitiveType");
5342
+ function getTypeOf(value) {
5343
+ if (value === null) return "null";
5344
+ if (value === void 0) return "undefined";
5345
+ if (Array.isArray(value)) return "array";
5346
+ return typeof value;
5347
+ }
5348
+ __name(getTypeOf, "getTypeOf");
5349
+ function isArrayOf(value, itemTypeGuard) {
5350
+ if (!Array.isArray(value)) return false;
5351
+ return value.every((item) => itemTypeGuard(item));
5352
+ }
5353
+ __name(isArrayOf, "isArrayOf");
5354
+ function toStr(value, defaultValue = "") {
5355
+ if (value === null || value === void 0) return defaultValue;
5356
+ try {
5357
+ if (typeof value === "object") {
5358
+ return JSON.stringify(value);
5359
+ }
5360
+ return String(value);
5361
+ } catch {
5362
+ return defaultValue;
5363
+ }
5364
+ }
5365
+ __name(toStr, "toStr");
5366
+ function toNum(value, defaultValue = 0) {
5367
+ if (value === null || value === void 0) return defaultValue;
5368
+ if (typeof value === "number") return value;
5369
+ try {
5370
+ const num = Number(value);
5371
+ return isNaN(num) ? defaultValue : num;
5372
+ } catch {
5373
+ return defaultValue;
5374
+ }
5375
+ }
5376
+ __name(toNum, "toNum");
5377
+ function toBool(value, defaultValue = false) {
5378
+ if (value === null || value === void 0) return defaultValue;
5379
+ if (typeof value === "boolean") return value;
5380
+ if (typeof value === "string") {
5381
+ const lowercased = value.toLowerCase();
5382
+ if (lowercased === "true" || lowercased === "yes" || lowercased === "y" || lowercased === "1") {
5383
+ return true;
5384
+ }
5385
+ if (lowercased === "false" || lowercased === "no" || lowercased === "n" || lowercased === "0") {
5386
+ return false;
5387
+ }
5388
+ }
5389
+ if (typeof value === "number") {
5390
+ return value !== 0;
5391
+ }
5392
+ return defaultValue;
5393
+ }
5394
+ __name(toBool, "toBool");
5395
+ function ensureType(value, expectedType, defaultValue) {
5396
+ if (getTypeOf(value) === expectedType) {
5397
+ return value;
5398
+ }
5399
+ return defaultValue;
5400
+ }
5401
+ __name(ensureType, "ensureType");
4467
5402
  function appendQueryParams(url, params) {
4468
5403
  const urlObj = new URL$1(url);
4469
5404
  for (const [key, value] of Object.entries(params)) {
@@ -6318,4 +7253,4 @@ var ExpressServer = class _ExpressServer {
6318
7253
  }
6319
7254
  };
6320
7255
 
6321
- export { After, BUILD_MARKER, BadGatewayException, BadRequestException, Before, Body, Cache, CircuitBreakerOpenError, CircuitBreakerState, ConflictException, Connect, ContentType, ContextStore, Controller, DIContainer, Delete, Env, Environment, ErrorResponse, ExpressServer, ForbiddenException, GatewayTimeoutException, Get, Head, Header, Headers, HttpCode, HttpError, HttpStatusCodes, Inject, Injectable, InsufficientStorageException, InternalServerErrorException, Log, MethodNotAllowedException, NoContentResponse, NotAcceptableException, NotFoundException, Options, PaginatedResponse, Param, Patch, PayloadTooLargeException, Post, Put, Query, RateLimit, Redirect, RedirectResponse, Req, RequestTimeoutException, Res, Roles, ServerConfigBuilder, ServiceUnavailableException, SortDirection, StoreKeys, SuccessResponse, TTLCache, Timeout, TooManyRequestsException, Trace, TypedContextKey, UnauthorizedException, UnprocessableEntityException, UnsupportedMediaTypeException, Use, Version, _globalThis, abortable, addRedactFields, addSensitiveFields, appendQueryParams, appendTextFile, capitalize, chunk, circuitBreaker, compact, copyDir, copyFile, countBy, countOccurrences, createChildLogger, createDeferred, createDirectory, createErrorResponse, createFinalErrorResponse, createHttpError, createPaginatedResponse, createRequestLogger, createSignedToken, createSuccessResponse, createTaskQueue, createTempDir, createTempFile, createUrlBuilder, debounce, decrypt, deepFreeze, deepObjMerge, defaultSensitiveFields, deleteDirRecursive, deleteFileIfExists, difference, emptyDir, encrypt, ensureDir, ensureEmptyDir, equalsIgnoreCase, errorHandler, extractFilterParams, extractPaginationParams, extractQueryParams, extractSortParams, fileExists, filterObject, findFilesByPattern, findInDir, findNewestFile, findOldestFile, flattenDeep, flattenObject, generateApiKey, generateRandomBytes, generateRandomBytesAsString, getAllPaths, getConfig, getDirSize, getDirStats, getDomain, getErrorMessage, getExtension, getFileSize, getFileStats, getFromContext, getLogger, getPaginationParams, getRedactCensor, getRequestId, getSubdirectories, getValueByPath, groupBy, hasErrorShape, hasRequiredProps, hash, healthCheck, hmac, intersect, isAlpha, isAlphanumeric, isArray, isBase64, isCreditCard, isDateInRange, isDirectory, isEmail, isEqual, isFile, isHexColor, isHttpError, isIPv4, isIPv6, isISODate, isLengthBetween, isNumberBetween, isNumeric, isObjEmpty, isObject, isPhone, isPlainObject, isPort, isStrongPassword, isURL, isUUID, isValidJSON, isValidUrl, joinPaths, listFiles, logError, logger, mapObject, mask, matchesPattern, md5, memoizeAsync, mergeSort, moveDir, moveFile, nanoId, normalizeUrl, omit, parseBooleanParam, parseNumberParam, parseQueryString, parseTypedQueryParams, partition, pick, pluck, random, randomBase64, randomHex, randomInt, randomString, range, rateLimit, readDirectory, readFileBuffer, readJsonFile, readTextFile, redact, registerControllers, removeQueryParams, requestId, responseTime, retry, reverse, runInBatches, runInSeries, runWithConcurrency, safeCompare, safeReadJsonFile, sendResponse, setConfig, setRedactCensor, setSensitiveFields, setValueByPath, settleAll, setupRequestContext, sha1, sha256, sha256Hmac, shuffle, singletonAsync, sleep, slugify, streamFile, stripHtml, take, takeWhile, throttle, timeout, toCamelCase, toKebabCase, toPascalCase, toSnakeCase, truncate, unique, uuid, validateAll, verifySignedToken, walkDir, watchDir, watchDirRecursive, waterfall, withErrorHandling, withTimeout, writeJsonFile, writeTextFile, zip };
7256
+ export { After, BUILD_MARKER, BadGatewayException, BadRequestException, Before, Body, Cache, CircuitBreakerOpenError, CircuitBreakerState, ConflictException, Connect, ContentType, ContextStore, Controller, DIContainer, Delete, Env, Environment, ErrorResponse, ExpressServer, ForbiddenException, GatewayTimeoutException, Get, Head, Header, Headers, HttpCode, HttpError, HttpStatusCodes, Inject, Injectable, InsufficientStorageException, InternalServerErrorException, Log, MethodNotAllowedException, NoContentResponse, NotAcceptableException, NotFoundException, Options, PaginatedResponse, Param, Patch, PayloadTooLargeException, Post, Put, Query, RateLimit, Redirect, RedirectResponse, Req, ReqCookie, ReqHeader, ReqId, ReqLogger, RequestTimeoutException, Res, Roles, ServerConfigBuilder, ServiceUnavailableException, SortDirection, StoreKeys, SuccessResponse, TTLCache, Timeout, TooManyRequestsException, Trace, TypedContextKey, UnauthorizedException, UnprocessableEntityException, UnsupportedMediaTypeException, Use, Version, _globalThis, abortable, addRedactFields, addSensitiveFields, addToDate, appendQueryParams, appendTextFile, bufferToStream, capitalize, chunk, circuitBreaker, compact, copyDir, copyFile, countBy, countOccurrences, createBatchStream, createChildLogger, createDeferred, createDirectory, createErrorResponse, createFinalErrorResponse, createHttpError, createLineStream, createPaginatedResponse, createParamDecorator, createParamDecoratorWithoutParam, createRequestLogger, createSignedToken, createSuccessResponse, createTaskQueue, createTempDir, createTempFile, createThrottleStream, createUrlBuilder, dateDiff, daysInMonth, debounce, decrypt, deepFreeze, deepObjMerge, defaultSensitiveFields, deleteDirRecursive, deleteFileIfExists, difference, emptyDir, encrypt, endOf, ensureDir, ensureEmptyDir, ensureType, equalsIgnoreCase, errorHandler, extractFilterParams, extractPaginationParams, extractQueryParams, extractSortParams, fileExists, filterObject, findFilesByPattern, findInDir, findNewestFile, findOldestFile, flattenDeep, flattenObject, formatDate, formatRelativeTime, generateApiKey, generateRandomBytes, generateRandomBytesAsString, getAllPaths, getConfig, getDirSize, getDirStats, getDomain, getErrorMessage, getExtension, getFileSize, getFileStats, getFromContext, getLogger, getPaginationParams, getRedactCensor, getRequestId, getSubdirectories, getTypeOf, getValueByPath, groupBy, hasErrorShape, hasRequiredProps, hash, healthCheck, hmac, inject, intersect, isAlpha, isAlphanumeric, isArray, isArrayOf, isBase64, isBetween, isCreditCard, isDateInRange, isDirectory, isEmail, isEqual, isFile, isHexColor, isHttpError, isIPv4, isIPv6, isISODate, isLeapYear, isLengthBetween, isNumberBetween, isNumeric, isObjEmpty, isObject, isPhone, isPlainObject, isPort, isPrimitiveType, isStrongPassword, isURL, isUUID, isValidJSON, isValidUrl, joinPaths, listFiles, logError, logger, mapObject, mask, matchesPattern, md5, memoize, memoizeAsync, mergeSort, moveDir, moveFile, nanoId, normalizeUrl, omit, parseBooleanParam, parseDate, parseNumberParam, parseQueryString, parseTypedQueryParams, partition, pick, pluck, random, randomBase64, randomHex, randomInt, randomString, range, rateLimit, readDirectory, readFileBuffer, readJsonFile, readTextFile, redact, registerControllers, removeQueryParams, requestId, responseTime, retry, reverse, runInBatches, runInSeries, runWithConcurrency, safeCompare, safeReadJsonFile, sendResponse, setConfig, setRedactCensor, setSensitiveFields, setValueByPath, settleAll, setupRequestContext, sha1, sha256, sha256Hmac, shuffle, singletonAsync, sleep, slugify, startOf, streamFile, streamToBuffer, streamToString, stripHtml, take, takeWhile, throttle, timeAsync, timeSync, timed, timeout, toBool, toCamelCase, toKebabCase, toNum, toPascalCase, toSnakeCase, toStr, trackMemoryUsage, truncate, unique, uuid, validateAll, verifySignedToken, walkDir, watchDir, watchDirRecursive, waterfall, withErrorHandling, withTimeout, writeJsonFile, writeTextFile, zip };