zod-envkit 1.0.1 → 1.0.3

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/CHANGELOG.md CHANGED
@@ -1,11 +1,34 @@
1
1
  # Changelog
2
2
 
3
- All notable changes to this project will be documented in this file.
4
-
3
+ All notable changes to this project will be documented in this file.
5
4
  This project follows [Semantic Versioning](https://semver.org/).
6
5
 
7
6
  ---
8
7
 
8
+ ## [1.0.2] – 2026-01-26
9
+
10
+ ### Added
11
+ - `zod-envkit show` command to display environment status in a readable table (with secret masking)
12
+ - `zod-envkit check` command to validate required variables with CI-friendly exit codes
13
+
14
+ ### Changed
15
+ - CLI now also searches for `env.meta.json` in `./examples/` by default
16
+ - CLI output and documentation updated accordingly
17
+
18
+ [1.0.2]: https://www.npmjs.com/package/zod-envkit/v/1.0.2
19
+
20
+ ---
21
+
22
+ ## [1.0.1] – 2026-01-26
23
+
24
+ ### Changed
25
+ - Packaging improvements for npm (ship only compiled output and docs)
26
+ - Documentation updates
27
+
28
+ [1.0.1]: https://www.npmjs.com/package/zod-envkit/v/1.0.1
29
+
30
+ ---
31
+
9
32
  ## [1.0.0] – 2026-01-26
10
33
 
11
34
  ### Added
@@ -16,11 +39,9 @@ This project follows [Semantic Versioning](https://semver.org/).
16
39
  - CLI tool to generate:
17
40
  - `.env.example`
18
41
  - `ENV.md` documentation
19
- - Automatic type inference for validated environment variables
20
42
  - Support for ESM and CommonJS builds
21
43
  - TypeScript declaration files (`.d.ts`) included
22
44
  - Basic test suite using Vitest
23
- - Clear and documented API surface
24
45
  - MIT license
25
46
 
26
47
  ### Design Decisions
@@ -29,15 +50,4 @@ This project follows [Semantic Versioning](https://semver.org/).
29
50
  - Environment variables treated as an explicit runtime contract
30
51
  - Small, framework-agnostic core
31
52
 
32
- ---
33
-
34
53
  [1.0.0]: https://www.npmjs.com/package/zod-envkit/v/1.0.0
35
-
36
-
37
- ## [1.0.1] – 2026-01-26
38
-
39
- ### Changed
40
- - Documentation and packaging improvements
41
- - Minor CLI/docs adjustments
42
-
43
- [1.0.1]: https://www.npmjs.com/package/zod-envkit/v/1.0.1
package/README.md CHANGED
@@ -1,26 +1,28 @@
1
1
  <div align="center">
2
- <br />
3
- <p>
4
- <img src="./zod-envkit.svg" width="546" alt="zod-envkit" /><
5
- </p>
6
- <br />
7
- <p>
8
- <a href="https://www.npmjs.com/package/zod-envkit"><img src="https://img.shields.io/npm/v/zod-envkit.svg?maxAge=100" alt="npm version" /></a>
9
- <a href="https://www.npmjs.com/package/zod-envkit"><img src="https://img.shields.io/npm/dt/zod-envkit.svg?maxAge=100" alt="npm downloads" /></a>
10
- </p>
11
- <p align="center">
12
- <b> A framework for developing bots based using discord.js. </b>
2
+ <br />
3
+ <p>
4
+ <img src="./zod-envkit.svg" width="546" alt="zod-envkit" />
5
+ </p>
6
+ <br />
7
+ <p>
8
+ <a href="https://www.npmjs.com/package/zod-envkit">
9
+ <img src="https://img.shields.io/npm/v/zod-envkit.svg?maxAge=100" alt="npm version" />
10
+ </a>
11
+ <a href="https://www.npmjs.com/package/zod-envkit">
12
+ <img src="https://img.shields.io/npm/dt/zod-envkit.svg?maxAge=100" alt="npm downloads" />
13
+ </a>
13
14
  </p>
14
15
  </div>
15
16
 
16
- Type-safe environment variable validation and documentation using Zod.
17
+ Type-safe environment variable validation and documentation using **Zod**.
18
+
19
+ **zod-envkit** is a small library + CLI that helps you treat environment variables as an **explicit runtime contract**, not an implicit guessing game.
17
20
 
18
- `zod-envkit` is a small library and CLI tool that helps you:
19
- - validate `process.env`
20
- - get **fully typed environment variables**
21
- - automatically generate `.env.example`
22
- - generate environment documentation (`ENV.md`)
23
- - treat environment variables as an **explicit contract**, not guesswork
21
+ - validate `process.env` at startup
22
+ - get fully typed environment variables
23
+ - generate `.env.example`
24
+ - generate readable documentation (`ENV.md`)
25
+ - inspect and verify env state via CLI
24
26
 
25
27
  No cloud. No magic. Just code.
26
28
 
@@ -28,18 +30,19 @@ No cloud. No magic. Just code.
28
30
 
29
31
  ## Why
30
32
 
31
- The problem:
33
+ Environment variables are critical, but usually poorly handled.
34
+
35
+ The usual problems:
32
36
  - `process.env` is just `string | undefined`
33
- - environment errors often appear **only at runtime**
34
- - `.env.example` and documentation are often outdated or missing
37
+ - missing or invalid variables fail **at runtime**
38
+ - `.env.example` and docs get outdated
39
+ - CI/CD breaks late and unpredictably
35
40
 
36
- The solution:
37
- - define your environment **once**
38
- - get:
39
- - early validation
40
- - TypeScript types
41
- - up-to-date `.env.example`
42
- - up-to-date documentation
41
+ **zod-envkit** solves this by making env:
42
+ - validated early
43
+ - typed
44
+ - documented
45
+ - checkable in CI
43
46
 
44
47
  ---
45
48
 
@@ -57,7 +60,9 @@ pnpm add zod-envkit
57
60
 
58
61
  ---
59
62
 
60
- ## Quick start
63
+ ## Library usage (runtime validation)
64
+
65
+ Create a single file responsible for loading and validating env.
61
66
 
62
67
  ```ts
63
68
  import "dotenv/config";
@@ -67,7 +72,7 @@ import { loadEnv, formatZodError } from "zod-envkit";
67
72
  const EnvSchema = z.object({
68
73
  NODE_ENV: z.enum(["development", "test", "production"]),
69
74
  PORT: z.coerce.number().int().min(1).max(65535),
70
- DATABASE_URL: z.string().url()
75
+ DATABASE_URL: z.string().url(),
71
76
  });
72
77
 
73
78
  const result = loadEnv(EnvSchema);
@@ -85,86 +90,102 @@ Now:
85
90
  * `env.PORT` is a **number**
86
91
  * `env.DATABASE_URL` is a **string**
87
92
  * TypeScript knows everything at compile time
93
+ * your app fails fast if env is invalid
88
94
 
89
95
  ---
90
96
 
91
- ## CLI: generate `.env.example` and `ENV.md`
97
+ ## CLI usage
98
+
99
+ The CLI works from a simple metadata file: `env.meta.json`.
92
100
 
93
- ### 1️⃣ Create `env.meta.json` in your project root
101
+ By default, it is searched in:
102
+
103
+ * `./env.meta.json`
104
+ * `./examples/env.meta.json`
105
+
106
+ ---
107
+
108
+ ### Example `env.meta.json`
94
109
 
95
110
  ```json
96
111
  {
97
112
  "NODE_ENV": {
98
- "description": "Application runtime environment",
99
- "example": "development"
113
+ "description": "Runtime mode",
114
+ "example": "development",
115
+ "required": true
100
116
  },
101
117
  "PORT": {
102
- "description": "HTTP server port",
103
- "example": "3000"
118
+ "description": "HTTP port",
119
+ "example": "3000",
120
+ "required": true
104
121
  },
105
122
  "DATABASE_URL": {
106
- "description": "PostgreSQL connection string",
107
- "example": "postgresql://user:pass@localhost:5432/db"
123
+ "description": "Postgres connection string",
124
+ "example": "postgresql://user:pass@localhost:5432/db",
125
+ "required": true
108
126
  }
109
127
  }
110
128
  ```
111
129
 
112
130
  ---
113
131
 
114
- ### 2️⃣ Run the CLI
132
+ ## CLI commands
133
+
134
+ ### Generate `.env.example` and `ENV.md`
135
+
136
+ (Default behavior)
115
137
 
116
138
  ```bash
117
139
  npx zod-envkit
118
140
  ```
119
141
 
120
- Or locally during development:
142
+ or explicitly:
121
143
 
122
144
  ```bash
123
- node dist/cli.js
145
+ npx zod-envkit generate
124
146
  ```
125
147
 
126
148
  ---
127
149
 
128
- ### 3️⃣ Output
150
+ ### Show current environment status
129
151
 
130
- #### `.env.example`
152
+ Loads `.env`, masks secrets, and displays a readable table.
131
153
 
132
- ```env
133
- # Application runtime environment
134
- NODE_ENV=development
154
+ ```bash
155
+ npx zod-envkit show
156
+ ```
135
157
 
136
- # HTTP server port
137
- PORT=3000
158
+ Example output:
138
159
 
139
- # PostgreSQL connection string
140
- DATABASE_URL=postgresql://user:pass@localhost:5432/db
141
- ```
160
+ * which variables are required
161
+ * which are present
162
+ * masked values for secrets
142
163
 
143
- #### `ENV.md`
164
+ ---
144
165
 
145
- ```md
146
- # Environment variables
166
+ ### Validate required variables (CI-friendly)
147
167
 
148
- | Key | Required | Example | Description |
149
- |---|---:|---|---|
150
- | NODE_ENV | yes | development | Application runtime environment |
151
- | PORT | yes | 3000 | HTTP server port |
152
- | DATABASE_URL | yes | postgresql://... | PostgreSQL connection string |
168
+ ```bash
169
+ npx zod-envkit check
153
170
  ```
154
171
 
172
+ * exits with code `1` if any required variable is missing
173
+ * perfect for CI/CD pipelines and pre-deploy checks
174
+
155
175
  ---
156
176
 
157
- ## CLI options
177
+ ### CLI options
158
178
 
159
179
  ```bash
160
180
  zod-envkit --help
161
181
  ```
162
182
 
183
+ Common flags:
184
+
163
185
  ```bash
164
- zod-envkit \
165
- --config env.meta.json \
166
- --out-example .env.example \
167
- --out-docs ENV.md
186
+ zod-envkit show \
187
+ --config examples/env.meta.json \
188
+ --env-file .env
168
189
  ```
169
190
 
170
191
  ---
@@ -176,6 +197,7 @@ zod-envkit \
176
197
  * ❌ no validation
177
198
  * ❌ no types
178
199
  * ❌ no documentation
200
+ * ❌ no CI checks
179
201
 
180
202
  `zod-envkit`:
181
203
 
@@ -184,39 +206,40 @@ zod-envkit \
184
206
  * ✅ documentation
185
207
  * ✅ CLI tooling
186
208
 
187
- They work **great together**.
209
+ They are designed to be used **together**.
188
210
 
189
211
  ---
190
212
 
191
- ## Design decisions
213
+ ## Design principles
192
214
 
193
- * does not try to automatically parse Zod schemas
194
- * does not couple to any framework
195
- * explicit configuration over magic
196
- * small, predictable API
197
- * library + CLI, both optional
215
+ * explicit configuration over magic
216
+ * no framework coupling
217
+ * small and predictable API
218
+ * library and CLI are independent but complementary
219
+ * environment variables are a runtime contract
198
220
 
199
221
  ---
200
222
 
201
223
  ## Roadmap
202
224
 
203
225
  * [ ] schema ↔ meta consistency checks
204
- * [ ] `zod-envkit check` (validation only)
205
- * [ ] grouping / sections in docs
206
- * [ ] prettier, human-friendly error output
226
+ * [ ] grouped sections in generated docs
227
+ * [ ] prettier, more human-friendly error output
207
228
  * [ ] JSON schema export
229
+ * [ ] stricter validation modes for production
208
230
 
209
231
  ---
210
232
 
211
233
  ## Who is this for?
212
234
 
213
235
  * backend and fullstack projects
214
- * Node.js / Bun services
236
+ * Node.js and Bun services
215
237
  * CI/CD pipelines
216
- * teams that treat env as part of their contract
238
+ * teams that want env errors to fail fast, not late
217
239
 
218
240
  ---
219
241
 
220
242
  ## License
221
243
 
222
244
  MIT
245
+
package/dist/cli.cjs CHANGED
@@ -27,6 +27,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
  var import_node_fs = __toESM(require("fs"), 1);
28
28
  var import_node_path = __toESM(require("path"), 1);
29
29
  var import_commander = require("commander");
30
+ var import_dotenv = __toESM(require("dotenv"), 1);
30
31
 
31
32
  // src/generate.ts
32
33
  function strLen(s) {
@@ -98,13 +99,111 @@ function generateEnvDocs(meta) {
98
99
  }
99
100
 
100
101
  // src/cli.ts
101
- var program = new import_commander.Command();
102
- program.name("zod-envkit").description("Generate .env.example and ENV.md from env.meta.json").option("-c, --config <file>", "Path to env meta json", "env.meta.json").option("--out-example <file>", "Output file for .env.example", ".env.example").option("--out-docs <file>", "Output file for docs", "ENV.md").action((opts) => {
103
- const configPath = import_node_path.default.resolve(process.cwd(), opts.config);
102
+ function maskValue(key, value) {
103
+ const k = key.toUpperCase();
104
+ const isSecret = k.includes("TOKEN") || k.includes("SECRET") || k.includes("PASSWORD") || k.includes("API_KEY") || k.includes("KEY");
105
+ if (!isSecret) return value;
106
+ if (value.length <= 4) return "*".repeat(value.length);
107
+ return value.slice(0, 2) + "*".repeat(Math.max(1, value.length - 4)) + value.slice(-2);
108
+ }
109
+ function padCenter2(text, width) {
110
+ const t = text ?? "";
111
+ const diff = width - t.length;
112
+ if (diff <= 0) return t;
113
+ const left = Math.floor(diff / 2);
114
+ const right = diff - left;
115
+ return " ".repeat(left) + t + " ".repeat(right);
116
+ }
117
+ function printTable(rows, headers) {
118
+ const widths = {};
119
+ for (const h of headers) widths[h] = h.length;
120
+ for (const row of rows) {
121
+ for (const h of headers) {
122
+ widths[h] = Math.max(widths[h], (row[h] ?? "").length);
123
+ }
124
+ }
125
+ for (const h of headers) widths[h] += 2;
126
+ const line = "|" + headers.map((h) => " " + padCenter2(h, widths[h]) + " ").join("|") + "|";
127
+ const sep = "|" + headers.map((h) => ":" + "-".repeat(Math.max(3, widths[h])) + ":").join("|") + "|";
128
+ console.log(line);
129
+ console.log(sep);
130
+ for (const row of rows) {
131
+ const rowLine = "|" + headers.map((h) => " " + padCenter2(row[h] ?? "", widths[h]) + " ").join("|") + "|";
132
+ console.log(rowLine);
133
+ }
134
+ }
135
+ function loadMeta(configFile) {
136
+ const cwd = process.cwd();
137
+ const candidates = [
138
+ import_node_path.default.resolve(cwd, configFile),
139
+ // ./env.meta.json (по умолчанию)
140
+ import_node_path.default.resolve(cwd, "examples", configFile),
141
+ // ./examples/env.meta.json
142
+ import_node_path.default.resolve(cwd, "examples", "env.meta.json")
143
+ // явный fallback
144
+ ];
145
+ const configPath = candidates.find((p) => import_node_fs.default.existsSync(p));
146
+ if (!configPath) {
147
+ console.error("\u274C env meta file not found.");
148
+ console.error("Tried:");
149
+ for (const p of candidates) console.error(`- ${p}`);
150
+ console.error("\nTip:");
151
+ console.error(" zod-envkit show -c examples/env.meta.json");
152
+ process.exit(1);
153
+ }
104
154
  const raw = import_node_fs.default.readFileSync(configPath, "utf8");
105
- const meta = JSON.parse(raw);
155
+ return JSON.parse(raw);
156
+ }
157
+ var program = new import_commander.Command();
158
+ program.name("zod-envkit").description("Env docs + runtime checks for Node.js projects");
159
+ program.command("generate").description("Generate .env.example and ENV.md from env.meta.json").option("-c, --config <file>", "Path to env meta json", "env.meta.json").option("--out-example <file>", "Output file for .env.example", ".env.example").option("--out-docs <file>", "Output file for docs", "ENV.md").action((opts) => {
160
+ const meta = loadMeta(opts.config);
106
161
  import_node_fs.default.writeFileSync(opts.outExample, generateEnvExample(meta), "utf8");
107
162
  import_node_fs.default.writeFileSync(opts.outDocs, generateEnvDocs(meta), "utf8");
108
163
  console.log(`Generated: ${opts.outExample}, ${opts.outDocs}`);
109
164
  });
110
- program.parse();
165
+ program.option("-c, --config <file>", "Path to env meta json", "env.meta.json").option("--out-example <file>", "Output file for .env.example", ".env.example").option("--out-docs <file>", "Output file for docs", "ENV.md");
166
+ program.command("show").description("Show current env status (loads .env, masks secrets)").option("-c, --config <file>", "Path to env meta json", "env.meta.json").option("--env-file <file>", "Path to .env file", ".env").action((opts) => {
167
+ import_dotenv.default.config({ path: import_node_path.default.resolve(process.cwd(), opts.envFile), quiet: true });
168
+ const meta = loadMeta(opts.config);
169
+ const rows = Object.entries(meta).map(([key, m]) => {
170
+ const required = m.required === false ? "no" : "yes";
171
+ const raw = process.env[key];
172
+ const present = raw && raw.length > 0 ? "yes" : "no";
173
+ const value = raw ? maskValue(key, raw) : "";
174
+ return {
175
+ Key: key,
176
+ Required: required,
177
+ Present: present,
178
+ Value: value,
179
+ Description: m.description ?? ""
180
+ };
181
+ });
182
+ printTable(rows, ["Key", "Required", "Present", "Value", "Description"]);
183
+ });
184
+ program.command("check").description("Exit with code 1 if any required env var is missing (loads .env)").option("-c, --config <file>", "Path to env meta json", "env.meta.json").option("--env-file <file>", "Path to .env file", ".env").action((opts) => {
185
+ import_dotenv.default.config({ path: import_node_path.default.resolve(process.cwd(), opts.envFile) });
186
+ const meta = loadMeta(opts.config);
187
+ const missing = [];
188
+ for (const [key, m] of Object.entries(meta)) {
189
+ const required = m.required !== false;
190
+ if (!required) continue;
191
+ const raw = process.env[key];
192
+ if (!raw || raw.length === 0) missing.push(key);
193
+ }
194
+ if (missing.length) {
195
+ console.error("\u274C Missing required environment variables:");
196
+ for (const k of missing) console.error(`- ${k}`);
197
+ process.exit(1);
198
+ }
199
+ console.log("\u2705 Environment looks good.");
200
+ });
201
+ program.parse(process.argv);
202
+ var hasSubcommand = process.argv.slice(2).some((a) => ["generate", "show", "check"].includes(a));
203
+ if (!hasSubcommand) {
204
+ const opts = program.opts();
205
+ const meta = loadMeta(opts.config ?? "env.meta.json");
206
+ import_node_fs.default.writeFileSync(opts.outExample ?? ".env.example", generateEnvExample(meta), "utf8");
207
+ import_node_fs.default.writeFileSync(opts.outDocs ?? "ENV.md", generateEnvDocs(meta), "utf8");
208
+ console.log(`Generated: ${opts.outExample ?? ".env.example"}, ${opts.outDocs ?? "ENV.md"}`);
209
+ }
package/dist/cli.js CHANGED
@@ -4,6 +4,7 @@
4
4
  import fs from "fs";
5
5
  import path from "path";
6
6
  import { Command } from "commander";
7
+ import dotenv from "dotenv";
7
8
 
8
9
  // src/generate.ts
9
10
  function strLen(s) {
@@ -75,13 +76,111 @@ function generateEnvDocs(meta) {
75
76
  }
76
77
 
77
78
  // src/cli.ts
78
- var program = new Command();
79
- program.name("zod-envkit").description("Generate .env.example and ENV.md from env.meta.json").option("-c, --config <file>", "Path to env meta json", "env.meta.json").option("--out-example <file>", "Output file for .env.example", ".env.example").option("--out-docs <file>", "Output file for docs", "ENV.md").action((opts) => {
80
- const configPath = path.resolve(process.cwd(), opts.config);
79
+ function maskValue(key, value) {
80
+ const k = key.toUpperCase();
81
+ const isSecret = k.includes("TOKEN") || k.includes("SECRET") || k.includes("PASSWORD") || k.includes("API_KEY") || k.includes("KEY");
82
+ if (!isSecret) return value;
83
+ if (value.length <= 4) return "*".repeat(value.length);
84
+ return value.slice(0, 2) + "*".repeat(Math.max(1, value.length - 4)) + value.slice(-2);
85
+ }
86
+ function padCenter2(text, width) {
87
+ const t = text ?? "";
88
+ const diff = width - t.length;
89
+ if (diff <= 0) return t;
90
+ const left = Math.floor(diff / 2);
91
+ const right = diff - left;
92
+ return " ".repeat(left) + t + " ".repeat(right);
93
+ }
94
+ function printTable(rows, headers) {
95
+ const widths = {};
96
+ for (const h of headers) widths[h] = h.length;
97
+ for (const row of rows) {
98
+ for (const h of headers) {
99
+ widths[h] = Math.max(widths[h], (row[h] ?? "").length);
100
+ }
101
+ }
102
+ for (const h of headers) widths[h] += 2;
103
+ const line = "|" + headers.map((h) => " " + padCenter2(h, widths[h]) + " ").join("|") + "|";
104
+ const sep = "|" + headers.map((h) => ":" + "-".repeat(Math.max(3, widths[h])) + ":").join("|") + "|";
105
+ console.log(line);
106
+ console.log(sep);
107
+ for (const row of rows) {
108
+ const rowLine = "|" + headers.map((h) => " " + padCenter2(row[h] ?? "", widths[h]) + " ").join("|") + "|";
109
+ console.log(rowLine);
110
+ }
111
+ }
112
+ function loadMeta(configFile) {
113
+ const cwd = process.cwd();
114
+ const candidates = [
115
+ path.resolve(cwd, configFile),
116
+ // ./env.meta.json (по умолчанию)
117
+ path.resolve(cwd, "examples", configFile),
118
+ // ./examples/env.meta.json
119
+ path.resolve(cwd, "examples", "env.meta.json")
120
+ // явный fallback
121
+ ];
122
+ const configPath = candidates.find((p) => fs.existsSync(p));
123
+ if (!configPath) {
124
+ console.error("\u274C env meta file not found.");
125
+ console.error("Tried:");
126
+ for (const p of candidates) console.error(`- ${p}`);
127
+ console.error("\nTip:");
128
+ console.error(" zod-envkit show -c examples/env.meta.json");
129
+ process.exit(1);
130
+ }
81
131
  const raw = fs.readFileSync(configPath, "utf8");
82
- const meta = JSON.parse(raw);
132
+ return JSON.parse(raw);
133
+ }
134
+ var program = new Command();
135
+ program.name("zod-envkit").description("Env docs + runtime checks for Node.js projects");
136
+ program.command("generate").description("Generate .env.example and ENV.md from env.meta.json").option("-c, --config <file>", "Path to env meta json", "env.meta.json").option("--out-example <file>", "Output file for .env.example", ".env.example").option("--out-docs <file>", "Output file for docs", "ENV.md").action((opts) => {
137
+ const meta = loadMeta(opts.config);
83
138
  fs.writeFileSync(opts.outExample, generateEnvExample(meta), "utf8");
84
139
  fs.writeFileSync(opts.outDocs, generateEnvDocs(meta), "utf8");
85
140
  console.log(`Generated: ${opts.outExample}, ${opts.outDocs}`);
86
141
  });
87
- program.parse();
142
+ program.option("-c, --config <file>", "Path to env meta json", "env.meta.json").option("--out-example <file>", "Output file for .env.example", ".env.example").option("--out-docs <file>", "Output file for docs", "ENV.md");
143
+ program.command("show").description("Show current env status (loads .env, masks secrets)").option("-c, --config <file>", "Path to env meta json", "env.meta.json").option("--env-file <file>", "Path to .env file", ".env").action((opts) => {
144
+ dotenv.config({ path: path.resolve(process.cwd(), opts.envFile), quiet: true });
145
+ const meta = loadMeta(opts.config);
146
+ const rows = Object.entries(meta).map(([key, m]) => {
147
+ const required = m.required === false ? "no" : "yes";
148
+ const raw = process.env[key];
149
+ const present = raw && raw.length > 0 ? "yes" : "no";
150
+ const value = raw ? maskValue(key, raw) : "";
151
+ return {
152
+ Key: key,
153
+ Required: required,
154
+ Present: present,
155
+ Value: value,
156
+ Description: m.description ?? ""
157
+ };
158
+ });
159
+ printTable(rows, ["Key", "Required", "Present", "Value", "Description"]);
160
+ });
161
+ program.command("check").description("Exit with code 1 if any required env var is missing (loads .env)").option("-c, --config <file>", "Path to env meta json", "env.meta.json").option("--env-file <file>", "Path to .env file", ".env").action((opts) => {
162
+ dotenv.config({ path: path.resolve(process.cwd(), opts.envFile) });
163
+ const meta = loadMeta(opts.config);
164
+ const missing = [];
165
+ for (const [key, m] of Object.entries(meta)) {
166
+ const required = m.required !== false;
167
+ if (!required) continue;
168
+ const raw = process.env[key];
169
+ if (!raw || raw.length === 0) missing.push(key);
170
+ }
171
+ if (missing.length) {
172
+ console.error("\u274C Missing required environment variables:");
173
+ for (const k of missing) console.error(`- ${k}`);
174
+ process.exit(1);
175
+ }
176
+ console.log("\u2705 Environment looks good.");
177
+ });
178
+ program.parse(process.argv);
179
+ var hasSubcommand = process.argv.slice(2).some((a) => ["generate", "show", "check"].includes(a));
180
+ if (!hasSubcommand) {
181
+ const opts = program.opts();
182
+ const meta = loadMeta(opts.config ?? "env.meta.json");
183
+ fs.writeFileSync(opts.outExample ?? ".env.example", generateEnvExample(meta), "utf8");
184
+ fs.writeFileSync(opts.outDocs ?? "ENV.md", generateEnvDocs(meta), "utf8");
185
+ console.log(`Generated: ${opts.outExample ?? ".env.example"}, ${opts.outDocs ?? "ENV.md"}`);
186
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zod-envkit",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Validate environment variables with Zod and generate .env.example",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",