envapt 2.1.1 → 2.2.1

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
@@ -3,8 +3,8 @@
3
3
  </p>
4
4
 
5
5
  <p align="center">
6
- A TypeScript environment configuration library that eliminates the boilerplate of transforming parsed <code>.env</code><br/>
7
- Get environment variables with correct runtime typing and fallbacks, template support, and automatic, built-in, & custom transformations.<br/>
6
+ A JavaScript/TypeScript environment configuration library that eliminates the boilerplate of transforming parsed <code>.env</code><br/>
7
+ Get environment variables with correct runtime typing and fallbacks, template support, and automatic, built-in, & custom transformations, and tagged template resolver.<br/>
8
8
  <strong>No more <code>process.env.PORT || '3000'</code> everywhere!</strong>
9
9
  </p>
10
10
  <div align="center">
@@ -27,12 +27,13 @@
27
27
 
28
28
  - 🔧 **Automatic Type Detection** - Runtime types inferred from fallback values
29
29
  - 🔗 **Template Variables** - `${VAR}` syntax with circular reference protection
30
- - 🎯 **Class Properties** - Functional and Decorator-based configuration for class members
30
+ - 🎯 **Class Properties** - Functional and Decorator-based configuration for class members _(Decorators: TypeScript only)_
31
31
  - 🏷️ **Built-in & Custom Converters** - Ready-to-use converters for common patterns + custom transformations
32
+ - 🔖 **Tagged Template Resolver** - Tagged template literals with environment variable resolution
32
33
  - 🌍 **Environment Detection** - Built-in development/staging/production handling
33
- - 📂 **Multiple .env Files** - Load from multiple sources
34
34
  - 💪 **Edge Case Handling** - Robust validation and parsing for all scenarios
