envapt 1.1.0 โ 2.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 +404 -77
- package/dist/index.cjs +468 -233
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +381 -186
- package/dist/index.d.ts +381 -186
- package/dist/index.mjs +850 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +13 -6
- package/dist/index.js +0 -621
- package/dist/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,67 +1,127 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/banner.png" alt="Envapt โ The apt way to handle environment variables" width="100%" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
A TypeScript environment configuration library that eliminates the boilerplate of parsing <code>.env</code> files.<br/>
|
|
7
|
+
Get environment variables with correct runtime typing and fallbacks, template support, and automatic, built-in, & custom transformations.<br/>
|
|
8
|
+
<strong>No more <code>process.env.PORT || '3000'</code> everywhere!</strong>
|
|
9
|
+
</p>
|
|
10
|
+
<div align="center">
|
|
11
|
+
<img alt="Build" src="https://img.shields.io/github/actions/workflow/status/materwelonDhruv/envapt/publish.yaml?branch=main&label=build&style=flat&logo=github&color=3fb950"></a>
|
|
12
|
+
<a href="https://codecov.io/github/materwelonDhruv/envapt"><img src="https://codecov.io/github/materwelonDhruv/envapt/branch/main/graph/badge.svg?token=IQ4GC645LO"/></a>
|
|
13
|
+
<a href="https://www.npmjs.com/package/envapt"><img alt="npm version" src="https://img.shields.io/npm/v/envapt?style=flat&color=cb3838&logo=npm"></a>
|
|
14
|
+
<a href="LICENSE"><img alt="License" src="https://img.shields.io/npm/l/envapt?style=flat&color=e97826&logo=apache"></a>
|
|
15
|
+
<a href="https://www.typescriptlang.org/"><img alt="Types" src="https://img.shields.io/badge/types-TypeScript-3178c6?style=flat&logo=typescript&logoColor=white"></a>
|
|
16
|
+
<a href="https://nodejs.org/api/esm.html"><img alt="ESM" src="https://img.shields.io/badge/ESM-Supported-ffca28?style=flat"></a>
|
|
17
|
+
<a href="https://nodejs.org/api/modules.html"><img alt="CJS" src="https://img.shields.io/badge/CJS-Supported-ff6b35?style=flat"></a>
|
|
18
|
+
<a href="https://nodejs.org/"><img alt="Node" src="https://img.shields.io/badge/node-%3E=22.0.0-339933?style=flat&logo=node.js&logoColor=white"></a>
|
|
19
|
+
<a href="https://bundlephobia.com/package/envapt"><img alt="Bundle Size" src="https://img.shields.io/bundlephobia/minzip/envapt?style=flat&color=success"></a>
|
|
20
|
+
<a href="https://www.npmjs.com/package/envapt"><img alt="Downloads" src="https://img.shields.io/npm/dm/envapt?style=flat&color=blue"></a>
|
|
21
|
+
<a href="https://github.com/materwelondhruv/envapt"><img alt="GitHub Stars" src="https://img.shields.io/github/stars/materwelondhruv/envapt?style=flat&color=yellow"></a>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## โจ Features
|
|
27
|
+
|
|
28
|
+
- ๐ง **Automatic Type Detection** - Runtime types inferred from fallback values
|
|
8
29
|
- ๐ **Template Variables** - `${VAR}` syntax with circular reference protection
|
|
9
|
-
- ๐ฏ **Class Properties** - Decorator-based configuration for class members
|
|
30
|
+
- ๐ฏ **Class Properties** - Functional and Decorator-based configuration for class members
|
|
10
31
|
- ๐ท๏ธ **Built-in & Custom Converters** - Ready-to-use converters for common patterns + custom transformations
|
|
11
32
|
- ๐ **Environment Detection** - Built-in development/staging/production handling
|
|
12
33
|
- ๐ **Multiple .env Files** - Load from multiple sources
|
|
13
|
-
- ๐ช **Edge Case Handling** - Robust parsing for all scenarios
|
|
34
|
+
- ๐ช **Edge Case Handling** - Robust validation and parsing for all scenarios
|
|
14
35
|
- ๐ก๏ธ **Type Safety** - Full TypeScript support with proper type inference
|
|
36
|
+
- โก **Lightweight** - Minimal overhead with [`dotenv`](https://www.npmjs.com/package/dotenv) bundled
|
|
15
37
|
|
|
16
|
-
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## ๐ Table of Contents
|
|
41
|
+
|
|
42
|
+
### โ๏ธ Essentials
|
|
17
43
|
|
|
18
44
|
- [Requirements](#requirements)
|
|
19
45
|
- [Quick Start](#quick-start)
|
|
20
46
|
- [Installation](#installation)
|
|
21
47
|
- [Basic Usage](#basic-usage)
|
|
48
|
+
|
|
49
|
+
### ๐งฌ API Reference
|
|
50
|
+
|
|
22
51
|
- [API Reference](#api-reference)
|
|
23
52
|
- [Decorator API](#decorator-api)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
53
|
+
- [Modern Syntax (Recommended)](#modern-syntax-recommended)
|
|
54
|
+
- [Classic Syntax](#classic-syntax)
|
|
55
|
+
- [Automatic Runtime Type Detection](#automatic-runtime-type-detection)
|
|
56
|
+
- [Primitive Converters](#primitive-converters)
|
|
57
|
+
- [Built-in Converters](#built-in-converters)
|
|
58
|
+
- [Custom Array Converters](#custom-array-converters)
|
|
59
|
+
- [Custom Converters](#custom-converters)
|
|
60
|
+
- [Handling Missing Values](#handling-missing-values)
|
|
28
61
|
- [Functional API](#functional-api)
|
|
62
|
+
- [Converter Type Quick Reference](#converter-type-quick-reference)
|
|
63
|
+
|
|
64
|
+
### ๐ Environment & Templates
|
|
65
|
+
|
|
29
66
|
- [Environment Detection](#environment-detection)
|
|
67
|
+
- [Environment Management](#environment-management)
|
|
30
68
|
- [Template Variables](#template-variables)
|
|
69
|
+
- [Circular Reference Protection](#circular-reference-protection)
|
|
70
|
+
|
|
71
|
+
### ๐ Configuration & Errors
|
|
72
|
+
|
|
73
|
+
- [Configuration](#configuration)
|
|
74
|
+
- [Multiple .env Files](#multiple-env-files)
|
|
75
|
+
- [Dotenv Configuration](#dotenv-configuration)
|
|
76
|
+
- [Error Handling](#error-handling)
|
|
77
|
+
- [Error Code Reference](#error-code-reference)
|
|
78
|
+
|
|
79
|
+
### ๐ Examples
|
|
80
|
+
|
|
31
81
|
- [Advanced Examples](#advanced-examples)
|
|
82
|
+
- [Complex Configuration](#complex-configuration)
|
|
83
|
+
|
|
84
|
+
---
|
|
32
85
|
|
|
33
86
|
## Requirements
|
|
34
87
|
|
|
35
|
-
|
|
36
|
-
- **TypeScript**: v5.8 or later
|
|
37
|
-
- **Dependencies**:
|
|
38
|
-
- `dotenv` (runtime dependency | bundled)
|
|
39
|
-
- **TypeScript Compiler Options**:
|
|
40
|
-
- `experimentalDecorators: true`
|
|
41
|
-
- `module: nodenext OR esnext`
|
|
42
|
-
- `moduleResolution: nodenext OR bundler`
|
|
43
|
-
- `target: ESnext`
|
|
44
|
-
- `lib: ESNext`
|
|
45
|
-
- **ESM Support**: Project uses ESM, so your environment and tooling should support ES modules.
|
|
88
|
+
#### ๐ข Runtime
|
|
46
89
|
|
|
47
|
-
|
|
90
|
+
- **Node.js**: `>=22.0.0`
|
|
91
|
+
_Recommended for full ESM and `nodenext` support_
|
|
48
92
|
|
|
49
|
-
|
|
93
|
+
- **TypeScript**: `>=5.8`
|
|
50
94
|
|
|
51
|
-
|
|
52
|
-
# npm
|
|
53
|
-
npm install envapt
|
|
95
|
+
#### ๐ฆ Runtime Dependency
|
|
54
96
|
|
|
55
|
-
|
|
56
|
-
pnpm add envapt
|
|
97
|
+
- **dotenv**: _(bundled at runtime)_
|
|
57
98
|
|
|
58
|
-
|
|
59
|
-
|
|
99
|
+
#### ๐ ๏ธ TypeScript Compiler Options
|
|
100
|
+
|
|
101
|
+
```jsonc
|
|
102
|
+
// tsconfig.json (required settings)
|
|
103
|
+
{
|
|
104
|
+
"experimentalDecorators": true,
|
|
105
|
+
"module": "esnext", // or "nodenext"
|
|
106
|
+
"moduleResolution": "bundler", // or "nodenext"
|
|
107
|
+
"target": "ESNext",
|
|
108
|
+
"lib": ["ESNext"]
|
|
109
|
+
}
|
|
60
110
|
```
|
|
61
111
|
|
|
112
|
+
## Quick Start
|
|
113
|
+
|
|
114
|
+
### Installation
|
|
115
|
+
|
|
116
|
+
| Package Manager | Command |
|
|
117
|
+
| --------------- | -------------------- |
|
|
118
|
+
| **npm** | `npm install envapt` |
|
|
119
|
+
| **pnpm** | `pnpm add envapt` |
|
|
120
|
+
| **yarn** | `yarn add envapt` |
|
|
121
|
+
|
|
62
122
|
### Basic Usage
|
|
63
123
|
|
|
64
|
-
Create a `.env` file:
|
|
124
|
+
**Step 1:** Create a `.env` file:
|
|
65
125
|
|
|
66
126
|
```env
|
|
67
127
|
APP_PORT=8443
|
|
@@ -72,22 +132,22 @@ MAX_CONNECTIONS=100
|
|
|
72
132
|
ALLOWED_ORIGINS=https://app.com,https://admin.com
|
|
73
133
|
```
|
|
74
134
|
|
|
75
|
-
Use with decorators
|
|
135
|
+
Use with decorators:
|
|
76
136
|
|
|
77
137
|
```ts
|
|
78
|
-
import { Envapt, Envapter } from 'envapt';
|
|
138
|
+
import { Envapt, Envapter, Converters } from 'envapt';
|
|
79
139
|
|
|
80
140
|
// Global app configuration (static properties)
|
|
81
141
|
class AppConfig extends Envapter {
|
|
82
142
|
@Envapt('APP_PORT', 3000)
|
|
83
143
|
static readonly port: number;
|
|
84
144
|
|
|
85
|
-
@Envapt('APP_URL', 'http://localhost:3000',
|
|
145
|
+
@Envapt('APP_URL', 'http://localhost:3000', Converters.Url)
|
|
86
146
|
static readonly url: URL;
|
|
87
147
|
|
|
88
148
|
@Envapt('ALLOWED_ORIGINS', {
|
|
89
149
|
fallback: ['http://localhost:3000'],
|
|
90
|
-
converter:
|
|
150
|
+
converter: Converters.Array
|
|
91
151
|
})
|
|
92
152
|
static readonly allowedOrigins: string[];
|
|
93
153
|
}
|
|
@@ -97,10 +157,10 @@ class DatabaseService {
|
|
|
97
157
|
@Envapt('DATABASE_URL', 'sqlite://memory')
|
|
98
158
|
declare readonly databaseUrl: string;
|
|
99
159
|
|
|
100
|
-
@Envapt('MAX_CONNECTIONS', { converter:
|
|
160
|
+
@Envapt('MAX_CONNECTIONS', { converter: Converters.Number, fallback: 10 })
|
|
101
161
|
declare readonly maxConnections: number;
|
|
102
162
|
|
|
103
|
-
@Envapt('REQUEST_TIMEOUT', { converter:
|
|
163
|
+
@Envapt('REQUEST_TIMEOUT', { converter: Converters.Time, fallback: 5000 })
|
|
104
164
|
declare readonly timeout: number; // Converts "5s" to 5000ms
|
|
105
165
|
|
|
106
166
|
async connect() {
|
|
@@ -117,8 +177,7 @@ const dbService = new DatabaseService();
|
|
|
117
177
|
await dbService.connect();
|
|
118
178
|
```
|
|
119
179
|
|
|
120
|
-
Or use functionally
|
|
121
|
-
<sub>Limited to primitives, String, Number, Boolean, Symbol, and BigInt. Does not support converters.</sub>
|
|
180
|
+
Or use functionally:
|
|
122
181
|
|
|
123
182
|
```ts
|
|
124
183
|
import { Envapter } from 'envapt';
|
|
@@ -151,9 +210,9 @@ The `@Envapt` decorator can be used on both **static** and **instance** class pr
|
|
|
151
210
|
@Envapt('ENV_VAR', fallback?, converter?)
|
|
152
211
|
```
|
|
153
212
|
|
|
154
|
-
#### Automatic Type Detection
|
|
213
|
+
#### Automatic Runtime Type Detection
|
|
155
214
|
|
|
156
|
-
Types are automatically inferred from fallback values.
|
|
215
|
+
Types are automatically inferred from fallback values.
|
|
157
216
|
|
|
158
217
|
```ts
|
|
159
218
|
class Config extends Envapter {
|
|
@@ -183,65 +242,144 @@ class Config extends Envapter {
|
|
|
183
242
|
}
|
|
184
243
|
```
|
|
185
244
|
|
|
245
|
+
#### Primitive Converters
|
|
246
|
+
|
|
247
|
+
Envapt allows using the 5 "primitive" type-like converters. These **will** coerce values.
|
|
248
|
+
|
|
249
|
+
> [!NOTE]
|
|
250
|
+
> The runtime validator will ignore this usage, allowing type coercion for flexibility.
|
|
251
|
+
|
|
252
|
+
**Valid Primitive Types:** `String`, `Number`, `Boolean`, `Symbol`, and `BigInt`.
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
class Config extends Envapter {
|
|
256
|
+
@Envapt('PORT_STRING', { fallback: 'hello-world', converter: String })
|
|
257
|
+
static readonly portAsString: string;
|
|
258
|
+
|
|
259
|
+
@Envapt('DEBUG_FLAG', { fallback: true, converter: Boolean })
|
|
260
|
+
static readonly debugMode: boolean;
|
|
261
|
+
|
|
262
|
+
@Envapt('USER_ID', { fallback: 12345, converter: Number })
|
|
263
|
+
static readonly userId: number;
|
|
264
|
+
|
|
265
|
+
@Envapt('MAX_SAFE_INT', { fallback: 9007199254740991n, converter: BigInt })
|
|
266
|
+
static readonly maxSafeInt: bigint;
|
|
267
|
+
|
|
268
|
+
@Envapt('APP_INSTANCE', { fallback: Symbol(main), converter: Symbol })
|
|
269
|
+
static readonly appInstance: symbol;
|
|
270
|
+
|
|
271
|
+
// Instance properties work the same way
|
|
272
|
+
@Envapt('CONNECTION_TIMEOUT', { fallback: 5000, converter: Number })
|
|
273
|
+
declare readonly timeout: number;
|
|
274
|
+
|
|
275
|
+
// Type coercion example
|
|
276
|
+
@Envapt('PERMISSIONS', { fallback: '72394823472342983', converter: BigInt })
|
|
277
|
+
declare readonly permissions: bigint; // Converts "72394823472342983" to BigInt
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**When to use primitive converters:**
|
|
282
|
+
|
|
283
|
+
- When you need explicit type coercion between incompatible types
|
|
284
|
+
- When working with external systems that provide values in unexpected formats
|
|
285
|
+
|
|
186
286
|
#### Built-in Converters
|
|
187
287
|
|
|
188
288
|
Envapt provides many built-in converters for common patterns:
|
|
189
289
|
|
|
290
|
+
> [!IMPORTANT]
|
|
291
|
+
> **Use the `Converters` enum** instead of string literals. They look better, and provide better type inference:
|
|
292
|
+
>
|
|
293
|
+
> ```ts
|
|
294
|
+
> import { Converters } from 'envapt';
|
|
295
|
+
> // โ
Recommended: Use enum
|
|
296
|
+
> @Envapt('PORT', { converter: Converters.Number, fallback: 3000 })
|
|
297
|
+
>
|
|
298
|
+
> // โ Discouraged: String literals (still supported for compatibility)
|
|
299
|
+
> @Envapt('PORT', { converter: 'number', fallback: 3000 })
|
|
300
|
+
> ```
|
|
301
|
+
|
|
302
|
+
> [!IMPORTANT]
|
|
303
|
+
> Built-in converters enforce **strict type validation** between the converter and fallback types. The converter's expected return type must match the fallback's type.
|
|
304
|
+
|
|
190
305
|
```ts
|
|
191
306
|
class Config extends Envapter {
|
|
192
307
|
// Basic types
|
|
193
|
-
@Envapt('APP_NAME', { converter:
|
|
308
|
+
@Envapt('APP_NAME', { converter: Converters.String, fallback: 'MyApp' })
|
|
194
309
|
static readonly appName: string;
|
|
195
310
|
|
|
196
|
-
@Envapt('PORT', { converter:
|
|
311
|
+
@Envapt('PORT', { converter: Converters.Number, fallback: 3000 })
|
|
197
312
|
static readonly port: number;
|
|
198
313
|
|
|
199
|
-
@Envapt('PRODUCTION_MODE', { converter:
|
|
314
|
+
@Envapt('PRODUCTION_MODE', { converter: Converters.Boolean, fallback: false })
|
|
200
315
|
static readonly productionMode: boolean;
|
|
201
316
|
|
|
202
317
|
// Advanced types
|
|
203
|
-
@Envapt('CORS_ORIGINS', { converter:
|
|
318
|
+
@Envapt('CORS_ORIGINS', { converter: Converters.Array, fallback: [] })
|
|
204
319
|
static readonly corsOrigins: string[];
|
|
205
320
|
|
|
206
|
-
@Envapt('CONFIG_JSON', { converter:
|
|
321
|
+
@Envapt('CONFIG_JSON', { converter: Converters.Json, fallback: {} })
|
|
207
322
|
static readonly config: object;
|
|
208
323
|
|
|
209
|
-
@Envapt('API_URL', { converter:
|
|
324
|
+
@Envapt('API_URL', { converter: Converters.Url, fallback: new URL('http://localhost') })
|
|
210
325
|
static readonly apiUrl: URL;
|
|
211
326
|
|
|
212
|
-
@Envapt('TIMEOUT', { converter:
|
|
327
|
+
@Envapt('TIMEOUT', { converter: Converters.Time, fallback: 5000 })
|
|
213
328
|
static readonly timeout: number; // Converts "30s" to 30000ms
|
|
214
329
|
|
|
215
330
|
// Instance properties work the same way
|
|
216
|
-
@Envapt('CACHE_TTL', { converter:
|
|
331
|
+
@Envapt('CACHE_TTL', { converter: Converters.Time, fallback: 3600000 })
|
|
217
332
|
declare readonly cacheTtl: number; // "1h" becomes 3600000ms
|
|
218
333
|
}
|
|
219
334
|
```
|
|
220
335
|
|
|
336
|
+
> [!WARNING]
|
|
337
|
+
> These will throw runtime errors due to type mismatches:
|
|
338
|
+
>
|
|
339
|
+
> ```ts
|
|
340
|
+
> // โ String converter with number fallback
|
|
341
|
+
> @Envapt('VAR', { converter: Converters.String, fallback: 42 })
|
|
342
|
+
>
|
|
343
|
+
> // โ URL converter with string fallback
|
|
344
|
+
> @Envapt('VAR', { converter: Converters.Url, fallback: 'http://example.com' })
|
|
345
|
+
>
|
|
346
|
+
> // โ
Use primitive constructors for type coercion instead
|
|
347
|
+
> @Envapt('VAR', { converter: String, fallback: 42 })
|
|
348
|
+
> ```
|
|
349
|
+
|
|
221
350
|
**Available Built-in Converters:**
|
|
222
351
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
352
|
+
| **Converter** | **Alias** | **Description** |
|
|
353
|
+
| -------------------- | ----------- | -------------------------------------------------------------------- |
|
|
354
|
+
| `Converters.String` | `'string'` | String values |
|
|
355
|
+
| `Converters.Number` | `'number'` | Numeric values (integers and floats) |
|
|
356
|
+
| `Converters.Integer` | `'integer'` | Integer values only |
|
|
357
|
+
| `Converters.Float` | `'float'` | Float values only |
|
|
358
|
+
| `Converters.Boolean` | `'boolean'` | Boolean values (`true`/`false`, `yes`/`no`, `on`/`off`, `1`/`0`) |
|
|
359
|
+
| `Converters.Bigint` | `'bigint'` | BigInt values for large integers |
|
|
360
|
+
| `Converters.Symbol` | `'symbol'` | Symbol values (creates symbols from string descriptions) |
|
|
361
|
+
| `Converters.Json` | `'json'` | JSON objects/arrays (safe parsing with fallback) |
|
|
362
|
+
| `Converters.Array` | `'array'` | Comma-separated string arrays |
|
|
363
|
+
| `Converters.Url` | `'url'` | URL objects |
|
|
364
|
+
| `Converters.Regexp` | `'regexp'` | Regular expressions (supports `/pattern/flags` syntax) |
|
|
365
|
+
| `Converters.Date` | `'date'` | Date objects (supports ISO strings and timestamps) |
|
|
366
|
+
| `Converters.Time` | `'time'` | Time values (e.g. `"5s"`, `"30m"`, `"2h"` converted to milliseconds) |
|
|
236
367
|
|
|
237
368
|
#### Custom Array Converters
|
|
238
369
|
|
|
239
370
|
For more control over array parsing:
|
|
240
371
|
|
|
372
|
+
> [!IMPORTANT]
|
|
373
|
+
> Array converters validate that:
|
|
374
|
+
>
|
|
375
|
+
> 1. **Fallback must be an array** (if provided)
|
|
376
|
+
> 2. **All fallback elements have consistent types** (no mixed types like `['string', 42, true]`)
|
|
377
|
+
> 3. **Array converter `type` matches fallback element types** (if `type` is specified)
|
|
378
|
+
|
|
241
379
|
```ts
|
|
242
380
|
class Config extends Envapter {
|
|
243
381
|
// Basic array (comma-separated strings)
|
|
244
|
-
@Envapt('TAGS', { converter:
|
|
382
|
+
@Envapt('TAGS', { converter: Converters.Array, fallback: [] })
|
|
245
383
|
static readonly tags: string[];
|
|
246
384
|
|
|
247
385
|
// Custom delimiter
|
|
@@ -249,7 +387,7 @@ class Config extends Envapter {
|
|
|
249
387
|
declare readonly allowedMethods: string[];
|
|
250
388
|
|
|
251
389
|
// Custom delimiter with type conversion
|
|
252
|
-
@Envapt('RATE_LIMITS', { converter: { delimiter: ',', type:
|
|
390
|
+
@Envapt('RATE_LIMITS', { converter: { delimiter: ',', type: Converters.Number }, fallback: [100] })
|
|
253
391
|
declare readonly rateLimits: number[];
|
|
254
392
|
|
|
255
393
|
@Envapt('FEATURE_FLAGS', { converter: { delimiter: ';', type: 'boolean' }, fallback: [false] })
|
|
@@ -257,10 +395,24 @@ class Config extends Envapter {
|
|
|
257
395
|
}
|
|
258
396
|
```
|
|
259
397
|
|
|
398
|
+
> [!WARNING]
|
|
399
|
+
> These will throw runtime validation errors:
|
|
400
|
+
>
|
|
401
|
+
> ```ts
|
|
402
|
+
> // โ Mixed types in fallback array
|
|
403
|
+
> @Envapt('MIXED', { converter: Converters.Array, fallback: ['string', 42, true] })
|
|
404
|
+
>
|
|
405
|
+
> // โ Array converter type doesn't match fallback elements
|
|
406
|
+
> @Envapt('NUMS', { converter: { delimiter: ',', type: Converters.Number }, fallback: ['not', 'numbers'] })
|
|
407
|
+
>
|
|
408
|
+
> // โ Non-array fallback with array converter
|
|
409
|
+
> @Envapt('INVALID', { converter: Converters.Array, fallback: 'not-an-array' })
|
|
410
|
+
> ```
|
|
411
|
+
|
|
260
412
|
**ArrayConverter Interface:**
|
|
261
413
|
|
|
262
414
|
- `delimiter: string` - The string used to split array elements
|
|
263
|
-
- `type?: BuiltInConverter` - Optional type to convert each element to (excludes
|
|
415
|
+
- `type?: BuiltInConverter` - Optional type to convert each element to (excludes `Converters.Array`, `Converters.Json`, and `Converters.Regexp`)
|
|
264
416
|
|
|
265
417
|
#### Custom Converters
|
|
266
418
|
|
|
@@ -304,7 +456,7 @@ class Config extends Envapter {
|
|
|
304
456
|
static readonly optionalFeature: string | undefined;
|
|
305
457
|
|
|
306
458
|
// Returns null if not found (no fallback provided)
|
|
307
|
-
@Envapt('MISSING_CONFIG', { converter:
|
|
459
|
+
@Envapt('MISSING_CONFIG', { converter: Converters.String })
|
|
308
460
|
static readonly missingConfig: string | null;
|
|
309
461
|
|
|
310
462
|
// Uses fallback if not found
|
|
@@ -319,19 +471,92 @@ class Config extends Envapter {
|
|
|
319
471
|
|
|
320
472
|
### Functional API
|
|
321
473
|
|
|
474
|
+
For functional-style environment variable on primitive types:
|
|
475
|
+
|
|
322
476
|
```ts
|
|
323
|
-
import { Envapter } from 'envapt';
|
|
477
|
+
import { Envapter, Converters } from 'envapt';
|
|
324
478
|
|
|
325
|
-
//
|
|
479
|
+
// Basic type-specific getters
|
|
326
480
|
const str = Envapter.get('STRING_VAR', 'default');
|
|
327
481
|
const num = Envapter.getNumber('NUMBER_VAR', 42);
|
|
328
482
|
const bool = Envapter.getBoolean('BOOLEAN_VAR', false);
|
|
483
|
+
const bigint = Envapter.getBigInt('BIGINT_VAR', 100n);
|
|
484
|
+
const symbol = Envapter.getSymbol('SYMBOL_VAR', Symbol('default'));
|
|
329
485
|
|
|
330
|
-
//
|
|
486
|
+
// Advanced converter methods
|
|
487
|
+
const jsonData = Envapter.getUsing('CONFIG_JSON', Converters.Json);
|
|
488
|
+
const urlArray = Envapter.getUsing('API_URLS', { delimiter: ',', type: Converters.Url });
|
|
489
|
+
const customData = Envapter.getWith('RAW_DATA', (raw) => raw?.split('|').map((s) => s.trim()));
|
|
490
|
+
|
|
491
|
+
// Instance methods (same API available)
|
|
331
492
|
const envapter = new Envapter();
|
|
332
493
|
const value = envapter.get('VAR', 'default');
|
|
494
|
+
const processed = envapter.getUsing('DATA', Converters.Array);
|
|
333
495
|
```
|
|
334
496
|
|
|
497
|
+
For functional-style environment variable access with converters:
|
|
498
|
+
|
|
499
|
+
```ts
|
|
500
|
+
import { Envapter, Converters } from 'envapt';
|
|
501
|
+
|
|
502
|
+
// Use built-in converters directly
|
|
503
|
+
const config = Envapter.getUsing('API_CONFIG', Converters.Json, { default: 'value' });
|
|
504
|
+
const urls = Envapter.getUsing('SERVICE_URLS', { delimiter: '|', type: Converters.Url });
|
|
505
|
+
const typedConfig = Envapter.getUsing<{ host: string; port: number; ssl: boolean }>('DATABASE_CONFIG', Converters.Json);
|
|
506
|
+
// typedConfig is now typed as { host: string; port: number; ssl: boolean } instead of JsonValue | undefined
|
|
507
|
+
|
|
508
|
+
// Use custom converter functions
|
|
509
|
+
const processedData = Envapter.getWith(
|
|
510
|
+
'RAW_DATA',
|
|
511
|
+
(raw, fallback) => {
|
|
512
|
+
if (!raw) return fallback ?? [];
|
|
513
|
+
return raw.split(',').map((item) => ({ name: item.trim(), enabled: true }));
|
|
514
|
+
},
|
|
515
|
+
[]
|
|
516
|
+
);
|
|
517
|
+
|
|
518
|
+
// Instance methods work the same way
|
|
519
|
+
const envapter = new Envapter();
|
|
520
|
+
const result = envapter.getUsing('DATABASE_CONFIG', Converters.Json);
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
> [!TIP]
|
|
524
|
+
> **Type Override with `getUsing`**
|
|
525
|
+
>
|
|
526
|
+
> You can explicitly specify the return type for `getUsing` when TypeScript's inference isn't specific enough (especially useful with `Converters.Json`):
|
|
527
|
+
>
|
|
528
|
+
> ```ts
|
|
529
|
+
> // Default behavior
|
|
530
|
+
> const config = Envapter.getUsing('CONFIG', Converters.Json); // type: JsonValue | undefined (undefined because no fallback)
|
|
531
|
+
>
|
|
532
|
+
> // Override with specific interface
|
|
533
|
+
> interface DatabaseConfig {
|
|
534
|
+
> host: string;
|
|
535
|
+
> port: number;
|
|
536
|
+
> ssl: boolean;
|
|
537
|
+
> }
|
|
538
|
+
> const dbConfig = Envapter.getUsing<DatabaseConfig>('DB_CONFIG', Converters.Json);
|
|
539
|
+
> // dbConfig is now properly typed as DatabaseConfig
|
|
540
|
+
> ```
|
|
541
|
+
>
|
|
542
|
+
> _Make sure the fallback value matches the expected type, if you use a fallback. Otherwise you'll see a TypeScript error._\
|
|
543
|
+
> _This does NOT validate the type at runtime. You'll need to handle that yourself._
|
|
544
|
+
|
|
545
|
+
### Converter Type Quick Reference
|
|
546
|
+
|
|
547
|
+
| **Use Case** | **Converter Type** | **Example** |
|
|
548
|
+
| ----------------------- | ------------------------- | --------------------------------------------------------- |
|
|
549
|
+
| **Type coercion** | Primitive constructors | `converter: String` |
|
|
550
|
+
| **Strict validation** | Built-in converters | `converter: Converters.String` |
|
|
551
|
+
| **Array parsing** | Built-in Array converters | `converter: { delimiter: ',', type?: Converters.String }` |
|
|
552
|
+
| **Complex transforms** | Custom function | `converter: (raw, fallback) => ...` |
|
|
553
|
+
| **Functional built-in** | `getUsing()` method | `Envapter.getUsing('VAR', Converters.Json)` |
|
|
554
|
+
| **Type override** | `getUsing<T>()` method | `Envapter.getUsing<MyType>('VAR', Converters.Json)` |
|
|
555
|
+
| **Functional custom** | `getWith()` method | `Envapter.getWith('VAR', (raw) => transform(raw))` |
|
|
556
|
+
|
|
557
|
+
> [!TIP]
|
|
558
|
+
> **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
|
+
|
|
335
560
|
## Environment Detection
|
|
336
561
|
|
|
337
562
|
Envapt automatically detects your environment from these variables (in order):
|
|
@@ -348,7 +573,7 @@ Supported values: `development`, `staging`, `production` (case-sensitive)
|
|
|
348
573
|
import { Envapter, EnvaptEnvironment } from 'envapt';
|
|
349
574
|
|
|
350
575
|
// Check current environment
|
|
351
|
-
console.log(Envapter.environment); // Environment.Development
|
|
576
|
+
console.log(Envapter.environment); // Environment.Development (default)
|
|
352
577
|
console.log(Envapter.isProduction); // false
|
|
353
578
|
console.log(Envapter.isDevelopment); // true
|
|
354
579
|
console.log(Envapter.isStaging); // false
|
|
@@ -358,6 +583,8 @@ Envapter.environment = EnvaptEnvironment.Production;
|
|
|
358
583
|
Envapter.environment = 'staging'; // string also works
|
|
359
584
|
```
|
|
360
585
|
|
|
586
|
+
## Configuration
|
|
587
|
+
|
|
361
588
|
### Multiple .env Files
|
|
362
589
|
|
|
363
590
|
```ts
|
|
@@ -373,6 +600,29 @@ Envapter.envPaths = resolve(__dirname, '.env.production');
|
|
|
373
600
|
// Or just don't set a path for it to default to .env at the root of your project
|
|
374
601
|
```
|
|
375
602
|
|
|
603
|
+
### Dotenv Configuration
|
|
604
|
+
|
|
605
|
+
Envapt allows you to customize dotenv behavior by setting configuration options:
|
|
606
|
+
|
|
607
|
+
```ts
|
|
608
|
+
import { Envapter } from 'envapt';
|
|
609
|
+
|
|
610
|
+
// Set dotenv configuration options
|
|
611
|
+
Envapter.dotenvConfig = {
|
|
612
|
+
encoding: 'latin1', // File encoding (default: 'utf8')
|
|
613
|
+
debug: true, // Enable debug logging
|
|
614
|
+
override: true, // Override existing environment variables
|
|
615
|
+
quiet: false, // Suppress non-error output (default: true)
|
|
616
|
+
DOTENV_KEY: 'key...' // Decryption key for .env.vault files
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
// Get current configuration
|
|
620
|
+
console.log(Envapter.dotenvConfig);
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
> [!NOTE]
|
|
624
|
+
> The `path` and `processEnv` options are managed internally by Envapter and cannot be set via `dotenvConfig`.
|
|
625
|
+
|
|
376
626
|
## Template Variables
|
|
377
627
|
|
|
378
628
|
Envapt supports variable interpolation with `${VARIABLE}` syntax:
|
|
@@ -396,19 +646,79 @@ CIRCULAR_B=${CIRCULAR_A}
|
|
|
396
646
|
|
|
397
647
|
Circular references are detected and preserved as-is rather than causing infinite loops.
|
|
398
648
|
|
|
649
|
+
## Error Handling
|
|
650
|
+
|
|
651
|
+
Envapt provides detailed error codes for better debugging and error handling:
|
|
652
|
+
|
|
653
|
+
```ts
|
|
654
|
+
import { EnvaptError, EnvaptErrorCodes } from 'envapt';
|
|
655
|
+
|
|
656
|
+
try {
|
|
657
|
+
// This will throw an error for invalid configuration
|
|
658
|
+
Envapter.dotenvConfig = { path: '.env.custom' };
|
|
659
|
+
} catch (error) {
|
|
660
|
+
if (error instanceof EnvaptError) {
|
|
661
|
+
console.log('Error code:', error.code);
|
|
662
|
+
console.log('Error message:', error.message);
|
|
663
|
+
|
|
664
|
+
// Handle specific error types
|
|
665
|
+
switch (error.code) {
|
|
666
|
+
case EnvaptErrorCodes.InvalidUserDefinedConfig:
|
|
667
|
+
console.log('Invalid configuration provided');
|
|
668
|
+
break;
|
|
669
|
+
case EnvaptErrorCodes.EnvFileNotFound:
|
|
670
|
+
console.log('Environment file not found');
|
|
671
|
+
break;
|
|
672
|
+
default:
|
|
673
|
+
console.warn('Unhandled error code:', error.code);
|
|
674
|
+
break;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
### Error Code Reference
|
|
681
|
+
|
|
682
|
+
#### ๐ง Fallback Errors (1xx)
|
|
683
|
+
|
|
684
|
+
| **Error Code** | **Description** |
|
|
685
|
+
| ---------------------------------------- | --------------------------------------------------------- |
|
|
686
|
+
| `InvalidFallback` (101) | Invalid fallback value provided |
|
|
687
|
+
| `InvalidFallbackType` (102) | Fallback value type doesn't match expected converter type |
|
|
688
|
+
| `ArrayFallbackElementTypeMismatch` (103) | Array fallback contains elements of wrong type |
|
|
689
|
+
| `FallbackConverterTypeMismatch` (104) | Fallback type doesn't match the specified converter |
|
|
690
|
+
|
|
691
|
+
#### ๐งช Converter Errors (2xx)
|
|
692
|
+
|
|
693
|
+
| **Error Code** | **Description** |
|
|
694
|
+
| --------------------------------- | ---------------------------------------------- |
|
|
695
|
+
| `InvalidArrayConverterType` (201) | Invalid array converter configuration provided |
|
|
696
|
+
| `InvalidBuiltInConverter` (202) | Invalid built-in converter specified |
|
|
697
|
+
| `InvalidCustomConverter` (203) | Custom converter function is invalid |
|
|
698
|
+
| `InvalidConverterType` (204) | Converter type is not recognized |
|
|
699
|
+
| `PrimitiveCoercionFailed` (205) | Primitive type coercion failed |
|
|
700
|
+
|
|
701
|
+
#### ๐ Environment File & Config Errors (3xx)
|
|
702
|
+
|
|
703
|
+
| **Error Code** | **Description** |
|
|
704
|
+
| -------------------------------- | ---------------------------------------------- |
|
|
705
|
+
| `MissingDelimiter` (301) | Delimiter is missing in array converter config |
|
|
706
|
+
| `InvalidUserDefinedConfig` (302) | Invalid user-defined configuration provided |
|
|
707
|
+
| `EnvFilesNotFound` (303) | Specified environment file doesn't exist |
|
|
708
|
+
|
|
399
709
|
## Advanced Examples
|
|
400
710
|
|
|
401
711
|
### Complex Configuration
|
|
402
712
|
|
|
403
713
|
```ts
|
|
404
|
-
import { Envapt, Envapter } from 'envapt';
|
|
714
|
+
import { Envapt, Envapter, Converters } from 'envapt';
|
|
405
715
|
|
|
406
716
|
class AppConfig extends Envapter {
|
|
407
717
|
// Global settings (static)
|
|
408
718
|
@Envapt('PORT', 3000)
|
|
409
719
|
static readonly port: number;
|
|
410
720
|
|
|
411
|
-
@Envapt('REQUEST_TIMEOUT', { converter:
|
|
721
|
+
@Envapt('REQUEST_TIMEOUT', { converter: Converters.Time, fallback: 10000 })
|
|
412
722
|
static readonly requestTimeout: number; // "5s" -> 5000ms (if env is set to "5s")
|
|
413
723
|
|
|
414
724
|
@Envapt('FEATURE_FLAGS', {
|
|
@@ -424,7 +734,7 @@ class AppConfig extends Envapter {
|
|
|
424
734
|
@Envapt('DB_URL', 'sqlite://memory')
|
|
425
735
|
declare readonly databaseUrl: string;
|
|
426
736
|
|
|
427
|
-
@Envapt('CACHE_TTL', { converter:
|
|
737
|
+
@Envapt('CACHE_TTL', { converter: Converters.Time, fallback: 3600000 })
|
|
428
738
|
declare readonly cacheTtl: number; // "1h" -> 3600000ms
|
|
429
739
|
|
|
430
740
|
@Envapt('REDIS_URLS', {
|
|
@@ -440,3 +750,20 @@ class AppConfig extends Envapter {
|
|
|
440
750
|
}
|
|
441
751
|
}
|
|
442
752
|
```
|
|
753
|
+
|
|
754
|
+
---
|
|
755
|
+
|
|
756
|
+
<hr/>
|
|
757
|
+
|
|
758
|
+
<p align="center">
|
|
759
|
+
<a href="https://github.com/materwelondhruv/envapt">โญ๏ธ Star it on GitHub</a> โข
|
|
760
|
+
<a href="https://github.com/materwelondhruv/envapt/issues">๐ Report a bug</a> โข
|
|
761
|
+
<a href="https://github.com/materwelondhruv/envapt/issues/new?labels=enhancement">๐ก Request a feature</a>
|
|
762
|
+
</p>
|
|
763
|
+
|
|
764
|
+
<p align="center">
|
|
765
|
+
<sub>
|
|
766
|
+
Built by <a href="https://github.com/materwelondhruv">@materwelonDhruv</a> โข Licensed under
|
|
767
|
+
<a href="LICENSE">Apache 2.0</a>
|
|
768
|
+
</sub>
|
|
769
|
+
</p>
|