yinzerflow 0.4.4 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts CHANGED
@@ -1,12 +1,5 @@
1
- export type CreateEnum<T> = T[keyof T];
2
- /**
3
- * Utility type for deep partial - makes all properties optional recursively
4
- * Used internally to create public configuration types from internal shapes
5
- */
6
- export type DeepPartial<T> = {
7
- [P in keyof T]?: T[P] extends object ? T[P] extends Array<infer U> ? Array<U> // Keep arrays as-is, don't make array items partial
8
- : DeepPartial<T[P]> : T[P];
9
- };
1
+ import { createClient } from 'redis';
2
+
10
3
  /**
11
4
  * Generic types for handler callbacks that define the structure of request data
12
5
  *
@@ -24,7 +17,7 @@ export type DeepPartial<T> = {
24
17
  * };
25
18
  *
26
19
  * // Custom types for specific endpoints
27
- * interface UserCreateContext extends InternalHandlerCallbackGenerics {
20
+ * interface UserCreateContext extends HandlerCallbackGenerics {
28
21
  * body: { name: string; email: string; age: number };
29
22
  * response: { id: string; name: string; email: string };
30
23
  * state: { requestId: string; userAgent: string };
@@ -41,7 +34,7 @@ export type DeepPartial<T> = {
41
34
  * };
42
35
  * ```
43
36
  */
