envapt 4.0.2 → 4.1.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/README.md CHANGED
@@ -34,6 +34,7 @@
34
34
  - 🔖 **Tagged Template Resolver** - Tagged template literals with environment variable resolution
35
35
  - 🌍 **Environment Detection** - Built-in development/staging/production handling
36
36
  - 💪 **Edge Case Handling** - Robust validation and parsing for all scenarios
37
+ - 🔑 **Multi-Key Lookups** - Provide ordered lists of env keys and Envapt will use the first value it finds
37
38
  - 🛡️ **Type Safety** - Full TypeScript support with proper type inference _(TypeScript optional)_
38
39
  - 📂 **Multiple .env Files** - Load from multiple sources
39
40
  - ⚡ **Lightweight** - Minimal overhead with [`dotenv`](https://www.npmjs.com/package/dotenv) bundled
@@ -163,6 +164,10 @@ console.log(`URL: ${url}`); // "http://localhost:8443"
163
164
  const corsOrigins = Envapter.getUsing('ALLOWED_ORIGINS', Converters.Array, []);
164
165
  const dbConfig = Envapter.getUsing('DATABASE_CONFIG', Converters.Json, {});
165
166
 
167
+ // Multi-key lookup: try primary, then replica, then fall back
168
+ const dbUrl = Envapter.get(['PRIMARY_DB_URL', 'REPLICA_DB_URL'], 'sqlite://memory');
169
+ const httpPort = Envapter.getNumber(['APP_PORT', 'PORT'], 3000);
170
+
166
171
  // Tagged template literals
167
172
  const message = Envapter.resolve`Server ${'APP_URL'} is ready!`;
168
173
  console.log(message); // "Server http://localhost:8443 is ready!"
@@ -182,6 +187,10 @@ class AppConfig extends Envapter {
182
187
  @Envapt('APP_URL', { fallback: new URL('http://localhost:3000'), converter: Converters.Url })
183
188
  static readonly url: URL;
184
189
 
190
+ // Prefer CLOUD_REDIS_URL but fall back to classic REDIS_URL when missing
191
+ @Envapt(['CLOUD_REDIS_URL', 'REDIS_URL'], 'redis://localhost:6379')
192
+ static readonly redisUrl: string;
193
+
185
194
  @Envapt('ALLOWED_ORIGINS', {
186
195
  fallback: ['http://localhost:3000'],
187
196
  converter: Converters.Array
@@ -241,6 +250,11 @@ The `@Envapt` decorator can be used on both **static** and **instance** class pr
241
250
  @Envapt('ENV_VAR', { fallback?: T, converter?: EnvConverter<T> })
242
251
  ```
243
252
 
253
+ > [!NOTE]
254
+ > The first argument can be a string **or** an ordered array of strings:
255
+ > `@Envapt(['PRIMARY_URL', 'SECONDARY_URL'], { fallback: 'https://example.com' })`.
256
+ > Envapt will resolve the first key that exists.
257
+ >
244
258
  > [!TIP]
245
259
  > **Generic Typing for Better IntelliSense**
246
260
  >
@@ -261,6 +275,13 @@ The `@Envapt` decorator can be used on both **static** and **instance** class pr
261
275
  @Envapt('ENV_VAR', fallback?, converter?)
262
276
  ```
263
277
 
278
+ Need to chain multiple keys with the classic API? Pass an array instead of a string:
279
+
280
+ ```ts
281
+ @Envapt(['HOST_PRIMARY', 'HOST_SECONDARY'], 'localhost')
282
+ static readonly host: string;
283
+ ```
284
+
264
285
  #### Automatic Runtime Type Detection
265
286
 
266
287
  Types are automatically inferred from fallback values.
@@ -557,6 +578,9 @@ const jsonData = Envapter.getUsing('CONFIG_JSON', Converters.Json);
557
578
  const urlArray = Envapter.getUsing('API_URLS', { delimiter: ',', type: Converters.Url });
558
579
  const customData = Envapter.getWith('RAW_DATA', (raw) => raw?.split('|').map((s) => s.trim()));
559
580
 
581
+ // Multi-key inputs work everywhere: Envapt will read left-to-right
582
+ const secretsHost = Envapter.get(['SECRETS_HOST', 'DEFAULT_HOST'], 'localhost');
583
+
560
584
  // Instance methods (same API available)
561
585
  const envapter = new Envapter();
562
586
  const value = envapter.get('VAR', 'default');
@@ -571,6 +595,7 @@ import { Envapter, Converters } from 'envapt';
571
595
  // Use built-in converters directly
572
596
  const config = Envapter.getUsing('API_CONFIG', Converters.Json, { default: 'value' });
573
597
  const urls = Envapter.getUsing('SERVICE_URLS', { delimiter: '|', type: Converters.Url });
598
+ const pgUrl = Envapter.getUsing(['PRIMARY_PG_URL', 'SECONDARY_PG_URL'], Converters.Url);
574
599
 
575
600
  // TypeScript: Use type override for better type inference
576
601
  const typedConfig = Envapter.getUsing<{ host: string; port: number; ssl: boolean }>('DATABASE_CONFIG', Converters.Json);
@@ -846,6 +871,7 @@ try {
846
871
  | `MissingDelimiter` (301) | Delimiter is missing in array converter config |
847
872
  | `InvalidUserDefinedConfig` (302) | Invalid user-defined configuration provided |
848
873
  | `EnvFilesNotFound` (303) | Specified environment file doesn't exist |
874
+ | `InvalidKeyInput` (304) | Invalid key input (not string or string array) |
849
875
 
850
876
  <div align="right">
851
877
 
package/dist/index.cjs CHANGED
@@ -26,6 +26,7 @@ var EnvaptErrorCodes = /* @__PURE__ */ ((EnvaptErrorCodes2) => {
26
26
  EnvaptErrorCodes2[EnvaptErrorCodes2["MissingDelimiter"] = 301] = "MissingDelimiter";
27
27
  EnvaptErrorCodes2[EnvaptErrorCodes2["InvalidUserDefinedConfig"] = 302] = "InvalidUserDefinedConfig";
28
28
  EnvaptErrorCodes2[EnvaptErrorCodes2["EnvFilesNotFound"] = 303] = "EnvFilesNotFound";
29
+ EnvaptErrorCodes2[EnvaptErrorCodes2["InvalidKeyInput"] = 304] = "InvalidKeyInput";
29
30
  return EnvaptErrorCodes2;
30
31
  })(EnvaptErrorCodes || {});
31
32
  var EnvaptError = class extends Error {
@@ -317,6 +318,26 @@ var EnvapterBase = class _EnvapterBase {
317
318
  EnvaptCache.clear();
318
319
  void this.config;
319
320
  }
321
+ static resolveKeyInput(keyInput) {
322
+ const keys = Array.isArray(keyInput) ? keyInput : [keyInput];
323
+ const normalizedKeys = keys;
324
+ if (normalizedKeys.length === 0) {
325
+ throw new EnvaptError(304 /* InvalidKeyInput */, "At least one environment key must be provided.");
326
+ }
327
+ if (normalizedKeys.some((k) => typeof k !== "string")) {
328
+ throw new EnvaptError(304 /* InvalidKeyInput */, "Environment keys must be strings.");
329
+ }
330
+ if (normalizedKeys.some((k) => k.trim() === "")) {
331
+ throw new EnvaptError(304 /* InvalidKeyInput */, "Environment keys cannot be empty strings.");
332
+ }
333
+ for (const candidate of normalizedKeys) {
334
+ const value = this.config.get(candidate);
335
+ if (value !== void 0) {
336
+ return { key: candidate, value };
337
+ }
338
+ }
339
+ return { key: normalizedKeys[0], value: void 0 };
340
+ }
320
341
  static get config() {
321
342
  if (EnvaptCache.size === 0) {
322
343
  const isolatedEnv = { ...process__default.default.env };
@@ -332,7 +353,7 @@ var EnvapterBase = class _EnvapterBase {
332
353
  * Get raw environment variable value without parsing or conversion.
333
354
  */
334
355
  getRaw(key) {
335
- return _EnvapterBase.config.get(key);
356
+ return _EnvapterBase.resolveKeyInput(key).value;
336
357
  }
337
358
  };
338
359
 
@@ -680,9 +701,10 @@ var PrimitiveMethods = class _PrimitiveMethods extends EnvironmentMethods {
680
701
  }
681
702
  static parser = new Parser(new _PrimitiveMethods());
682
703
  static _get(key, type, def) {
683
- const rawVal = this.config.get(key);
704
+ const { key: resolvedKey, value } = this.resolveKeyInput(key);
705
+ const rawVal = value;
684
706
  if (!rawVal) return def;
685
- const parsed = this.parser.resolveTemplate(key, String(rawVal));
707
+ const parsed = this.parser.resolveTemplate(resolvedKey, String(rawVal));
686
708
  let result;
687
709
  if (type === 1 /* Number */) result = BuiltInConverters.number(parsed, def);
688
710
  else if (type === 2 /* Boolean */)
@@ -696,20 +718,19 @@ var PrimitiveMethods = class _PrimitiveMethods extends EnvironmentMethods {
696
718
  }
697
719
  /**
698
720
  * Get a string environment variable with optional fallback.
699
- * Supports template variable resolution using $\{VAR\} syntax.
721
+ * Supports template variable resolution using `${VAR}` syntax.
722
+ * Accepts a single key or an ordered array of keys (first match wins).
700
723
  */
701
724
  static get(key, def) {
702
725
  return this._get(key, 0 /* String */, def);
703
726
  }
704
- /**
705
- * @see {@link PrimitiveMethods.get}
706
- */
707
727
  get(key, def) {
708
728
  return _PrimitiveMethods._get(key, 0 /* String */, def);
709
729
  }
710
730
  /**
711
731
  * Get a number environment variable with optional fallback.
712
732
  * Automatically converts string values to numbers.
733
+ * Accepts a single key or an ordered array of keys (first match wins).
713
734
  */
714
735
  static getNumber(key, def) {
715
736
  return this._get(key, 1 /* Number */, def);
@@ -723,6 +744,7 @@ var PrimitiveMethods = class _PrimitiveMethods extends EnvironmentMethods {
723
744
  /**
724
745
  * Get a boolean environment variable with optional fallback.
725
746
  * Recognizes: `1`, `yes`, `true`, 'on' as **true**; `0`, `no`, `false`, 'off' as **false** (case-insensitive).
747
+ * Accepts a single key or an ordered array of keys (first match wins).
726
748
  */
727
749
  static getBoolean(key, def) {
728
750
  return this._get(key, 2 /* Boolean */, def);
@@ -736,6 +758,7 @@ var PrimitiveMethods = class _PrimitiveMethods extends EnvironmentMethods {
736
758
  /**
737
759
  * Get a bigint environment variable with optional fallback.
738
760
  * Automatically converts string values to bigint.
761
+ * Accepts a single key or an ordered array of keys (first match wins).
739
762
  */
740
763
  static getBigInt(key, def) {
741
764
  return this._get(key, 3 /* BigInt */, def);
@@ -749,6 +772,7 @@ var PrimitiveMethods = class _PrimitiveMethods extends EnvironmentMethods {
749
772
  /**
750
773
  * Get a symbol environment variable with optional fallback.
751
774
  * Creates a symbol from the string value.
775
+ * Accepts a single key or an ordered array of keys (first match wins).
752
776
  */
753
777
  static getSymbol(key, def) {
754
778
  return this._get(key, 4 /* Symbol */, def);
@@ -767,10 +791,10 @@ var AdvancedMethods = class _AdvancedMethods extends PrimitiveMethods {
767
791
  __name(this, "AdvancedMethods");
768
792
  }
769
793
  static getUsing(key, converter, fallback) {
770
- const rawVal = this.config.get(key);
771
- if (!rawVal) return fallback;
794
+ const { key: resolvedKey, value } = this.resolveKeyInput(key);
795
+ if (!value) return fallback;
772
796
  const hasFallback = fallback !== void 0;
773
- const result = this.parser.convertValue(key, fallback, converter, hasFallback);
797
+ const result = this.parser.convertValue(resolvedKey, fallback, converter, hasFallback);
774
798
  return result;
775
799
  }
776
800
  getUsing(key, converter, fallback) {
@@ -778,13 +802,14 @@ var AdvancedMethods = class _AdvancedMethods extends PrimitiveMethods {
778
802
  }
779
803
  /**
780
804
  * Get an environment variable using a custom converter function.
805
+ * Accepts a single key or an ordered list for automatic fallback.
781
806
  */
782
807
  static getWith(key, converter, fallback) {
783
- const rawVal = this.config.get(key);
784
- if (!rawVal) return fallback;
808
+ const { key: resolvedKey, value } = this.resolveKeyInput(key);
809
+ if (!value) return fallback;
785
810
  const hasFallback = fallback !== void 0;
786
811
  const result = this.parser.convertValue(
787
- key,
812
+ resolvedKey,
788
813
  fallback,
789
814
  converter,
790
815
  hasFallback