@catbee/utils 0.0.3 → 0.0.5

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 (98) hide show
  1. package/README.md +693 -245
  2. package/build/esm/config.d.ts +18 -7
  3. package/build/esm/config.js +23 -7
  4. package/build/esm/config.js.map +1 -1
  5. package/build/esm/index.d.ts +2 -0
  6. package/build/esm/index.js +2 -0
  7. package/build/esm/index.js.map +1 -1
  8. package/build/esm/utils/cache.utils.js +16 -16
  9. package/build/esm/utils/cache.utils.js.map +1 -1
  10. package/build/esm/utils/context-store.utils.d.ts +1 -1
  11. package/build/esm/utils/context-store.utils.js +1 -1
  12. package/build/esm/utils/context-store.utils.js.map +1 -1
  13. package/build/esm/utils/crypto.utils.js +3 -2
  14. package/build/esm/utils/crypto.utils.js.map +1 -1
  15. package/build/esm/utils/decorators.utils.d.ts +292 -17
  16. package/build/esm/utils/decorators.utils.js +372 -40
  17. package/build/esm/utils/decorators.utils.js.map +1 -1
  18. package/build/esm/utils/env.utils.d.ts +186 -23
  19. package/build/esm/utils/env.utils.js +494 -91
  20. package/build/esm/utils/env.utils.js.map +1 -1
  21. package/build/esm/utils/exception.utils.js +1 -1
  22. package/build/esm/utils/exception.utils.js.map +1 -1
  23. package/build/esm/utils/logger.utils.d.ts +107 -0
  24. package/build/esm/utils/logger.utils.js +210 -19
  25. package/build/esm/utils/logger.utils.js.map +1 -1
  26. package/build/esm/utils/middleware.utils.d.ts +42 -34
  27. package/build/esm/utils/middleware.utils.js +88 -51
  28. package/build/esm/utils/middleware.utils.js.map +1 -1
  29. package/build/esm/utils/request.utils.d.ts +77 -0
  30. package/build/esm/utils/request.utils.js +173 -0
  31. package/build/esm/utils/request.utils.js.map +1 -0
  32. package/build/esm/utils/response.utils.js +4 -4
  33. package/build/esm/utils/response.utils.js.map +1 -1
  34. package/build/esnext/config.d.ts +18 -7
  35. package/build/esnext/config.js +23 -7
  36. package/build/esnext/config.js.map +1 -1
  37. package/build/esnext/index.d.ts +2 -0
  38. package/build/esnext/index.js +2 -0
  39. package/build/esnext/index.js.map +1 -1
  40. package/build/esnext/utils/cache.utils.js +16 -16
  41. package/build/esnext/utils/cache.utils.js.map +1 -1
  42. package/build/esnext/utils/context-store.utils.d.ts +1 -1
  43. package/build/esnext/utils/context-store.utils.js +1 -1
  44. package/build/esnext/utils/context-store.utils.js.map +1 -1
  45. package/build/esnext/utils/crypto.utils.js +3 -2
  46. package/build/esnext/utils/crypto.utils.js.map +1 -1
  47. package/build/esnext/utils/decorators.utils.d.ts +292 -17
  48. package/build/esnext/utils/decorators.utils.js +348 -21
  49. package/build/esnext/utils/decorators.utils.js.map +1 -1
  50. package/build/esnext/utils/env.utils.d.ts +186 -23
  51. package/build/esnext/utils/env.utils.js +445 -77
  52. package/build/esnext/utils/env.utils.js.map +1 -1
  53. package/build/esnext/utils/exception.utils.js +1 -1
  54. package/build/esnext/utils/exception.utils.js.map +1 -1
  55. package/build/esnext/utils/logger.utils.d.ts +107 -0
  56. package/build/esnext/utils/logger.utils.js +181 -19
  57. package/build/esnext/utils/logger.utils.js.map +1 -1
  58. package/build/esnext/utils/middleware.utils.d.ts +42 -34
  59. package/build/esnext/utils/middleware.utils.js +86 -51
  60. package/build/esnext/utils/middleware.utils.js.map +1 -1
  61. package/build/esnext/utils/request.utils.d.ts +77 -0
  62. package/build/esnext/utils/request.utils.js +145 -0
  63. package/build/esnext/utils/request.utils.js.map +1 -0
  64. package/build/esnext/utils/response.utils.js +4 -4
  65. package/build/esnext/utils/response.utils.js.map +1 -1
  66. package/build/src/config.d.ts +18 -7
  67. package/build/src/config.js +26 -8
  68. package/build/src/config.js.map +1 -1
  69. package/build/src/index.d.ts +2 -0
  70. package/build/src/index.js +5 -0
  71. package/build/src/index.js.map +1 -1
  72. package/build/src/utils/cache.utils.js +15 -15
  73. package/build/src/utils/cache.utils.js.map +1 -1
  74. package/build/src/utils/context-store.utils.d.ts +1 -1
  75. package/build/src/utils/context-store.utils.js +1 -1
  76. package/build/src/utils/context-store.utils.js.map +1 -1
  77. package/build/src/utils/crypto.utils.js +2 -1
  78. package/build/src/utils/crypto.utils.js.map +1 -1
  79. package/build/src/utils/decorators.utils.d.ts +292 -17
  80. package/build/src/utils/decorators.utils.js +350 -21
  81. package/build/src/utils/decorators.utils.js.map +1 -1
  82. package/build/src/utils/env.utils.d.ts +186 -23
  83. package/build/src/utils/env.utils.js +444 -76
  84. package/build/src/utils/env.utils.js.map +1 -1
  85. package/build/src/utils/exception.utils.js +1 -1
  86. package/build/src/utils/exception.utils.js.map +1 -1
  87. package/build/src/utils/logger.utils.d.ts +107 -0
  88. package/build/src/utils/logger.utils.js +187 -19
  89. package/build/src/utils/logger.utils.js.map +1 -1
  90. package/build/src/utils/middleware.utils.d.ts +42 -34
  91. package/build/src/utils/middleware.utils.js +86 -50
  92. package/build/src/utils/middleware.utils.js.map +1 -1
  93. package/build/src/utils/request.utils.d.ts +77 -0
  94. package/build/src/utils/request.utils.js +152 -0
  95. package/build/src/utils/request.utils.js.map +1 -0
  96. package/build/src/utils/response.utils.js +4 -4
  97. package/build/src/utils/response.utils.js.map +1 -1
  98. package/package.json +11 -6
