@catbee/utils 2.0.0-next.0 → 2.0.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.
Files changed (122) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +102 -52
  3. package/array/index.cjs +215 -74
  4. package/array/index.d.ts +345 -2
  5. package/array/index.mjs +201 -74
  6. package/async/index.cjs +116 -39
  7. package/async/index.d.ts +292 -2
  8. package/async/index.mjs +116 -40
  9. package/cache/index.cjs +2 -2
  10. package/cache/index.d.ts +156 -2
  11. package/cache/index.mjs +3 -3
  12. package/config/index.cjs +80 -66
  13. package/config/index.d.ts +65 -3
  14. package/config/index.mjs +77 -65
  15. package/context-store/index.cjs +2 -3
  16. package/context-store/index.d.ts +193 -2
  17. package/context-store/index.mjs +2 -3
  18. package/crypto/index.cjs +55 -5
  19. package/crypto/index.d.ts +225 -2
  20. package/crypto/index.mjs +52 -7
  21. package/date/index.cjs +676 -2
  22. package/date/index.d.ts +676 -2
  23. package/date/index.mjs +665 -3
  24. package/decorator/index.cjs +2172 -0
  25. package/{decorators/decorators.utils.d.ts → decorator/index.d.ts} +58 -54
  26. package/decorator/index.mjs +2131 -0
  27. package/{dir → directory}/index.cjs +5 -4
  28. package/{dir/dir.utils.d.ts → directory/index.d.ts} +24 -21
  29. package/{dir → directory}/index.mjs +5 -4
  30. package/env/index.cjs +100 -68
  31. package/env/index.d.ts +391 -2
  32. package/env/index.mjs +100 -68
  33. package/exception/index.cjs +1 -1
  34. package/exception/index.d.ts +233 -2
  35. package/exception/index.mjs +1 -1
  36. package/fs/index.cjs +71 -37
  37. package/fs/index.d.ts +206 -2
  38. package/fs/index.mjs +65 -35
  39. package/http-status-codes/index.cjs +1 -1
  40. package/http-status-codes/index.d.ts +268 -2
  41. package/http-status-codes/index.mjs +1 -1
  42. package/id/index.cjs +1 -1
  43. package/id/index.d.ts +38 -2
  44. package/id/index.mjs +1 -1
  45. package/index.cjs +13 -13
  46. package/index.d.ts +5 -5
  47. package/index.mjs +5 -5
  48. package/logger/index.cjs +13 -15
  49. package/logger/index.d.ts +190 -2
  50. package/logger/index.mjs +14 -16
  51. package/middleware/index.cjs +1 -1
  52. package/middleware/index.d.ts +104 -2
  53. package/middleware/index.mjs +1 -1
  54. package/object/index.cjs +379 -0
  55. package/{obj/obj.utils.d.ts → object/index.d.ts} +73 -33
  56. package/object/index.mjs +360 -0
  57. package/package.json +41 -23
  58. package/performance/index.cjs +4 -4
  59. package/performance/index.d.ts +139 -2
  60. package/performance/index.mjs +4 -4
  61. package/request/index.cjs +37 -25
  62. package/request/index.d.ts +242 -3
  63. package/request/index.mjs +37 -25
  64. package/response/index.cjs +1 -1
  65. package/response/index.d.ts +319 -3
  66. package/response/index.mjs +1 -1
  67. package/server/index.cjs +249 -146
  68. package/server/index.d.ts +866 -5
  69. package/server/index.mjs +248 -144
  70. package/stream/index.cjs +1 -1
  71. package/stream/index.d.ts +91 -2
  72. package/stream/index.mjs +1 -1
  73. package/string/index.cjs +34 -1
  74. package/string/index.d.ts +146 -2
  75. package/string/index.mjs +31 -2
  76. package/type/index.cjs +19 -2
  77. package/type/index.d.ts +144 -2
  78. package/type/index.mjs +17 -3
  79. package/types/index.cjs +1 -1
  80. package/types/index.d.ts +775 -5
  81. package/types/index.mjs +1 -1
  82. package/url/index.cjs +63 -5
  83. package/url/index.d.ts +200 -2
  84. package/url/index.mjs +59 -6
  85. package/{validate → validation}/index.cjs +91 -44
  86. package/{validate/validate.utils.d.ts → validation/index.d.ts} +33 -24
  87. package/{validate → validation}/index.mjs +87 -44
  88. package/array/array.utils.d.ts +0 -191
  89. package/async/async.utils.d.ts +0 -296
  90. package/cache/cache.utils.d.ts +0 -176
  91. package/config/config.d.ts +0 -57
  92. package/context-store/context-store.utils.d.ts +0 -212
  93. package/crypto/crypto.utils.d.ts +0 -183
  94. package/date/date.utils.d.ts +0 -190
  95. package/decorators/index.cjs +0 -913
  96. package/decorators/index.d.ts +0 -25
  97. package/decorators/index.mjs +0 -872
  98. package/dir/index.d.ts +0 -25
  99. package/env/env.utils.d.ts +0 -400
  100. package/exception/exception.utils.d.ts +0 -253
  101. package/fs/fs.utils.d.ts +0 -196
  102. package/http-status-codes/http-status-codes.d.ts +0 -289
  103. package/id/id.utils.d.ts +0 -59
  104. package/logger/logger.utils.d.ts +0 -210
  105. package/middleware/middleware.utils.d.ts +0 -123
  106. package/obj/index.cjs +0 -317
  107. package/obj/index.d.ts +0 -25
  108. package/obj/index.mjs +0 -301
  109. package/performance/performance.utils.d.ts +0 -159
  110. package/request/request.utils.d.ts +0 -109
  111. package/response/response.utils.d.ts +0 -186
  112. package/server/server.builder.d.ts +0 -531
  113. package/server/server.d.ts +0 -303
  114. package/stream/stream.utils.d.ts +0 -111
  115. package/string/string.utils.d.ts +0 -124
  116. package/type/type.utils.d.ts +0 -129
  117. package/types/api-response.d.ts +0 -175
  118. package/types/common.d.ts +0 -148
  119. package/types/config.d.ts +0 -88
  120. package/types/server.d.ts +0 -291
  121. package/url/url.utils.d.ts +0 -164
  122. package/validate/index.d.ts +0 -25
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * The MIT License
3
3
  *