35
- - 🛡️ **Type Safety** - Full TypeScript support with proper type inference
35
+ - 🛡️ **Type Safety** - Full TypeScript support with proper type inference _(TypeScript optional)_
36
+ - 📂 **Multiple .env Files** - Load from multiple sources
36
37
  - ⚡ **Lightweight** - Minimal overhead with [`dotenv`](https://www.npmjs.com/package/dotenv) bundled
37
38
 
38
39
  ---
@@ -59,6 +60,7 @@
59
60
  - [Custom Converters](#custom-converters)
60
61
  - [Handling Missing Values](#handling-missing-values)
61
62
  - [Functional API](#functional-api)
63
+ - [Tagged Template Resolver](#tagged-template-resolver)
62
64
  - [Converter Type Quick Reference](#converter-type-quick-reference)
63
65
 
64
66
  ### 🌍 Environment & Templates
@@ -79,7 +81,8 @@
79
81
  ### 🚀 Examples
80
82
 
81
83
  - [Advanced Examples](#advanced-examples)
82
- - [Complex Configuration](#complex-configuration)
84
+ - [JavaScript](#javascript)
85
+ - [TypeScript](#typescript)
83
86
 
84
87
  ---
85
88
 
@@ -90,16 +93,16 @@
90
93
  - **Node.js**: `>=22.0.0`
91
94
  _Recommended for full ESM and `nodenext` support_
92
95
 
93
- - **TypeScript**: `>=5.8`
94
-
95
96
  #### 📦 Runtime Dependency
96
97
 
97
98
  - **dotenv**: _(bundled at runtime)_
98
99
 
99
- #### 🛠️ TypeScript Compiler Options
100
+ #### 🛠️ TypeScript Users Only
101
+
102
+ - **TypeScript**: `>=5.8` _(Only required for decorator API)_
100
103
 
101
104
  ```jsonc
102
- // tsconfig.json (required settings)
105
+ // tsconfig.json (required settings for decorators)
103
106
  {
104
107
  "experimentalDecorators": true,
105
108
  "module": "esnext", // or "nodenext"
@@ -109,6 +112,9 @@
109
112
  }
110
113
  ```
111
114
 
115
+ > [!NOTE]
116
+ > **JavaScript users** can use all features except the `@Envapt` decorator API. The [Functional API](#functional-api), [Tagged Template Resolver](#tagged-template-resolver), and all converters work perfectly in plain JavaScript.
117
+
112
118
  ## Quick Start
113
119
 
114
120
  ### Installation
@@ -132,7 +138,29 @@ MAX_CONNECTIONS=100
132
138
  ALLOWED_ORIGINS=https://app.com,https://admin.com
133
139
  ```
134
140
 
135
- Use with decorators:
141
+ **JavaScript Example (Functional API):**
142
+
143
+ ```js
144
+ import { Envapter, Converters } from 'envapt';
145
+
146
+ // Basic usage
147
+ const port = Envapter.getNumber('APP_PORT', 3000);
148
+ const url = Envapter.get('APP_URL', 'http://localhost:3000');
149
+ const isProduction = Envapter.isProduction;
150
+
151
+ console.log(`Server running on port ${port}`); // 8443
152
+ console.log(`URL: ${url}`); // "http://localhost:8443"
153
+
154
+ // Advanced converters
155
+ const corsOrigins = Envapter.getUsing('ALLOWED_ORIGINS', Converters.Array, []);
156
+ const dbConfig = Envapter.getUsing('DATABASE_CONFIG', Converters.Json, {});
157
+
158
+ // Tagged template literals
159
+ const message = Envapter.resolve`Server ${'APP_URL'} is ready!`;
160
+ console.log(message); // "Server http://localhost:8443 is ready!"
161
+ ```
162
+
163
+ **TypeScript Example (Decorator API):**
136
164
 
137
165
  ```ts
138
166
  import { Envapt, Envapter, Converters } from 'envapt';
@@ -171,30 +199,23 @@ class DatabaseService {
171
199
 
172
200
  // Usage
173
201
  console.log(AppConfig.port); // 8443 (number)
174
- console.log(AppConfig.url.href); // "http://localhost:8443" (templated!)
202
+ console.log(AppConfig.url.href); // "http://localhost:8443"
175
203
 
176
204
  const dbService = new DatabaseService();
177
205
  await dbService.connect();
178
206
  ```
179
207
 
180
- Or use functionally:
181
-
182
- ```ts
183
- import { Envapter } from 'envapt';
184
-
185
- const port = Envapter.getNumber('APP_PORT', 3000);
186
- const url = Envapter.get('APP_URL', 'http://localhost:3000');
187
- const isProduction = Envapter.getBoolean('IS_PRODUCTION', false);
188
- ```
189
-
190
208
  ## API Reference
191
209
 
192
210
  ### Decorator API
193
211
 
212
+ > [!IMPORTANT]
213
+ > **TypeScript Only**: The `@Envapt` decorator API requires TypeScript with `experimentalDecorators: true`. JavaScript users should use the [Functional API](#functional-api) instead.
214
+
194
215
  The `@Envapt` decorator can be used on both **static** and **instance** class properties:
195
216
 
196
- - **Static properties**: Use for global configuration that's shared across your entire application (e.g., app port, global features, environment settings)
197
- - **Instance properties**: Use for service-specific configuration that may vary per service or when you want the configuration tied to a specific class instance (e.g., database connections, service endpoints, per-service settings)
217
+ - **Static properties**: Can use for global configuration that's shared across your entire application (e.g., app port, global features, environment settings)
218
+ - **Instance properties**: Can use for service-specific configuration that may vary per service or when you want the configuration tied to a specific class instance (e.g., database connections, service endpoints, per-service settings)
198
219
 
199
220
  **Important**: Instance properties must be declared with `declare` keyword or `!` assertion since they're populated by the decorator rather than set in a constructor.
200
221
 
@@ -471,9 +492,9 @@ class Config extends Envapter {
471
492
 
472
493
  ### Functional API
473
494
 
474
- For functional-style environment variable on primitive types:
495
+ For functional-style environment variable access on primitive types:
475
496
 
476
- ```ts
497
+ ```js
477
498
  import { Envapter, Converters } from 'envapt';
478
499
 
479
500
  // Basic type-specific getters
@@ -496,12 +517,14 @@ const processed = envapter.getUsing('DATA', Converters.Array);
496
517
 
497
518
  For functional-style environment variable access with converters:
498
519
 
499
- ```ts
520
+ ```js
500
521
  import { Envapter, Converters } from 'envapt';
501
522
 
502
523
  // Use built-in converters directly
503
524
  const config = Envapter.getUsing('API_CONFIG', Converters.Json, { default: 'value' });
504
525
  const urls = Envapter.getUsing('SERVICE_URLS', { delimiter: '|', type: Converters.Url });
526
+
527
+ // TypeScript: Use type override for better type inference
505
528
  const typedConfig = Envapter.getUsing<{ host: string; port: number; ssl: boolean }>('DATABASE_CONFIG', Converters.Json);
506
529
  // typedConfig is now typed as { host: string; port: number; ssl: boolean } instead of JsonValue | undefined
507
530
 
@@ -557,6 +580,50 @@ const result = envapter.getUsing('DATABASE_CONFIG', Converters.Json);
557
580
  > [!TIP]
558
581
  > **Use the `Converters` enum**. They look better. Start with built-in converters, use primitive constructors when you need coercion, and custom converters for complex transforms.
559
582
 
583
+ ### Tagged Template Resolver
584
+
585
+ Envapt provides a convenient tagged template literal syntax for resolving environment variables directly in template strings:
586
+
587
+ ```js
588
+ import { Envapter } from 'envapt';
589
+
590
+ // Given these environment variables:
591
+ // API_HOST=api.example.com
592
+ // API_PORT=8080
593
+ // API_URL=https://${API_HOST}:${API_PORT}
594
+ // SERVICE_NAME=UserService
595
+
596
+ // Use tagged template literals for string interpolation
597
+ const endpoint = Envapter.resolve`Connecting to ${'SERVICE_NAME'} at ${'API_URL'}`;
598
+ // Returns: "Connecting to UserService at https://api.example.com:8080"
599
+
600
+ const logMessage = Envapter.resolve`Starting ${'SERVICE_NAME'} on port ${'API_PORT'}`;
601
+ // Returns: "Starting UserService on port 8080"
602
+
603
+ // Works with instance methods too
604
+ const envapter = new Envapter();
605
+ const status = envapter.resolve`${'SERVICE_NAME'} is running`;
606
+ // Returns: "UserService is running"
607
+ ```
608
+
609
+ Works seamlessly with template variables in your `.env` file:
610
+
611
+ ```env
612
+ # Your .env file
613
+ API_HOST=api.example.com
614
+ API_PORT=8080
615
+ API_URL=https://${API_HOST}:${API_PORT} # Template resolved first
616
+ SERVICE_NAME=UserService
617
+ ```
618
+
619
+ ```ts
620
+ const message = Envapter.resolve`Service ${'SERVICE_NAME'} endpoint: ${'API_URL'}`;
621
+ // Returns: "Service UserService endpoint: https://api.example.com:8080"
622
+ ```
623
+
624
+ > [!NOTE]
625
+ > Tagged template literals work with any environment variables, including those that use `${VAR}` template syntax in your `.env` file. The template resolution happens first, then the tagged template interpolation.
626
+
560
627
  ## Environment Detection
561
628
 
562
629
  Envapt automatically detects your environment from these variables (in order):
@@ -569,7 +636,7 @@ Supported values: `development`, `staging`, `production` (case-sensitive)
569
636
 
570
637
  ### Environment Management
571
638
 
572
- ```ts
639
+ ```js
573
640
  import { Envapter, EnvaptEnvironment } from 'envapt';
574
641
 
575
642
  // Check current environment
@@ -587,7 +654,7 @@ Envapter.environment = 'staging'; // string also works
587
654
 
588
655
  ### Multiple .env Files
589
656
 
590
- ```ts
657
+ ```js
591
658
  import { resolve } from 'node:path';
592
659
  import { Envapter } from 'envapt';
593
660
 
@@ -604,7 +671,7 @@ Envapter.envPaths = resolve(__dirname, '.env.production');
604
671
 
605
672
  Envapt allows you to customize dotenv behavior by setting configuration options:
606
673
 
607
- ```ts
674
+ ```js
608
675
  import { Envapter } from 'envapt';
609
676
 
610
677
  // Set dotenv configuration options
@@ -650,7 +717,7 @@ Circular references are detected and preserved as-is rather than causing infinit
650
717
 
651
718
  Envapt provides detailed error codes for better debugging and error handling:
652
719
 
653
- ```ts
720
+ ```js
654
721
  import { EnvaptError, EnvaptErrorCodes } from 'envapt';
655
722
 
656
723
  try {
@@ -708,7 +775,46 @@ try {
708
775
 
709
776
  ## Advanced Examples
710
777
 
711
- ### Complex Configuration
778
+ ### JavaScript
779
+
780
+ ```js
781
+ import { Envapter, Converters } from 'envapt';
782
+
783
+ // Global configuration
784
+ const config = {
785
+ port: Envapter.getNumber('PORT', 3000),
786
+ requestTimeout: Envapter.getUsing('REQUEST_TIMEOUT', Converters.Time, 10000), // "5s" -> 5000ms
787
+ featureFlags: Envapter.getWith(
788
+ 'FEATURE_FLAGS',
789
+ (raw, fallback) => {
790
+ if (!raw) return fallback;
791
+ return new Set(raw.split(',').map((s) => s.trim()));
792
+ },
793
+ new Set(['basic'])
794
+ )
795
+ };
796
+
797
+ // Service configuration
798
+ class DatabaseService {
799
+ constructor() {
800
+ this.databaseUrl = Envapter.get('DB_URL', 'sqlite://memory');
801
+ this.cacheTtl = Envapter.getUsing('CACHE_TTL', Converters.Time, 3600000); // "1h" -> 3600000ms
802
+ this.redisUrls = Envapter.getWith(
803
+ 'REDIS_URLS',
804
+ (raw, fallback) => (raw ? raw.split(',').map((s) => new URL(s)) : fallback),
805
+ [new URL('redis://localhost:6379')]
806
+ );
807
+ }
808
+
809
+ async initialize() {
810
+ console.log(`App running on port ${config.port}`);
811
+ console.log(`Database: ${this.databaseUrl}`);
812
+ console.log(`Cache TTL: ${this.cacheTtl}ms`);
813
+ }
814
+ }
815
+ ```
816
+
817
+ ### TypeScript
712
818
 
713
819
  ```ts
714
820
  import { Envapt, Envapter, Converters } from 'envapt';
package/dist/index.cjs CHANGED
@@ -788,17 +788,46 @@ var AdvancedMethods = class _AdvancedMethods extends PrimitiveMethods {
788
788
  };
789
789
 
790
790
  // src/Envapter.ts
791
- var Envapter = class extends AdvancedMethods {
791
+ var Envapter = class _Envapter extends AdvancedMethods {
792
792
  static {
793
793
  __name(this, "Envapter");
794
794
  }
795
+ /**
796
+ * Tagged template literal for resolving environment variables in template strings.
797
+ *
798
+ * @example
799
+ * ```ts
800
+ * // Given API_HOST=api.example.com and API_PORT=8080 in environment
801
+ * const endpoint = Envapter.resolve`Connecting to ${'API_HOST'}:${'API_PORT'}`;
802
+ * // Returns: "Connecting to api.example.com:8080"
803
+ *
804
+ * // Works with template variables in .env too:
805
+ * // API_URL=https://${API_HOST}:${API_PORT}
806
+ * const message = Envapter.resolve`Service endpoint: ${'API_URL'}`;
807
+ * // Returns: "Service endpoint: https://api.example.com:8080"
808
+ * ```
809
+ */
810
+ static resolve(strings, ...keys) {
811
+ return strings.reduce((result, string, i) => {
812
+ const envKey = keys[i];
813
+ const envValue = envKey ? super.get(envKey, "") : "";
814
+ return result + string + envValue;
815
+ }, "");
816
+ }
817
+ /**
818
+ * @see {@link Envapter.resolve}
819
+ */
820
+ resolve(strings, ...keys) {
821
+ return _Envapter.resolve(strings, ...keys);
822
+ }
795
823
  };
796
824
 
797
825
  // src/Envapt.ts
798
826
  function createPropertyDecorator(key, fallback, converter, hasFallback) {
799
827
  return function(target, prop) {
800
828
  const propKey = String(prop);
801
- const cacheKey = `${target.constructor.name}.${propKey}`;
829
+ const className = typeof target === "function" ? target.name : target.constructor.name;
830
+ const cacheKey = `${className}.${propKey}`;
802
831
  Object.defineProperty(target, propKey, {
803
832
  get: /* @__PURE__ */ __name(function() {
804
833
  let value = EnvaptCache.get(cacheKey);
@@ -853,6 +882,7 @@ var Converters = /* @__PURE__ */ ((Converters2) => {
853
882
 
854
883
  exports.Converters = Converters;
855
884
  exports.Envapt = Envapt;
885
+ exports.EnvaptError = EnvaptError;
856
886
  exports.EnvaptErrorCodes = EnvaptErrorCodes;
857
887
  exports.Envapter = Envapter;
858
888
  exports.Environment = Environment;