@@ -1,14 +1,3 @@
1
- var __values = (this && this.__values) || function(o) {
2
- var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
3
- if (m) return m.call(o);
4
- if (o && typeof o.length === "number") return {
5
- next: function () {
6
- if (o && i >= o.length) o = void 0;
7
- return { value: o && o[i++], done: !o };
8
- }
9
- };
10
- throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
11
- };
12
1
  var __read = (this && this.__read) || function (o, n) {
13
2
  var m = typeof Symbol === "function" && o[Symbol.iterator];
14
3
  if (!m) return o;
@@ -25,8 +14,28 @@ var __read = (this && this.__read) || function (o, n) {
25
14
  }
26
15
  return ar;
27
16
  };
17
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
18
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
19
+ if (ar || !(i in from)) {
20
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
21
+ ar[i] = from[i];
22
+ }
23
+ }
24
+ return to.concat(ar || Array.prototype.slice.call(from));
25
+ };
26
+ var __values = (this && this.__values) || function(o) {
27
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
28
+ if (m) return m.call(o);
29
+ if (o && typeof o.length === "number") return {
30
+ next: function () {
31
+ if (o && i >= o.length) o = void 0;
32
+ return { value: o && o[i++], done: !o };
33
+ }
34
+ };
35
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
36
+ };
28
37
  /* eslint-disable n/no-process-env */
29
- import { existsSync } from 'fs';
38
+ import { existsSync, readFileSync } from 'fs';
30
39
  import { isAbsolute, resolve } from 'path';
