ultraenv 1.0.1 → 1.0.2
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 +67 -1857
- package/dist/{chunk-GC7RXHLA.js → chunk-2MBSLURI.js} +16 -17
- package/dist/{chunk-IGFVP24Q.js → chunk-3AF476D7.js} +1 -1
- package/dist/{chunk-N5PAV4NM.js → chunk-5WUBB633.js} +17 -26
- package/dist/{chunk-2USZPWLZ.js → chunk-6NFA23AY.js} +28 -39
- package/dist/{chunk-AWN6ADV7.js → chunk-6X3BUE5S.js} +45 -54
- package/dist/{chunk-NBOABPHM.js → chunk-72UKVOO5.js} +24 -40
- package/dist/{chunk-JB7RKV3C.js → chunk-7AHG2IP4.js} +1 -7
- package/dist/{chunk-6KS56D6E.js → chunk-7VJ7LK2M.js} +1 -1
- package/dist/{chunk-CIFMBJ4H.js → chunk-BNUHE2ZI.js} +57 -21
- package/dist/{chunk-HFXQGJY3.js → chunk-CVJPO3QY.js} +13 -27
- package/dist/{chunk-IKPTKALB.js → chunk-F7YSINGU.js} +1 -3
- package/dist/{chunk-3VYXPTYV.js → chunk-FSKVYBEP.js} +1 -1
- package/dist/{chunk-4XUYMRK5.js → chunk-GJC64ZG7.js} +4 -3
- package/dist/{chunk-YMMP4VQL.js → chunk-H3QEGEZ6.js} +1 -1
- package/dist/{chunk-CHVO6NWI.js → chunk-LFIKYFPS.js} +2 -2
- package/dist/{chunk-3UV2QNJL.js → chunk-LJSCUOD4.js} +19 -21
- package/dist/{chunk-YVWLXFUT.js → chunk-LQZK6BBQ.js} +2 -2
- package/dist/{chunk-5G2DU52U.js → chunk-N7GOHQBF.js} +7 -1
- package/dist/{chunk-MSXMESFP.js → chunk-OBLMAUCF.js} +8 -14
- package/dist/{chunk-UEWYFN6A.js → chunk-RJTUAMK3.js} +16 -29
- package/dist/{chunk-MNVFG7H4.js → chunk-XPZC32UY.js} +16 -25
- package/dist/{chunk-WMHN5RW2.js → chunk-YLGJQOMM.js} +3 -9
- package/dist/{ci-check-sync-VBMSVWIV.js → ci-check-sync-UO5PARKO.js} +4 -4
- package/dist/{ci-scan-24MT5XGS.js → ci-scan-5D7QBN5X.js} +2 -5
- package/dist/{ci-setup-C2NKEFRD.js → ci-setup-J34DS6KD.js} +2 -2
- package/dist/{ci-validate-7AW24LSQ.js → ci-validate-LWP5NBDN.js} +4 -4
- package/dist/cli/index.cjs +470 -390
- package/dist/cli/index.js +130 -55
- package/dist/comparator-AIRTWBOL.js +13 -0
- package/dist/{config-O5YRQP5Z.js → config-67GDO3CW.js} +3 -3
- package/dist/{debug-PTPXAF3K.js → debug-6VCX3QSP.js} +6 -6
- package/dist/{declaration-LEME4AFZ.js → declaration-YGOVZOXG.js} +3 -3
- package/dist/{doctor-FZAUPKHS.js → doctor-FF7QOTP2.js} +7 -5
- package/dist/{envs-compare-5K3HESX5.js → envs-compare-P7GPKGQX.js} +4 -4
- package/dist/{envs-create-2XXHXMGA.js → envs-create-ISG4SECU.js} +4 -4
- package/dist/{envs-list-NQM5252B.js → envs-list-PUW67HOC.js} +5 -5
- package/dist/{envs-switch-6L2AQYID.js → envs-switch-P4YDJ6LG.js} +4 -4
- package/dist/{envs-validate-FL73Q76T.js → envs-validate-VNKBKYO3.js} +6 -9
- package/dist/{fs-VH7ATUS3.js → fs-7HKOZY5K.js} +2 -2
- package/dist/{generator-LFZBMZZS.js → generator-O23ATCIY.js} +4 -4
- package/dist/{help-3XJBXEHE.js → help-THFLI6YT.js} +108 -26
- package/dist/index.cjs +295 -381
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +81 -100
- package/dist/{init-Y7JQ2KYJ.js → init-KBLVTHQW.js} +15 -11
- package/dist/{install-hook-SKXIV6NV.js → install-hook-42F22BLY.js} +2 -2
- package/dist/{json-schema-I26YNQBH.js → json-schema-YULPWDKA.js} +3 -3
- package/dist/{key-manager-O3G55WPU.js → key-manager-WDWPX3IQ.js} +3 -3
- package/dist/middleware/express.cjs +1 -3
- package/dist/middleware/express.js +1 -1
- package/dist/middleware/fastify.cjs +1 -7
- package/dist/middleware/fastify.js +1 -1
- package/dist/{module-IDIZPP4M.js → module-FGH2V6N2.js} +3 -3
- package/dist/{protect-NCWPM6VC.js → protect-A4G7LQFJ.js} +26 -24
- package/dist/{scan-TRLY36TT.js → scan-4BXGHR33.js} +1 -1
- package/dist/schema/index.cjs +57 -21
- package/dist/schema/index.js +1 -1
- package/dist/{sync-TMHMTLH2.js → sync-MYLMDDY6.js} +23 -14
- package/dist/{typegen-SQOSXBWM.js → typegen-GLBRHWSK.js} +17 -23
- package/dist/{validate-IOAM5HWS.js → validate-J73ETKXD.js} +5 -5
- package/dist/{vault-decrypt-U6HJZNBV.js → vault-decrypt-V3GY5HES.js} +7 -7
- package/dist/{vault-diff-B3ZOQTWI.js → vault-diff-QVE6S6KP.js} +5 -5
- package/dist/{vault-encrypt-GUSLCSKS.js → vault-encrypt-WUBY3OVF.js} +7 -7
- package/dist/{vault-init-GUBOTOUL.js → vault-init-EWSAED44.js} +5 -5
- package/dist/{vault-rekey-DAHT7JCN.js → vault-rekey-VODMGCNA.js} +7 -7
- package/dist/{vault-status-GDLRU2OK.js → vault-status-YXDK6O7X.js} +4 -4
- package/dist/{vault-verify-CD76FJSF.js → vault-verify-SSXGTVBK.js} +7 -7
- package/package.json +1 -1
- package/dist/comparator-RDKX3OI7.js +0 -13
package/README.md
CHANGED
|
@@ -103,1953 +103,163 @@ Create an `env.ts` file:
|
|
|
103
103
|
import { defineEnv, t } from 'ultraenv';
|
|
104
104
|
|
|
105
105
|
const env = defineEnv({
|
|
106
|
-
// Required string with URL validation
|
|
107
106
|
DATABASE_URL: t.string().format('url').required(),
|
|
108
|
-
|
|
109
|
-
// Number with port validation and a default
|
|
110
107
|
PORT: t.number().port().default(3000),
|
|
111
|
-
|
|
112
|
-
// Enum with literal union types
|
|
113
108
|
NODE_ENV: t.enum(['development', 'staging', 'production'] as const).required(),
|
|
114
|
-
|
|
115
|
-
// Boolean with default
|
|
116
109
|
DEBUG: t.boolean().default(false),
|
|
117
|
-
|
|
118
|
-
// Optional email
|
|
119
110
|
ADMIN_EMAIL: t.email().optional(),
|
|
120
|
-
|
|
121
|
-
// Array with custom separator
|
|
122
111
|
ALLOWED_ORIGINS: t.array().separator(';').default(['http://localhost:3000']),
|
|
123
|
-
|
|
124
|
-
// Duration string
|
|
125
112
|
CACHE_TTL: t.duration().default('1h'),
|
|
126
|
-
|
|
127
|
-
// Bytes string
|
|
128
113
|
MAX_UPLOAD_SIZE: t.bytes().default('10MB'),
|
|
129
114
|
});
|
|
130
115
|
|
|
131
116
|
export default env;
|
|
132
|
-
|
|
133
|
-
// TypeScript knows the exact types:
|
|
134
|
-
// env.DATABASE_URL → string
|
|
135
|
-
// env.PORT → number (not string!)
|
|
136
|
-
// env.NODE_ENV → 'development' | 'staging' | 'production'
|
|
137
|
-
// env.DEBUG → boolean
|
|
138
|
-
// env.ADMIN_EMAIL → string | undefined
|
|
139
|
-
// env.ALLOWED_ORIGINS → readonly string[]
|
|
140
|
-
// env.CACHE_TTL → string
|
|
141
|
-
// env.MAX_UPLOAD_SIZE → string
|
|
142
117
|
```
|
|
143
118
|
|
|
144
119
|
### Step 3 — Use your typed env everywhere
|
|
145
120
|
|
|
146
121
|
```typescript
|
|
147
|
-
// Any file in your project:
|
|
148
122
|
import env from './env';
|
|
149
123
|
|
|
150
|
-
// Fully typed — no more `process.env.PORT as unknown as number`
|
|
151
124
|
const server = createServer({
|
|
152
125
|
port: env.PORT, // number
|
|
153
126
|
host: env.HOST, // string
|
|
154
|
-
databaseUrl: env.DATABASE_URL,
|
|
127
|
+
databaseUrl: env.DATABASE_URL,
|
|
155
128
|
});
|
|
156
|
-
|
|
157
|
-
if (env.NODE_ENV === 'development') {
|
|
158
|
-
// TypeScript knows the exact enum values!
|
|
159
|
-
console.log('Development mode:', env.DEBUG);
|
|
160
|
-
}
|
|
161
129
|
```
|
|
162
130
|
|
|
163
|
-
That's it. Your environment is validated, typed, and safe.
|
|
164
|
-
|
|
165
131
|
---
|
|
166
132
|
|
|
167
133
|
## 📦 Installation
|
|
168
134
|
|
|
169
|
-
### npm
|
|
170
|
-
|
|
171
135
|
```bash
|
|
172
136
|
npm install ultraenv
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
### pnpm
|
|
176
|
-
|
|
177
|
-
```bash
|
|
178
137
|
pnpm add ultraenv
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
### yarn
|
|
182
|
-
|
|
183
|
-
```bash
|
|
184
138
|
yarn add ultraenv
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
### bun
|
|
188
|
-
|
|
189
|
-
```bash
|
|
190
139
|
bun add ultraenv
|
|
191
140
|
```
|
|
192
141
|
|
|
193
|
-
### Global CLI
|
|
142
|
+
### Global CLI
|
|
194
143
|
|
|
195
144
|
```bash
|
|
196
145
|
npm install -g ultraenv
|
|
197
|
-
|
|
198
|
-
# Then use the CLI anywhere
|
|
199
146
|
ultraenv init
|
|
200
147
|
ultraenv validate
|
|
201
148
|
ultraenv scan
|
|
202
149
|
```
|
|
203
150
|
|
|
204
|
-
### Verify Installation
|
|
205
|
-
|
|
206
|
-
```bash
|
|
207
|
-
npx ultraenv --version
|
|
208
|
-
# → ultraenv v1.0.0
|
|
209
|
-
```
|
|
210
|
-
|
|
211
151
|
---
|
|
212
152
|
|
|
213
153
|
## 🔧 CLI Command Reference
|
|
214
154
|
|
|
215
|
-
ultraenv ships with a powerful CLI for managing your environment files from the terminal.
|
|
216
|
-
|
|
217
|
-
### Core Commands
|
|
218
|
-
|
|
219
|
-
| Command | Description |
|
|
220
|
-
|---|---|
|
|
221
|
-
| `ultraenv init` | Initialize project with config, `.env`, `.env.example`, and `.gitignore` |
|
|
222
|
-
| `ultraenv validate` | Load and validate environment variables |
|
|
223
|
-
| `ultraenv typegen` | Generate TypeScript types from schema |
|
|
224
|
-
| `ultraenv sync` | Sync `.env.example` with `.env` (check, generate, interactive, watch modes) |
|
|
225
|
-
| `ultraenv scan` | Scan codebase for leaked secrets |
|
|
226
|
-
| `ultraenv debug` | Show diagnostics and debugging info |
|
|
227
|
-
| `ultraenv protect` | Check and update `.gitignore` for secret protection |
|
|
228
|
-
| `ultraenv doctor` | Run self-checks and diagnose common issues |
|
|
229
|
-
| `ultraenv help` | Show help for any command |
|
|
230
|
-
|
|
231
|
-
### Vault Commands
|
|
232
|
-
|
|
233
|
-
| Command | Description |
|
|
234
|
-
|---|---|
|
|
235
|
-
| `ultraenv vault init` | Initialize encrypted vault |
|
|
236
|
-
| `ultraenv vault encrypt` | Encrypt environment to vault |
|
|
237
|
-
| `ultraenv vault decrypt` | Decrypt environment from vault |
|
|
238
|
-
| `ultraenv vault rekey` | Rotate encryption keys |
|
|
239
|
-
| `ultraenv vault status` | Show vault status |
|
|
240
|
-
| `ultraenv vault diff` | Compare local env vs vault |
|
|
241
|
-
| `ultraenv vault verify` | Verify vault integrity |
|
|
242
|
-
|
|
243
|
-
### Environment Commands
|
|
244
|
-
|
|
245
|
-
| Command | Description |
|
|
246
|
-
|---|---|
|
|
247
|
-
| `ultraenv envs list` | List all environments |
|
|
248
|
-
| `ultraenv envs compare` | Compare two environments |
|
|
249
|
-
| `ultraenv envs validate` | Validate all environments |
|
|
250
|
-
| `ultraenv envs create` | Create a new environment |
|
|
251
|
-
| `ultraenv envs switch` | Switch active environment |
|
|
252
|
-
|
|
253
|
-
### CI Commands
|
|
254
|
-
|
|
255
|
-
| Command | Description |
|
|
256
|
-
|---|---|
|
|
257
|
-
| `ultraenv ci validate` | Validate environment in CI (strict mode) |
|
|
258
|
-
| `ultraenv ci check-sync` | Check `.env` ↔ `.env.example` sync |
|
|
259
|
-
| `ultraenv ci scan` | Scan for secrets (SARIF output) |
|
|
260
|
-
| `ultraenv ci setup` | Generate CI config files |
|
|
261
|
-
|
|
262
|
-
### Utility Commands
|
|
263
|
-
|
|
264
155
|
| Command | Description |
|
|
265
156
|
|---|---|
|
|
266
|
-
| `ultraenv
|
|
267
|
-
| `ultraenv
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
|
272
|
-
|
|
273
|
-
|
|
|
274
|
-
|
|
|
275
|
-
|
|
|
276
|
-
|
|
|
277
|
-
| `--no-color` | Disable color output |
|
|
278
|
-
| `-q`, `--quiet` | Suppress non-error output |
|
|
279
|
-
| `-v`, `--version` | Show version |
|
|
280
|
-
| `-h`, `--help` | Show help |
|
|
281
|
-
|
|
282
|
-
### Examples
|
|
283
|
-
|
|
284
|
-
```bash
|
|
285
|
-
# Initialize a new project
|
|
286
|
-
ultraenv init
|
|
287
|
-
|
|
288
|
-
# Validate current environment
|
|
289
|
-
ultraenv validate --format json
|
|
290
|
-
|
|
291
|
-
# Generate TypeScript types
|
|
292
|
-
ultraenv typegen --format declaration --out src/env.d.ts
|
|
293
|
-
|
|
294
|
-
# Scan for secrets (terminal output)
|
|
295
|
-
ultraenv scan --scope files
|
|
296
|
-
|
|
297
|
-
# Scan for secrets (SARIF for GitHub)
|
|
298
|
-
ultraenv scan --scope all --format sarif --output results.sarif
|
|
299
|
-
|
|
300
|
-
# Sync .env.example
|
|
301
|
-
ultraenv sync --mode generate
|
|
302
|
-
ultraenv sync --mode watch
|
|
303
|
-
|
|
304
|
-
# Vault operations
|
|
305
|
-
ultraenv vault init --env production
|
|
306
|
-
ultraenv vault encrypt --env production
|
|
307
|
-
ultraenv vault decrypt --env production
|
|
308
|
-
|
|
309
|
-
# Setup CI pipeline
|
|
310
|
-
ultraenv ci setup --platform github
|
|
311
|
-
|
|
312
|
-
# Install pre-commit hook
|
|
313
|
-
ultraenv install-hook
|
|
314
|
-
```
|
|
157
|
+
| `ultraenv init` | Initialize project |
|
|
158
|
+
| `ultraenv validate` | Validate environment variables |
|
|
159
|
+
| `ultraenv typegen` | Generate TypeScript types |
|
|
160
|
+
| `ultraenv sync` | Sync `.env.example` |
|
|
161
|
+
| `ultraenv scan` | Scan for leaked secrets |
|
|
162
|
+
| `ultraenv debug` | Show diagnostics |
|
|
163
|
+
| `ultraenv protect` | Check `.gitignore` protection |
|
|
164
|
+
| `ultraenv doctor` | Run self-checks |
|
|
165
|
+
| `ultraenv vault *` | Vault encrypt/decrypt/rekey |
|
|
166
|
+
| `ultraenv envs *` | Multi-environment management |
|
|
167
|
+
| `ultraenv ci *` | CI/CD integration commands |
|
|
315
168
|
|
|
316
169
|
---
|
|
317
170
|
|
|
318
171
|
## 📐 Schema Reference
|
|
319
172
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
### The `t` Factory
|
|
323
|
-
|
|
324
|
-
All schema builders are created via the `t` factory object:
|
|
173
|
+
All schema builders via the `t` factory:
|
|
325
174
|
|
|
326
175
|
```typescript
|
|
327
176
|
import { defineEnv, t } from 'ultraenv';
|
|
328
|
-
```
|
|
329
|
-
|
|
330
|
-
---
|
|
331
|
-
|
|
332
|
-
### Core Validators
|
|
333
|
-
|
|
334
|
-
#### `t.string()` — String Validation
|
|
335
|
-
|
|
336
|
-
```typescript
|
|
337
|
-
// Basic string
|
|
338
|
-
API_KEY: t.string().required(),
|
|
339
|
-
|
|
340
|
-
// With format validation
|
|
341
|
-
WEBSITE_URL: t.string().format('url').required(),
|
|
342
|
-
ADMIN_EMAIL: t.string().format('email').optional(),
|
|
343
|
-
APP_ID: t.string().format('uuid').required(),
|
|
344
|
-
|
|
345
|
-
// Length constraints
|
|
346
|
-
USERNAME: t.string().minLength(3).maxLength(50).required(),
|
|
347
|
-
|
|
348
|
-
// Regex pattern
|
|
349
|
-
SLUG: t.string().pattern(/^[a-z0-9-]+$/).required(),
|
|
350
|
-
|
|
351
|
-
// Enum shorthand
|
|
352
|
-
LOG_LEVEL: t.string().enum(['debug', 'info', 'warn', 'error'] as const).default('info'),
|
|
353
|
-
|
|
354
|
-
// Auto-trim whitespace
|
|
355
|
-
TRIMMED_VALUE: t.string().trim().required(),
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
| Method | Parameter | Description |
|
|
359
|
-
|---|---|---|
|
|
360
|
-
| `.required()` | — | Field must be set |
|
|
361
|
-
| `.optional()` | — | Field may be undefined |
|
|
362
|
-
| `.default(value)` | `string` | Default when not set |
|
|
363
|
-
| `.description(desc)` | `string` | JSDoc description |
|
|
364
|
-
| `.minLength(n)` | `number` | Minimum string length |
|
|
365
|
-
| `.maxLength(n)` | `number` | Maximum string length |
|
|
366
|
-
| `.pattern(regex)` | `RegExp` | Must match regex |
|
|
367
|
-
| `.format(fmt)` | `string` | Predefined format shortcut |
|
|
368
|
-
| `.enum(values)` | `readonly string[]` | Allowed values |
|
|
369
|
-
| `.trim()` | `boolean?` | Trim whitespace |
|
|
370
|
-
| `.transform(fn)` | `(v) => v` | Transform after parse |
|
|
371
|
-
| `.validate(fn)` | `(v) => string \| void` | Custom validation |
|
|
372
|
-
| `.deprecated(msg)` | `string` | Deprecation warning |
|
|
373
|
-
| `.secret()` | — | Mark as secret (masked in output) |
|
|
374
|
-
| `.alias(name)` | `string` | Alternative variable name |
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
#### `t.number()` — Number Validation
|
|
378
|
-
|
|
379
|
-
```typescript
|
|
380
|
-
// Basic number (parses from string)
|
|
381
|
-
PORT: t.number().required(),
|
|
382
|
-
|
|
383
|
-
// Port validation (1-65535)
|
|
384
|
-
PORT: t.number().port().default(3000),
|
|
385
|
-
|
|
386
|
-
// Range constraints
|
|
387
|
-
MIN_AGE: t.number().min(0).max(150).required(),
|
|
388
|
-
|
|
389
|
-
// Integer constraint
|
|
390
|
-
PAGE_SIZE: t.number().integer().min(1).max(100).default(20),
|
|
391
|
-
|
|
392
|
-
// Positive / negative / non-negative
|
|
393
|
-
BALANCE: t.number().nonNegative().default(0),
|
|
394
|
-
DISCOUNT: t.number().negative().optional(),
|
|
395
|
-
|
|
396
|
-
// Finite check (no NaN or Infinity)
|
|
397
|
-
RATIO: t.number().finite().positive().required(),
|
|
398
|
-
|
|
399
|
-
// Custom parser
|
|
400
|
-
HEX_PORT: t.number().parse(v => parseInt(v, 16)).default(0x1F90),
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
| Method | Parameter | Description |
|
|
404
|
-
|---|---|---|
|
|
405
|
-
| `.min(n)` | `number` | Minimum value |
|
|
406
|
-
| `.max(n)` | `number` | Maximum value |
|
|
407
|
-
| `.integer()` | — | Must be integer |
|
|
408
|
-
| `.positive()` | — | Must be > 0 |
|
|
409
|
-
| `.negative()` | — | Must be < 0 |
|
|
410
|
-
| `.nonNegative()` | — | Must be >= 0 |
|
|
411
|
-
| `.finite()` | — | No NaN / Infinity |
|
|
412
|
-
| `.port()` | — | Valid port (1–65535) |
|
|
413
|
-
| `.parse(fn)` | `(raw) => number` | Custom parser |
|
|
414
|
-
|
|
415
|
-
---
|
|
416
|
-
|
|
417
|
-
#### `t.boolean()` — Boolean Validation
|
|
418
|
-
|
|
419
|
-
```typescript
|
|
420
|
-
DEBUG: t.boolean().default(false),
|
|
421
|
-
ENABLE_CACHE: t.boolean().required(),
|
|
422
|
-
|
|
423
|
-
// Custom truthy/falsy values
|
|
424
|
-
FEATURE_FLAG: t.boolean()
|
|
425
|
-
.truthy(['on', 'enabled', '1', 'yes'])
|
|
426
|
-
.falsy(['off', 'disabled', '0', 'no'])
|
|
427
|
-
.default(false),
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
Default truthy: `'true'`, `'1'`, `'yes'`, `'on'`, `'TRUE'`, `'True'`, `'YES'`, `'ON'`
|
|
431
|
-
|
|
432
|
-
Default falsy: `'false'`, `'0'`, `'no'`, `'off'`, `'FALSE'`, `'False'`, `'NO'`, `'OFF'`, `''`
|
|
433
|
-
|
|
434
|
-
---
|
|
435
|
-
|
|
436
|
-
#### `t.enum()` — Enum / Literal Union
|
|
437
|
-
|
|
438
|
-
```typescript
|
|
439
|
-
// TypeScript literal union inference
|
|
440
|
-
NODE_ENV: t.enum(['development', 'staging', 'production'] as const).required(),
|
|
441
|
-
// Type: 'development' | 'staging' | 'production'
|
|
442
|
-
|
|
443
|
-
LOG_LEVEL: t.enum(['debug', 'info', 'warn', 'error', 'fatal'] as const)
|
|
444
|
-
.caseInsensitive()
|
|
445
|
-
.default('info'),
|
|
446
|
-
|
|
447
|
-
COLOR_SCHEME: t.enum(['light', 'dark', 'system'] as const).optional(),
|
|
448
|
-
```
|
|
449
|
-
|
|
450
|
-
| Method | Parameter | Description |
|
|
451
|
-
|---|---|---|
|
|
452
|
-
| `.caseInsensitive()` | `boolean?` | Case-insensitive matching |
|
|
453
|
-
|
|
454
|
-
---
|
|
455
|
-
|
|
456
|
-
### Advanced Validators
|
|
457
|
-
|
|
458
|
-
#### `t.url()` — URL Validation
|
|
459
|
-
|
|
460
|
-
```typescript
|
|
461
|
-
PUBLIC_URL: t.url().required(),
|
|
462
|
-
API_ENDPOINT: t.url({ protocols: ['https'] }).required(),
|
|
463
|
-
REDIRECT_URL: t.url({ allowRelative: true }).optional(),
|
|
464
|
-
```
|
|
465
|
-
|
|
466
|
-
Options: `protocols`, `allowRelative`, `allowQuery`, `requireTld`
|
|
467
|
-
|
|
468
|
-
---
|
|
469
|
-
|
|
470
|
-
#### `t.email()` — Email Validation
|
|
471
|
-
|
|
472
|
-
```typescript
|
|
473
|
-
ADMIN_EMAIL: t.email().required(),
|
|
474
|
-
CONTACT: t.email({ allowDisplayName: true }).optional(),
|
|
475
|
-
```
|
|
476
|
-
|
|
477
|
-
---
|
|
478
|
-
|
|
479
|
-
#### `t.ip()` — IP Address Validation
|
|
480
|
-
|
|
481
|
-
```typescript
|
|
482
|
-
SERVER_IP: t.ip().required(), // IPv4 or IPv6
|
|
483
|
-
BIND_ADDRESS: t.ipv4().required(), // IPv4 only
|
|
484
|
-
LISTEN_IPV6: t.ipv6().optional(), // IPv6 only
|
|
485
|
-
```
|
|
486
|
-
|
|
487
|
-
---
|
|
488
|
-
|
|
489
|
-
#### `t.hostname()` — Hostname Validation
|
|
490
|
-
|
|
491
|
-
```typescript
|
|
492
|
-
SERVER_HOST: t.hostname().default('localhost'),
|
|
493
|
-
ALLOWED_HOST: t.hostname({ allowWildcard: true }).required(),
|
|
494
|
-
```
|
|
495
|
-
|
|
496
|
-
---
|
|
497
|
-
|
|
498
|
-
#### `t.port()` — Port Validation
|
|
499
|
-
|
|
500
|
-
```typescript
|
|
501
|
-
PORT: t.port().default(3000),
|
|
502
|
-
REDIS_PORT: t.port().default(6379),
|
|
503
|
-
```
|
|
504
|
-
|
|
505
|
-
Validates 1–65535 range.
|
|
506
|
-
|
|
507
|
-
---
|
|
508
|
-
|
|
509
|
-
#### `t.uuid()` — UUID Validation
|
|
510
|
-
|
|
511
|
-
```typescript
|
|
512
|
-
REQUEST_ID: t.uuid().required(),
|
|
513
|
-
SESSION_ID: t.uuid({ version: 4 }).required(),
|
|
514
|
-
```
|
|
515
|
-
|
|
516
|
-
Supports versions: 1, 3, 4, 5, or `null` for any.
|
|
517
|
-
|
|
518
|
-
---
|
|
519
|
-
|
|
520
|
-
#### `t.array()` — Array (Delimited String)
|
|
521
|
-
|
|
522
|
-
```typescript
|
|
523
|
-
ALLOWED_ORIGINS: t.array().required(), // comma-separated
|
|
524
|
-
TAGS: t.array().separator(';').required(), // semicolon-separated
|
|
525
|
-
FEATURES: t.array().trimItems().filterEmpty().required(), // clean items
|
|
526
|
-
ROLES: t.array().minItems(1).maxItems(10).unique().required(),
|
|
527
|
-
```
|
|
528
|
-
|
|
529
|
-
| Method | Parameter | Description |
|
|
530
|
-
|---|---|---|
|
|
531
|
-
| `.separator(sep)` | `string` | Split character (default: `,`) |
|
|
532
|
-
| `.trimItems()` | `boolean?` | Trim whitespace from items |
|
|
533
|
-
| `.filterEmpty()` | `boolean?` | Remove empty strings |
|
|
534
|
-
| `.minItems(n)` | `number` | Minimum items |
|
|
535
|
-
| `.maxItems(n)` | `number` | Maximum items |
|
|
536
|
-
| `.unique()` | — | Deduplicate items |
|
|
537
|
-
|
|
538
|
-
---
|
|
539
|
-
|
|
540
|
-
#### `t.json()` — JSON Parsing
|
|
541
|
-
|
|
542
|
-
```typescript
|
|
543
|
-
FEATURE_FLAGS: t.json().required(), // unknown
|
|
544
|
-
APP_CONFIG: t.json<{ theme: string; lang: string }>().required(),
|
|
545
|
-
MIDDLEWARE_CONFIG: t.json().reviver((key, value) => value).optional(),
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
---
|
|
549
|
-
|
|
550
|
-
#### `t.date()` — Date Validation
|
|
551
|
-
|
|
552
|
-
```typescript
|
|
553
|
-
START_DATE: t.date().required(),
|
|
554
|
-
EXPIRY: t.date().min(new Date('2024-01-01')).optional(),
|
|
555
|
-
CREATED: t.date().format('YYYY-MM-DD').required(),
|
|
556
|
-
```
|
|
557
|
-
|
|
558
|
-
---
|
|
559
|
-
|
|
560
|
-
#### `t.bigint()` — BigInt Validation
|
|
561
|
-
|
|
562
|
-
```typescript
|
|
563
|
-
MAX_SAFE_INTEGER: t.bigint().required(),
|
|
564
|
-
SATOSHIS: t.bigint().min(0n).parse(v => BigInt(v)).required(),
|
|
565
|
-
```
|
|
566
|
-
|
|
567
|
-
---
|
|
568
|
-
|
|
569
|
-
#### `t.regex()` — Regex Validation
|
|
570
|
-
|
|
571
|
-
```typescript
|
|
572
|
-
PATTERN: t.regex().required(),
|
|
573
|
-
ROUTE_MATCHER: t.regex({ flags: 'i' }).optional(),
|
|
574
|
-
```
|
|
575
|
-
|
|
576
|
-
---
|
|
577
|
-
|
|
578
|
-
### String Format Validators
|
|
579
|
-
|
|
580
|
-
#### `t.hex()` — Hexadecimal String
|
|
581
|
-
|
|
582
|
-
```typescript
|
|
583
|
-
COLOR: t.hex().required(),
|
|
584
|
-
API_KEY_HEX: t.hex({ minLength: 32, maxLength: 64 }).required(),
|
|
585
|
-
```
|
|
586
|
-
|
|
587
|
-
---
|
|
588
|
-
|
|
589
|
-
#### `t.base64()` — Base64 String
|
|
590
|
-
|
|
591
|
-
```typescript
|
|
592
|
-
ENCODED_DATA: t.base64().required(),
|
|
593
|
-
CERT_B64: t.base64({ paddingRequired: true }).required(),
|
|
594
|
-
```
|
|
595
|
-
|
|
596
|
-
---
|
|
597
|
-
|
|
598
|
-
#### `t.semver()` — Semantic Version
|
|
599
|
-
|
|
600
|
-
```typescript
|
|
601
|
-
APP_VERSION: t.semver().required(),
|
|
602
|
-
MIN_VERSION: t.semver({ loose: true }).optional(),
|
|
603
|
-
```
|
|
604
|
-
|
|
605
|
-
---
|
|
606
|
-
|
|
607
|
-
#### `t.cron()` — Cron Expression
|
|
608
|
-
|
|
609
|
-
```typescript
|
|
610
|
-
SCHEDULE: t.cron().required(),
|
|
611
|
-
BACKUP_CRON: t.cron({ allowSeconds: true }).default('0 2 * * *'),
|
|
612
|
-
```
|
|
613
|
-
|
|
614
|
-
---
|
|
615
|
-
|
|
616
|
-
#### `t.duration()` — Duration String
|
|
617
177
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
MAX_UPLOAD: t.bytes().default('10MB'),
|
|
633
|
-
MEMORY_LIMIT: t.bytes().default('512MB'),
|
|
634
|
-
DISK_QUOTA: t.bytes().default('1GB'),
|
|
178
|
+
t.string().format('url').required()
|
|
179
|
+
t.number().port().default(3000)
|
|
180
|
+
t.boolean().default(false)
|
|
181
|
+
t.enum(['a', 'b'] as const).required()
|
|
182
|
+
t.url({ protocols: ['https'] }).required()
|
|
183
|
+
t.email().optional()
|
|
184
|
+
t.array().separator(';').trimItems().required()
|
|
185
|
+
t.json<{ theme: string }>().required()
|
|
186
|
+
t.duration().default('1h')
|
|
187
|
+
t.bytes().default('10MB')
|
|
188
|
+
t.path({ mustExist: false }).default('./uploads')
|
|
189
|
+
t.uuid({ version: 4 }).required()
|
|
190
|
+
t.ip().required()
|
|
191
|
+
t.cron().default('0 2 * * *')
|
|
635
192
|
```
|
|
636
193
|
|
|
637
|
-
Supported units: `B`, `KB`, `MB`, `GB`, `TB`, `PB`
|
|
638
|
-
|
|
639
194
|
---
|
|
640
195
|
|
|
641
|
-
|
|
196
|
+
## 🔐 Encryption & Vault
|
|
642
197
|
|
|
643
|
-
```
|
|
644
|
-
|
|
645
|
-
|
|
198
|
+
```bash
|
|
199
|
+
ultraenv vault init --env production
|
|
200
|
+
ultraenv vault encrypt --env production
|
|
201
|
+
git add .env.vault # safe to commit!
|
|
202
|
+
ultraenv vault decrypt --env production
|
|
646
203
|
```
|
|
647
204
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
```typescript
|
|
653
|
-
DEFAULT_LOCALE: t.locale().default('en-US'),
|
|
654
|
-
SUPPORTED_LOCALES: t.locale().required(),
|
|
655
|
-
```
|
|
205
|
+
- **Algorithm**: AES-256-GCM
|
|
206
|
+
- `.env.vault` → commit ✅
|
|
207
|
+
- `.env.keys` → gitignore ❌
|
|
656
208
|
|
|
657
209
|
---
|
|
658
210
|
|
|
659
|
-
|
|
211
|
+
## 🔍 Secret Scanning
|
|
660
212
|
|
|
661
|
-
```
|
|
662
|
-
|
|
663
|
-
|
|
213
|
+
```bash
|
|
214
|
+
ultraenv scan # Scan files
|
|
215
|
+
ultraenv scan --scope git-history # Scan git history
|
|
216
|
+
ultraenv scan --format sarif --output results.sarif # GitHub Code Scanning
|
|
664
217
|
```
|
|
665
218
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
---
|
|
669
|
-
|
|
670
|
-
#### `t.country()` — ISO Country Code
|
|
671
|
-
|
|
672
|
-
```typescript
|
|
673
|
-
COUNTRY: t.country().required(), // ISO 3166-1 alpha-2
|
|
674
|
-
REGION: t.country({ format: 'alpha-3' }).optional(), // ISO 3166-1 alpha-3
|
|
675
|
-
```
|
|
219
|
+
55+ patterns: AWS, GitHub, Google, Stripe, Slack, private keys, DB URLs, and more.
|
|
676
220
|
|
|
677
221
|
---
|
|
678
222
|
|
|
679
|
-
|
|
223
|
+
## 🤝 Contributing
|
|
680
224
|
|
|
681
|
-
```
|
|
682
|
-
|
|
225
|
+
```bash
|
|
226
|
+
git clone https://github.com/Avinashvelu03/ultraenv.git
|
|
227
|
+
cd ultraenv && npm install
|
|
228
|
+
npm test
|
|
229
|
+
npm run build
|
|
683
230
|
```
|
|
684
231
|
|
|
685
232
|
---
|
|
686
233
|
|
|
687
|
-
|
|
234
|
+
## 📜 License
|
|
688
235
|
|
|
689
|
-
|
|
690
|
-
CONFIG_PATH: t.path().required(),
|
|
691
|
-
OUTPUT_DIR: t.path({ mustExist: false }).optional(),
|
|
692
|
-
LOG_FILE: t.path({ mustExist: false, resolve: true }).default('/var/log/app.log'),
|
|
693
|
-
```
|
|
236
|
+
[MIT](LICENSE) © 2024 [Avinash Velu](https://github.com/Avinashvelu03)
|
|
694
237
|
|
|
695
238
|
---
|
|
696
239
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
All validators support these common modifiers:
|
|
700
|
-
|
|
701
|
-
#### `.required()` / `.optional()`
|
|
702
|
-
|
|
703
|
-
```typescript
|
|
704
|
-
REQUIRED_VAR: t.string().required(), // throws if missing
|
|
705
|
-
OPTIONAL_VAR: t.string().optional(), // value is string | undefined
|
|
706
|
-
```
|
|
240
|
+
## 🔐 Support ultraenv
|
|
707
241
|
|
|
708
|
-
|
|
242
|
+
<div align="center">
|
|
709
243
|
|
|
710
|
-
```typescript
|
|
711
|
-
PORT: t.number().default(3000),
|
|
712
|
-
HOST: t.string().default('localhost'),
|
|
713
|
-
ENABLED: t.boolean().default(false),
|
|
714
244
|
```
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
.description('Primary PostgreSQL connection string')
|
|
722
|
-
.required(),
|
|
245
|
+
██████╗ ██████╗ ███╗ ██╗ █████╗ ████████╗███████╗
|
|
246
|
+
██╔══██╗██╔═══██╗████╗ ██║██╔══██╗╚══██╔══╝██╔════╝
|
|
247
|
+
██║ ██║██║ ██║██╔██╗ ██║███████║ ██║ █████╗
|
|
248
|
+
██║ ██║██║ ██║██║╚██╗██║██╔══██║ ██║ ██╔══╝
|
|
249
|
+
██████╔╝╚██████╔╝██║ ╚████║██║ ██║ ██║ ███████╗
|
|
250
|
+
╚═════╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝ ╚══════╝
|
|
723
251
|
```
|
|
724
252
|
|
|
725
|
-
|
|
253
|
+
> *ultraenv is solo-built and freely available to every developer on Earth.*
|
|
254
|
+
> *If it saved your secrets, saved your sanity, or caught a leak before prod — it earned your support.*
|
|
726
255
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
```typescript
|
|
730
|
-
PORT: t.number()
|
|
731
|
-
.transform(v => Math.floor(v))
|
|
732
|
-
.default(3000),
|
|
256
|
+
[](https://ko-fi.com/avinashvelu)
|
|
257
|
+
[](https://github.com/sponsors/Avinashvelu03)
|
|
733
258
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
#### `.validate(fn)`
|
|
740
|
-
|
|
741
|
-
```typescript
|
|
742
|
-
PASSWORD: t.string()
|
|
743
|
-
.minLength(12)
|
|
744
|
-
.validate(v => {
|
|
745
|
-
if (!/[A-Z]/.test(v)) return 'Must contain an uppercase letter';
|
|
746
|
-
if (!/[0-9]/.test(v)) return 'Must contain a number';
|
|
747
|
-
return undefined; // passes validation
|
|
748
|
-
})
|
|
749
|
-
.required(),
|
|
750
|
-
```
|
|
751
|
-
|
|
752
|
-
#### `.deprecated(msg)`
|
|
753
|
-
|
|
754
|
-
```typescript
|
|
755
|
-
OLD_API_KEY: t.string()
|
|
756
|
-
.deprecated('Use NEW_API_KEY instead — will be removed in v2.0')
|
|
757
|
-
.optional(),
|
|
758
|
-
```
|
|
759
|
-
|
|
760
|
-
#### `.secret()`
|
|
761
|
-
|
|
762
|
-
```typescript
|
|
763
|
-
DATABASE_PASSWORD: t.string().secret().required(),
|
|
764
|
-
```
|
|
765
|
-
|
|
766
|
-
Secret values are masked in logs, scan results, and CLI output.
|
|
767
|
-
|
|
768
|
-
#### `.alias(name)`
|
|
769
|
-
|
|
770
|
-
```typescript
|
|
771
|
-
DB_URL: t.string()
|
|
772
|
-
.alias('DATABASE_URL')
|
|
773
|
-
.format('url')
|
|
774
|
-
.required(),
|
|
775
|
-
```
|
|
776
|
-
|
|
777
|
-
Allows the variable to be referenced by an alternative name.
|
|
778
|
-
|
|
779
|
-
#### `.conditional(config)`
|
|
780
|
-
|
|
781
|
-
```typescript
|
|
782
|
-
STRIPE_KEY: t.string()
|
|
783
|
-
.conditional({
|
|
784
|
-
check: (env) => env.PAYMENT_PROVIDER === 'stripe',
|
|
785
|
-
then: (schema) => schema.required(),
|
|
786
|
-
otherwise: (schema) => schema.optional(),
|
|
787
|
-
}),
|
|
788
|
-
```
|
|
789
|
-
|
|
790
|
-
Apply different requirements based on other env values.
|
|
791
|
-
|
|
792
|
-
---
|
|
793
|
-
|
|
794
|
-
### Complete Schema Example
|
|
795
|
-
|
|
796
|
-
Here's a comprehensive real-world schema:
|
|
797
|
-
|
|
798
|
-
```typescript
|
|
799
|
-
import { defineEnv, t } from 'ultraenv';
|
|
800
|
-
|
|
801
|
-
const env = defineEnv({
|
|
802
|
-
// ── Application ──────────────────────────────────
|
|
803
|
-
NODE_ENV: t.enum(['development', 'staging', 'production'] as const)
|
|
804
|
-
.description('Application environment')
|
|
805
|
-
.default('development'),
|
|
806
|
-
|
|
807
|
-
APP_NAME: t.string()
|
|
808
|
-
.minLength(1)
|
|
809
|
-
.maxLength(100)
|
|
810
|
-
.description('Application name')
|
|
811
|
-
.required(),
|
|
812
|
-
|
|
813
|
-
APP_URL: t.url()
|
|
814
|
-
.description('Canonical application URL')
|
|
815
|
-
.required(),
|
|
816
|
-
|
|
817
|
-
// ── Server ───────────────────────────────────────
|
|
818
|
-
PORT: t.number()
|
|
819
|
-
.port()
|
|
820
|
-
.description('Server listening port')
|
|
821
|
-
.default(3000),
|
|
822
|
-
|
|
823
|
-
HOST: t.string()
|
|
824
|
-
.hostname()
|
|
825
|
-
.description('Server bind address')
|
|
826
|
-
.default('localhost'),
|
|
827
|
-
|
|
828
|
-
// ── Database ─────────────────────────────────────
|
|
829
|
-
DATABASE_URL: t.string()
|
|
830
|
-
.format('url')
|
|
831
|
-
.description('PostgreSQL connection string')
|
|
832
|
-
.secret()
|
|
833
|
-
.required(),
|
|
834
|
-
|
|
835
|
-
DATABASE_POOL_SIZE: t.number()
|
|
836
|
-
.integer()
|
|
837
|
-
.min(1)
|
|
838
|
-
.max(100)
|
|
839
|
-
.default(10),
|
|
840
|
-
|
|
841
|
-
// ── Authentication ───────────────────────────────
|
|
842
|
-
JWT_SECRET: t.string()
|
|
843
|
-
.minLength(32)
|
|
844
|
-
.description('JWT signing secret')
|
|
845
|
-
.secret()
|
|
846
|
-
.required(),
|
|
847
|
-
|
|
848
|
-
JWT_EXPIRY: t.duration()
|
|
849
|
-
.description('JWT token expiry duration')
|
|
850
|
-
.default('15m'),
|
|
851
|
-
|
|
852
|
-
REFRESH_TOKEN_EXPIRY: t.duration()
|
|
853
|
-
.description('Refresh token expiry duration')
|
|
854
|
-
.default('7d'),
|
|
855
|
-
|
|
856
|
-
// ── Redis ────────────────────────────────────────
|
|
857
|
-
REDIS_URL: t.string()
|
|
858
|
-
.format('url')
|
|
859
|
-
.optional()
|
|
860
|
-
.description('Redis connection URL'),
|
|
861
|
-
|
|
862
|
-
REDIS_TTL: t.duration()
|
|
863
|
-
.default('1h')
|
|
864
|
-
.description('Default Redis key TTL'),
|
|
865
|
-
|
|
866
|
-
// ── Email ────────────────────────────────────────
|
|
867
|
-
SMTP_HOST: t.string()
|
|
868
|
-
.hostname()
|
|
869
|
-
.optional(),
|
|
870
|
-
|
|
871
|
-
SMTP_PORT: t.number()
|
|
872
|
-
.port()
|
|
873
|
-
.default(587),
|
|
874
|
-
|
|
875
|
-
SMTP_USER: t.email()
|
|
876
|
-
.optional(),
|
|
877
|
-
|
|
878
|
-
SMTP_PASSWORD: t.string()
|
|
879
|
-
.secret()
|
|
880
|
-
.optional(),
|
|
881
|
-
|
|
882
|
-
// ── File Uploads ─────────────────────────────────
|
|
883
|
-
MAX_UPLOAD_SIZE: t.bytes()
|
|
884
|
-
.default('10MB')
|
|
885
|
-
.description('Maximum file upload size'),
|
|
886
|
-
|
|
887
|
-
UPLOAD_DIR: t.path({ mustExist: false })
|
|
888
|
-
.default('./uploads'),
|
|
889
|
-
|
|
890
|
-
// ── External APIs ────────────────────────────────
|
|
891
|
-
STRIPE_SECRET_KEY: t.string()
|
|
892
|
-
.secret()
|
|
893
|
-
.optional()
|
|
894
|
-
.deprecated('Use STRIPE_API_KEY instead'),
|
|
895
|
-
|
|
896
|
-
STRIPE_API_KEY: t.string()
|
|
897
|
-
.secret()
|
|
898
|
-
.optional(),
|
|
899
|
-
|
|
900
|
-
STRIPE_WEBHOOK_SECRET: t.string()
|
|
901
|
-
.secret()
|
|
902
|
-
.optional(),
|
|
903
|
-
|
|
904
|
-
// ── Internationalization ─────────────────────────
|
|
905
|
-
DEFAULT_LOCALE: t.locale()
|
|
906
|
-
.default('en-US'),
|
|
907
|
-
|
|
908
|
-
DEFAULT_TIMEZONE: t.timezone()
|
|
909
|
-
.default('UTC'),
|
|
910
|
-
|
|
911
|
-
// ── Feature Flags ────────────────────────────────
|
|
912
|
-
ENABLE_ANALYTICS: t.boolean()
|
|
913
|
-
.default(false),
|
|
914
|
-
|
|
915
|
-
ENABLE_RATE_LIMITING: t.boolean()
|
|
916
|
-
.default(true),
|
|
917
|
-
|
|
918
|
-
RATE_LIMIT_MAX: t.number()
|
|
919
|
-
.positive()
|
|
920
|
-
.default(100),
|
|
921
|
-
|
|
922
|
-
// ── Logging ──────────────────────────────────────
|
|
923
|
-
LOG_LEVEL: t.enum(['debug', 'info', 'warn', 'error', 'fatal'] as const)
|
|
924
|
-
.default('info'),
|
|
925
|
-
|
|
926
|
-
LOG_FORMAT: t.enum(['json', 'text', 'pretty'] as const)
|
|
927
|
-
.default('json'),
|
|
928
|
-
|
|
929
|
-
// ── CORS ─────────────────────────────────────────
|
|
930
|
-
ALLOWED_ORIGINS: t.array()
|
|
931
|
-
.separator(';')
|
|
932
|
-
.trimItems()
|
|
933
|
-
.default(['http://localhost:3000']),
|
|
934
|
-
|
|
935
|
-
// ── Health & Monitoring ──────────────────────────
|
|
936
|
-
HEALTH_CHECK_PATH: t.string()
|
|
937
|
-
.default('/health'),
|
|
938
|
-
|
|
939
|
-
SENTRY_DSN: t.url({ allowRelative: false })
|
|
940
|
-
.optional(),
|
|
941
|
-
|
|
942
|
-
// ── Cron Jobs ────────────────────────────────────
|
|
943
|
-
CLEANUP_CRON: t.cron()
|
|
944
|
-
.default('0 3 * * *')
|
|
945
|
-
.description('Daily cleanup schedule (3 AM)'),
|
|
946
|
-
});
|
|
947
|
-
|
|
948
|
-
export default env;
|
|
949
|
-
|
|
950
|
-
// All values are fully typed:
|
|
951
|
-
// env.DATABASE_URL → string
|
|
952
|
-
// env.PORT → number
|
|
953
|
-
// env.NODE_ENV → 'development' | 'staging' | 'production'
|
|
954
|
-
// env.MAX_UPLOAD_SIZE → string
|
|
955
|
-
// env.JWT_EXPIRY → string
|
|
956
|
-
// env.ALLOWED_ORIGINS → readonly string[]
|
|
957
|
-
// env.STRIPE_API_KEY → string | undefined
|
|
958
|
-
```
|
|
959
|
-
|
|
960
|
-
---
|
|
961
|
-
|
|
962
|
-
## 🔐 Encryption & Vault
|
|
963
|
-
|
|
964
|
-
ultraenv includes a built-in encrypted vault for securely managing secrets across environments.
|
|
965
|
-
|
|
966
|
-
### Quick Setup
|
|
967
|
-
|
|
968
|
-
```bash
|
|
969
|
-
# 1. Initialize the vault
|
|
970
|
-
ultraenv vault init --env production
|
|
971
|
-
|
|
972
|
-
# 2. Encrypt your environment
|
|
973
|
-
ultraenv vault encrypt --env production
|
|
974
|
-
|
|
975
|
-
# 3. Commit the vault (safe — it's encrypted!)
|
|
976
|
-
git add .env.vault
|
|
977
|
-
git commit -m "Add encrypted production vault"
|
|
978
|
-
|
|
979
|
-
# 4. Team members decrypt with the key
|
|
980
|
-
ultraenv vault decrypt --env production
|
|
981
|
-
```
|
|
982
|
-
|
|
983
|
-
### Programmatic API
|
|
984
|
-
|
|
985
|
-
```typescript
|
|
986
|
-
import {
|
|
987
|
-
encryptValue,
|
|
988
|
-
decryptValue,
|
|
989
|
-
isEncryptedValue,
|
|
990
|
-
generateMasterKey,
|
|
991
|
-
formatKey,
|
|
992
|
-
} from 'ultraenv';
|
|
993
|
-
|
|
994
|
-
// Generate a new master key
|
|
995
|
-
const key = generateMasterKey(); // Buffer (32 bytes)
|
|
996
|
-
const keyFormatted = formatKey(key); // "ultraenv_key_v1_..."
|
|
997
|
-
|
|
998
|
-
// Encrypt a single value
|
|
999
|
-
const encrypted = await encryptValue(
|
|
1000
|
-
'my-super-secret-password',
|
|
1001
|
-
key,
|
|
1002
|
-
);
|
|
1003
|
-
// → "encrypted:v1:aes-256-gcm:...base64..."
|
|
1004
|
-
|
|
1005
|
-
// Check if a value is encrypted
|
|
1006
|
-
isEncryptedValue(encrypted); // true
|
|
1007
|
-
|
|
1008
|
-
// Decrypt
|
|
1009
|
-
const decrypted = await decryptValue(encrypted, key);
|
|
1010
|
-
// → "my-super-secret-password"
|
|
1011
|
-
```
|
|
1012
|
-
|
|
1013
|
-
### Vault Workflow
|
|
1014
|
-
|
|
1015
|
-
```bash
|
|
1016
|
-
# Full workflow
|
|
1017
|
-
ultraenv vault init --env development
|
|
1018
|
-
ultraenv vault init --env staging
|
|
1019
|
-
ultraenv vault init --env production
|
|
1020
|
-
|
|
1021
|
-
ultraenv vault encrypt --env production
|
|
1022
|
-
|
|
1023
|
-
# Check status
|
|
1024
|
-
ultraenv vault status
|
|
1025
|
-
|
|
1026
|
-
# Compare local vs vault
|
|
1027
|
-
ultraenv vault diff
|
|
1028
|
-
|
|
1029
|
-
# Verify integrity
|
|
1030
|
-
ultraenv vault verify
|
|
1031
|
-
|
|
1032
|
-
# Rotate keys (re-encrypt with new key)
|
|
1033
|
-
ultraenv vault rekey --env production
|
|
1034
|
-
```
|
|
1035
|
-
|
|
1036
|
-
### Vault Files
|
|
1037
|
-
|
|
1038
|
-
| File | Description | Git |
|
|
1039
|
-
|---|---|---|
|
|
1040
|
-
| `.env.vault` | Encrypted secrets (safe to commit) | ✅ Commit |
|
|
1041
|
-
| `.env.keys` | Decryption keys (NEVER commit) | ❌ Gitignore |
|
|
1042
|
-
|
|
1043
|
-
### Encryption Details
|
|
1044
|
-
|
|
1045
|
-
- **Algorithm**: AES-256-GCM (authenticated encryption)
|
|
1046
|
-
- **Key derivation**: HKDF with random salt
|
|
1047
|
-
- **IV**: 12-byte random nonce per value
|
|
1048
|
-
- **Auth tag**: 16-byte GCM authentication tag
|
|
1049
|
-
- **Key format**: `ultraenv_key_v1_<base64>` (32-byte / 256-bit key)
|
|
1050
|
-
|
|
1051
|
-
> 📖 See [docs/VAULT_GUIDE.md](docs/VAULT_GUIDE.md) for the complete vault documentation.
|
|
1052
|
-
|
|
1053
|
-
---
|
|
1054
|
-
|
|
1055
|
-
## 🔍 Secret Scanning
|
|
1056
|
-
|
|
1057
|
-
ultraenv includes a powerful secret scanner that detects **55+ patterns** for leaked credentials, API keys, tokens, and secrets in your codebase.
|
|
1058
|
-
|
|
1059
|
-
### Quick Scan
|
|
1060
|
-
|
|
1061
|
-
```bash
|
|
1062
|
-
# Scan current directory
|
|
1063
|
-
ultraenv scan
|
|
1064
|
-
|
|
1065
|
-
# Scan specific paths
|
|
1066
|
-
ultraenv scan src/ config/
|
|
1067
|
-
|
|
1068
|
-
# Scan git history (thorough)
|
|
1069
|
-
ultraenv scan --scope git-history
|
|
1070
|
-
|
|
1071
|
-
# Scan staged files (pre-commit)
|
|
1072
|
-
ultraenv scan --scope staged
|
|
1073
|
-
|
|
1074
|
-
# Scan diff against main
|
|
1075
|
-
ultraenv scan --scope diff --from main
|
|
1076
|
-
|
|
1077
|
-
# Scan everything
|
|
1078
|
-
ultraenv scan --scope all
|
|
1079
|
-
```
|
|
1080
|
-
|
|
1081
|
-
### Output Formats
|
|
1082
|
-
|
|
1083
|
-
```bash
|
|
1084
|
-
# Terminal (default — human-readable)
|
|
1085
|
-
ultraenv scan --format terminal
|
|
1086
|
-
|
|
1087
|
-
# JSON (for integrations)
|
|
1088
|
-
ultraenv scan --format json
|
|
1089
|
-
|
|
1090
|
-
# SARIF (for GitHub Code Scanning)
|
|
1091
|
-
ultraenv scan --format sarif --output results.sarif
|
|
1092
|
-
```
|
|
1093
|
-
|
|
1094
|
-
### Detected Secret Categories
|
|
1095
|
-
|
|
1096
|
-
| Category | Patterns | Examples |
|
|
1097
|
-
|---|---|---|
|
|
1098
|
-
| **AWS** | 4 | Access Key ID, Secret Key, Session Token, Account ID |
|
|
1099
|
-
| **GitHub** | 6 | PAT, OAuth Token, App Token, Refresh Token, Webhook Secret |
|
|
1100
|
-
| **Google** | 5 | API Key, OAuth Client ID/Secret, Firebase Key, Service Account |
|
|
1101
|
-
| **Stripe** | 3 | Secret Key, Publishable Key, Restricted Key |
|
|
1102
|
-
| **Slack** | 4 | Bot Token, User Token, App Token, Webhook URL |
|
|
1103
|
-
| **Private Keys** | 8 | RSA, EC, DSA, OpenSSH, PGP, PKCS#8, Encrypted, Certificate |
|
|
1104
|
-
| **Database** | 5 | MongoDB, PostgreSQL, MySQL, Redis, CouchDB URLs |
|
|
1105
|
-
| **Cloud** | 4 | Azure, DigitalOcean, Heroku, Cloudflare |
|
|
1106
|
-
| **Auth** | 4 | JWT, Base64 Creds, Auth0, Generic Secret |
|
|
1107
|
-
| **Messaging** | 2 | Telegram Bot, Discord Bot |
|
|
1108
|
-
| **Email** | 2 | SendGrid, Mailgun |
|
|
1109
|
-
| **DevOps** | 2 | PagerDuty, Datadog |
|
|
1110
|
-
| **Generic** | 5+ | API Key, Token, Password, High-Entropy Strings |
|
|
1111
|
-
| **.env** | 1 | Secret-like variable patterns |
|
|
1112
|
-
|
|
1113
|
-
### Programmatic API
|
|
1114
|
-
|
|
1115
|
-
```typescript
|
|
1116
|
-
import { scan, formatScanResult, addCustomPattern } from 'ultraenv';
|
|
1117
|
-
|
|
1118
|
-
// Add a custom pattern
|
|
1119
|
-
addCustomPattern({
|
|
1120
|
-
id: 'my-company-api-key',
|
|
1121
|
-
name: 'My Company API Key',
|
|
1122
|
-
pattern: /MCKEY_[A-Za-z0-9]{32}/g,
|
|
1123
|
-
confidence: 0.9,
|
|
1124
|
-
severity: 'critical',
|
|
1125
|
-
description: 'Company-specific API key',
|
|
1126
|
-
remediation: 'Rotate the key and store in a vault.',
|
|
1127
|
-
category: 'internal',
|
|
1128
|
-
});
|
|
1129
|
-
|
|
1130
|
-
// Run a scan
|
|
1131
|
-
const result = await scan({
|
|
1132
|
-
cwd: '/path/to/project',
|
|
1133
|
-
include: ['src/', 'config/'],
|
|
1134
|
-
exclude: ['**/*.test.ts', '**/node_modules/**'],
|
|
1135
|
-
scanGitHistory: false,
|
|
1136
|
-
});
|
|
1137
|
-
|
|
1138
|
-
if (result.found) {
|
|
1139
|
-
console.log(`Found ${result.secrets.length} potential secrets!`);
|
|
1140
|
-
for (const secret of result.secrets) {
|
|
1141
|
-
console.log(` ${secret.type} in ${secret.file}:${secret.line}`);
|
|
1142
|
-
console.log(` Confidence: ${(secret.confidence * 100).toFixed(0)}%`);
|
|
1143
|
-
console.log(` Remediation: ${secret.pattern.remediation}`);
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1146
|
-
```
|
|
1147
|
-
|
|
1148
|
-
### Git Hook Integration
|
|
1149
|
-
|
|
1150
|
-
```bash
|
|
1151
|
-
# Install pre-commit hook
|
|
1152
|
-
ultraenv install-hook
|
|
1153
|
-
|
|
1154
|
-
# This runs `ultraenv scan --scope staged` before every commit
|
|
1155
|
-
# Commits with detected secrets will be blocked
|
|
1156
|
-
```
|
|
1157
|
-
|
|
1158
|
-
> 📖 See [docs/SECRET_SCANNING.md](docs/SECRET_SCANNING.md) for the complete scanning documentation.
|
|
1159
|
-
|
|
1160
|
-
---
|
|
1161
|
-
|
|
1162
|
-
## 📝 TypeScript Type Generation
|
|
1163
|
-
|
|
1164
|
-
ultraenv can automatically generate TypeScript type definitions from your schema.
|
|
1165
|
-
|
|
1166
|
-
### Declaration File (`.d.ts`)
|
|
1167
|
-
|
|
1168
|
-
```bash
|
|
1169
|
-
ultraenv typegen --format declaration --out src/env.d.ts
|
|
1170
|
-
```
|
|
1171
|
-
|
|
1172
|
-
Generates:
|
|
1173
|
-
|
|
1174
|
-
```typescript
|
|
1175
|
-
// Auto-generated by ultraenv — DO NOT EDIT
|
|
1176
|
-
declare namespace NodeJS {
|
|
1177
|
-
interface ProcessEnv {
|
|
1178
|
-
/** Application environment */
|
|
1179
|
-
NODE_ENV: 'development' | 'staging' | 'production';
|
|
1180
|
-
|
|
1181
|
-
/** Primary PostgreSQL connection string */
|
|
1182
|
-
DATABASE_URL: string;
|
|
1183
|
-
|
|
1184
|
-
/** Server listening port */
|
|
1185
|
-
PORT: number;
|
|
1186
|
-
|
|
1187
|
-
/** Application URL */
|
|
1188
|
-
APP_URL: string;
|
|
1189
|
-
|
|
1190
|
-
/** JWT signing secret */
|
|
1191
|
-
JWT_SECRET: string;
|
|
1192
|
-
|
|
1193
|
-
/** Optional admin email */
|
|
1194
|
-
ADMIN_EMAIL?: string;
|
|
1195
|
-
|
|
1196
|
-
// ... all your env vars with types and JSDoc
|
|
1197
|
-
}
|
|
1198
|
-
}
|
|
1199
|
-
```
|
|
1200
|
-
|
|
1201
|
-
### TypeScript Module
|
|
1202
|
-
|
|
1203
|
-
```bash
|
|
1204
|
-
ultraenv typegen --format module --out src/env.ts
|
|
1205
|
-
```
|
|
1206
|
-
|
|
1207
|
-
Generates a typed module with runtime values:
|
|
1208
|
-
|
|
1209
|
-
```typescript
|
|
1210
|
-
// Auto-generated by ultraenv — DO NOT EDIT
|
|
1211
|
-
export interface Env {
|
|
1212
|
-
/** Application environment */
|
|
1213
|
-
readonly NODE_ENV: 'development' | 'staging' | 'production';
|
|
1214
|
-
/** Primary PostgreSQL connection string */
|
|
1215
|
-
readonly DATABASE_URL: string;
|
|
1216
|
-
// ...
|
|
1217
|
-
}
|
|
1218
|
-
|
|
1219
|
-
export const env: Env = {
|
|
1220
|
-
NODE_ENV: process.env.NODE_ENV as Env['NODE_ENV'],
|
|
1221
|
-
DATABASE_URL: process.env.DATABASE_URL as string,
|
|
1222
|
-
// ...
|
|
1223
|
-
};
|
|
1224
|
-
|
|
1225
|
-
export default env;
|
|
1226
|
-
```
|
|
1227
|
-
|
|
1228
|
-
### JSON Schema
|
|
1229
|
-
|
|
1230
|
-
```bash
|
|
1231
|
-
ultraenv typegen --format json-schema --out env.schema.json
|
|
1232
|
-
```
|
|
1233
|
-
|
|
1234
|
-
Generates a standard JSON Schema for tool integrations.
|
|
1235
|
-
|
|
1236
|
-
### Watch Mode
|
|
1237
|
-
|
|
1238
|
-
```bash
|
|
1239
|
-
ultraenv typegen --format declaration --out src/env.d.ts --watch
|
|
1240
|
-
```
|
|
1241
|
-
|
|
1242
|
-
Automatically regenerates types when your `.env` or config changes.
|
|
1243
|
-
|
|
1244
|
-
### Programmatic API
|
|
1245
|
-
|
|
1246
|
-
```typescript
|
|
1247
|
-
import {
|
|
1248
|
-
generateDeclaration,
|
|
1249
|
-
generateModule,
|
|
1250
|
-
generateJsonSchema,
|
|
1251
|
-
} from 'ultraenv';
|
|
1252
|
-
|
|
1253
|
-
// Generate declaration content
|
|
1254
|
-
const dts = generateDeclaration(envVars, schema, {
|
|
1255
|
-
interfaceName: 'Env',
|
|
1256
|
-
jsdoc: true,
|
|
1257
|
-
indent: 4,
|
|
1258
|
-
});
|
|
1259
|
-
|
|
1260
|
-
// Generate module content
|
|
1261
|
-
const moduleContent = generateModule(schema, {
|
|
1262
|
-
interfaceName: 'Env',
|
|
1263
|
-
jsdoc: true,
|
|
1264
|
-
indent: 2,
|
|
1265
|
-
});
|
|
1266
|
-
|
|
1267
|
-
// Generate JSON Schema
|
|
1268
|
-
const jsonSchema = generateJsonSchema(schema, {
|
|
1269
|
-
includeDescriptions: true,
|
|
1270
|
-
indent: 2,
|
|
1271
|
-
});
|
|
1272
|
-
```
|
|
1273
|
-
|
|
1274
|
-
> 📖 See [docs/TYPE_GENERATION.md](docs/TYPE_GENERATION.md) for the complete type generation documentation.
|
|
1275
|
-
|
|
1276
|
-
---
|
|
1277
|
-
|
|
1278
|
-
## 🔄 .env.example Sync
|
|
1279
|
-
|
|
1280
|
-
Never let your `.env.example` drift out of sync with your `.env` file again.
|
|
1281
|
-
|
|
1282
|
-
### Check Sync
|
|
1283
|
-
|
|
1284
|
-
```bash
|
|
1285
|
-
ultraenv sync --mode check
|
|
1286
|
-
```
|
|
1287
|
-
|
|
1288
|
-
Output:
|
|
1289
|
-
|
|
1290
|
-
```
|
|
1291
|
-
🔄 .env Sync Check
|
|
1292
|
-
|
|
1293
|
-
✅ 12 variables match
|
|
1294
|
-
⚠️ 2 variables missing from .env.example:
|
|
1295
|
-
- STRIPE_API_KEY
|
|
1296
|
-
- REDIS_URL
|
|
1297
|
-
ℹ️ 1 variable in .env.example but not in .env:
|
|
1298
|
-
- OLD_FEATURE_FLAG
|
|
1299
|
-
|
|
1300
|
-
ACTION NEEDED
|
|
1301
|
-
Your .env is out of sync with .env.example.
|
|
1302
|
-
Run "ultraenv sync --mode generate" to update .env.example
|
|
1303
|
-
```
|
|
1304
|
-
|
|
1305
|
-
### Generate
|
|
1306
|
-
|
|
1307
|
-
```bash
|
|
1308
|
-
ultraenv sync --mode generate
|
|
1309
|
-
```
|
|
1310
|
-
|
|
1311
|
-
Auto-generates `.env.example` with types, defaults, and descriptions.
|
|
1312
|
-
|
|
1313
|
-
### Interactive Mode
|
|
1314
|
-
|
|
1315
|
-
```bash
|
|
1316
|
-
ultraenv sync --mode interactive
|
|
1317
|
-
```
|
|
1318
|
-
|
|
1319
|
-
Shows differences and lets you review before updating.
|
|
1320
|
-
|
|
1321
|
-
### Watch Mode
|
|
1322
|
-
|
|
1323
|
-
```bash
|
|
1324
|
-
ultraenv sync --mode watch
|
|
1325
|
-
```
|
|
1326
|
-
|
|
1327
|
-
Watches `.env` for changes and auto-updates `.env.example`.
|
|
1328
|
-
|
|
1329
|
-
---
|
|
1330
|
-
|
|
1331
|
-
## 🌍 Multi-Environment Management
|
|
1332
|
-
|
|
1333
|
-
ultraenv supports **11 `.env` file variants** with proper priority cascading.
|
|
1334
|
-
|
|
1335
|
-
### Supported Files (Lowest → Highest Priority)
|
|
1336
|
-
|
|
1337
|
-
| File | Use Case |
|
|
1338
|
-
|---|---|
|
|
1339
|
-
| `.env` | Default, shared across all environments |
|
|
1340
|
-
| `.env.local` | Local overrides (gitignored) |
|
|
1341
|
-
| `.env.development` | Development-specific |
|
|
1342
|
-
| `.env.development.local` | Local dev overrides (gitignored) |
|
|
1343
|
-
| `.env.test` | Test-specific |
|
|
1344
|
-
| `.env.test.local` | Local test overrides (gitignored) |
|
|
1345
|
-
| `.env.production` | Production-specific |
|
|
1346
|
-
| `.env.production.local` | Local prod overrides (gitignored) |
|
|
1347
|
-
| `.env.staging` | Staging-specific |
|
|
1348
|
-
| `.env.staging.local` | Local staging overrides (gitignored) |
|
|
1349
|
-
| `.env.ci` | CI/CD-specific |
|
|
1350
|
-
|
|
1351
|
-
### Cascade Order
|
|
1352
|
-
|
|
1353
|
-
Variables are merged in priority order — **higher-priority files override lower**:
|
|
1354
|
-
|
|
1355
|
-
```
|
|
1356
|
-
.env → .env.local → .env.development → .env.development.local → .env.production → ...
|
|
1357
|
-
```
|
|
1358
|
-
|
|
1359
|
-
### CLI Commands
|
|
1360
|
-
|
|
1361
|
-
```bash
|
|
1362
|
-
# List all environments
|
|
1363
|
-
ultraenv envs list
|
|
1364
|
-
|
|
1365
|
-
# Compare two environments
|
|
1366
|
-
ultraenv envs compare development production
|
|
1367
|
-
|
|
1368
|
-
# Validate all environments
|
|
1369
|
-
ultraenv envs validate
|
|
1370
|
-
|
|
1371
|
-
# Create a new environment
|
|
1372
|
-
ultraenv envs create staging
|
|
1373
|
-
|
|
1374
|
-
# Switch active environment
|
|
1375
|
-
ultraenv envs switch production
|
|
1376
|
-
```
|
|
1377
|
-
|
|
1378
|
-
---
|
|
1379
|
-
|
|
1380
|
-
## 🧩 Framework Presets
|
|
1381
|
-
|
|
1382
|
-
ultraenv ships with **9 built-in framework presets** that provide ready-made schemas, file loading order, and framework-specific validation.
|
|
1383
|
-
|
|
1384
|
-
### Available Presets
|
|
1385
|
-
|
|
1386
|
-
| Preset | ID | Tags |
|
|
1387
|
-
|---|---|---|
|
|
1388
|
-
| **Next.js** | `nextjs` | `framework`, `react`, `ssr`, `fullstack` |
|
|
1389
|
-
| **Vite** | `vite` | `framework`, `build-tool` |
|
|
1390
|
-
| **Nuxt** | `nuxt` | `framework`, `vue`, `ssr` |
|
|
1391
|
-
| **Remix** | `remix` | `framework`, `react`, `ssr` |
|
|
1392
|
-
| **SvelteKit** | `sveltekit` | `framework`, `svelte`, `ssr` |
|
|
1393
|
-
| **Express** | `express` | `backend`, `server` |
|
|
1394
|
-
| **Fastify** | `fastify` | `backend`, `server` |
|
|
1395
|
-
| **Docker** | `docker` | `container`, `devops` |
|
|
1396
|
-
| **AWS Lambda** | `aws-lambda` | `serverless`, `cloud` |
|
|
1397
|
-
|
|
1398
|
-
### Using Presets
|
|
1399
|
-
|
|
1400
|
-
```typescript
|
|
1401
|
-
import { defineEnv, t, getPreset } from 'ultraenv';
|
|
1402
|
-
|
|
1403
|
-
// Get the Next.js preset schema
|
|
1404
|
-
const nextjsPreset = getPreset('nextjs');
|
|
1405
|
-
|
|
1406
|
-
// Use it directly
|
|
1407
|
-
const env = defineEnv(nextjsPreset.schema);
|
|
1408
|
-
|
|
1409
|
-
// Or extend it with your own variables
|
|
1410
|
-
const env = defineEnv({
|
|
1411
|
-
...nextjsPreset.schema,
|
|
1412
|
-
|
|
1413
|
-
// Your custom variables
|
|
1414
|
-
CUSTOM_FEATURE: t.boolean().default(false),
|
|
1415
|
-
ANALYTICS_KEY: t.string().secret().optional(),
|
|
1416
|
-
});
|
|
1417
|
-
```
|
|
1418
|
-
|
|
1419
|
-
### Next.js Preset Features
|
|
1420
|
-
|
|
1421
|
-
The Next.js preset includes:
|
|
1422
|
-
|
|
1423
|
-
- **Client/server variable separation** — warns if secrets are in `NEXT_PUBLIC_*` vars
|
|
1424
|
-
- **Common Next.js variables** — `DATABASE_URL`, `NEXTAUTH_SECRET`, `NEXT_TELEMETRY_DISABLED`, etc.
|
|
1425
|
-
- **Image optimization** — `NEXT_PUBLIC_IMAGE_DOMAINS`, `NEXT_PUBLIC_IMAGE_REMOTE_PATTERNS`
|
|
1426
|
-
- **Proper file loading order** — `.env`, `.env.local`, `.env.development`, `.env.production`
|
|
1427
|
-
|
|
1428
|
-
### Client Leak Detection
|
|
1429
|
-
|
|
1430
|
-
```typescript
|
|
1431
|
-
import { detectClientLeakCandidates } from 'ultraenv';
|
|
1432
|
-
|
|
1433
|
-
const warnings = detectClientLeakCandidates(process.env);
|
|
1434
|
-
// → ['NEXT_PUBLIC_SECRET_KEY: appears to be a secret exposed to the client bundle']
|
|
1435
|
-
```
|
|
1436
|
-
|
|
1437
|
-
> 📖 See [docs/FRAMEWORK_PRESETS.md](docs/FRAMEWORK_PRESETS.md) for complete preset documentation.
|
|
1438
|
-
|
|
1439
|
-
---
|
|
1440
|
-
|
|
1441
|
-
## 🔄 CI/CD Integration
|
|
1442
|
-
|
|
1443
|
-
### GitHub Actions Setup
|
|
1444
|
-
|
|
1445
|
-
```bash
|
|
1446
|
-
ultraenv ci setup --platform github
|
|
1447
|
-
```
|
|
1448
|
-
|
|
1449
|
-
Generates `.github/workflows/ultraenv.yml`:
|
|
1450
|
-
|
|
1451
|
-
```yaml
|
|
1452
|
-
name: Ultraenv CI
|
|
1453
|
-
|
|
1454
|
-
on:
|
|
1455
|
-
push:
|
|
1456
|
-
branches: [main]
|
|
1457
|
-
pull_request:
|
|
1458
|
-
branches: [main]
|
|
1459
|
-
|
|
1460
|
-
jobs:
|
|
1461
|
-
validate:
|
|
1462
|
-
runs-on: ubuntu-latest
|
|
1463
|
-
steps:
|
|
1464
|
-
- uses: actions/checkout@v4
|
|
1465
|
-
|
|
1466
|
-
- name: Setup Node.js
|
|
1467
|
-
uses: actions/setup-node@v4
|
|
1468
|
-
with:
|
|
1469
|
-
node-version: '20'
|
|
1470
|
-
|
|
1471
|
-
- name: Install ultraenv
|
|
1472
|
-
run: npm install -g ultraenv
|
|
1473
|
-
|
|
1474
|
-
- name: Validate environment
|
|
1475
|
-
run: ultraenv ci validate --strict
|
|
1476
|
-
|
|
1477
|
-
- name: Check .env sync
|
|
1478
|
-
run: ultraenv ci check-sync
|
|
1479
|
-
|
|
1480
|
-
- name: Scan for secrets
|
|
1481
|
-
run: ultraenv ci scan --format sarif --output results.sarif
|
|
1482
|
-
continue-on-error: true
|
|
1483
|
-
|
|
1484
|
-
- name: Upload SARIF results
|
|
1485
|
-
if: always()
|
|
1486
|
-
uses: github/codeql-action/upload-sarif@v3
|
|
1487
|
-
with:
|
|
1488
|
-
sarif_file: results.sarif
|
|
1489
|
-
continue-on-error: true
|
|
1490
|
-
```
|
|
1491
|
-
|
|
1492
|
-
### GitLab CI Setup
|
|
1493
|
-
|
|
1494
|
-
```bash
|
|
1495
|
-
ultraenv ci setup --platform gitlab
|
|
1496
|
-
```
|
|
1497
|
-
|
|
1498
|
-
### CI Commands
|
|
1499
|
-
|
|
1500
|
-
```bash
|
|
1501
|
-
# Validate in strict mode (fails on warnings)
|
|
1502
|
-
ultraenv ci validate --strict
|
|
1503
|
-
|
|
1504
|
-
# Check .env ↔ .env.example sync
|
|
1505
|
-
ultraenv ci check-sync
|
|
1506
|
-
|
|
1507
|
-
# Scan for secrets with SARIF output
|
|
1508
|
-
ultraenv ci scan --format sarif --output results.sarif
|
|
1509
|
-
```
|
|
1510
|
-
|
|
1511
|
-
### GitHub Code Scanning Integration
|
|
1512
|
-
|
|
1513
|
-
The SARIF output integrates directly with [GitHub Code Scanning](https://docs.github.com/en/code-security/code-scanning):
|
|
1514
|
-
|
|
1515
|
-
```yaml
|
|
1516
|
-
- name: Scan for secrets
|
|
1517
|
-
run: ultraenv ci scan --format sarif --output results.sarif
|
|
1518
|
-
|
|
1519
|
-
- name: Upload SARIF results
|
|
1520
|
-
uses: github/codeql-action/upload-sarif@v3
|
|
1521
|
-
with:
|
|
1522
|
-
sarif_file: results.sarif
|
|
1523
|
-
```
|
|
1524
|
-
|
|
1525
|
-
> 📖 See [docs/CI_CD_INTEGRATION.md](docs/CI_CD_INTEGRATION.md) for the complete CI/CD documentation.
|
|
1526
|
-
|
|
1527
|
-
---
|
|
1528
|
-
|
|
1529
|
-
## 📚 Programmatic API Reference
|
|
1530
|
-
|
|
1531
|
-
### Core Loading
|
|
1532
|
-
|
|
1533
|
-
```typescript
|
|
1534
|
-
import { load, loadSync, loadWithResult } from 'ultraenv';
|
|
1535
|
-
|
|
1536
|
-
// Simple load (dotenv-compatible)
|
|
1537
|
-
load();
|
|
1538
|
-
// Reads .env, sets process.env
|
|
1539
|
-
|
|
1540
|
-
// With options
|
|
1541
|
-
load({
|
|
1542
|
-
envDir: './config',
|
|
1543
|
-
expandVariables: true,
|
|
1544
|
-
overrideProcessEnv: true,
|
|
1545
|
-
mergeStrategy: 'last-wins',
|
|
1546
|
-
});
|
|
1547
|
-
|
|
1548
|
-
// Full result
|
|
1549
|
-
const result = loadWithResult();
|
|
1550
|
-
result.env // Record<string, string>
|
|
1551
|
-
result.metadata // { totalVars, filesParsed, loadTimeMs, ... }
|
|
1552
|
-
result.parsed // ParsedEnvFile[]
|
|
1553
|
-
```
|
|
1554
|
-
|
|
1555
|
-
### Schema Validation
|
|
1556
|
-
|
|
1557
|
-
```typescript
|
|
1558
|
-
import { defineEnv, tryDefineEnv, validate, t } from 'ultraenv';
|
|
1559
|
-
|
|
1560
|
-
// Strict: throws on validation failure
|
|
1561
|
-
const env = defineEnv({
|
|
1562
|
-
PORT: t.number().port().default(3000),
|
|
1563
|
-
NODE_ENV: t.enum(['development', 'production'] as const).required(),
|
|
1564
|
-
});
|
|
1565
|
-
|
|
1566
|
-
// Non-throwing variant
|
|
1567
|
-
const result = tryDefineEnv({
|
|
1568
|
-
PORT: t.number().port().default(3000),
|
|
1569
|
-
NODE_ENV: t.enum(['development', 'production'] as const).required(),
|
|
1570
|
-
});
|
|
1571
|
-
|
|
1572
|
-
if (result.valid) {
|
|
1573
|
-
console.log(result.values); // typed values
|
|
1574
|
-
} else {
|
|
1575
|
-
console.log(result.errors); // validation errors
|
|
1576
|
-
console.log(result.unknown); // unknown variables
|
|
1577
|
-
}
|
|
1578
|
-
```
|
|
1579
|
-
|
|
1580
|
-
### Watching for Changes
|
|
1581
|
-
|
|
1582
|
-
```typescript
|
|
1583
|
-
import { createWatcher } from 'ultraenv';
|
|
1584
|
-
|
|
1585
|
-
const watcher = createWatcher({
|
|
1586
|
-
files: ['.env', '.env.local'],
|
|
1587
|
-
recursive: false,
|
|
1588
|
-
debounceMs: 100,
|
|
1589
|
-
initial: true,
|
|
1590
|
-
});
|
|
1591
|
-
|
|
1592
|
-
watcher.on('change', (event) => {
|
|
1593
|
-
console.log(`File ${event.path} was ${event.type}`);
|
|
1594
|
-
// Reload your environment here
|
|
1595
|
-
});
|
|
1596
|
-
|
|
1597
|
-
watcher.start();
|
|
1598
|
-
|
|
1599
|
-
// Later...
|
|
1600
|
-
watcher.stop();
|
|
1601
|
-
```
|
|
1602
|
-
|
|
1603
|
-
### Health Checks
|
|
1604
|
-
|
|
1605
|
-
```typescript
|
|
1606
|
-
import {
|
|
1607
|
-
healthCheck,
|
|
1608
|
-
liveCheck,
|
|
1609
|
-
readinessCheck,
|
|
1610
|
-
} from 'ultraenv';
|
|
1611
|
-
|
|
1612
|
-
// Full health check (safe to expose via HTTP)
|
|
1613
|
-
const health = healthCheck();
|
|
1614
|
-
// → { status: 'ok', loaded: 42, valid: 42, environment: 'production', ... }
|
|
1615
|
-
|
|
1616
|
-
// Liveness probe (minimal)
|
|
1617
|
-
const live = liveCheck();
|
|
1618
|
-
// → { status: 'ok', timestamp: '...' }
|
|
1619
|
-
|
|
1620
|
-
// Readiness probe (checks specific vars)
|
|
1621
|
-
const ready = readinessCheck(['DATABASE_URL', 'REDIS_URL']);
|
|
1622
|
-
// → { status: 'ok', ready: true, missing: [], timestamp: '...' }
|
|
1623
|
-
```
|
|
1624
|
-
|
|
1625
|
-
### Express Middleware
|
|
1626
|
-
|
|
1627
|
-
```typescript
|
|
1628
|
-
import express from 'express';
|
|
1629
|
-
import { ultraenvMiddleware, healthCheckRoute } from 'ultraenv';
|
|
1630
|
-
|
|
1631
|
-
const app = express();
|
|
1632
|
-
|
|
1633
|
-
// Load and validate env
|
|
1634
|
-
app.use(ultraenvMiddleware({
|
|
1635
|
-
schema: {
|
|
1636
|
-
PORT: { type: 'number', port: true, default: 3000 },
|
|
1637
|
-
},
|
|
1638
|
-
}));
|
|
1639
|
-
|
|
1640
|
-
// Health check endpoint
|
|
1641
|
-
app.get('/health', healthCheckRoute());
|
|
1642
|
-
|
|
1643
|
-
// readiness endpoint
|
|
1644
|
-
app.get('/ready', readinessCheck(['DATABASE_URL']));
|
|
1645
|
-
```
|
|
1646
|
-
|
|
1647
|
-
### Fastify Plugin
|
|
1648
|
-
|
|
1649
|
-
```typescript
|
|
1650
|
-
import Fastify from 'fastify';
|
|
1651
|
-
import { createUltraenvPlugin } from 'ultraenv/fastify';
|
|
1652
|
-
|
|
1653
|
-
const app = Fastify();
|
|
1654
|
-
|
|
1655
|
-
app.register(createUltraenvPlugin({
|
|
1656
|
-
schema: {
|
|
1657
|
-
DATABASE_URL: { type: 'string', format: 'url' },
|
|
1658
|
-
},
|
|
1659
|
-
}));
|
|
1660
|
-
```
|
|
1661
|
-
|
|
1662
|
-
### Error Handling
|
|
1663
|
-
|
|
1664
|
-
```typescript
|
|
1665
|
-
import {
|
|
1666
|
-
UltraenvError,
|
|
1667
|
-
ValidationError,
|
|
1668
|
-
ParseError,
|
|
1669
|
-
EncryptionError,
|
|
1670
|
-
VaultError,
|
|
1671
|
-
ScanError,
|
|
1672
|
-
ConfigError,
|
|
1673
|
-
isUltraenvError,
|
|
1674
|
-
} from 'ultraenv';
|
|
1675
|
-
|
|
1676
|
-
try {
|
|
1677
|
-
// ... ultraenv operations
|
|
1678
|
-
} catch (error) {
|
|
1679
|
-
if (isUltraenvError(error)) {
|
|
1680
|
-
console.log(error.code); // 'VALIDATION_ERROR'
|
|
1681
|
-
console.log(error.message); // Human-readable message
|
|
1682
|
-
console.log(error.hint); // Actionable fix suggestion
|
|
1683
|
-
}
|
|
1684
|
-
}
|
|
1685
|
-
```
|
|
1686
|
-
|
|
1687
|
-
### Vault API
|
|
1688
|
-
|
|
1689
|
-
```typescript
|
|
1690
|
-
import {
|
|
1691
|
-
encryptEnvironment,
|
|
1692
|
-
decryptEnvironment,
|
|
1693
|
-
encryptValue,
|
|
1694
|
-
decryptValue,
|
|
1695
|
-
isEncryptedValue,
|
|
1696
|
-
generateMasterKey,
|
|
1697
|
-
deriveEnvironmentKey,
|
|
1698
|
-
formatKey,
|
|
1699
|
-
parseKey,
|
|
1700
|
-
generateKeysFile,
|
|
1701
|
-
parseKeysFile,
|
|
1702
|
-
rotateKey,
|
|
1703
|
-
} from 'ultraenv';
|
|
1704
|
-
```
|
|
1705
|
-
|
|
1706
|
-
### Scanner API
|
|
1707
|
-
|
|
1708
|
-
```typescript
|
|
1709
|
-
import {
|
|
1710
|
-
scan,
|
|
1711
|
-
scanFiles,
|
|
1712
|
-
scanGitHistory,
|
|
1713
|
-
scanStagedFiles,
|
|
1714
|
-
scanDiff,
|
|
1715
|
-
matchPatterns,
|
|
1716
|
-
addCustomPattern,
|
|
1717
|
-
removeCustomPattern,
|
|
1718
|
-
resetPatterns,
|
|
1719
|
-
detectHighEntropyStrings,
|
|
1720
|
-
formatScanResult,
|
|
1721
|
-
} from 'ultraenv';
|
|
1722
|
-
```
|
|
1723
|
-
|
|
1724
|
-
### Sync API
|
|
1725
|
-
|
|
1726
|
-
```typescript
|
|
1727
|
-
import {
|
|
1728
|
-
generateExampleFile,
|
|
1729
|
-
generateExampleContent,
|
|
1730
|
-
needsUpdate,
|
|
1731
|
-
compareSync,
|
|
1732
|
-
compareValues,
|
|
1733
|
-
createSyncWatcher,
|
|
1734
|
-
} from 'ultraenv';
|
|
1735
|
-
```
|
|
1736
|
-
|
|
1737
|
-
### Type Generation API
|
|
1738
|
-
|
|
1739
|
-
```typescript
|
|
1740
|
-
import {
|
|
1741
|
-
generateDeclaration,
|
|
1742
|
-
generateModule,
|
|
1743
|
-
generateJsonSchema,
|
|
1744
|
-
createTypegenWatcher,
|
|
1745
|
-
} from 'ultraenv';
|
|
1746
|
-
```
|
|
1747
|
-
|
|
1748
|
-
### Environment Management API
|
|
1749
|
-
|
|
1750
|
-
```typescript
|
|
1751
|
-
import {
|
|
1752
|
-
listEnvironments,
|
|
1753
|
-
validateAllEnvironments,
|
|
1754
|
-
switchEnvironment,
|
|
1755
|
-
getActiveEnvironment,
|
|
1756
|
-
discoverEnvironments,
|
|
1757
|
-
compareEnvironments,
|
|
1758
|
-
formatComparison,
|
|
1759
|
-
createEnvironment,
|
|
1760
|
-
removeEnvironment,
|
|
1761
|
-
duplicateEnvironment,
|
|
1762
|
-
} from 'ultraenv';
|
|
1763
|
-
```
|
|
1764
|
-
|
|
1765
|
-
### Parser API (Advanced)
|
|
1766
|
-
|
|
1767
|
-
```typescript
|
|
1768
|
-
import {
|
|
1769
|
-
parseEnvFile,
|
|
1770
|
-
expandVariables,
|
|
1771
|
-
resolveCascade,
|
|
1772
|
-
mergeCascade,
|
|
1773
|
-
} from 'ultraenv';
|
|
1774
|
-
|
|
1775
|
-
// Parse raw .env content
|
|
1776
|
-
const parsed = parseEnvFile(content, filePath);
|
|
1777
|
-
// → { path, vars: [{ key, value, raw, source, lineNumber, comment }], exists }
|
|
1778
|
-
|
|
1779
|
-
// Expand variable references
|
|
1780
|
-
const expanded = expandVariables(env, env, { maxDepth: 10 });
|
|
1781
|
-
```
|
|
1782
|
-
|
|
1783
|
-
> 📖 See [docs/API_REFERENCE.md](docs/API_REFERENCE.md) for the complete API documentation.
|
|
1784
|
-
|
|
1785
|
-
---
|
|
1786
|
-
|
|
1787
|
-
## 🔀 Migration from dotenv
|
|
1788
|
-
|
|
1789
|
-
ultraenv is a **drop-in replacement** for dotenv. Migration takes just minutes.
|
|
1790
|
-
|
|
1791
|
-
### Before (dotenv)
|
|
1792
|
-
|
|
1793
|
-
```typescript
|
|
1794
|
-
// Load .env
|
|
1795
|
-
import dotenv from 'dotenv';
|
|
1796
|
-
dotenv.config();
|
|
1797
|
-
|
|
1798
|
-
// Use env vars (always strings!)
|
|
1799
|
-
const port = parseInt(process.env.PORT || '3000', 10);
|
|
1800
|
-
const dbUrl = process.env.DATABASE_URL!;
|
|
1801
|
-
const debug = process.env.DEBUG === 'true';
|
|
1802
|
-
```
|
|
1803
|
-
|
|
1804
|
-
### After (ultraenv)
|
|
1805
|
-
|
|
1806
|
-
```typescript
|
|
1807
|
-
// Option 1: Simple drop-in replacement
|
|
1808
|
-
import { load } from 'ultraenv';
|
|
1809
|
-
load(); // That's it!
|
|
1810
|
-
|
|
1811
|
-
// Option 2: Full type safety
|
|
1812
|
-
import { defineEnv, t } from 'ultraenv';
|
|
1813
|
-
|
|
1814
|
-
const env = defineEnv({
|
|
1815
|
-
PORT: t.number().port().default(3000),
|
|
1816
|
-
DATABASE_URL: t.string().format('url').required(),
|
|
1817
|
-
DEBUG: t.boolean().default(false),
|
|
1818
|
-
});
|
|
1819
|
-
|
|
1820
|
-
// Now everything is typed!
|
|
1821
|
-
const port = env.PORT; // number
|
|
1822
|
-
const dbUrl = env.DATABASE_URL; // string
|
|
1823
|
-
const debug = env.DEBUG; // boolean
|
|
1824
|
-
```
|
|
1825
|
-
|
|
1826
|
-
### Migration Checklist
|
|
1827
|
-
|
|
1828
|
-
- [ ] Replace `import dotenv from 'dotenv'` with `import { load } from 'ultraenv'`
|
|
1829
|
-
- [ ] Replace `dotenv.config()` with `load()`
|
|
1830
|
-
- [ ] (Optional) Add a schema with `defineEnv()` for type safety
|
|
1831
|
-
- [ ] (Optional) Replace all `process.env.X` references with typed `env.X`
|
|
1832
|
-
- [ ] (Optional) Run `ultraenv scan` to find leaked secrets
|
|
1833
|
-
- [ ] (Optional) Run `ultraenv sync` to generate `.env.example`
|
|
1834
|
-
- [ ] (Optional) Set up `ultraenv ci setup` for CI/CD
|
|
1835
|
-
|
|
1836
|
-
> 📖 See [docs/MIGRATION_FROM_DOTENV.md](docs/MIGRATION_FROM_DOTENV.md) for the complete migration guide.
|
|
1837
|
-
|
|
1838
|
-
---
|
|
1839
|
-
|
|
1840
|
-
## ⚙️ Configuration Reference
|
|
1841
|
-
|
|
1842
|
-
### Config File Locations (searched in order)
|
|
1843
|
-
|
|
1844
|
-
1. `.ultraenvrc.json`
|
|
1845
|
-
2. `.ultraenvrc.yaml`
|
|
1846
|
-
3. `.ultraenvrc.yml`
|
|
1847
|
-
4. `ultraenv.config.js`
|
|
1848
|
-
5. `ultraenv.config.cjs`
|
|
1849
|
-
6. `"ultraenv"` key in `package.json`
|
|
1850
|
-
|
|
1851
|
-
### Example Configuration
|
|
1852
|
-
|
|
1853
|
-
```json
|
|
1854
|
-
{
|
|
1855
|
-
"envDir": ".",
|
|
1856
|
-
"files": [".env", ".env.local", ".env.production"],
|
|
1857
|
-
"encoding": "utf-8",
|
|
1858
|
-
"expandVariables": true,
|
|
1859
|
-
"overrideProcessEnv": false,
|
|
1860
|
-
"mergeStrategy": "last-wins",
|
|
1861
|
-
"prefixErrors": true,
|
|
1862
|
-
"silent": false,
|
|
1863
|
-
"outputFormat": "terminal",
|
|
1864
|
-
"debug": false,
|
|
1865
|
-
"maxValueLength": 1048576,
|
|
1866
|
-
"maxInterpolationDepth": 10,
|
|
1867
|
-
"schema": {
|
|
1868
|
-
"PORT": { "type": "number", "port": true, "default": 3000 },
|
|
1869
|
-
"DATABASE_URL": { "type": "string", "format": "url" }
|
|
1870
|
-
},
|
|
1871
|
-
"vault": {
|
|
1872
|
-
"vaultFile": ".env.vault",
|
|
1873
|
-
"keysFile": ".env.keys",
|
|
1874
|
-
"environment": "production",
|
|
1875
|
-
"autoGenerateKey": true
|
|
1876
|
-
},
|
|
1877
|
-
"scan": {
|
|
1878
|
-
"include": ["src/", "config/"],
|
|
1879
|
-
"exclude": ["**/*.test.ts", "node_modules/**"],
|
|
1880
|
-
"scanGitHistory": false,
|
|
1881
|
-
"maxFileSize": 1048576,
|
|
1882
|
-
"failFast": false
|
|
1883
|
-
}
|
|
1884
|
-
}
|
|
1885
|
-
```
|
|
1886
|
-
|
|
1887
|
-
### Configuration Options
|
|
1888
|
-
|
|
1889
|
-
| Option | Type | Default | Description |
|
|
1890
|
-
|---|---|---|---|
|
|
1891
|
-
| `envDir` | `string` | `'.'` | Directory containing `.env` files |
|
|
1892
|
-
| `files` | `string[]` | `[]` | Specific `.env` files to load |
|
|
1893
|
-
| `encoding` | `string` | `'utf-8'` | File encoding |
|
|
1894
|
-
| `expandVariables` | `boolean` | `true` | Expand `$VAR` references |
|
|
1895
|
-
| `overrideProcessEnv` | `boolean` | `false` | Override existing `process.env` values |
|
|
1896
|
-
| `mergeStrategy` | `string` | `'last-wins'` | Merge strategy: `first-wins`, `last-wins`, `error-on-conflict` |
|
|
1897
|
-
| `prefixErrors` | `boolean` | `true` | Prefix error messages with "ultraenv" |
|
|
1898
|
-
| `silent` | `boolean` | `false` | Suppress all output |
|
|
1899
|
-
| `outputFormat` | `string` | `'terminal'` | Output format: `terminal`, `json`, `silent` |
|
|
1900
|
-
| `debug` | `boolean` | `false` | Enable debug logging |
|
|
1901
|
-
| `maxValueLength` | `number` | `1048576` | Max value length in bytes (1 MB) |
|
|
1902
|
-
| `maxInterpolationDepth` | `number` | `10` | Max variable expansion depth |
|
|
1903
|
-
|
|
1904
|
-
---
|
|
1905
|
-
|
|
1906
|
-
## ❓ FAQ
|
|
1907
|
-
|
|
1908
|
-
### General
|
|
1909
|
-
|
|
1910
|
-
**Q: Is ultraenv really zero dependencies?**
|
|
1911
|
-
|
|
1912
|
-
A: Yes! ultraenv has zero runtime dependencies. The entire library (parser, validator, scanner, vault, CLI, typegen) is built with Node.js built-in modules only. Dev dependencies (TypeScript, Vitest, ESLint, tsup) are only used during development.
|
|
1913
|
-
|
|
1914
|
-
**Q: Does it work with ESM and CommonJS?**
|
|
1915
|
-
|
|
1916
|
-
A: Yes! ultraenv ships dual CJS/ESM builds:
|
|
1917
|
-
- `import { load } from 'ultraenv'` (ESM)
|
|
1918
|
-
- `const { load } = require('ultraenv')` (CJS)
|
|
1919
|
-
|
|
1920
|
-
**Q: What Node.js versions are supported?**
|
|
1921
|
-
|
|
1922
|
-
A: Node.js >= 18.0.0. This aligns with the current Node.js LTS cycle.
|
|
1923
|
-
|
|
1924
|
-
### Schema & Validation
|
|
1925
|
-
|
|
1926
|
-
**Q: Can I use zod schemas with ultraenv?**
|
|
1927
|
-
|
|
1928
|
-
A: ultraenv has its own schema engine that's purpose-built for env vars. It's lighter and has env-specific validators (port, URL, duration, bytes, etc.) that zod doesn't provide out of the box. However, you can convert between them if needed.
|
|
1929
|
-
|
|
1930
|
-
**Q: What happens when validation fails?**
|
|
1931
|
-
|
|
1932
|
-
A: `defineEnv()` throws a detailed error with all validation failures. Use `tryDefineEnv()` for a non-throwing variant that returns `{ valid, errors, warnings, values }`.
|
|
1933
|
-
|
|
1934
|
-
**Q: Can I validate without throwing?**
|
|
1935
|
-
|
|
1936
|
-
A: Yes! Use `tryDefineEnv()`:
|
|
1937
|
-
|
|
1938
|
-
```typescript
|
|
1939
|
-
const result = tryDefineEnv(schema);
|
|
1940
|
-
if (!result.valid) {
|
|
1941
|
-
console.log(result.errors);
|
|
1942
|
-
}
|
|
1943
|
-
```
|
|
1944
|
-
|
|
1945
|
-
**Q: How do default values work?**
|
|
1946
|
-
|
|
1947
|
-
A: If a variable is not set in the environment, the default value is used. If no default is set and the variable is not optional, validation fails.
|
|
1948
|
-
|
|
1949
|
-
### Vault & Security
|
|
1950
|
-
|
|
1951
|
-
**Q: Is the vault safe to commit to git?**
|
|
1952
|
-
|
|
1953
|
-
A: Yes! The `.env.vault` file contains only AES-256-GCM encrypted data. Without the decryption key (stored in `.env.keys`, which is gitignored), the vault contents are unreadable.
|
|
1954
|
-
|
|
1955
|
-
**Q: What if I lose my encryption key?**
|
|
1956
|
-
|
|
1957
|
-
A: You cannot recover encrypted values without the key. This is by design. Use `ultraenv vault rekey` to rotate keys, and always store keys in a secure secrets manager (e.g., GitHub Secrets, AWS Secrets Manager, HashiCorp Vault).
|
|
1958
|
-
|
|
1959
|
-
**Q: How does key rotation work?**
|
|
1960
|
-
|
|
1961
|
-
A: `ultraenv vault rekey` generates a new key and re-encrypts all values in the vault. Update your `.env.keys` file and CI secrets with the new key.
|
|
1962
|
-
|
|
1963
|
-
### Secret Scanning
|
|
1964
|
-
|
|
1965
|
-
**Q: How accurate is the secret scanner?**
|
|
1966
|
-
|
|
1967
|
-
A: The scanner uses a combination of regex pattern matching (55+ patterns) and Shannon entropy analysis. Each pattern has a confidence score. High-confidence patterns (>0.9) are very accurate; lower-confidence patterns may have false positives.
|
|
1968
|
-
|
|
1969
|
-
**Q: Can I add custom patterns?**
|
|
1970
|
-
|
|
1971
|
-
A: Yes! Use `addCustomPattern()`:
|
|
1972
|
-
|
|
1973
|
-
```typescript
|
|
1974
|
-
import { addCustomPattern } from 'ultraenv';
|
|
1975
|
-
addCustomPattern({
|
|
1976
|
-
id: 'my-company-token',
|
|
1977
|
-
name: 'My Company Token',
|
|
1978
|
-
pattern: /MYCO_[A-Za-z0-9]{32}/g,
|
|
1979
|
-
confidence: 0.9,
|
|
1980
|
-
severity: 'critical',
|
|
1981
|
-
description: 'Company internal API token',
|
|
1982
|
-
remediation: 'Rotate and store in vault.',
|
|
1983
|
-
category: 'internal',
|
|
1984
|
-
});
|
|
1985
|
-
```
|
|
1986
|
-
|
|
1987
|
-
**Q: Can I suppress false positives?**
|
|
1988
|
-
|
|
1989
|
-
A: Add a comment `// ultraenv-ignore` or `# ultraenv-ignore` on the line before or on the same line as the detected secret.
|
|
1990
|
-
|
|
1991
|
-
### CI/CD
|
|
1992
|
-
|
|
1993
|
-
**Q: How do I use ultraenv in CI?**
|
|
1994
|
-
|
|
1995
|
-
A: Run `ultraenv ci setup --platform github` or `--platform gitlab` to generate CI config files. Or manually add the CI commands to your pipeline.
|
|
1996
|
-
|
|
1997
|
-
**Q: Does ultraenv scan integrate with GitHub Code Scanning?**
|
|
1998
|
-
|
|
1999
|
-
A: Yes! Use `ultraenv scan --format sarif --output results.sarif` and upload with the `github/codeql-action/upload-sarif` action.
|
|
2000
|
-
|
|
2001
|
-
---
|
|
2002
|
-
|
|
2003
|
-
## 🤝 Contributing
|
|
2004
|
-
|
|
2005
|
-
We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
2006
|
-
|
|
2007
|
-
### Quick Start
|
|
2008
|
-
|
|
2009
|
-
1. Fork the repository
|
|
2010
|
-
2. Clone your fork: `git clone https://github.com/your-username/ultraenv.git`
|
|
2011
|
-
3. Install dependencies: `npm install`
|
|
2012
|
-
4. Build: `npm run build`
|
|
2013
|
-
5. Run tests: `npm test`
|
|
2014
|
-
6. Run linter: `npm run lint`
|
|
2015
|
-
|
|
2016
|
-
### Development Scripts
|
|
2017
|
-
|
|
2018
|
-
| Script | Description |
|
|
2019
|
-
|---|---|
|
|
2020
|
-
| `npm run build` | Build the library |
|
|
2021
|
-
| `npm run dev` | Build in watch mode |
|
|
2022
|
-
| `npm test` | Run tests |
|
|
2023
|
-
| `npm run test:watch` | Run tests in watch mode |
|
|
2024
|
-
| `npm run test:coverage` | Run tests with coverage |
|
|
2025
|
-
| `npm run lint` | Run ESLint |
|
|
2026
|
-
| `npm run lint:fix` | Fix ESLint issues |
|
|
2027
|
-
| `npm run format` | Format with Prettier |
|
|
2028
|
-
| `npm run typecheck` | TypeScript type checking |
|
|
2029
|
-
|
|
2030
|
-
---
|
|
2031
|
-
|
|
2032
|
-
## 📜 License
|
|
2033
|
-
|
|
2034
|
-
[MIT](LICENSE) © 2024 [Avinash Velu](https://github.com/Avinashvelu03)
|
|
2035
|
-
|
|
2036
|
-
---
|
|
2037
|
-
|
|
2038
|
-
## 🙏 Acknowledgments
|
|
2039
|
-
|
|
2040
|
-
- [dotenv](https://github.com/motdotla/dotenv) — Inspiration for the core `.env` parsing
|
|
2041
|
-
- [envalid](https://github.com/af/envalid) — Inspiration for schema-based validation
|
|
2042
|
-
- [@t3-oss/env](https://github.com/t3-oss/env-core) — Inspiration for TypeScript inference
|
|
2043
|
-
- [gitleaks](https://github.com/gitleaks/gitleaks) — Inspiration for secret scanning patterns
|
|
2044
|
-
- [trufflehog](https://github.com/trufflesecurity/trufflehog) — Inspiration for secret detection
|
|
2045
|
-
|
|
2046
|
-
---
|
|
2047
|
-
|
|
2048
|
-
## ⭐ Star History
|
|
2049
|
-
|
|
2050
|
-
If you find ultraenv useful, please consider giving it a star on [GitHub](https://github.com/Avinashvelu03/ultraenv). It helps the project grow and reach more developers!
|
|
2051
|
-
|
|
2052
|
-
<div align="center">
|
|
259
|
+
**Zero-cost support:**
|
|
260
|
+
- ⭐ [Star on GitHub](https://github.com/Avinashvelu03/ultraenv)
|
|
261
|
+
- 🐛 [Report a bug or request a feature](https://github.com/Avinashvelu03/ultraenv/issues)
|
|
262
|
+
- 📣 Share ultraenv with your team or in your community
|
|
2053
263
|
|
|
2054
264
|
**Made with ❤️ by [Avinash Velu](https://github.com/Avinashvelu03)**
|
|
2055
265
|
|