44
- export interface InternalHandlerCallbackGenerics {
37
+ export interface HandlerCallbackGenerics {
45
38
  /**
46
39
  * The expected type of the request body
47
40
  *
@@ -50,7 +43,7 @@ export interface InternalHandlerCallbackGenerics {
50
43
  *
51
44
  * @example
52
45
  * ```typescript
53
- * interface LoginContext extends InternalHandlerCallbackGenerics {
46
+ * interface LoginContext extends HandlerCallbackGenerics {
54
47
  * body: { username: string; password: string };
55
48
  * }
56
49
  *
@@ -69,7 +62,7 @@ export interface InternalHandlerCallbackGenerics {
69
62
  *
70
63
  * @example
71
64
  * ```typescript
72
- * interface UserListContext extends InternalHandlerCallbackGenerics {
65
+ * interface UserListContext extends HandlerCallbackGenerics {
73
66
  * response: { users: Array<{ id: string; name: string }>; total: number };
74
67
  * }
75
68
  *
@@ -93,7 +86,7 @@ export interface InternalHandlerCallbackGenerics {
93
86
  *
94
87
  * @example
95
88
  * ```typescript
96
- * interface SearchContext extends InternalHandlerCallbackGenerics {
89
+ * interface SearchContext extends HandlerCallbackGenerics {
97
90
  * query: { q: string; limit?: string; page?: string };
98
91
  * }
99
92
  *
@@ -106,7 +99,7 @@ export interface InternalHandlerCallbackGenerics {
106
99
  * };
107
100
  * ```
108
101
  */
109
- query?: Record<string, string>;
102
+ query?: Record<string, unknown>;
110
103
  /**
111
104
  * The expected type of route parameters
112
105
  *
@@ -115,7 +108,7 @@ export interface InternalHandlerCallbackGenerics {
115
108
  *
116
109
  * @example
117
110
  * ```typescript
118
- * interface UserDetailContext extends InternalHandlerCallbackGenerics {
111
+ * interface UserDetailContext extends HandlerCallbackGenerics {
119
112
  * params: { id: string; tab?: string };
120
113
  * }
121
114
  *
@@ -138,7 +131,7 @@ export interface InternalHandlerCallbackGenerics {
138
131
  *
139
132
  * @example
140
133
  * ```typescript
141
- * interface AuthContext extends InternalHandlerCallbackGenerics {
134
+ * interface AuthContext extends HandlerCallbackGenerics {
142
135
  * state: {
143
136
  * user: User;
144
137
  * permissions: string[];
@@ -160,6 +153,15 @@ export interface InternalHandlerCallbackGenerics {
160
153
  */
161
154
  state?: Record<string, unknown>;
162
155
  }
156
+ export type CreateEnum<T> = T[keyof T];
157
+ /**
158
+ * Utility type for deep partial - makes all properties optional recursively
159
+ * Used internally to create public configuration types from internal shapes
160
+ */
161
+ export type DeepPartial<T> = {
162
+ [P in keyof T]?: T[P] extends object ? T[P] extends Array<infer U> ? Array<U> // Keep arrays as-is, don't make array items partial
163
+ : DeepPartial<T[P]> : T[P];
164
+ };
163
165
  export declare const httpStatus: {
164
166
  readonly ok: "OK";
165
167
  readonly created: "Created";
@@ -464,7 +466,7 @@ export type InternalHttpMethod = CreateEnum<typeof httpMethod>;
464
466
  * @see {@link Response} for setting headers in responses
465
467
  */
466
468
  export type InternalHttpHeaders = Lowercase<CreateEnum<typeof httpHeaders>> | string;
467
- interface Request$1<T extends InternalHandlerCallbackGenerics = InternalHandlerCallbackGenerics> {
469
+ interface Request$1<T extends HandlerCallbackGenerics = HandlerCallbackGenerics> {
468
470
  /**
469
471
  * The HTTP protocol version (e.g., "HTTP/1.1").
470
472
  *
@@ -591,7 +593,7 @@ interface Request$1<T extends InternalHandlerCallbackGenerics = InternalHandlerC
591
593
  * };
592
594
  *
593
595
  * // Typed body with generics
594
- * interface CreateUserRequest extends InternalHandlerCallbackGenerics {
596
+ * interface CreateUserRequest extends HandlerCallbackGenerics {
595
597
  * body: { name: string; email: string; age: number };
596
598
  * }
597
599
  *
@@ -608,7 +610,7 @@ interface Request$1<T extends InternalHandlerCallbackGenerics = InternalHandlerC
608
610
  * };
609
611
  * ```
610
612
  *
611
- * @see {@link InternalHandlerCallbackGenerics} for custom body typing
613
+ * @see {@link HandlerCallbackGenerics} for custom body typing
612
614
  */
613
615
  body: T["body"];
614
616
  /**
@@ -637,7 +639,7 @@ interface Request$1<T extends InternalHandlerCallbackGenerics = InternalHandlerC
637
639
  * };
638
640
  *
639
641
  * // Typed query parameters with generics
640
- * interface UserListRequest extends InternalHandlerCallbackGenerics {
642
+ * interface UserListRequest extends HandlerCallbackGenerics {
641
643
  * query: { page: string; limit: string; search?: string; sort?: string };
642
644
  * }
643
645
  *
@@ -658,7 +660,7 @@ interface Request$1<T extends InternalHandlerCallbackGenerics = InternalHandlerC
658
660
  * };
659
661
  * ```
660
662
  *
661
- * @see {@link InternalHandlerCallbackGenerics} for custom query typing
663
+ * @see {@link HandlerCallbackGenerics} for custom query typing
662
664
  */
663
665
  query: T["query"];
664
666
  /**
@@ -688,7 +690,7 @@ interface Request$1<T extends InternalHandlerCallbackGenerics = InternalHandlerC
688
690
  * };
689
691
  *
690
692
  * // Typed route parameters with generics
691
- * interface UserDetailRequest extends InternalHandlerCallbackGenerics {
693
+ * interface UserDetailRequest extends HandlerCallbackGenerics {
692
694
  * params: { id: string; tab?: string };
693
695
  * }
694
696
  *
@@ -712,7 +714,7 @@ interface Request$1<T extends InternalHandlerCallbackGenerics = InternalHandlerC
712
714
  * };
713
715
  * ```
714
716
  *
715
- * @see {@link InternalHandlerCallbackGenerics} for custom params typing
717
+ * @see {@link HandlerCallbackGenerics} for custom params typing
716
718
  */
717
719
  params: T["params"];
718
720
  /**
@@ -945,7 +947,7 @@ interface Response$1 {
945
947
  * It contains the request and response objects, plus any custom state data
946
948
  * defined by the user through generics.
947
949
  *
948
- * @template T - Extends InternalHandlerCallbackGenerics to provide custom typing
950
+ * @template T - Extends HandlerCallbackGenerics to provide custom typing
949
951
  *
950
952
  * @example
951
953
  * ```typescript
@@ -968,7 +970,7 @@ interface Response$1 {
968
970
  * };
969
971
  *
970
972
  * // Advanced usage with custom state typing
971
- * interface AuthContext extends InternalHandlerCallbackGenerics {
973
+ * interface AuthContext extends HandlerCallbackGenerics {
972
974
  * state: {
973
975
  * user: User;
974
976
  * permissions: string[];
@@ -991,7 +993,7 @@ interface Response$1 {
991
993
  * };
992
994
  * ```
993
995
  */
994
- export interface Context<T extends InternalHandlerCallbackGenerics = InternalHandlerCallbackGenerics> {
996
+ export interface Context<T extends HandlerCallbackGenerics = HandlerCallbackGenerics> {
995
997
  /**
996
998
  * The incoming request object containing all request data and metadata
997
999
  *
@@ -1086,7 +1088,7 @@ export interface Context<T extends InternalHandlerCallbackGenerics = InternalHan
1086
1088
  * - **Void**: No response body (useful for middleware)
1087
1089
  * - **Error**: Thrown errors are caught by error handlers
1088
1090
  *
1089
- * @template T - Extends InternalHandlerCallbackGenerics for custom typing
1091
+ * @template T - Extends HandlerCallbackGenerics for custom typing
1090
1092
  * @param ctx - The request context containing request, response, and state objects
1091
1093
  * @param error - Optional error object (only provided to error handlers)
1092
1094
  * @returns Response data, promise, or void
@@ -1102,7 +1104,7 @@ export interface Context<T extends InternalHandlerCallbackGenerics = InternalHan
1102
1104
  * };
1103
1105
  *
1104
1106
  * // Typed route handler with custom state
1105
- * interface UserContext extends InternalHandlerCallbackGenerics {
1107
+ * interface UserContext extends HandlerCallbackGenerics {
1106
1108
  * body: { name: string; email: string };
1107
1109
  * response: { id: string; name: string; email: string };
1108
1110
  * state: { user: User; permissions: string[] };
@@ -1145,9 +1147,9 @@ export interface Context<T extends InternalHandlerCallbackGenerics = InternalHan
1145
1147
  * ```
1146
1148
  *
1147
1149
  * @see {@link Context} for context interface details
1148
- * @see {@link InternalHandlerCallbackGenerics} for custom typing options
1150
+ * @see {@link HandlerCallbackGenerics} for custom typing options
1149
1151
  */
1150
- export type HandlerCallback<T extends InternalHandlerCallbackGenerics = InternalHandlerCallbackGenerics> = (ctx: Context<T>, error?: unknown) => Promise<T["response"] | void> | T["response"] | void;
1152
+ export type HandlerCallback<T extends HandlerCallbackGenerics = HandlerCallbackGenerics> = (ctx: Context<T>, error?: unknown) => Promise<T["response"] | void> | T["response"] | void;
1151
1153
  export type InternalGlobalHookOptions = {
1152
1154
  routesToExclude: Array<string>;
1153
1155
  } & {
@@ -1603,6 +1605,231 @@ export interface LoggerConfig {
1603
1605
  error: (...args: Array<unknown>) => void;
1604
1606
  } | undefined;
1605
1607
  }
1608
+ /**
1609
+ Time duration string format
1610
+
1611
+ Format: number followed by unit (s, m, h, d)
1612
+
1613
+ Units:
1614
+ - s: seconds
1615
+ - m: minutes
1616
+ - h: hours
1617
+ - d: days
1618
+
1619
+ @example
1620
+ ```typescript
1621
+ '30s' // 30 seconds
1622
+ '15m' // 15 minutes
1623
+ '2h' // 2 hours
1624
+ '1d' // 1 day
1625
+ ```
1626
+ */
1627
+ export type TimeString = `${number}${"d" | "h" | "m" | "s"}`;
1628
+ export declare const rateLimitAlgorithm: {
1629
+ readonly slidingWindowCounter: "sliding-window-counter";
1630
+ };
1631
+ export declare const rateLimitStoreType: {
1632
+ readonly memory: "memory";
1633
+ readonly redis: "redis";
1634
+ };
1635
+ /**
1636
+ * Internal type for rate limit algorithm enum
1637
+ * Generated from rateLimitAlgorithm constant
1638
+ */
1639
+ export type RateLimitAlgorithm = CreateEnum<typeof rateLimitAlgorithm>;
1640
+ /**
1641
+ * Internal type for rate limit store type enum
1642
+ * Generated from rateLimitStoreType constant
1643
+ */
1644
+ export type RateLimitStoreType = CreateEnum<typeof rateLimitStoreType>;
1645
+ export interface BaseStoreConfig {
1646
+ /** Type of the store @default 'memory' */
1647
+ type: RateLimitStoreType;
1648
+ }
1649
+ export interface MemoryStoreConfig extends BaseStoreConfig {
1650
+ type: "memory";
1651
+ }
1652
+ /**
1653
+ * Redis store configuration
1654
+ */
1655
+ export interface RedisStoreConfig extends BaseStoreConfig {
1656
+ type: "redis";
1657
+ /** Redis client instance of ioredis or redis */
1658
+ client: ReturnType<typeof createClient>;
1659
+ /** Key prefix for the rate limit keys @default 'rate_limit:' */
1660
+ keyPrefix?: string;
1661
+ /** Maximum number of connection retry attempts @default 3 */
1662
+ maxRetries?: number;
1663
+ /** Delay between retry attempts in milliseconds @default 1000 */
1664
+ retryDelay?: number;
1665
+ }
1666
+ /**
1667
+ * Configuration for rate limit store
1668
+ * This is a union type to future proof the code for other store types
1669
+ */
1670
+ export type StoreConfig = MemoryStoreConfig | RedisStoreConfig;
1671
+ /**
1672
+ * Options for per-route rate limiting
1673
+ *
1674
+ * Allows overriding global rate limit settings for specific routes.
1675
+ * Use with the `rateLimit()` hook function in route options.
1676
+ *
1677
+ * @example
1678
+ * ```typescript
1679
+ * import { rateLimit } from 'yinzerflow';
1680
+ * import type { RateLimitOptions } from 'yinzerflow';
1681
+ *
1682
+ * // Using friendly time format
1683
+ * const options: RateLimitOptions = {
1684
+ * max: 5,
1685
+ * windowMs: '1m' // 1 minute
1686
+ * };
1687
+ *
1688
+ * // Or using milliseconds directly
1689
+ * const optionsMs: RateLimitOptions = {
1690
+ * max: 5,
1691
+ * windowMs: 60000 // 1 minute
1692
+ * };
1693
+ *
1694
+ * app.post('/api/auth/login',
1695
+ * { beforeRoute: [rateLimit(options)] },
1696
+ * async (ctx) => {
1697
+ * // Login logic
1698
+ * }
1699
+ * );
1700
+ * ```
1701
+ */
1702
+ export interface RateLimitOptions {
1703
+ /**
1704
+ * Enable or disable rate limiting
1705
+ * @default true
1706
+ * @example
1707
+ * ```typescript
1708
+ * enabled: true
1709
+ * ```
1710
+ */
1711
+ enabled?: boolean;
1712
+ /**
1713
+ * Rate limiting algorithm to use
1714
+ * @default 'sliding-window-counter'
1715
+ *
1716
+ * Available algorithms:
1717
+ * - 'sliding-window-counter': Memory efficient, Redis-ready, 99%+ accurate (recommended)
1718
+ * - 'token-bucket': (Future) Allows smooth traffic patterns with continuous refill
1719
+ *
1720
+ * @example
1721
+ * ```typescript
1722
+ * import { rateLimitAlgorithm } from 'yinzerflow';
1723
+ *
1724
+ * algorithm: rateLimitAlgorithm.slidingWindowCounter
1725
+ * ```
1726
+ */
1727
+ algorithm?: RateLimitAlgorithm;
1728
+ /**
1729
+ * Rate limiting store configuration
1730
+ *
1731
+ * Configure the storage backend for rate limiting data.
1732
+ * If not provided, defaults to in-memory storage.
1733
+ *
1734
+ * @example
1735
+ * ```typescript
1736
+ * // In-memory store (default)
1737
+ * const config = { algorithm: 'sliding-window-counter', window: '15m', max: 100 };
1738
+ *
1739
+ * // Redis store
1740
+ * const config = {
1741
+ * algorithm: 'sliding-window-counter',
1742
+ * window: '15m',
1743
+ * max: 100,
1744
+ * store: {
1745
+ * type: 'redis',
1746
+ * client: redisClient,
1747
+ * keyPrefix: 'myapp:rate_limit:'
1748
+ * }
1749
+ * };
1750
+ * ```
1751
+ */
1752
+ store?: StoreConfig;
1753
+ /**
1754
+ * Time window for rate limiting
1755
+ *
1756
+ * Accepts either:
1757
+ * - Friendly format: '30s', '15m', '2h', '1d'
1758
+ * - Milliseconds: 900000
1759
+ *
1760
+ * @default '15m' (15 minutes / 900000ms)
1761
+ *
1762
+ * @example
1763
+ * ```typescript
1764
+ * window: '30s' // 30 seconds
1765
+ * window: '15m' // 15 minutes
1766
+ * window: '2h' // 2 hours
1767
+ * window: '1d' // 1 day
1768
+ * ```
1769
+ */
1770
+ window?: TimeString | number;
1771
+ /**
1772
+ * Maximum requests per window
1773
+ * @default 100
1774
+ */
1775
+ max?: number;
1776
+ /**
1777
+ * Include standard rate limit headers in responses
1778
+ * @default true
1779
+ */
1780
+ standardHeaders?: boolean;
1781
+ /**
1782
+ * Skip counting successful requests (status < 400)
1783
+ * @default false
1784
+ */
1785
+ skipSuccessfulRequests?: boolean;
1786
+ /**
1787
+ * Skip counting failed requests (status >= 400)
1788
+ * @default false
1789
+ */
1790
+ skipFailedRequests?: boolean;
1791
+ /**
1792
+ * Store configuration
1793
+ * @default { type: 'memory' }
1794
+ */
1795
+ store?: StoreConfig;
1796
+ /**
1797
+ * Custom key generator function for identifying clients
1798
+ * @default (ctx) => ctx.request.ipAddress
1799
+ *
1800
+ * @example
1801
+ * ```typescript
1802
+ * // Rate limit by user ID instead of IP
1803
+ * keyGenerator: (ctx) => ctx.state.userId || ctx.request.ipAddress
1804
+ * ```
1805
+ */
1806
+ keyGenerator?: (context: Context<any>) => string;
1807
+ /**
1808
+ * Custom handler function called when rate limit is exceeded
1809
+ *
1810
+ * @param context - Request context
1811
+ * @param retryAfter - Seconds until the client can retry
1812
+ * @returns Response to send to client
1813
+ *
1814
+ * @example
1815
+ * ```typescript
1816
+ * handler: (ctx, retryAfter) => {
1817
+ * ctx.response.setStatusCode(429);
1818
+ * return {
1819
+ * error: 'Too many requests',
1820
+ * retryAfter,
1821
+ * message: 'Please slow down'
1822
+ * };
1823
+ * }
1824
+ * ```
1825
+ */
1826
+ handler?: HandlerCallback<{
1827
+ response: {
1828
+ success: false;
1829
+ message: string;
1830
+ };
1831
+ }>;
1832
+ }
1606
1833
  /**
1607
1834
  * Internal CORS Configuration Options
1608
1835
  * Provides fine-grained control over Cross-Origin Resource Sharing
@@ -1682,45 +1909,6 @@ export interface InternalCorsEnabledConfiguration {
1682
1909
  */
1683
1910
  optionsSuccessStatus: InternalHttpStatusCode;
1684
1911
  }
1685
- /**
1686
- * Internal Connection Options
1687
- */
1688
- export interface InternalConnectionOptions {
1689
- /**
1690
- * Default socket timeout in milliseconds (30 seconds)
1691
- *
1692
- * Standard timeout for most socket connections.
1693
- * It is long enough for slow clients but short enough to prevent idle connections from staying open indefinitely.
1694
- */
1695
- socketTimeout: number;
1696
- /**
1697
- * Default graceful shutdown timeout in milliseconds (30 seconds)
1698
- *
1699
- * This is the maximum time to wait for a connection to complete before the server will close it.
1700
- */
1701
- gracefulShutdownTimeout: number;
1702
- /**
1703
- * Default keep-alive timeout in milliseconds (65 seconds)
1704
- *
1705
- * This is the maximum time a connection can be idle before the server will close it.
1706
- * This is to allow for a connection to stay open for a period of time so subsequent requests can be handled without a new connection.
1707
- * This is also to prevent a connection from staying open indefinitely.
1708
- * This is also useful for load balancing and preventing a single server from being overwhelmed by a large number of connections.
1709
- * AWS recommends a keep-alive timeout of 65 seconds because there idle timeout is 60 seconds.
1710
- */
1711
- keepAliveTimeout: number;
1712
- /**
1713
- * Default headers timeout in milliseconds (66 seconds)
1714
- *
1715
- * This is the maximum time to wait for a header from the client.
1716
- * This is to allow for a connection to stay open for a period of time so subsequent requests can be handled without a new connection.
1717
- * This is also to prevent a connection from staying open indefinitely.
1718
- * This is also useful for load balancing and preventing a single server from being overwhelmed by a large number of connections.
1719
- * It is recommended to set this value to be greater than the keep-alive timeout to prevent the server from closing the connection prematurely
1720
- * before the keep-alive timeout has expired.
1721
- */
1722
- headersTimeout: number;
1723
- }
1724
1912
  /**
1725
1913
  * Internal Body Parser Security Configuration
1726
1914
  * Protects against DoS attacks, prototype pollution, and memory exhaustion
@@ -1946,15 +2134,22 @@ export interface InternalServerConfiguration {
1946
2134
  */
1947
2135
  ipSecurity: InternalIpValidationConfig;
1948
2136
  /**
1949
- * Server connection options
2137
+ * Rate limiting configuration
2138
+ * Protects against DoS attacks and API abuse by limiting requests per IP
2139
+ * @default enabled with 100 requests per 15 minutes
1950
2140
  */
1951
- connectionOptions: InternalConnectionOptions;
2141
+ rateLimit?: RateLimitOptions;
1952
2142
  /**
1953
- * Automatic graceful shutdown configuration
1954
- * When enabled, YinzerFlow automatically sets up signal handlers for SIGTERM and SIGINT
2143
+ * Graceful shutdown timeout configuration
2144
+ * When set to a value greater than 0, YinzerFlow automatically sets up signal handlers for SIGTERM and SIGINT
2145
+ * and waits for all requests to complete before shutting down.
2146
+ * If the value is 0 (disabled), you must manually handle graceful shutdown by calling `app.close()` and `process.exit(0)`.
2147
+ * Note: If using container orchestrations, your container configuration should be at least 1 second more
2148
+ * than the graceful shutdown timeout to ensure all requests are completed, otherwise the container will be
2149
+ * killed before all requests are completed.
1955
2150
  * @default true
1956
2151
  */
1957
- autoGracefulShutdown: boolean;
2152
+ gracefulShutdownTimeout: TimeString | number;
1958
2153
  }
1959
2154
  export type HttpMethodHandlers = Record<Lowercase<keyof typeof httpMethod>, InternalSetupMethod>;
1960
2155
  export type RouteGroupMethod = (prefix: string, callback: (group: RouteGroup) => void, options?: InternalRouteRegistryOptions) => RouteGroup;
@@ -2292,7 +2487,7 @@ declare class HookRegistryImpl implements InternalHookRegistryImpl {
2292
2487
  *
2293
2488
  * // Full configuration example
2294
2489
  * const app = new YinzerFlow({
2295
- * port: 9000,
2490
+ * port: 3000,
2296
2491
  * host: '0.0.0.0',
2297
2492
  * logLevel: 'debug',
2298
2493
  * networkLogs: true,
@@ -2349,6 +2544,7 @@ declare class SetupImpl implements InternalSetupImpl {
2349
2544
  export declare class YinzerFlow extends SetupImpl {
2350
2545
  private _isListening;
2351
2546
  private _server?;
2547
+ private _globalRateLimiter?;
2352
2548
  constructor(configuration?: ServerConfiguration);
2353
2549
  private _setupServer;
2354
2550
  private _processRequest;
@@ -2382,6 +2578,12 @@ export declare const log: {
2382
2578
  table: (data: unknown, ...additionalArgs: Array<unknown>) => void;
2383
2579
  levels: typeof LOG_LEVELS;
2384
2580
  };
2581
+ export declare const rateLimitHook: (rateLimitOptions: RateLimitOptions) => HandlerCallback<{
2582
+ response: {
2583
+ success: false;
2584
+ message: string;
2585
+ };
2586
+ }>;
2385
2587
  export declare const colors: {
2386
2588
  readonly reset: "\u001B[0m";
2387
2589
  readonly cyan: "\u001B[96m";