4
- * Copyright (c) 2025 Catbee Technologies. https://catbee.in/license
4
+ * Copyright (c) 2026 Catbee Technologies. https://catbee.in/license
5
5
  *
6
6
  * Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  * of this software and associated documentation files (the "Software"), to deal
@@ -142,7 +142,7 @@ function watchDir(dirPath, callback) {
142
142
  __name(watchDir, "watchDir");
143
143
  async function findFilesByPattern(pattern, options = {}) {
144
144
  const matchPattern = /* @__PURE__ */ __name((name, pattern2) => {
145
- let escaped = pattern2.replaceAll(/[.+^${}()|[\]\\]/g, "\\$&");
145
+ let escaped = pattern2.replaceAll(/[.+^${}()|[\]\\]/g, String.raw`\$&`);
146
146
  escaped = escaped.replaceAll("*", ".*").replaceAll("?", ".");
147
147
  const regex = new RegExp(`^${escaped}$`);
148
148
  return regex.test(name);
@@ -152,7 +152,7 @@ async function findFilesByPattern(pattern, options = {}) {
152
152
  async function walk(dir, segIndex) {
153
153
  if (segIndex >= segments.length) return [];
154
154
  const segment = segments[segIndex];
155
- const entries = await fs__default.default.promises.readdir(dir, {
155
+ const entries = await fsp__default.default.readdir(dir, {
156
156
  withFileTypes: true
157
157
  });
158
158
  let matchedFiles = [];
@@ -254,7 +254,8 @@ async function findNewestFile(dirPath, recursive = false) {
254
254
  path: file,
255
255
  mtime: (await fsp__default.default.stat(file)).mtime
256
256
  })));
257
- return stats.sort((a, b) => b.mtime.getTime() - a.mtime.getTime())[0].path;
257
+ const sorted = stats.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
258
+ return sorted[0].path;
258
259
  }
259
260
  __name(findNewestFile, "findNewestFile");
260
261
  async function findOldestFile(dirPath, recursive = false) {
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * The MIT License
3
3
  *
4
- * Copyright (c) 2025 Catbee Technologies. https://catbee.in/license
4
+ * Copyright (c) 2026 Catbee Technologies. https://catbee.in/license
5
5
  *
6
6
  * Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  * of this software and associated documentation files (the "Software"), to deal
@@ -22,7 +22,8 @@
22
22
  * SOFTWARE.
23
23
  */
24
24
 
25
- import fs from 'fs';
25
+ import fs from 'node:fs';
26
+
26
27
  /**
27
28
  * Ensures that a directory exists, creating parent directories if needed (like `mkdir -p`).
28
29
  *
@@ -30,7 +31,7 @@ import fs from 'fs';
30
31
  * @returns {Promise<void>} Resolves when the directory exists.
31
32
  * @throws {Error} If directory cannot be created.
32
33
  */
33
- export declare function ensureDir(dirPath: string): Promise<void>;
34
+ declare function ensureDir(dirPath: string): Promise<void>;
34
35
  /**
35
36
  * Recursively lists all files in a directory.
36
37
  *
@@ -39,7 +40,7 @@ export declare function ensureDir(dirPath: string): Promise<void>;
39
40
  * @returns {Promise<string[]>} Array of absolute file paths.
40
41
  * @throws {Error} If the directory cannot be read.
41
42
  */
42
- export declare function listFiles(dirPath: string, recursive?: boolean): Promise<string[]>;
43
+ declare function listFiles(dirPath: string, recursive?: boolean): Promise<string[]>;
43
44
  /**
44
45
  * Deletes a directory and all its contents recursively (like `rm -rf`).
45
46
  *
@@ -47,14 +48,14 @@ export declare function listFiles(dirPath: string, recursive?: boolean): Promise
47
48
  * @returns {Promise<void>} Resolves when deletion is complete.
48
49
  * @throws {Error} If deletion fails.
49
50
  */
50
- export declare function deleteDirRecursive(dirPath: string): Promise<void>;
51
+ declare function deleteDirRecursive(dirPath: string): Promise<void>;
51
52
  /**
52
53
  * Checks whether a given path is a directory.
53
54
  *
54
55
  * @param {string} pathStr - Path to check.
55
56
  * @returns {Promise<boolean>} True if the path is a directory, else false.
56
57
  */
57
- export declare function isDirectory(pathStr: string): Promise<boolean>;
58
+ declare function isDirectory(pathStr: string): Promise<boolean>;
58
59
  /**
59
60
  * Recursively copies a directory and all its contents to a destination.
60
61
  *
@@ -63,7 +64,7 @@ export declare function isDirectory(pathStr: string): Promise<boolean>;
63
64
  * @returns {Promise<void>} Resolves when copy is complete.
64
65
  * @throws {Error} If source does not exist or copy fails.
65
66
  */
66
- export declare function copyDir(src: string, dest: string): Promise<void>;
67
+ declare function copyDir(src: string, dest: string): Promise<void>;
67
68
  /**
68
69
  * Moves a directory to a new location by copying and deleting the original.
69
70
  *
@@ -72,7 +73,7 @@ export declare function copyDir(src: string, dest: string): Promise<void>;
72
73
  * @returns {Promise<void>} Resolves when move is complete.
73
74
  * @throws {Error} If copy or deletion fails.
74
75
  */
75
- export declare function moveDir(src: string, dest: string): Promise<void>;
76
+ declare function moveDir(src: string, dest: string): Promise<void>;
76
77
  /**
77
78
  * Empties a directory by deleting all files and subdirectories inside it.
78
79
  *
@@ -80,7 +81,7 @@ export declare function moveDir(src: string, dest: string): Promise<void>;
80
81
  * @returns {Promise<void>} Resolves when the directory has been emptied.
81
82
  * @throws {Error} If files or subdirectories cannot be removed.
82
83
  */
83
- export declare function emptyDir(dirPath: string): Promise<void>;
84
+ declare function emptyDir(dirPath: string): Promise<void>;
84
85
  /**
85
86
  * Calculates the total size (in bytes) of all files in a directory (recursive).
86
87
  *
@@ -88,7 +89,7 @@ export declare function emptyDir(dirPath: string): Promise<void>;
88
89
  * @returns {Promise<number>} Total size in bytes.
89
90
  * @throws {Error} If any file stats cannot be read.
90
91
  */
91
- export declare function getDirSize(dirPath: string): Promise<number>;
92
+ declare function getDirSize(dirPath: string): Promise<number>;
92
93
  /**
93
94
  * Watches a directory for file changes and calls a callback on each event.
94
95
  *
@@ -96,7 +97,7 @@ export declare function getDirSize(dirPath: string): Promise<number>;
96
97
  * @param {(eventType: "rename" | "change", filename: string | null) => void} callback - Callback for each change event.
97
98
  * @returns {() => void} A function to stop watching the directory.
98
99
  */
99
- export declare function watchDir(dirPath: string, callback: (eventType: 'rename' | 'change', filename: string | null) => void): () => void;
100
+ declare function watchDir(dirPath: string, callback: (eventType: 'rename' | 'change', filename: string | null) => void): () => void;
100
101
  /**
101
102
  * Recursively finds files matching a simple pattern (supports '*' and '?').
102
103
  *
@@ -106,7 +107,7 @@ export declare function watchDir(dirPath: string, callback: (eventType: 'rename'
106
107
  * @param {boolean} [options.dot=false] - Include dotfiles in matches.
107
108
  * @returns {Promise<string[]>} Array of matched file paths.
108
109
  */
109
- export declare function findFilesByPattern(pattern: string, options?: {
110
+ declare function findFilesByPattern(pattern: string, options?: {
110
111
  cwd?: string;
111
112
  dot?: boolean;
112
113
  }): Promise<string[]>;
@@ -118,7 +119,7 @@ export declare function findFilesByPattern(pattern: string, options?: {
118
119
  * @returns {Promise<string[]>} Array of absolute subdirectory paths.
119
120
  * @throws {Error} If directory cannot be read.
120
121
  */
121
- export declare function getSubdirectories(dirPath: string, recursive?: boolean): Promise<string[]>;
122
+ declare function getSubdirectories(dirPath: string, recursive?: boolean): Promise<string[]>;
122
123
  /**
123
124
  * Ensures a directory exists and is empty.
124
125
  *
@@ -126,7 +127,7 @@ export declare function getSubdirectories(dirPath: string, recursive?: boolean):
126
127
  * @returns {Promise<void>} Resolves when the directory exists and is empty.
127
128
  * @throws {Error} If directory cannot be created or emptied.
128
129
  */
129
- export declare function ensureEmptyDir(dirPath: string): Promise<void>;
130
+ declare function ensureEmptyDir(dirPath: string): Promise<void>;
130
131
  /**
131
132
  * Creates a temporary directory with optional auto-cleanup.
132
133
  *
@@ -137,7 +138,7 @@ export declare function ensureEmptyDir(dirPath: string): Promise<void>;
137
138
  * @returns {Promise<{ path: string, cleanup: () => Promise<void> }>} Object with directory path and cleanup function.
138
139
  * @throws {Error} If directory cannot be created.
139
140
  */
140
- export declare function createTempDir(options?: {
141
+ declare function createTempDir(options?: {
141
142
  prefix?: string;
142
143
  parentDir?: string;
143
144
  cleanup?: boolean;
@@ -153,7 +154,7 @@ export declare function createTempDir(options?: {
153
154
  * @returns {Promise<string | null>} Path to the newest file or null if no files.
154
155
  * @throws {Error} If directory cannot be read.
155
156
  */
156
- export declare function findNewestFile(dirPath: string, recursive?: boolean): Promise<string | null>;
157
+ declare function findNewestFile(dirPath: string, recursive?: boolean): Promise<string | null>;
157
158
  /**
158
159
  * Finds the oldest file in a directory.
159
160
  *
@@ -162,7 +163,7 @@ export declare function findNewestFile(dirPath: string, recursive?: boolean): Pr
162
163
  * @returns {Promise<string | null>} Path to the oldest file or null if no files.
163
164
  * @throws {Error} If directory cannot be read.
164
165
  */
165
- export declare function findOldestFile(dirPath: string, recursive?: boolean): Promise<string | null>;
166
+ declare function findOldestFile(dirPath: string, recursive?: boolean): Promise<string | null>;
166
167
  /**
167
168
  * Finds files or directories in a directory matching a predicate function.
168
169
  *
@@ -172,7 +173,7 @@ export declare function findOldestFile(dirPath: string, recursive?: boolean): Pr
172
173
  * @returns {Promise<string[]>} Array of matching paths.
173
174
  * @throws {Error} If directory cannot be read.
174
175
  */
175
- export declare function findInDir(dirPath: string, predicate: (path: string, stat: fs.Stats) => boolean | Promise<boolean>, recursive?: boolean): Promise<string[]>;
176
+ declare function findInDir(dirPath: string, predicate: (path: string, stat: fs.Stats) => boolean | Promise<boolean>, recursive?: boolean): Promise<string[]>;
176
177
  /**
177
178
  * Watches a directory recursively for file changes.
178
179
  *
@@ -182,7 +183,7 @@ export declare function findInDir(dirPath: string, predicate: (path: string, sta
182
183
  * @returns {Promise<() => void>} A function to stop watching the directory.
183
184
  * @throws {Error} If directory cannot be watched.
184
185
  */
185
- export declare function watchDirRecursive(dirPath: string, callback: (eventType: 'rename' | 'change', filename: string) => void, includeSubdirs?: boolean): Promise<() => void>;
186
+ declare function watchDirRecursive(dirPath: string, callback: (eventType: 'rename' | 'change', filename: string) => void, includeSubdirs?: boolean): Promise<() => void>;
186
187
  /**
187
188
  * Gets detailed directory statistics including file count, directory count, and size.
188
189
  *
@@ -190,7 +191,7 @@ export declare function watchDirRecursive(dirPath: string, callback: (eventType:
190
191
  * @returns {Promise<{ fileCount: number, dirCount: number, totalSize: number }>} Directory statistics.
191
192
  * @throws {Error} If directory cannot be read.
192
193
  */
193
- export declare function getDirStats(dirPath: string): Promise<{
194
+ declare function getDirStats(dirPath: string): Promise<{
194
195
  fileCount: number;
195
196
  dirCount: number;
196
197
  totalSize: number;
@@ -205,7 +206,7 @@ export declare function getDirStats(dirPath: string): Promise<{
205
206
  * @param {'pre' | 'post'} [options.traversalOrder='pre'] - Whether to visit directories before or after their contents.
206
207
  * @throws {Error} If directory cannot be read.
207
208
  */
208
- export declare function walkDir(dirPath: string, options: {
209
+ declare function walkDir(dirPath: string, options: {
209
210
  visitorFn: (entry: {
210
211
  path: string;
211
212
  name: string;
@@ -214,3 +215,5 @@ export declare function walkDir(dirPath: string, options: {
214
215
  }) => boolean | void | Promise<boolean | void>;
215
216
  traversalOrder?: 'pre' | 'post';
216
217
  }): Promise<void>;
218
+
219
+ export { copyDir, createTempDir, deleteDirRecursive, emptyDir, ensureDir, ensureEmptyDir, findFilesByPattern, findInDir, findNewestFile, findOldestFile, getDirSize, getDirStats, getSubdirectories, isDirectory, listFiles, moveDir, walkDir, watchDir, watchDirRecursive };
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * The MIT License
3
3
  *
4
- * Copyright (c) 2025 Catbee Technologies. https://catbee.in/license
4
+ * Copyright (c) 2026 Catbee Technologies. https://catbee.in/license
5
5
  *
6
6
  * Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  * of this software and associated documentation files (the "Software"), to deal
@@ -133,7 +133,7 @@ function watchDir(dirPath, callback) {
133
133
  __name(watchDir, "watchDir");
134
134
  async function findFilesByPattern(pattern, options = {}) {
135
135
  const matchPattern = /* @__PURE__ */ __name((name, pattern2) => {
136
- let escaped = pattern2.replaceAll(/[.+^${}()|[\]\\]/g, "\\$&");
136
+ let escaped = pattern2.replaceAll(/[.+^${}()|[\]\\]/g, String.raw`\$&`);
137
137
  escaped = escaped.replaceAll("*", ".*").replaceAll("?", ".");
138
138
  const regex = new RegExp(`^${escaped}$`);
139
139
  return regex.test(name);
@@ -143,7 +143,7 @@ async function findFilesByPattern(pattern, options = {}) {
143
143
  async function walk(dir, segIndex) {
144
144
  if (segIndex >= segments.length) return [];
145
145
  const segment = segments[segIndex];
146
- const entries = await fs.promises.readdir(dir, {
146
+ const entries = await fsp.readdir(dir, {
147
147
  withFileTypes: true
148
148
  });
149
149
  let matchedFiles = [];
@@ -245,7 +245,8 @@ async function findNewestFile(dirPath, recursive = false) {
245
245
  path: file,
246
246
  mtime: (await fsp.stat(file)).mtime
247
247
  })));
248
- return stats.sort((a, b) => b.mtime.getTime() - a.mtime.getTime())[0].path;
248
+ const sorted = stats.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
249
+ return sorted[0].path;
249
250
  }
250
251
  __name(findNewestFile, "findNewestFile");
251
252
  async function findOldestFile(dirPath, recursive = false) {
package/env/index.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * The MIT License
3
3
  *
4
- * Copyright (c) 2025 Catbee Technologies. https://catbee.in/license
4
+ * Copyright (c) 2026 Catbee Technologies. https://catbee.in/license
5
5
  *
6
6
  * Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  * of this software and associated documentation files (the "Software"), to deal
@@ -26,6 +26,8 @@
26
26
 
27
27
  var fs = require('fs');
28
28
  var path = require('path');
29
+ var date = require('@catbee/utils/date');
30
+ var validation = require('@catbee/utils/validation');
29
31
 
30
32
  var __defProp = Object.defineProperty;
31
33
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
@@ -83,9 +85,7 @@ var Env = class _Env {
83
85
  static set(key, value) {
84
86
  process.env[key] = value;
85
87
  this.cache.delete(key);
86
- for (const cacheKey of [
87
- ...this.cache.keys()
88
- ]) {
88
+ for (const cacheKey of this.cache.keys()) {
89
89
  if (cacheKey.includes(`:${key}`)) {
90
90
  this.cache.delete(cacheKey);
91
91
  }
@@ -114,7 +114,7 @@ var Env = class _Env {
114
114
  */
115
115
  static get(key, defaultValue) {
116
116
  let value = process.env[key] ?? defaultValue;
117
- if (value && value.includes("${")) {
117
+ if (value?.includes("${")) {
118
118
  value = value.replace(/\${([A-Za-z0-9_]+)}/g, (_, varName) => {
119
119
  return process.env[varName] ?? "";
120
120
  });
@@ -172,7 +172,7 @@ var Env = class _Env {
172
172
  return defaultValue;
173
173
  }
174
174
  const numberValue = Number(value);
175
- if (isNaN(numberValue)) {
175
+ if (Number.isNaN(numberValue)) {
176
176
  throw new Error(`Environment variable '${key}' is not a valid number, got: "${value}"`);
177
177
  }
178
178
  this.cache.set(`number:${key}`, numberValue);
@@ -363,7 +363,7 @@ var Env = class _Env {
363
363
  static getNumberArray(key, defaultValue = [], splitter = ",") {
364
364
  return _Env.getArray(key, defaultValue, splitter, (item) => {
365
365
  const num = Number(item);
366
- if (isNaN(num)) {
366
+ if (Number.isNaN(num)) {
367
367
  throw new Error(`Value "${item}" in array '${key}' is not a valid number`);
368
368
  }
369
369
  return num;
@@ -439,20 +439,33 @@ var Env = class _Env {
439
439
  if (!value) {
440
440
  throw new Error(`URL environment variable '${key}' is missing or empty`);
441
441
  }
442
- let url;
442
+ const url = this.parseUrl(value, key);
443
+ this.validateProtocol(url, key, options);
444
+ this.validateHostname(url, key, options);
445
+ this.cache.set(cacheKey, value);
446
+ return value;
447
+ }
448
+ static parseUrl(value, key) {
443
449
  try {
444
- url = new URL(value);
450
+ return new URL(value);
445
451
  } catch {
446
452
  throw new Error(`Environment variable '${key}' is not a valid URL: "${value}"`);
447
453
  }
454
+ }
455
+ static validateProtocol(url, key, options) {
448
456
  if (options.protocols && options.protocols.length > 0) {
449
457
  const protocol = url.protocol.replace(":", "");
450
458
  if (!options.protocols.includes(protocol)) {
451
459
  throw new Error(`Environment variable '${key}' must use one of the protocols: ${options.protocols.join(", ")}. Got: ${protocol}`);
452
460
  }
453
461
  }
462
+ }
463
+ static isIp(hostname) {
464
+ return /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/.test(hostname);
465
+ }
466
+ static validateHostname(url, key, options) {
454
467
  const { hostname } = url;
455
- const isIp = /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/.test(hostname);
468
+ const isIp = _Env.isIp(hostname);
456
469
  const isLocalhost = hostname === "localhost";
457
470
  if (isIp && options.allowIp === false) {
458
471
  throw new Error(`Environment variable '${key}' cannot be an IP address: "${hostname}"`);
@@ -465,8 +478,6 @@ var Env = class _Env {
465
478
  throw new Error(`Environment variable '${key}' must have a valid host with TLD: "${hostname}"`);
466
479
  }
467
480
  }
468
- this.cache.set(cacheKey, value);
469
- return value;
470
481
  }
471
482
  /**
472
483
  * Retrieves an email environment variable and validates it.
@@ -490,8 +501,7 @@ var Env = class _Env {
490
501
  }
491
502
  return defaultValue;
492
503
  }
493
- const emailRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
494
- if (!emailRegex.test(value)) {
504
+ if (!validation.isEmail(value)) {
495
505
  throw new Error(`Environment variable '${key}' is not a valid email address: "${value}"`);
496
506
  }
497
507
  this.cache.set(`email:${key}`, value);
@@ -579,7 +589,7 @@ var Env = class _Env {
579
589
  return defaultValue instanceof Date ? defaultValue : /* @__PURE__ */ new Date();
580
590
  }
581
591
  const date = new Date(value);
582
- if (isNaN(date.getTime())) {
592
+ if (Number.isNaN(date.getTime())) {
583
593
  throw new Error(`Environment variable '${key}' is not a valid date: "${value}"`);
584
594
  }
585
595
  return date;
@@ -603,26 +613,9 @@ var Env = class _Env {
603
613
  if (this.cache.has(cacheKey)) {
604
614
  return this.cache.get(cacheKey);
605
615
  }
606
- const value = _Env.get(key, String(defaultValue));
607
- if (!value) return 0;
608
- if (/^\d+$/.test(value)) {
609
- const ms2 = parseInt(value, 10);
610
- this.cache.set(cacheKey, ms2);
611
- return ms2;
612
- }
613
- const durationRegex = /^(?:(\d+)y)?(?:(\d+)w)?(?:(\d+)d)?(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?(?:(\d+)ms)?$/;
614
- const matches = value.match(durationRegex);
615
- if (!matches || matches[0] === "") {
616
- throw new Error(`Environment variable '${key}' has invalid duration format: "${value}". Use formats like 1y, 2w, 3d, 4h, 5m, 6s, or 7ms.`);
617
- }
618
- let ms = 0;
619
- if (matches[1]) ms += parseInt(matches[1], 10) * 31536e6;
620
- if (matches[2]) ms += parseInt(matches[2], 10) * 6048e5;
621
- if (matches[3]) ms += parseInt(matches[3], 10) * 864e5;
622
- if (matches[4]) ms += parseInt(matches[4], 10) * 36e5;
623
- if (matches[5]) ms += parseInt(matches[5], 10) * 6e4;
624
- if (matches[6]) ms += parseInt(matches[6], 10) * 1e3;
625
- if (matches[7]) ms += parseInt(matches[7], 10);
616
+ const raw = _Env.get(key, String(defaultValue));
617
+ if (!raw) return 0;
618
+ const ms = date.parseDuration(raw);
626
619
  this.cache.set(cacheKey, ms);
627
620
  return ms;
628
621
  }
@@ -667,63 +660,102 @@ var Env = class _Env {
667
660
  throw new Error(`Environment file not found: "${path}"`);
668
661
  }
669
662
  const content = fs.readFileSync(path, "utf8");
663
+ const variables = this.parseEnvContent(content);
664
+ for (const [k, v] of Object.entries(variables)) {
665
+ if (!process.env[k]) {
666
+ process.env[k] = v;
667
+ }
668
+ }
669
+ return variables;
670
+ }
671
+ static parseEnvContent(content) {
670
672
  const variables = {};
671
673
  const lines = content.split(/\r?\n/);
672
674
  let i = 0;
673
675
  while (i < lines.length) {
674
676
  let line = lines[i].trim();
675
- if (!line || line.startsWith("#")) {
677
+ if (this.isCommentOrEmpty(line)) {
676
678
  i++;
677
679
  continue;
678
680
  }
679
- const match = line.match(/^([^=]+)=(.*)$/);
681
+ const match = this.extractKeyValue(line);
680
682
  if (!match) {
681
683
  i++;
682
684
  continue;
683
685
  }
684
686
  const key = match[1].trim();
685
687
  let value = match[2].trim();
686
- if (!/^['"]/.test(value)) {
687
- const hashIndex = value.indexOf(" #");
688
- if (hashIndex !== -1) {
689
- value = value.slice(0, hashIndex).trim();
690
- }
688
+ if (!this.isQuoted(value)) {
689
+ value = this.stripInlineComment(value);
691
690
  }
692
- if ((value.startsWith('"') || value.startsWith("'")) && value.length > 1) {
693
- const quote = value[0];
694
- if (!value.endsWith(quote) || value.length === 1) {
695
- let multilineValue = value.slice(1);
696
- i++;
697
- while (i < lines.length) {
698
- const nextLine = lines[i];
699
- if (nextLine.endsWith(quote)) {
700
- multilineValue += "\n" + nextLine.slice(0, -1);
701
- break;
702
- } else {
703
- multilineValue += "\n" + nextLine;
704
- }
705
- i++;
706
- }
707
- value = multilineValue;
708
- } else {
709
- value = value.slice(1, -1);
710
- }
691
+ if (this.isQuoted(value) && value.length > 1) {
692
+ const result = this.parseQuotedValue(lines, i, value);
693
+ value = result.value;
694
+ i = result.nextIndex;
711
695
  } else {
712
- i++;
713
- while (i < lines.length && !lines[i].includes("=") && lines[i].trim() !== "") {
714
- value += "\n" + lines[i];
715
- i++;
716
- }
717
- i--;
696
+ const result = this.parseUnquotedMultiline(lines, i, value);
697
+ value = result.value;
698
+ i = result.nextIndex;
718
699
  }
719
700
  if (!process.env[key]) {
720
- process.env[key] = value;
721
701
  variables[key] = value;
722
702
  }
723
703
  i++;
724
704
  }
725
705
  return variables;
726
706
  }
707
+ static isCommentOrEmpty(line) {
708
+ return !line || line.startsWith("#");
709
+ }
710
+ static extractKeyValue(line) {
711
+ return line.match(/^([^=]+)=(.*)$/);
712
+ }
713
+ static isQuoted(value) {
714
+ return /^['"]/.test(value);
715
+ }
716
+ static stripInlineComment(value) {
717
+ const hashIndex = value.indexOf(" #");
718
+ if (hashIndex !== -1) {
719
+ return value.slice(0, hashIndex).trim();
720
+ }
721
+ return value;
722
+ }
723
+ static parseQuotedValue(lines, currentIndex, value) {
724
+ const quote = value[0];
725
+ if (!value.endsWith(quote) || value.length === 1) {
726
+ let multilineValue = value.slice(1);
727
+ let i = currentIndex + 1;
728
+ while (i < lines.length) {
729
+ const nextLine = lines[i];
730
+ if (nextLine.endsWith(quote)) {
731
+ multilineValue += "\n" + nextLine.slice(0, -1);
732
+ break;
733
+ } else {
734
+ multilineValue += "\n" + nextLine;
735
+ }
736
+ i++;
737
+ }
738
+ return {
739
+ value: multilineValue,
740
+ nextIndex: i
741
+ };
742
+ }
743
+ return {
744
+ value: value.slice(1, -1),
745
+ nextIndex: currentIndex
746
+ };
747
+ }
748
+ static parseUnquotedMultiline(lines, currentIndex, value) {
749
+ let i = currentIndex + 1;
750
+ while (i < lines.length && !lines[i].includes("=") && lines[i].trim() !== "") {
751
+ value += "\n" + lines[i];
752
+ i++;
753
+ }
754
+ return {
755
+ value,
756
+ nextIndex: i - 1
757
+ };
758
+ }
727
759
  /**
728
760
  * Checks whether the specified environment variable exists.
729
761
  *