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