env-typed-checker 0.2.3 → 0.2.4
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 +225 -190
- package/dist/{chunk-L5DK6LRX.mjs → chunk-CCJEPMPN.mjs} +55 -5
- package/dist/chunk-CCJEPMPN.mjs.map +1 -0
- package/dist/cli/index.js +187 -23
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +134 -20
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.d.mts +11 -3
- package/dist/index.d.ts +11 -3
- package/dist/index.js +54 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +2 -2
- package/dist/chunk-L5DK6LRX.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,299 +1,334 @@
|
|
|
1
|
-
# env-typed-checker
|
|
1
|
+
# env-typed-checker
|
|
2
2
|
|
|
3
|
-
Validate and parse environment variables
|
|
3
|
+
Validate and parse environment variables with a small schema, using either:
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
- TypeScript/Node API (`envDoctor`)
|
|
6
|
+
- CLI (`env-typed-checker check` and `generate`)
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- ❌ Invalid URLs / emails / JSON
|
|
10
|
-
- ❌ Values not matching allowed enums or regex patterns
|
|
11
|
-
- ✅ Optional values + defaults
|
|
12
|
-
- ✅ CLI checks for CI + .env generation
|
|
8
|
+
This package helps your app fail fast when config is wrong.
|
|
9
|
+
This README is the complete guide for installing, configuring, and using the package.
|
|
13
10
|
|
|
11
|
+
## Why use this
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
- Catch missing env vars early
|
|
14
|
+
- Parse types (`number`, `boolean`, `json`) safely
|
|
15
|
+
- Validate `url`, `email`, `enum`, and `regex` values
|
|
16
|
+
- Keep one schema for runtime + CI checks
|
|
17
|
+
- Generate `.env` / `.env.example` from schema
|
|
18
|
+
- Redact secret values in CLI validation output
|
|
16
19
|
|
|
17
|
-
##
|
|
18
|
-
|
|
19
|
-
- Simple schema syntax (`"number"`, `"boolean?"`, `"email"` `"url"`, `"json"`, `"string"`)
|
|
20
|
-
- Advanced specs: `enum` and `regex`
|
|
21
|
-
- Optional values with `?` and `optional: true`
|
|
22
|
-
- Defaults (typed + validated)
|
|
23
|
-
- CLI:
|
|
24
|
-
- `check` → validate env
|
|
25
|
-
- `generate` → generate/update `.env` from schema (no overwrite by default)
|
|
26
|
-
- Uses `.env` via `dotenv` (optional)
|
|
27
|
-
- Friendly aggregated errors (see everything that’s wrong at once)
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
## 📦 Install
|
|
20
|
+
## Install
|
|
32
21
|
|
|
33
22
|
```bash
|
|
34
23
|
npm install env-typed-checker
|
|
35
24
|
```
|
|
36
25
|
|
|
37
|
-
Or run
|
|
26
|
+
Or run directly:
|
|
38
27
|
|
|
39
28
|
```bash
|
|
40
29
|
npx env-typed-checker --help
|
|
41
30
|
```
|
|
42
31
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
```ts
|
|
46
|
-
import { envDoctor } from "env-typed-checker";
|
|
32
|
+
Requirements:
|
|
47
33
|
|
|
48
|
-
|
|
49
|
-
PORT: "number",
|
|
50
|
-
DB_URL: "url",
|
|
51
|
-
ADMIN_EMAIL: "email",
|
|
52
|
-
DEBUG: "boolean?",
|
|
53
|
-
});
|
|
34
|
+
- Node.js `>=18`
|
|
54
35
|
|
|
55
|
-
|
|
36
|
+
## Quick start (recommended): one schema for CLI + code
|
|
56
37
|
|
|
57
|
-
|
|
38
|
+
If you are new to this package, start with this pattern.
|
|
58
39
|
|
|
59
|
-
|
|
60
|
-
* DB_URL → `string` (validated as URL)
|
|
61
|
-
* ADMIN_EMAIL → `string` (validated as email)
|
|
62
|
-
* DEBUG → `boolean` | `undefined` (optional)
|
|
40
|
+
### 1) Create `env.schema.json`
|
|
63
41
|
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"PORT": { "type": "number", "default": 3000, "description": "HTTP port", "example": "8080" },
|
|
45
|
+
"DB_URL": { "type": "url", "description": "Primary database URL", "example": "postgres://user:pass@localhost:5432/app", "secret": true },
|
|
46
|
+
"ADMIN_EMAIL": { "type": "email", "description": "Ops contact email", "example": "ops@example.com" },
|
|
47
|
+
"DEBUG": "boolean?",
|
|
48
|
+
"NODE_ENV": { "type": "enum", "values": ["dev", "prod"], "default": "dev" },
|
|
49
|
+
"SLUG": { "type": "regex", "pattern": "^[a-z0-9-]+$", "flags": "i" },
|
|
50
|
+
"FEATURE_FLAGS": { "type": "json", "default": { "beta": false } }
|
|
51
|
+
}
|
|
52
|
+
```
|
|
64
53
|
|
|
65
|
-
###
|
|
66
|
-
| Type | Description |
|
|
67
|
-
| :--- | :--- |
|
|
68
|
-
| **string** | Any string value |
|
|
69
|
-
| **number** | A finite number (automatically parsed from string) |
|
|
70
|
-
| **boolean** | Supports `true` / `false`, `1` / `0`, and `yes` / `no` |
|
|
71
|
-
| **json** | Validates and parses a valid JSON string |
|
|
72
|
-
| **url** | Validates for a properly formatted URL |
|
|
73
|
-
| **email** | Validates for a properly formatted email |
|
|
54
|
+
### 2) Reuse the same schema in app code
|
|
74
55
|
|
|
56
|
+
ESM (NodeNext / `"type": "module"`):
|
|
75
57
|
|
|
76
|
-
### Optional Values
|
|
77
|
-
Add ? to make a variable optional:
|
|
78
58
|
```ts
|
|
79
|
-
|
|
80
|
-
|
|
59
|
+
// src/config.ts
|
|
60
|
+
import { envDoctor, type EnvDoctorSchema } from "env-typed-checker";
|
|
61
|
+
import schema from "../env.schema.json" assert { type: "json" };
|
|
81
62
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
```ts
|
|
85
|
-
envDoctor({
|
|
86
|
-
DEBUG: { type: "boolean", optional: true },
|
|
63
|
+
export const config = envDoctor(schema as EnvDoctorSchema, {
|
|
64
|
+
loadDotEnv: true,
|
|
87
65
|
});
|
|
88
66
|
```
|
|
89
|
-
Missing optional vars become `undefined` unless you provide a default.
|
|
90
67
|
|
|
91
|
-
|
|
92
|
-
Defaults can be provided in object-style specs.
|
|
68
|
+
CommonJS (or TS compiling to CJS):
|
|
93
69
|
|
|
94
70
|
```ts
|
|
95
|
-
|
|
71
|
+
// src/config.ts
|
|
72
|
+
import { envDoctor, type EnvDoctorSchema } from "env-typed-checker";
|
|
96
73
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
DEBUG: { type: "boolean", default: "false" }, // string is allowed (parsed)
|
|
100
|
-
NODE_ENV: { type: "enum", values: ["dev", "prod"], default: "dev" },
|
|
101
|
-
});
|
|
102
|
-
```
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
75
|
+
const schema = require("../env.schema.json") as EnvDoctorSchema;
|
|
103
76
|
|
|
104
|
-
|
|
77
|
+
export const config = envDoctor(schema, { loadDotEnv: true });
|
|
78
|
+
```
|
|
105
79
|
|
|
106
|
-
|
|
107
|
-
- `boolean` default: boolean or string (string will be parsed)
|
|
108
|
-
- `url/email` defaults must be strings and must validate
|
|
109
|
-
- `json` defaults can be any JSON-like value
|
|
80
|
+
TypeScript JSON import note:
|
|
110
81
|
|
|
111
|
-
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"compilerOptions": {
|
|
85
|
+
"resolveJsonModule": true,
|
|
86
|
+
"esModuleInterop": true
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
112
90
|
|
|
113
|
-
###
|
|
91
|
+
### 3) Validate env via CLI
|
|
114
92
|
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
NODE_ENV: { type: "enum", values: ["dev", "prod"] },
|
|
118
|
-
});
|
|
93
|
+
```bash
|
|
94
|
+
npx env-typed-checker check --schema env.schema.json
|
|
119
95
|
```
|
|
120
96
|
|
|
121
|
-
###
|
|
97
|
+
### 4) Generate `.env.example` from the same schema
|
|
122
98
|
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
SLUG: { type: "regex", pattern: "^[a-z0-9-]+$", flags: "i" },
|
|
126
|
-
});
|
|
99
|
+
```bash
|
|
100
|
+
npx env-typed-checker generate --schema env.schema.json --out .env.example --comment-docs --example-values
|
|
127
101
|
```
|
|
128
102
|
|
|
129
|
-
|
|
103
|
+
Example output:
|
|
130
104
|
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
105
|
+
```env
|
|
106
|
+
# HTTP port
|
|
107
|
+
# example: 8080
|
|
108
|
+
PORT=8080
|
|
109
|
+
|
|
110
|
+
# Primary database URL
|
|
111
|
+
# example: postgres://user:pass@localhost:5432/app
|
|
112
|
+
DB_URL=postgres://user:pass@localhost:5432/app
|
|
136
113
|
```
|
|
137
114
|
|
|
115
|
+
### 5) Use in CI (GitHub Actions annotations)
|
|
138
116
|
|
|
139
|
-
|
|
117
|
+
```yml
|
|
118
|
+
- name: Validate env
|
|
119
|
+
run: npx env-typed-checker check --schema env.schema.json --format github
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## API quick reference
|
|
140
123
|
|
|
141
124
|
```ts
|
|
142
125
|
import { envDoctor } from "env-typed-checker";
|
|
143
126
|
|
|
144
|
-
const
|
|
145
|
-
{
|
|
146
|
-
|
|
127
|
+
const config = envDoctor(
|
|
128
|
+
{
|
|
129
|
+
PORT: "number",
|
|
130
|
+
DEBUG: "boolean?",
|
|
131
|
+
NODE_ENV: { type: "enum", values: ["dev", "prod"] },
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
loadDotEnv: true,
|
|
135
|
+
env: process.env,
|
|
136
|
+
},
|
|
147
137
|
);
|
|
148
|
-
|
|
149
|
-
console.log(cfg.PORT); // 3000
|
|
150
138
|
```
|
|
151
139
|
|
|
152
|
-
###
|
|
140
|
+
### Parsed output types
|
|
153
141
|
|
|
154
|
-
|
|
142
|
+
| Schema type | Parsed result |
|
|
143
|
+
| :--- | :--- |
|
|
144
|
+
| `string` | `string` |
|
|
145
|
+
| `number` | `number` |
|
|
146
|
+
| `boolean` | `boolean` |
|
|
147
|
+
| `json` | `unknown` |
|
|
148
|
+
| `url` | `string` |
|
|
149
|
+
| `email` | `string` |
|
|
150
|
+
| `enum` | one of listed values |
|
|
151
|
+
| `regex` | `string` |
|
|
155
152
|
|
|
156
|
-
|
|
157
|
-
PORT=abc
|
|
158
|
-
DB_URL=not-a-url
|
|
159
|
-
NODE_ENV=staging
|
|
160
|
-
```
|
|
153
|
+
## Schema reference
|
|
161
154
|
|
|
162
|
-
|
|
163
|
-
import { envDoctor } from "env-typed-checker";
|
|
155
|
+
### Primitive shorthand
|
|
164
156
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
157
|
+
```json
|
|
158
|
+
{
|
|
159
|
+
"PORT": "number",
|
|
160
|
+
"DEBUG": "boolean?",
|
|
161
|
+
"DB_URL": "url"
|
|
162
|
+
}
|
|
170
163
|
```
|
|
171
164
|
|
|
172
|
-
|
|
165
|
+
- Add `?` to mark optional keys.
|
|
166
|
+
- Missing optional keys become `undefined` (unless default exists).
|
|
173
167
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
168
|
+
### Object syntax
|
|
169
|
+
|
|
170
|
+
```json
|
|
171
|
+
{
|
|
172
|
+
"PORT": { "type": "number", "default": 3000 },
|
|
173
|
+
"DEBUG": { "type": "boolean", "optional": true },
|
|
174
|
+
"NODE_ENV": { "type": "enum", "values": ["dev", "prod"] },
|
|
175
|
+
"SLUG": { "type": "regex", "pattern": "^[a-z0-9-]+$", "flags": "i" }
|
|
176
|
+
}
|
|
179
177
|
```
|
|
180
|
-
All errors are shown together so you can fix them in one go.
|
|
181
178
|
|
|
182
|
-
|
|
179
|
+
### Defaults
|
|
183
180
|
|
|
184
|
-
|
|
181
|
+
- `number`: number or numeric string
|
|
182
|
+
- `boolean`: boolean or boolean-like string
|
|
183
|
+
- `url` / `email`: valid strings
|
|
184
|
+
- `json`: any JSON-like value
|
|
185
185
|
|
|
186
|
-
|
|
186
|
+
### Metadata fields (object syntax only)
|
|
187
187
|
|
|
188
|
-
`env.schema.json`
|
|
189
188
|
```json
|
|
190
189
|
{
|
|
191
|
-
"
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
190
|
+
"API_KEY": {
|
|
191
|
+
"type": "string",
|
|
192
|
+
"description": "External service token",
|
|
193
|
+
"example": "sk_test_123",
|
|
194
|
+
"secret": true
|
|
195
|
+
}
|
|
197
196
|
}
|
|
198
197
|
```
|
|
199
198
|
|
|
200
|
-
|
|
199
|
+
- `description?: string` used by `generate --comment-docs`
|
|
200
|
+
- `example?: string` used in comments and optional generation values
|
|
201
|
+
- `secret?: boolean` redacts invalid raw values in CLI `check`
|
|
201
202
|
|
|
202
|
-
|
|
203
|
-
|
|
203
|
+
If you need metadata for a primitive shorthand key, wrap it in object syntax:
|
|
204
|
+
|
|
205
|
+
```json
|
|
206
|
+
{
|
|
207
|
+
"PORT": { "type": "number", "description": "HTTP port" }
|
|
208
|
+
}
|
|
204
209
|
```
|
|
205
210
|
|
|
206
|
-
|
|
211
|
+
### Runtime behavior notes
|
|
212
|
+
|
|
213
|
+
- Empty strings (`""`) are treated as missing values.
|
|
214
|
+
- Validation errors are aggregated and returned together.
|
|
215
|
+
- CLI `check` merges `.env` values over current shell env when dotenv loading is enabled.
|
|
216
|
+
|
|
217
|
+
## CLI reference
|
|
218
|
+
|
|
219
|
+
### `check`
|
|
207
220
|
|
|
208
221
|
```bash
|
|
209
|
-
|
|
210
|
-
|
|
222
|
+
env-typed-checker check --schema <file> [--env-file <file>] [--no-dotenv] [--format pretty|json|github]
|
|
223
|
+
```
|
|
211
224
|
|
|
212
|
-
|
|
213
|
-
|
|
225
|
+
Flags:
|
|
226
|
+
|
|
227
|
+
- `--schema <file>` required schema file
|
|
228
|
+
- `--env-file <file>` env file path (default `.env`)
|
|
229
|
+
- `--no-dotenv` validate only current `process.env`
|
|
230
|
+
- `--format pretty|json|github` output style (default `pretty`)
|
|
231
|
+
|
|
232
|
+
### `check` output formats
|
|
233
|
+
|
|
234
|
+
`pretty` (default):
|
|
235
|
+
|
|
236
|
+
```text
|
|
237
|
+
ENV validation failed
|
|
238
|
+
- PORT: expected number, got "abc"
|
|
214
239
|
```
|
|
215
240
|
|
|
216
|
-
|
|
241
|
+
`json`:
|
|
242
|
+
|
|
243
|
+
```json
|
|
244
|
+
{"error":"ENV validation failed","issues":[{"key":"PORT","kind":"invalid","message":"expected number, got \"abc\""}]}
|
|
245
|
+
```
|
|
217
246
|
|
|
218
|
-
|
|
247
|
+
`github` (for workflow annotations):
|
|
219
248
|
|
|
220
|
-
```
|
|
221
|
-
|
|
249
|
+
```text
|
|
250
|
+
::error title=ENV validation::PORT: expected number, got "abc"
|
|
222
251
|
```
|
|
223
|
-
### By default:
|
|
224
252
|
|
|
225
|
-
|
|
226
|
-
- mode: update
|
|
253
|
+
When a key has `secret: true`, CLI output redacts raw invalid values:
|
|
227
254
|
|
|
228
|
-
|
|
255
|
+
```text
|
|
256
|
+
SECRET_TOKEN: expected number, got "<redacted>"
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### `generate`
|
|
229
260
|
|
|
230
261
|
```bash
|
|
231
|
-
|
|
232
|
-
|
|
262
|
+
env-typed-checker generate --schema <file> [--out <file>] [--mode update|create] [--no-defaults] [--comment-types] [--comment-docs] [--example-values]
|
|
263
|
+
```
|
|
233
264
|
|
|
234
|
-
|
|
235
|
-
npx env-typed-checker generate --schema env.schema.json --out .env --mode=create
|
|
265
|
+
Defaults:
|
|
236
266
|
|
|
237
|
-
|
|
238
|
-
|
|
267
|
+
- output file: `.env`
|
|
268
|
+
- mode: `update`
|
|
269
|
+
- behavior: append only missing keys
|
|
239
270
|
|
|
240
|
-
|
|
241
|
-
npx env-typed-checker generate --schema env.schema.json --no-defaults
|
|
271
|
+
Flags:
|
|
242
272
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
273
|
+
- `--out <file>` custom output path
|
|
274
|
+
- `--mode create` fail if file already exists
|
|
275
|
+
- `--mode update` append missing keys only
|
|
276
|
+
- `--no-defaults` write empty values instead of defaults
|
|
277
|
+
- `--comment-types` add inline type comments
|
|
278
|
+
- `--comment-docs` add `description` and `example` comments above keys
|
|
279
|
+
- `--example-values` use schema `example` values as generated values
|
|
246
280
|
|
|
247
|
-
|
|
281
|
+
## Exit codes
|
|
248
282
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
283
|
+
- `0` success
|
|
284
|
+
- `1` validation failed
|
|
285
|
+
- `2` CLI usage error or unexpected error
|
|
252
286
|
|
|
253
|
-
##
|
|
287
|
+
## CI examples
|
|
254
288
|
|
|
255
|
-
|
|
289
|
+
GitHub Actions with annotations:
|
|
256
290
|
|
|
257
291
|
```yml
|
|
258
292
|
- name: Validate env
|
|
259
|
-
run: npx env-typed-checker check --schema env.schema.json
|
|
293
|
+
run: npx env-typed-checker check --schema env.schema.json --format github
|
|
260
294
|
```
|
|
261
|
-
|
|
295
|
+
|
|
296
|
+
With custom env file:
|
|
262
297
|
|
|
263
298
|
```yml
|
|
264
299
|
- name: Validate env
|
|
265
|
-
run: npx env-typed-checker check --schema env.schema.json --env-file .env.ci
|
|
300
|
+
run: npx env-typed-checker check --schema env.schema.json --env-file .env.ci --format github
|
|
266
301
|
```
|
|
267
302
|
|
|
268
|
-
##
|
|
269
|
-
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
npm test
|
|
273
|
-
```
|
|
274
|
-
### Common scripts:
|
|
275
|
-
```bash
|
|
276
|
-
npm run build # build package
|
|
277
|
-
npm run test # run tests
|
|
278
|
-
npm run typecheck # TypeScript check
|
|
279
|
-
npm run dev # watch build
|
|
280
|
-
```
|
|
303
|
+
## Testing with custom env in code
|
|
304
|
+
|
|
305
|
+
```ts
|
|
306
|
+
import { envDoctor } from "env-typed-checker";
|
|
281
307
|
|
|
282
|
-
|
|
283
|
-
|
|
308
|
+
const cfg = envDoctor(
|
|
309
|
+
{ PORT: "number" },
|
|
310
|
+
{ loadDotEnv: false, env: { PORT: "3000" } },
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
console.log(cfg.PORT); // 3000
|
|
314
|
+
```
|
|
284
315
|
|
|
285
|
-
|
|
286
|
-
* Add more schema features
|
|
287
|
-
* Improve CLI output formatting
|
|
288
|
-
* Add integrations / templates
|
|
316
|
+
## Common mistakes
|
|
289
317
|
|
|
318
|
+
- Missing `--schema` in CLI commands
|
|
319
|
+
- Using shorthand string syntax when you need metadata (`description`, `example`, `secret`)
|
|
320
|
+
- Forgetting `resolveJsonModule` when importing `env.schema.json` in TypeScript
|
|
290
321
|
|
|
291
|
-
|
|
292
|
-
MIT
|
|
322
|
+
## Development
|
|
293
323
|
|
|
324
|
+
```bash
|
|
325
|
+
npm install
|
|
326
|
+
npm run lint
|
|
327
|
+
npm run typecheck
|
|
328
|
+
npm run test:coverage
|
|
329
|
+
npm run build
|
|
330
|
+
```
|
|
294
331
|
|
|
295
|
-
|
|
332
|
+
## License
|
|
296
333
|
|
|
297
|
-
|
|
298
|
-
::contentReference[oaicite:0]{index=0}
|
|
299
|
-
```
|
|
334
|
+
MIT
|
|
@@ -102,6 +102,33 @@ function coerceDefault(kind, def) {
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
|
+
function normalizeMeta(schemaValue) {
|
|
106
|
+
let description = void 0;
|
|
107
|
+
if (hasOwn(schemaValue, "description")) {
|
|
108
|
+
const d = schemaValue.description;
|
|
109
|
+
if (d !== void 0 && typeof d !== "string") {
|
|
110
|
+
throw new Error(`"description" must be a string if provided`);
|
|
111
|
+
}
|
|
112
|
+
description = d;
|
|
113
|
+
}
|
|
114
|
+
let example = void 0;
|
|
115
|
+
if (hasOwn(schemaValue, "example")) {
|
|
116
|
+
const ex = schemaValue.example;
|
|
117
|
+
if (ex !== void 0 && typeof ex !== "string") {
|
|
118
|
+
throw new Error(`"example" must be a string if provided`);
|
|
119
|
+
}
|
|
120
|
+
example = ex;
|
|
121
|
+
}
|
|
122
|
+
let secret = false;
|
|
123
|
+
if (hasOwn(schemaValue, "secret")) {
|
|
124
|
+
const s = schemaValue.secret;
|
|
125
|
+
if (s !== void 0 && typeof s !== "boolean") {
|
|
126
|
+
throw new Error(`"secret" must be a boolean if provided`);
|
|
127
|
+
}
|
|
128
|
+
secret = s === true;
|
|
129
|
+
}
|
|
130
|
+
return { description, example, secret };
|
|
131
|
+
}
|
|
105
132
|
function normalizeSpec(schemaValue) {
|
|
106
133
|
if (typeof schemaValue === "string") {
|
|
107
134
|
const optional = schemaValue.endsWith("?");
|
|
@@ -119,12 +146,17 @@ function normalizeSpec(schemaValue) {
|
|
|
119
146
|
`Unsupported type "${schemaValue}". Supported: string, number, boolean, json, url, email (optional with ?)`
|
|
120
147
|
);
|
|
121
148
|
}
|
|
122
|
-
return {
|
|
149
|
+
return {
|
|
150
|
+
kind: base,
|
|
151
|
+
optional,
|
|
152
|
+
secret: false
|
|
153
|
+
};
|
|
123
154
|
}
|
|
124
155
|
if (!schemaValue || typeof schemaValue !== "object" || Array.isArray(schemaValue)) {
|
|
125
156
|
throw new Error("Schema value must be a string or object spec.");
|
|
126
157
|
}
|
|
127
158
|
const t = schemaValue.type;
|
|
159
|
+
const meta = normalizeMeta(schemaValue);
|
|
128
160
|
const primitiveAllowed = [
|
|
129
161
|
"string",
|
|
130
162
|
"number",
|
|
@@ -136,7 +168,12 @@ function normalizeSpec(schemaValue) {
|
|
|
136
168
|
if (primitiveAllowed.includes(t)) {
|
|
137
169
|
const optional = !!schemaValue.optional;
|
|
138
170
|
const defaultValue = hasOwn(schemaValue, "default") ? coerceDefault(t, schemaValue.default) : void 0;
|
|
139
|
-
return {
|
|
171
|
+
return {
|
|
172
|
+
kind: t,
|
|
173
|
+
optional,
|
|
174
|
+
defaultValue,
|
|
175
|
+
...meta
|
|
176
|
+
};
|
|
140
177
|
}
|
|
141
178
|
if (t === "enum") {
|
|
142
179
|
const values = schemaValue.values;
|
|
@@ -157,7 +194,13 @@ function normalizeSpec(schemaValue) {
|
|
|
157
194
|
}
|
|
158
195
|
defaultValue = def;
|
|
159
196
|
}
|
|
160
|
-
return {
|
|
197
|
+
return {
|
|
198
|
+
kind: "enum",
|
|
199
|
+
optional,
|
|
200
|
+
values,
|
|
201
|
+
defaultValue,
|
|
202
|
+
...meta
|
|
203
|
+
};
|
|
161
204
|
}
|
|
162
205
|
if (t === "regex") {
|
|
163
206
|
const pattern = schemaValue.pattern;
|
|
@@ -187,7 +230,14 @@ function normalizeSpec(schemaValue) {
|
|
|
187
230
|
}
|
|
188
231
|
defaultValue = def;
|
|
189
232
|
}
|
|
190
|
-
return {
|
|
233
|
+
return {
|
|
234
|
+
kind: "regex",
|
|
235
|
+
optional,
|
|
236
|
+
re,
|
|
237
|
+
display,
|
|
238
|
+
defaultValue,
|
|
239
|
+
...meta
|
|
240
|
+
};
|
|
191
241
|
}
|
|
192
242
|
throw new Error(
|
|
193
243
|
`Unsupported object spec type "${String(t)}". Supported: primitives (string/number/boolean/json/url/email), enum, regex`
|
|
@@ -259,4 +309,4 @@ export {
|
|
|
259
309
|
normalizeSpec,
|
|
260
310
|
envDoctor
|
|
261
311
|
};
|
|
262
|
-
//# sourceMappingURL=chunk-
|
|
312
|
+
//# sourceMappingURL=chunk-CCJEPMPN.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/envDoctor.ts","../src/errors/EnvDoctorError.ts","../src/validators/primitives.ts","../src/core/runner.ts"],"sourcesContent":["import * as dotenv from \"dotenv\";\nimport { validateAndParse } from \"./runner\";\nimport type { EnvDoctorOptions, EnvDoctorResult, EnvDoctorSchema } from \"../types\";\n\nexport type { EnvDoctorOptions, EnvDoctorSchema } from \"../types\";\nexport { EnvDoctorError } from \"../errors/EnvDoctorError\";\n\nexport function envDoctor<TSchema extends EnvDoctorSchema>(\n schema: TSchema,\n options: EnvDoctorOptions = {}\n): EnvDoctorResult<TSchema> {\n const { loadDotEnv = true, env = process.env } = options;\n\n if (loadDotEnv) dotenv.config();\n\n return validateAndParse(schema, env);\n}\n","export type EnvDoctorIssue =\n | { key: string; kind: \"missing\"; message: string }\n | { key: string; kind: \"invalid\"; message: string };\n\nexport class EnvDoctorError extends Error {\n public readonly issues: EnvDoctorIssue[];\n\n constructor(issues: EnvDoctorIssue[]) {\n const header = \"ENV validation failed\";\n const lines = issues.map((i) => `- ${i.key}: ${i.message}`);\n super([header, ...lines].join(\"\\n\"));\n this.name = \"EnvDoctorError\";\n this.issues = issues;\n }\n}\n","import type { EnvBaseType, EnvSchemaValue } from \"../types\";\n\nconst EMAIL_RE = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\nexport function parseByType(type: EnvBaseType, raw: string): unknown {\n switch (type) {\n case \"string\":\n return raw;\n\n case \"number\": {\n const n = Number(raw.trim());\n if (!Number.isFinite(n)) throw new Error(`expected number, got \"${raw}\"`);\n return n;\n }\n\n case \"boolean\": {\n const v = raw.trim().toLowerCase();\n if ([\"true\", \"1\", \"yes\", \"y\", \"on\"].includes(v)) return true;\n if ([\"false\", \"0\", \"no\", \"n\", \"off\"].includes(v)) return false;\n throw new Error(\n `expected boolean (true/false/1/0/yes/no/on/off), got \"${raw}\"`,\n );\n }\n\n case \"json\": {\n try {\n return JSON.parse(raw);\n } catch {\n throw new Error(`expected json, got \"${raw}\"`);\n }\n }\n\n case \"url\": {\n try {\n new URL(raw);\n return raw;\n } catch {\n throw new Error(`expected url, got \"${raw}\"`);\n }\n }\n\n case \"email\": {\n const s = raw.trim();\n if (!EMAIL_RE.test(s)) {\n throw new Error(`expected email, got \"${raw}\"`);\n }\n return s;\n }\n\n default: {\n throw new Error(`unsupported primitive type: ${String(type)}`);\n }\n }\n}\n\nfunction hasOwn(obj: unknown, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(obj, key);\n}\n\n/**\n * Type-check + normalize a default value into the right runtime type.\n * - Allows string defaults for number/boolean (parsed)\n * - Validates url/email defaults\n * - json defaults can be any value\n */\nfunction coerceDefault(kind: EnvBaseType, def: unknown): unknown {\n if (def === undefined) return undefined;\n\n switch (kind) {\n case \"string\": {\n if (typeof def !== \"string\")\n throw new Error(`default for string must be a string`);\n return def;\n }\n\n case \"number\": {\n if (typeof def === \"number\") {\n if (!Number.isFinite(def))\n throw new Error(`default for number must be finite`);\n return def;\n }\n if (typeof def === \"string\") return parseByType(\"number\", def);\n throw new Error(`default for number must be number or string`);\n }\n\n case \"boolean\": {\n if (typeof def === \"boolean\") return def;\n if (typeof def === \"string\") return parseByType(\"boolean\", def);\n throw new Error(`default for boolean must be boolean or string`);\n }\n\n case \"json\": {\n // allow any JSON-ish value\n return def;\n }\n\n case \"url\": {\n if (typeof def !== \"string\")\n throw new Error(`default for url must be a string`);\n return parseByType(\"url\", def);\n }\n\n case \"email\": {\n if (typeof def !== \"string\")\n throw new Error(`default for email must be a string`);\n return parseByType(\"email\", def);\n }\n\n /* c8 ignore next */\n default: {\n throw new Error(`unsupported default kind: ${String(kind)}`);\n }\n }\n}\n\n/** Normalized spec used by the runner */\nexport type NormalizedSpec =\n | {\n kind: EnvBaseType;\n optional: boolean;\n defaultValue?: unknown;\n description?: string;\n example?: string;\n secret: boolean;\n }\n | {\n kind: \"enum\";\n optional: boolean;\n values: readonly string[];\n defaultValue?: string;\n description?: string;\n example?: string;\n secret: boolean;\n }\n | {\n kind: \"regex\";\n optional: boolean;\n re: RegExp;\n display: string;\n defaultValue?: string;\n description?: string;\n example?: string;\n secret: boolean;\n };\n\nfunction normalizeMeta(schemaValue: Record<string, unknown>): {\n description?: string;\n example?: string;\n secret: boolean;\n} {\n let description: string | undefined = undefined;\n if (hasOwn(schemaValue, \"description\")) {\n const d = schemaValue.description;\n if (d !== undefined && typeof d !== \"string\") {\n throw new Error(`\"description\" must be a string if provided`);\n }\n description = d;\n }\n\n let example: string | undefined = undefined;\n if (hasOwn(schemaValue, \"example\")) {\n const ex = schemaValue.example;\n if (ex !== undefined && typeof ex !== \"string\") {\n throw new Error(`\"example\" must be a string if provided`);\n }\n example = ex;\n }\n\n let secret = false;\n if (hasOwn(schemaValue, \"secret\")) {\n const s = schemaValue.secret;\n if (s !== undefined && typeof s !== \"boolean\") {\n throw new Error(`\"secret\" must be a boolean if provided`);\n }\n secret = s === true;\n }\n\n return { description, example, secret };\n}\n\nexport function normalizeSpec(schemaValue: EnvSchemaValue): NormalizedSpec {\n // --------------------\n // String style: \"number?\" etc.\n // --------------------\n if (typeof schemaValue === \"string\") {\n const optional = schemaValue.endsWith(\"?\");\n const base = optional ? schemaValue.slice(0, -1) : schemaValue;\n\n const allowed: readonly EnvBaseType[] = [\n \"string\",\n \"number\",\n \"boolean\",\n \"json\",\n \"url\",\n \"email\",\n ];\n\n if (!allowed.includes(base as EnvBaseType)) {\n throw new Error(\n `Unsupported type \"${schemaValue}\". Supported: string, number, boolean, json, url, email (optional with ?)`,\n );\n }\n\n return {\n kind: base as EnvBaseType,\n optional,\n secret: false,\n };\n }\n\n // --------------------\n // Object style\n // --------------------\n if (\n !schemaValue ||\n typeof schemaValue !== \"object\" ||\n Array.isArray(schemaValue)\n ) {\n throw new Error(\"Schema value must be a string or object spec.\");\n }\n\n const t = (schemaValue as any).type;\n const meta = normalizeMeta(schemaValue as Record<string, unknown>);\n\n // --------------------\n // Primitive object spec: { type: \"number\", optional?: true, default?: ... }\n // --------------------\n const primitiveAllowed: readonly EnvBaseType[] = [\n \"string\",\n \"number\",\n \"boolean\",\n \"json\",\n \"url\",\n \"email\",\n ];\n\n if (primitiveAllowed.includes(t)) {\n const optional = !!(schemaValue as any).optional;\n const defaultValue = hasOwn(schemaValue, \"default\")\n ? coerceDefault(t as EnvBaseType, (schemaValue as any).default)\n : undefined;\n\n return {\n kind: t as EnvBaseType,\n optional,\n defaultValue,\n ...meta,\n };\n }\n\n // --------------------\n // Enum: { type: \"enum\", values: [...], optional?: true, default?: \"dev\" }\n // --------------------\n if (t === \"enum\") {\n const values = (schemaValue as any).values;\n if (\n !Array.isArray(values) ||\n values.length === 0 ||\n !values.every((v: any) => typeof v === \"string\")\n ) {\n throw new Error(`enum spec requires \"values\": string[] (non-empty)`);\n }\n\n const optional = !!(schemaValue as any).optional;\n\n let defaultValue: string | undefined = undefined;\n if (hasOwn(schemaValue, \"default\")) {\n const def = (schemaValue as any).default;\n if (typeof def !== \"string\") {\n throw new Error(`default for enum must be a string`);\n }\n if (!values.includes(def)) {\n throw new Error(\n `default \"${def}\" must be one of [${values.join(\", \")}]`,\n );\n }\n defaultValue = def;\n }\n\n return {\n kind: \"enum\",\n optional,\n values,\n defaultValue,\n ...meta,\n };\n }\n\n // --------------------\n // Regex: { type: \"regex\", pattern: \"...\", flags?: \"...\", optional?: true, default?: \"abc\" }\n // --------------------\n if (t === \"regex\") {\n const pattern = (schemaValue as any).pattern;\n const flags = (schemaValue as any).flags;\n\n if (typeof pattern !== \"string\" || pattern.length === 0) {\n throw new Error(`regex spec requires \"pattern\": string`);\n }\n if (flags !== undefined && typeof flags !== \"string\") {\n throw new Error(`regex spec \"flags\" must be a string if provided`);\n }\n\n let re: RegExp;\n try {\n re = new RegExp(pattern, flags);\n } catch (e) {\n throw new Error(`invalid regex: ${String(e)}`);\n }\n\n const display = `/${pattern}/${flags ?? \"\"}`;\n const optional = !!(schemaValue as any).optional;\n\n let defaultValue: string | undefined = undefined;\n if (hasOwn(schemaValue, \"default\")) {\n const def = (schemaValue as any).default;\n if (typeof def !== \"string\") {\n throw new Error(`default for regex must be a string`);\n }\n if (!re.test(def)) {\n throw new Error(`default \"${def}\" does not match ${display}`);\n }\n defaultValue = def;\n }\n\n return {\n kind: \"regex\",\n optional,\n re,\n display,\n defaultValue,\n ...meta,\n };\n }\n\n throw new Error(\n `Unsupported object spec type \"${String(t)}\". Supported: primitives (string/number/boolean/json/url/email), enum, regex`,\n );\n}\n","import { EnvDoctorError, type EnvDoctorIssue } from \"../errors/EnvDoctorError\";\nimport { normalizeSpec, parseByType } from \"../validators/primitives\";\nimport type { EnvDoctorResult, EnvDoctorSchema } from \"../types\";\n\nexport function validateAndParse<TSchema extends EnvDoctorSchema>(\n schema: TSchema,\n env: Record<string, string | undefined>,\n): EnvDoctorResult<TSchema> {\n const issues: EnvDoctorIssue[] = [];\n const out: Record<string, unknown> = {};\n\n for (const [key, schemaValue] of Object.entries(schema)) {\n let spec: ReturnType<typeof normalizeSpec>;\n\n try {\n spec = normalizeSpec(schemaValue);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n issues.push({ key, kind: \"invalid\", message: msg });\n continue;\n }\n\n const raw = env[key];\n\n // treat undefined or empty string as \"missing\"\n if (raw === undefined || raw === \"\") {\n if (spec.defaultValue !== undefined) {\n out[key] = spec.defaultValue;\n } else if (spec.optional) {\n out[key] = undefined;\n } else {\n issues.push({\n key,\n kind: \"missing\",\n message: \"missing required environment variable\",\n });\n }\n continue;\n }\n\n try {\n if (spec.kind === \"enum\") {\n if (!spec.values.includes(raw)) {\n throw new Error(\n `expected one of [${spec.values.join(\", \")}], got \"${raw}\"`,\n );\n }\n out[key] = raw;\n } else if (spec.kind === \"regex\") {\n if (!spec.re.test(raw)) {\n throw new Error(`does not match ${spec.display}`);\n }\n out[key] = raw;\n } else {\n // primitives: string/number/boolean/json/url/email\n out[key] = parseByType(spec.kind, raw);\n }\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n issues.push({ key, kind: \"invalid\", message: msg });\n }\n }\n\n if (issues.length > 0) throw new EnvDoctorError(issues);\n\n return out as EnvDoctorResult<TSchema>;\n}\n"],"mappings":"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","names":[]}
|