31
40
  /**
32
41
  * Enum representing valid application environments.
@@ -59,7 +68,7 @@ var Env = /** @class */ (function () {
59
68
  * @returns {boolean} `true` if NODE_ENV is 'production', else `false`.
60
69
  */
61
70
  Env.isProd = function () {
62
- return Env.get('NODE_ENV') === Environment.PRODUCTION;
71
+ return Env.get('NODE_ENV', Environment.DEVELOPMENT) === Environment.PRODUCTION;
63
72
  };
64
73
  /**
65
74
  * Checks if the current NODE_ENV is 'testing'.
@@ -67,7 +76,7 @@ var Env = /** @class */ (function () {
67
76
  * @returns {boolean} `true` if NODE_ENV is 'testing', else `false`.
68
77
  */
69
78
  Env.isTest = function () {
70
- return Env.get('NODE_ENV') === Environment.TESTING;
79
+ return Env.get('NODE_ENV', Environment.DEVELOPMENT) === Environment.TESTING;
71
80
  };
72
81
  /**
73
82
  * Checks if the current NODE_ENV is 'staging'.
@@ -75,7 +84,7 @@ var Env = /** @class */ (function () {
75
84
  * @returns {boolean} `true` if NODE_ENV is 'staging', else `false`.
76
85
  */
77
86
  Env.isStaging = function () {
78
- return Env.get('NODE_ENV') === Environment.STAGING;
87
+ return Env.get('NODE_ENV', Environment.DEVELOPMENT) === Environment.STAGING;
79
88
  };
80
89
  /**
81
90
  * Sets an environment variable (only affects runtime memory).
@@ -84,7 +93,25 @@ var Env = /** @class */ (function () {
84
93
  * @param {string} value - The value to set.
85
94
  */
86
95
  Env.set = function (key, value) {
96
+ var e_1, _a;
87
97
  process.env[key] = value;
98
+ // Clear all cached values for this key with any prefix
99
+ this.cache.delete(key);
100
+ try {
101
+ for (var _b = __values(__spreadArray([], __read(this.cache.keys()), false)), _c = _b.next(); !_c.done; _c = _b.next()) {
102
+ var cacheKey = _c.value;
103
+ if (cacheKey.includes(":".concat(key))) {
104
+ this.cache.delete(cacheKey);
105
+ }
106
+ }
107
+ }
108
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
109
+ finally {
110
+ try {
111
+ if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
112
+ }
113
+ finally { if (e_1) throw e_1.error; }
114
+ }
88
115
  };
89
116
  /**
90
117
  * Returns all environment variables as an object.
@@ -96,14 +123,28 @@ var Env = /** @class */ (function () {
96
123
  };
97
124
  /**
98
125
  * Retrieves a string environment variable with a fallback default.
126
+ * Supports variable expansion with ${VAR_NAME} syntax.
99
127
  *
100
128
  * @param {string} key - The environment variable key.
101
129
  * @param {string} [defaultValue] - Value to return if the key is missing.
102
- * @returns {string | undefined} The env value or the fallback.
130
+ * @returns {string} The env value or the fallback.
131
+ *
132
+ * @example
133
+ * // If DATABASE_URL is "postgres://localhost:5432/${DB_NAME}"
134
+ * // and DB_NAME is "myapp"
135
+ * const url = Env.get('DATABASE_URL', ''); // "postgres://localhost:5432/myapp"
103
136
  */
104
137
  Env.get = function (key, defaultValue) {
105
138
  var _a;
106
- return (_a = process.env[key]) !== null && _a !== void 0 ? _a : defaultValue;
139
+ var value = (_a = process.env[key]) !== null && _a !== void 0 ? _a : defaultValue;
140
+ // Expand ${VAR} references
141
+ if (value && value.includes('${')) {
142
+ value = value.replace(/\${([A-Za-z0-9_]+)}/g, function (_, varName) {
143
+ var _a;
144
+ return (_a = process.env[varName]) !== null && _a !== void 0 ? _a : '';
145
+ });
146
+ }
147
+ return value;
107
148
  };
108
149
  /**
109
150
  * Retrieves a string environment variable and throws if it's missing.
@@ -115,9 +156,29 @@ var Env = /** @class */ (function () {
115
156
  Env.getRequired = function (key) {
116
157
  var value = process.env[key];
117
158
  if (value === undefined) {
118
- throw new Error("Required env ".concat(key, " is missing"));
159
+ throw new Error("Required environment variable '".concat(key, "' is missing"));
119
160
  }
120
- return value;
161
+ return Env.get(key, ''); // Use get to handle variable expansion
162
+ };
163
+ /**
164
+ * Retrieves a value using a default generating function if the key doesn't exist.
165
+ * Useful for expensive default calculations.
166
+ *
167
+ * @param {string} key - The environment variable key.
168
+ * @param {() => string} defaultFn - Function that generates default value.
169
+ * @returns {string} The environment value or generated default.
170
+ *
171
+ * @example
172
+ * const hostname = Env.getWithDefault('HOSTNAME', () => {
173
+ * // Only called if HOSTNAME is not set
174
+ * return require('os').hostname();
175
+ * });
176
+ */
177
+ Env.getWithDefault = function (key, defaultFn) {
178
+ if (Env.has(key)) {
179
+ return Env.get(key, '');
180
+ }
181
+ return defaultFn();
121
182
  };
122
183
  /**
123
184
  * Retrieves an environment variable as a number, or returns a default.
@@ -128,12 +189,18 @@ var Env = /** @class */ (function () {
128
189
  * @throws {Error} If the value is not a valid number.
129
190
  */
130
191
  Env.getNumber = function (key, defaultValue) {
131
- var _a;
132
- var value = (_a = process.env[key]) !== null && _a !== void 0 ? _a : defaultValue;
192
+ if (this.cache.has("number:".concat(key))) {
193
+ return this.cache.get("number:".concat(key));
194
+ }
195
+ var value = process.env[key];
196
+ if (value === undefined) {
197
+ return defaultValue;
198
+ }
133
199
  var numberValue = Number(value);
134
200
  if (isNaN(numberValue)) {
135
- throw new Error("Env ".concat(key, " is not a number"));
201
+ throw new Error("Environment variable '".concat(key, "' is not a valid number, got: \"").concat(value, "\""));
136
202
  }
203
+ this.cache.set("number:".concat(key), numberValue);
137
204
  return numberValue;
138
205
  };
139
206
  /**
@@ -146,13 +213,38 @@ var Env = /** @class */ (function () {
146
213
  Env.getNumberRequired = function (key) {
147
214
  var value = process.env[key];
148
215
  if (value === undefined) {
149
- throw new Error("Required env ".concat(key, " is missing"));
216
+ throw new Error("Required environment variable '".concat(key, "' is missing"));
150
217
  }
151
- var numberValue = Number(value);
152
- if (isNaN(numberValue)) {
153
- throw new Error("Required env ".concat(key, " is not a number"));
218
+ return Env.getNumber(key, 0); // The default is ignored since we know the key exists
219
+ };
220
+ /**
221
+ * Retrieves an integer environment variable and validates it.
222
+ *
223
+ * @param {string} key - The environment variable key.
224
+ * @param {number} defaultValue - Fallback number if key is not present.
225
+ * @param {object} [options] - Validation options.
226
+ * @param {number} [options.min] - Minimum allowed value.
227
+ * @param {number} [options.max] - Maximum allowed value.
228
+ * @returns {number} The parsed integer.
229
+ *
230
+ * @example
231
+ * // Require PORT to be between 1000 and 9999
232
+ * const port = Env.getInteger('PORT', 3000, { min: 1000, max: 9999 });
233
+ */
234
+ Env.getInteger = function (key, defaultValue, options) {
235
+ if (options === void 0) { options = {}; }
236
+ var num = Env.getNumber(key, defaultValue);
237
+ var intValue = Math.floor(num);
238
+ if (intValue !== num) {
239
+ throw new Error("Environment variable '".concat(key, "' must be an integer, got: ").concat(num));
154
240
  }
155
- return numberValue;
241
+ if (options.min !== undefined && intValue < options.min) {
242
+ throw new Error("Environment variable '".concat(key, "' must be at least ").concat(options.min, ", got: ").concat(intValue));
243
+ }
244
+ if (options.max !== undefined && intValue > options.max) {
245
+ throw new Error("Environment variable '".concat(key, "' must be at most ").concat(options.max, ", got: ").concat(intValue));
246
+ }
247
+ return intValue;
156
248
  };
157
249
  /**
158
250
  * Retrieves an environment variable as a boolean.
@@ -162,16 +254,30 @@ var Env = /** @class */ (function () {
162
254
  * @param {boolean} [defaultValue=false] - Optional fallback value if key is missing.
163
255
  * @returns {boolean} Parsed boolean.
164
256
  * @throws {Error} If the value is not a recognized boolean string.
257
+ *
258
+ * @example
259
+ * // If DEBUG=yes
260
+ * const isDebug = Env.getBoolean('DEBUG', false); // true
165
261
  */
166
262
  Env.getBoolean = function (key, defaultValue) {
167
- var _a;
168
263
  if (defaultValue === void 0) { defaultValue = false; }
169
- var value = ((_a = process.env[key]) !== null && _a !== void 0 ? _a : defaultValue).toString().toLowerCase();
170
- if (['true', '1', 'yes', 'on'].includes(value))
264
+ if (this.cache.has("bool:".concat(key))) {
265
+ return this.cache.get("bool:".concat(key));
266
+ }
267
+ var value = process.env[key];
268
+ if (value === undefined) {
269
+ return defaultValue;
270
+ }
271
+ var lowerValue = value.toLowerCase();
272
+ if (['true', '1', 'yes', 'on'].includes(lowerValue)) {
273
+ this.cache.set("bool:".concat(key), true);
171
274
  return true;
172
- if (['false', '0', 'no', 'off'].includes(value))
275
+ }
276
+ if (['false', '0', 'no', 'off'].includes(lowerValue)) {
277
+ this.cache.set("bool:".concat(key), false);
173
278
  return false;
174
- throw new Error("Env variable ".concat(key, " is not a boolean"));
279
+ }
280
+ throw new Error("Environment variable '".concat(key, "' is not a valid boolean, got: \"").concat(value, "\". Use true/false, yes/no, 1/0, or on/off."));
175
281
  };
176
282
  /**
177
283
  * Retrieves a required environment variable as a boolean.
@@ -183,9 +289,9 @@ var Env = /** @class */ (function () {
183
289
  Env.getBooleanRequired = function (key) {
184
290
  var value = process.env[key];
185
291
  if (value === undefined) {
186
- throw new Error("Required env ".concat(key, " is missing"));
292
+ throw new Error("Required environment variable '".concat(key, "' is missing"));
187
293
  }
188
- return this.getBoolean(key); // reuse logic
294
+ return Env.getBoolean(key);
189
295
  };
190
296
  /**
191
297
  * Parses a stringified JSON object from an environment variable.
@@ -195,18 +301,28 @@ var Env = /** @class */ (function () {
195
301
  * @param {T} defaultValue - Value to return if key is missing.
196
302
  * @returns {T} Parsed object or default.
197
303
  * @throws {Error} If the value is not valid JSON.
304
+ *
305
+ * @example
306
+ * // If CONFIG='{"debug":true,"api":{"url":"https://api.example.com"}}'
307
+ * const config = Env.getJSON('CONFIG', { debug: false });
308
+ * // { debug: true, api: { url: "https://api.example.com" }}
198
309
  */
199
310
  Env.getJSON = function (key, defaultValue) {
311
+ if (this.cache.has("json:".concat(key))) {
312
+ return this.cache.get("json:".concat(key));
313
+ }
200
314
  var v = process.env[key];
201
- if (v !== undefined) {
202
- try {
203
- return JSON.parse(v);
204
- }
205
- catch (_a) {
206
- throw new Error("Env variable ".concat(key, " is not a valid JSON string"));
207
- }
315
+ if (v === undefined) {
316
+ return defaultValue;
317
+ }
318
+ try {
319
+ var parsed = JSON.parse(v);
320
+ this.cache.set("json:".concat(key), parsed);
321
+ return parsed;
322
+ }
323
+ catch (error) {
324
+ throw new Error("Environment variable '".concat(key, "' is not valid JSON: ").concat(error.message));
208
325
  }
209
- return defaultValue;
210
326
  };
211
327
  /**
212
328
  * Parses a comma-separated string as an array.
@@ -215,19 +331,69 @@ var Env = /** @class */ (function () {
215
331
  * @param {string} key - The environment variable key.
216
332
  * @param {T[]} [defaultValue=[]] - Array to return if value is empty or missing.
217
333
  * @param {string} [splitter=','] - Delimiter to split on.
218
- * @returns {string[] | T[]} An array of strings.
334
+ * @param {(item: string) => T} [transform] - Optional function to transform each item.
335
+ * @returns {T[]} An array of items.
336
+ *
337
+ * @example
338
+ * // If ALLOWED_IPS=127.0.0.1,192.168.1.1,10.0.0.1
339
+ * const ips = Env.getArray('ALLOWED_IPS');
340
+ * // ["127.0.0.1", "192.168.1.1", "10.0.0.1"]
341
+ *
342
+ * // With transformation function
343
+ * const ports = Env.getArray('PORTS', [], ',', (p) => parseInt(p, 10));
219
344
  */
220
- Env.getArray = function (key, defaultValue, splitter) {
345
+ Env.getArray = function (key, defaultValue, splitter, transform) {
221
346
  if (defaultValue === void 0) { defaultValue = []; }
222
347
  if (splitter === void 0) { splitter = ','; }
348
+ var cacheKey = "array:".concat(key, ":").concat(splitter, ":").concat(transform ? 'transformed' : 'raw');
349
+ if (this.cache.has(cacheKey)) {
350
+ return this.cache.get(cacheKey);
351
+ }
223
352
  var value = process.env[key];
224
353
  if (!value || value.trim() === '') {
225
354
  return defaultValue;
226
355
  }
227
- return value
356
+ var items = value
228
357
  .split(splitter)
229
358
  .map(function (item) { return item.trim(); })
230
359
  .filter(function (item) { return item.length > 0; });
360
+ if (transform) {
361
+ try {
362
+ var result_1 = items.map(transform);
363
+ this.cache.set(cacheKey, result_1);
364
+ return result_1;
365
+ }
366
+ catch (error) {
367
+ throw new Error("Failed to transform items in '".concat(key, "': ").concat(error.message));
368
+ }
369
+ }
370
+ var result = items;
371
+ this.cache.set(cacheKey, result);
372
+ return result;
373
+ };
374
+ /**
375
+ * Parses a comma-separated list of numbers.
376
+ *
377
+ * @param {string} key - The environment variable key.
378
+ * @param {number[]} [defaultValue=[]] - Default value if not present.
379
+ * @param {string} [splitter=','] - Delimiter to split on.
380
+ * @returns {number[]} Array of parsed numbers.
381
+ *
382
+ * @example
383
+ * // If ALLOWED_PORTS=80,443,3000,8080
384
+ * const ports = Env.getNumberArray('ALLOWED_PORTS');
385
+ * // [80, 443, 3000, 8080]
386
+ */
387
+ Env.getNumberArray = function (key, defaultValue, splitter) {
388
+ if (defaultValue === void 0) { defaultValue = []; }
389
+ if (splitter === void 0) { splitter = ','; }
390
+ return Env.getArray(key, defaultValue, splitter, function (item) {
391
+ var num = Number(item);
392
+ if (isNaN(num)) {
393
+ throw new Error("Value \"".concat(item, "\" in array '").concat(key, "' is not a valid number"));
394
+ }
395
+ return num;
396
+ });
231
397
  };
232
398
  /**
233
399
  * Retrieves an enum-like environment variable value, validating against allowed values.
@@ -238,12 +404,38 @@ var Env = /** @class */ (function () {
238
404
  * @param {T} [defaultValue] - Optional fallback value.
239
405
  * @returns {T} The validated environment value.
240
406
  * @throws {Error} If missing or invalid.
407
+ *
408
+ * @example
409
+ * // If LOG_LEVEL=debug
410
+ * const level = Env.getEnum('LOG_LEVEL', ['debug', 'info', 'warn', 'error'] as const, 'info');
411
+ * // 'debug' (typed as 'debug' | 'info' | 'warn' | 'error')
241
412
  */
242
413
  Env.getEnum = function (key, allowedValues, defaultValue) {
243
- var _a;
244
- var value = (_a = process.env[key]) !== null && _a !== void 0 ? _a : defaultValue;
245
- if (!value || !allowedValues.includes(value)) {
246
- throw new Error("Env ".concat(key, " must be one of ").concat(allowedValues.join(', '), ". Received: ").concat(value));
414
+ var value = process.env[key];
415
+ if (!value) {
416
+ return defaultValue;
417
+ }
418
+ if (!allowedValues.includes(value)) {
419
+ throw new Error("Environment variable '".concat(key, "' must be one of: ").concat(allowedValues.join(', '), ". Received: \"").concat(value, "\""));
420
+ }
421
+ return value;
422
+ };
423
+ /**
424
+ * Retrieves an enum-like numeric environment variable.
425
+ *
426
+ * @param {string} key - The environment variable key.
427
+ * @param {number[]} allowedValues - Array of accepted values.
428
+ * @param {number} defaultValue - Default value if not present.
429
+ * @returns {number} The validated value.
430
+ *
431
+ * @example
432
+ * // If NODE_VERSION=16
433
+ * const version = Env.getNumberEnum('NODE_VERSION', [14, 16, 18], 16);
434
+ */
435
+ Env.getNumberEnum = function (key, allowedValues, defaultValue) {
436
+ var value = Env.getNumber(key, defaultValue);
437
+ if (!allowedValues.includes(value)) {
438
+ throw new Error("Environment variable '".concat(key, "' must be one of: ").concat(allowedValues.join(', '), ". Received: ").concat(value));
247
439
  }
248
440
  return value;
249
441
  };
@@ -252,42 +444,67 @@ var Env = /** @class */ (function () {
252
444
  *
253
445
  * @param {string} key - The environment variable key.
254
446
  * @param {string} [defaultValue] - Optional fallback value.
255
- * @param {object} [options] - Validation options.
256
- * @param {string[]} [options.protocols] - Allowed protocols.
257
- * @param {boolean} [options.requireTld=true] - Whether TLD is required.
447
+ * @param {UrlOptions} [options] - Validation options.
258
448
  * @returns {string} The validated URL.
259
449
  * @throws {Error} If URL is invalid.
450
+ *
451
+ * @example
452
+ * // Validate API URL requires HTTPS
453
+ * const apiUrl = Env.getUrl('API_URL', 'https://api.example.com', {
454
+ * protocols: ['https'],
455
+ * requireTld: true,
456
+ * allowIp: false
457
+ * });
260
458
  */
261
459
  Env.getUrl = function (key, defaultValue, options) {
262
460
  if (options === void 0) { options = {}; }
461
+ var cacheKey = "url:".concat(key, ":").concat(JSON.stringify(options));
462
+ if (this.cache.has(cacheKey)) {
463
+ return this.cache.get(cacheKey);
464
+ }
263
465
  var value = Env.get(key, defaultValue);
264
466
  if (!value) {
265
- throw new Error("Env ".concat(key, " is missing or empty"));
467
+ throw new Error("URL environment variable '".concat(key, "' is missing or empty"));
266
468
  }
267
469
  var url;
268
470
  try {
269
471
  url = new URL(value);
270
472
  }
271
473
  catch (_a) {
272
- throw new Error("Env ".concat(key, " is not a valid URL"));
474
+ throw new Error("Environment variable '".concat(key, "' is not a valid URL: \"").concat(value, "\""));
273
475
  }
476
+ // Validate protocol
274
477
  if (options.protocols && options.protocols.length > 0) {
275
478
  var protocol = url.protocol.replace(':', '');
276
479
  if (!options.protocols.includes(protocol)) {
277
- throw new Error("Env ".concat(key, " must use one of the protocols: ").concat(options.protocols.join(', ')));
480
+ throw new Error("Environment variable '".concat(key, "' must use one of the protocols: ").concat(options.protocols.join(', '), ". Got: ").concat(protocol));
278
481
  }
279
482
  }
483
+ // Validate hostname
484
+ var hostname = url.hostname;
485
+ var isIp = /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/.test(hostname);
486
+ var isLocalhost = hostname === 'localhost';
487
+ // Check if hostname is an IP address when not allowed
488
+ if (isIp && options.allowIp === false) {
489
+ throw new Error("Environment variable '".concat(key, "' cannot be an IP address: \"").concat(hostname, "\""));
490
+ }
491
+ // Check if hostname is localhost when not allowed
492
+ if (isLocalhost && options.allowLocalhost === false) {
493
+ throw new Error("Environment variable '".concat(key, "' cannot be localhost"));
494
+ }
495
+ // Check for TLD requirement - include localhost in check if requireTld is true and allowLocalhost is false
280
496
  if (options.requireTld === true) {
281
- // Check if hostname has a TLD (contains at least one dot and doesn't end with a dot)
282
- // localhost, 127.0.0.1, etc. don't have TLDs
283
- var hostname = url.hostname;
284
- if (!hostname.includes('.') ||
285
- hostname === 'localhost' ||
286
- /^[\d.]+$/.test(hostname) || // IP address
287
- hostname.endsWith('.')) {
288
- throw new Error("Env ".concat(key, " must have a valid host with TLD"));
497
+ if (isLocalhost && options.allowLocalhost !== false) {
498
+ // Localhost is allowed, so no TLD check needed for it
499
+ }
500
+ else if (isIp && options.allowIp !== false) {
501
+ // IP is allowed, so no TLD check needed for it
502
+ }
503
+ else if (!hostname.includes('.') || hostname.endsWith('.')) {
504
+ throw new Error("Environment variable '".concat(key, "' must have a valid host with TLD: \"").concat(hostname, "\""));
289
505
  }
290
506
  }
507
+ this.cache.set(cacheKey, value);
291
508
  return value;
292
509
  };
293
510
  /**
@@ -297,20 +514,27 @@ var Env = /** @class */ (function () {
297
514
  * @param {string} [defaultValue] - Optional fallback value.
298
515
  * @returns {string} The validated email address.
299
516
  * @throws {Error} If email is invalid.
517
+ *
518
+ * @example
519
+ * const supportEmail = Env.getEmail('SUPPORT_EMAIL', 'support@example.com');
300
520
  */
301
521
  Env.getEmail = function (key, defaultValue) {
522
+ if (this.cache.has("email:".concat(key))) {
523
+ return this.cache.get("email:".concat(key));
524
+ }
302
525
  var value = Env.get(key, defaultValue);
303
526
  if (!value) {
304
527
  if (defaultValue === undefined) {
305
- throw new Error("Email env ".concat(key, " is missing"));
528
+ throw new Error("Email environment variable '".concat(key, "' is missing"));
306
529
  }
307
530
  return defaultValue;
308
531
  }
309
- // Simple email validation regex
310
- var emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
532
+ // More comprehensive email validation regex
533
+ var 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])?)*$/;
311
534
  if (!emailRegex.test(value)) {
312
- throw new Error("Env ".concat(key, " is not a valid email address"));
535
+ throw new Error("Environment variable '".concat(key, "' is not a valid email address: \"").concat(value, "\""));
313
536
  }
537
+ this.cache.set("email:".concat(key), value);
314
538
  return value;
315
539
  };
316
540
  /**
@@ -318,78 +542,143 @@ var Env = /** @class */ (function () {
318
542
  *
319
543
  * @param {string} key - The environment variable key.
320
544
  * @param {string} [defaultValue] - Optional fallback value.
321
- * @param {object} [options] - Validation options.
322
- * @param {boolean} [options.mustExist=false] - Whether path must exist.
323
- * @param {boolean} [options.makeAbsolute=true] - Convert relative paths to absolute.
545
+ * @param {PathOptions} [options] - Validation options.
324
546
  * @returns {string} The validated path.
325
547
  * @throws {Error} If path is invalid.
548
+ *
549
+ * @example
550
+ * // Require that the path exists and is a .json file
551
+ * const configPath = Env.getPath('CONFIG_PATH', './config.json', {
552
+ * mustExist: true,
553
+ * allowedExtensions: ['.json', '.yaml']
554
+ * });
326
555
  */
327
556
  Env.getPath = function (key, defaultValue, options) {
328
557
  if (options === void 0) { options = {}; }
558
+ var cacheKey = "path:".concat(key, ":").concat(JSON.stringify(options));
559
+ if (this.cache.has(cacheKey)) {
560
+ return this.cache.get(cacheKey);
561
+ }
329
562
  var value = Env.get(key, defaultValue);
330
563
  if (!value) {
331
564
  if (defaultValue === undefined) {
332
- throw new Error("Path env ".concat(key, " is missing"));
565
+ throw new Error("Path environment variable '".concat(key, "' is missing"));
333
566
  }
334
567
  return defaultValue;
335
568
  }
336
569
  var path = options.makeAbsolute !== false && !isAbsolute(value) ? resolve(process.cwd(), value) : value;
570
+ // Validate path exists if required
337
571
  if (options.mustExist && !existsSync(path)) {
338
- throw new Error("Path in env ".concat(key, " does not exist: ").concat(path));
572
+ throw new Error("Path in environment variable '".concat(key, "' does not exist: \"").concat(path, "\""));
573
+ }
574
+ // Validate file extension if specified
575
+ if (options.allowedExtensions && options.allowedExtensions.length > 0) {
576
+ var hasValidExtension = options.allowedExtensions.some(function (ext) { return path.toLowerCase().endsWith(ext.toLowerCase()); });
577
+ if (!hasValidExtension) {
578
+ throw new Error("Path in environment variable '".concat(key, "' must have one of these extensions: ").concat(options.allowedExtensions.join(', '), ". Got: \"").concat(path, "\""));
579
+ }
339
580
  }
581
+ this.cache.set(cacheKey, path);
340
582
  return path;
341
583
  };
342
584
  /**
343
585
  * Retrieves a port environment variable and validates it.
344
586
  *
345
587
  * @param {string} key - The environment variable key.
346
- * @param {number} [defaultValue=3000] - Optional fallback value.
588
+ * @param {number} [defaultValue] - Optional fallback value.
347
589
  * @returns {number} The validated port number.
348
590
  * @throws {Error} If port is invalid.
591
+ *
592
+ * @example
593
+ * const serverPort = Env.getPort('PORT', 3000);
349
594
  */
350
595
  Env.getPort = function (key, defaultValue) {
351
- if (defaultValue === void 0) { defaultValue = 3000; }
352
- var port = Env.getNumber(key, defaultValue);
353
- if (port < 0 || port > 65535) {
354
- throw new Error("Env ".concat(key, " must be a valid port number (0-65535)"));
596
+ try {
597
+ return Env.getInteger(key, defaultValue, { min: 0, max: 65535 });
598
+ }
599
+ catch (error) {
600
+ // Rewrite the error message to match the expected pattern
601
+ if (error.message.includes('must be at most 65535')) {
602
+ throw new Error("Environment variable '".concat(key, "' must be a valid port number (0-65535)"));
603
+ }
604
+ throw error;
605
+ }
606
+ };
607
+ /**
608
+ * Retrieves an ISO date string and converts it to a Date object.
609
+ *
610
+ * @param {string} key - The environment variable key.
611
+ * @param {string|Date} [defaultValue] - Optional fallback value.
612
+ * @returns {Date} The parsed Date object.
613
+ *
614
+ * @example
615
+ * // If EXPIRY_DATE=2023-12-31T23:59:59Z
616
+ * const expiryDate = Env.getDate('EXPIRY_DATE', new Date());
617
+ */
618
+ Env.getDate = function (key, defaultValue) {
619
+ if (defaultValue === void 0) { defaultValue = new Date(); }
620
+ var value = Env.get(key, defaultValue instanceof Date ? defaultValue.toISOString() : defaultValue);
621
+ if (!value) {
622
+ return defaultValue instanceof Date ? defaultValue : new Date();
623
+ }
624
+ var date = new Date(value);
625
+ if (isNaN(date.getTime())) {
626
+ throw new Error("Environment variable '".concat(key, "' is not a valid date: \"").concat(value, "\""));
355
627
  }
356
- return port;
628
+ return date;
357
629
  };
358
630
  /**
359
- * Retrieves a duration string and converts to milliseconds.
631
+ * Retrieves a duration string and converts it to milliseconds.
360
632
  * Supports formats like "1d", "2h", "30m", "45s", "100ms" or combinations like "1h30m".
361
633
  *
362
634
  * @param {string} key - The environment variable key.
363
635
  * @param {string|number} [defaultValue='0'] - Optional fallback value.
364
636
  * @returns {number} The duration in milliseconds.
365
637
  * @throws {Error} If duration format is invalid.
638
+ *
639
+ * @example
640
+ * // If CACHE_TTL=2h30m
641
+ * const cacheTtlMs = Env.getDuration('CACHE_TTL', '1h');
642
+ * // 9000000 (2.5 hours in milliseconds)
366
643
  */
367
644
  Env.getDuration = function (key, defaultValue) {
368
645
  if (defaultValue === void 0) { defaultValue = '0'; }
646
+ var cacheKey = "duration:".concat(key);
647
+ if (this.cache.has(cacheKey)) {
648
+ return this.cache.get(cacheKey);
649
+ }
369
650
  var value = Env.get(key, String(defaultValue));
370
651
  if (!value)
371
652
  return 0;
372
653
  // Handle plain number input (assume milliseconds)
373
654
  if (/^\d+$/.test(value)) {
374
- return parseInt(value, 10);
655
+ var ms_1 = parseInt(value, 10);
656
+ this.cache.set(cacheKey, ms_1);
657
+ return ms_1;
375
658
  }
376
- // Parse duration string like "1d2h30m15s"
377
- var durationRegex = /(\d+d)?(\d+h)?(\d+m)?(\d+s)?(\d+ms)?/;
659
+ // Enhanced duration parsing with more precise regex
660
+ var durationRegex = /^(?:(\d+)y)?(?:(\d+)w)?(?:(\d+)d)?(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?(?:(\d+)ms)?$/;
378
661
  var matches = value.match(durationRegex);
379
662
  if (!matches || matches[0] === '') {
380
- throw new Error("Env ".concat(key, " has invalid duration format. Use 1d, 2h, 30m, 45s, or 100ms."));
663
+ throw new Error("Environment variable '".concat(key, "' has invalid duration format: \"").concat(value, "\". ") +
664
+ "Use formats like 1y, 2w, 3d, 4h, 5m, 6s, or 7ms.");
381
665
  }
382
666
  var ms = 0;
383
667
  if (matches[1])
384
- ms += parseInt(matches[1], 10) * 86400000; // days
668
+ ms += parseInt(matches[1], 10) * 31536000000; // years (approximate)
385
669
  if (matches[2])
386
- ms += parseInt(matches[2], 10) * 3600000; // hours
670
+ ms += parseInt(matches[2], 10) * 604800000; // weeks
387
671
  if (matches[3])
388
- ms += parseInt(matches[3], 10) * 60000; // minutes
672
+ ms += parseInt(matches[3], 10) * 86400000; // days
389
673
  if (matches[4])
390
- ms += parseInt(matches[4], 10) * 1000; // seconds
674
+ ms += parseInt(matches[4], 10) * 3600000; // hours
391
675
  if (matches[5])
392
- ms += parseInt(matches[5], 10); // milliseconds
676
+ ms += parseInt(matches[5], 10) * 60000; // minutes
677
+ if (matches[6])
678
+ ms += parseInt(matches[6], 10) * 1000; // seconds
679
+ if (matches[7])
680
+ ms += parseInt(matches[7], 10); // milliseconds
681
+ this.cache.set(cacheKey, ms);
393
682
  return ms;
394
683
  };
395
684
  /**
@@ -397,9 +686,13 @@ var Env = /** @class */ (function () {
397
686
  *
398
687
  * @param {string[]} [sensitiveKeys=['password', 'secret', 'key', 'token', 'auth']] - Keys to mask.
399
688
  * @returns {Record<string, string>} Environment variables with sensitive values masked.
689
+ *
690
+ * @example
691
+ * console.log(Env.getSafeEnv(['password', 'secret', 'key']));
692
+ * // { DATABASE_URL: "postgres://...", API_KEY: "******", ... }
400
693
  */
401
694
  Env.getSafeEnv = function (sensitiveKeys) {
402
- var e_1, _a;
695
+ var e_2, _a;
403
696
  if (sensitiveKeys === void 0) { sensitiveKeys = ['password', 'secret', 'key', 'token', 'auth']; }
404
697
  var safeEnv = {};
405
698
  var _loop_1 = function (key, value) {
@@ -414,15 +707,98 @@ var Env = /** @class */ (function () {
414
707
  _loop_1(key, value);
415
708
  }
416
709
  }
417
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
710
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
418
711
  finally {
419
712
  try {
420
713
  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
421
714
  }
422
- finally { if (e_1) throw e_1.error; }
715
+ finally { if (e_2) throw e_2.error; }
423
716
  }
424
717
  return safeEnv;
425
718
  };
719
+ /**
720
+ * Loads environment variables from a .env file.
721
+ * Does not override existing variables.
722
+ *
723
+ * @param {string} [path='.env'] - Path to the .env file.
724
+ * @returns {Record<string, string>} Loaded environment variables.
725
+ *
726
+ * @example
727
+ * // Load variables from .env.development
728
+ * Env.loadFromFile('.env.development');
729
+ */
730
+ Env.loadFromFile = function (path) {
731
+ if (path === void 0) { path = '.env'; }
732
+ if (!existsSync(path)) {
733
+ throw new Error("Environment file not found: \"".concat(path, "\""));
734
+ }
735
+ var content = readFileSync(path, 'utf8');
736
+ var variables = {};
737
+ var lines = content.split(/\r?\n/);
738
+ var i = 0;
739
+ while (i < lines.length) {
740
+ var line = lines[i].trim();
741
+ // Skip empty lines or comment-only lines
742
+ if (!line || line.startsWith('#')) {
743
+ i++;
744
+ continue;
745
+ }
746
+ var match = line.match(/^([^=]+)=(.*)$/);
747
+ if (!match) {
748
+ i++;
749
+ continue;
750
+ }
751
+ var key = match[1].trim();
752
+ var value = match[2].trim();
753
+ // Remove inline comment for unquoted values
754
+ if (!/^['"]/.test(value)) {
755
+ var hashIndex = value.indexOf(' #');
756
+ if (hashIndex !== -1) {
757
+ value = value.slice(0, hashIndex).trim();
758
+ }
759
+ }
760
+ // Handle quoted values
761
+ if ((value.startsWith('"') || value.startsWith("'")) && value.length > 1) {
762
+ var quote = value[0];
763
+ if (!value.endsWith(quote) || value.length === 1) {
764
+ // multiline quoted
765
+ var multilineValue = value.slice(1);
766
+ i++;
767
+ while (i < lines.length) {
768
+ var nextLine = lines[i];
769
+ if (nextLine.endsWith(quote)) {
770
+ multilineValue += '\n' + nextLine.slice(0, -1);
771
+ break;
772
+ }
773
+ else {
774
+ multilineValue += '\n' + nextLine;
775
+ }
776
+ i++;
777
+ }
778
+ value = multilineValue;
779
+ }
780
+ else {
781
+ // single-line quoted
782
+ value = value.slice(1, -1);
783
+ }
784
+ }
785
+ else {
786
+ // Handle unquoted multiline: continue collecting until next key=value or empty line
787
+ i++;
788
+ while (i < lines.length && !lines[i].includes('=') && lines[i].trim() !== '') {
789
+ value += '\n' + lines[i];
790
+ i++;
791
+ }
792
+ i--; // adjust for outer loop
793
+ }
794
+ if (!process.env[key]) {
795
+ process.env[key] = value;
796
+ variables[key] = value;
797
+ }
798
+ i++;
799
+ }
800
+ return variables;
801
+ };
426
802
  /**
427
803
  * Checks whether the specified environment variable exists.
428
804
  *
@@ -439,8 +815,35 @@ var Env = /** @class */ (function () {
439
815
  * @returns {void}
440
816
  */
441
817
  Env.delete = function (key) {
818
+ var e_3, _a;
442
819
  delete process.env[key];
820
+ this.cache.delete(key);
821
+ try {
822
+ // Also clear all cached variations of this key
823
+ for (var _b = __values(this.cache.keys()), _c = _b.next(); !_c.done; _c = _b.next()) {
824
+ var cacheKey = _c.value;
825
+ if (cacheKey.includes(":".concat(key))) {
826
+ this.cache.delete(cacheKey);
827
+ }
828
+ }
829
+ }
830
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
831
+ finally {
832
+ try {
833
+ if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
834
+ }
835
+ finally { if (e_3) throw e_3.error; }
836
+ }
837
+ };
838
+ /**
839
+ * Clears the internal cache of parsed environment values.
840
+ * Useful for testing or when environment variables might change.
841
+ */
842
+ Env.clearCache = function () {
843
+ this.cache.clear();
443
844
  };
845
+ // Cache for parsed values to avoid repeated parsing of complex values
846
+ Env.cache = new Map();
444
847
  return Env;
445
848
  }());
446
849
  export { Env };