zod-envkit 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -3
- package/dist/cli/index.cjs +13 -10
- package/dist/cli/index.js +13 -10
- package/package.json +16 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,16 +1,41 @@
|
|
|
1
|
-
|
|
1
|
+
# [1.2.0](https://github.com/nxtxe/zod-envkit/compare/v1.1.2...v1.2.0) (2026-02-16)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* stabilize preprod test suite, CLI E2E coverage, and CI pipeline ([463ea6e](https://github.com/nxtxe/zod-envkit/commit/463ea6e4676662dbbbe82bc79e0d85b079b524f8))
|
|
7
|
+
|
|
8
|
+
## [1.1.2](https://github.com/nxtxe/zod-envkit/compare/v1.1.1...v1.1.2) (2026-01-29)
|
|
2
9
|
|
|
3
10
|
|
|
4
11
|
### Bug Fixes
|
|
5
12
|
|
|
6
|
-
*
|
|
7
|
-
*
|
|
13
|
+
* stabilize strict env validation and CI ([3055f18](https://github.com/nxtxe/zod-envkit/commit/3055f1859479212fbdc105cb0f183c6018df2376))
|
|
14
|
+
* tests ([a691dab](https://github.com/nxtxe/zod-envkit/commit/a691dab0f207685bd96688e36aaabcdb5784f956))
|
|
8
15
|
|
|
9
16
|
# Changelog
|
|
10
17
|
|
|
11
18
|
All notable changes to this project will be documented in this file.
|
|
12
19
|
This project follows [Semantic Versioning](https://semver.org/).
|
|
13
20
|
|
|
21
|
+
## [1.1.2](https://github.com/nxtxe/zod-envkit/compare/v1.1.1...v1.1.2) (2026-01-29)
|
|
22
|
+
|
|
23
|
+
### Bug Fixes
|
|
24
|
+
|
|
25
|
+
* **cli:** fix strict mode to validate only dotenv-loaded variables
|
|
26
|
+
* **cli:** stabilize option validation and documentation links
|
|
27
|
+
* **ci:** ensure CLI smoke tests and docs build in CI
|
|
28
|
+
* internal cleanup to align behavior with documented API contract
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
## [1.1.1](https://github.com/nxtxe/zod-envkit/compare/v1.1.0...v1.1.1) (2026-01-29)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
### Bug Fixes
|
|
35
|
+
|
|
36
|
+
* **cli:** stabilize options validation and docs links ([640b12b](https://github.com/nxtxe/zod-envkit/commit/640b12bf95304da40c998374ba7ed08b7400e88d))
|
|
37
|
+
* stabilize public API, CLI behavior, and documentation ([4604298](https://github.com/nxtxe/zod-envkit/commit/46042981f146f021919da8a4a713b2d251c542d9))
|
|
38
|
+
|
|
14
39
|
---
|
|
15
40
|
|
|
16
41
|
## [1.1.0] – 2026-01-27
|
package/dist/cli/index.cjs
CHANGED
|
@@ -346,6 +346,7 @@ function loadDotEnv(files) {
|
|
|
346
346
|
const list = files?.split(",").map((s) => s.trim()).filter(Boolean) ?? [".env"];
|
|
347
347
|
const loaded = [];
|
|
348
348
|
const skipped = [];
|
|
349
|
+
const merged = {};
|
|
349
350
|
for (const f of list) {
|
|
350
351
|
const abs = resolvePath(f);
|
|
351
352
|
if (!import_node_fs3.default.existsSync(abs)) {
|
|
@@ -354,10 +355,11 @@ function loadDotEnv(files) {
|
|
|
354
355
|
}
|
|
355
356
|
const raw = import_node_fs3.default.readFileSync(abs, "utf8");
|
|
356
357
|
const parsed = import_dotenv.default.parse(raw);
|
|
358
|
+
for (const [k, v] of Object.entries(parsed)) merged[k] = v;
|
|
357
359
|
for (const [k, v] of Object.entries(parsed)) process.env[k] = v;
|
|
358
360
|
loaded.push(f);
|
|
359
361
|
}
|
|
360
|
-
return { loaded, skipped };
|
|
362
|
+
return { loaded, skipped, env: merged };
|
|
361
363
|
}
|
|
362
364
|
|
|
363
365
|
// src/env.ts
|
|
@@ -491,9 +493,9 @@ function registerShow(program2, getLang2) {
|
|
|
491
493
|
|
|
492
494
|
// src/cli/commands/check.ts
|
|
493
495
|
function registerCheck(program2, getLang2) {
|
|
494
|
-
program2.command("check").description("Exit with code 1 if env is invalid (loads dotenv)").option("-c, --config <file>", "Path to env meta json", "env.meta.json").option("--dotenv <list>", "Comma-separated dotenv files (default: .env)", ".env").option("--strict", "Fail if unknown env vars are present").action((opts) => {
|
|
496
|
+
program2.command("check").description("Exit with code 1 if env is invalid (loads dotenv)").option("-c, --config <file>", "Path to env meta json", "env.meta.json").option("--dotenv <list>", "Comma-separated dotenv files (default: .env)", ".env").option("--strict", "Fail if unknown env vars are present (dotenv-only)").action((opts) => {
|
|
495
497
|
const lang = getLang2();
|
|
496
|
-
loadDotEnv(
|
|
498
|
+
const loaded = loadDotEnv(opts.dotenv);
|
|
497
499
|
const { meta } = loadMeta(lang, opts.config);
|
|
498
500
|
const missing = getMissingEnv(meta, process.env);
|
|
499
501
|
if (missing.length) {
|
|
@@ -501,8 +503,8 @@ function registerCheck(program2, getLang2) {
|
|
|
501
503
|
for (const k of missing) console.error(`- ${k}`);
|
|
502
504
|
process.exit(1);
|
|
503
505
|
}
|
|
504
|
-
if (
|
|
505
|
-
const unknown = getUnknownEnv(meta,
|
|
506
|
+
if (opts.strict) {
|
|
507
|
+
const unknown = getUnknownEnv(meta, loaded.env);
|
|
506
508
|
if (unknown.length) {
|
|
507
509
|
console.error(`\u274C ${t(lang, "UNKNOWN_ENV")}`);
|
|
508
510
|
for (const k of unknown) console.error(`- ${k}`);
|
|
@@ -538,18 +540,19 @@ function metaFromEnv(env, defaultGroup) {
|
|
|
538
540
|
return meta;
|
|
539
541
|
}
|
|
540
542
|
function registerInit(program2, getLang2) {
|
|
541
|
-
program2.command("init").description("Initialize env.meta.json from .env.example (or generate .env.example from meta)").option("--input <file>", "Input file (
|
|
543
|
+
program2.command("init").description("Initialize env.meta.json from .env.example (or generate .env.example from meta)").option("--input <file>", "Input file (.env.example or env.meta.json)").option("--output <file>", "Output file (env.meta.json or .env.example)").option("--from-meta", "Generate .env.example from env.meta.json instead").option("--group <name>", "Default group for all vars (when generating meta)").action((opts) => {
|
|
542
544
|
const lang = getLang2();
|
|
543
|
-
const
|
|
544
|
-
const
|
|
545
|
-
|
|
545
|
+
const fromMeta = Boolean(opts.fromMeta);
|
|
546
|
+
const input = String(opts.input ?? (fromMeta ? "env.meta.json" : ".env.example"));
|
|
547
|
+
const output = String(opts.output ?? (fromMeta ? ".env.example" : "env.meta.json"));
|
|
548
|
+
if (fromMeta) {
|
|
546
549
|
const { meta: meta2 } = loadMeta(lang, input);
|
|
547
550
|
import_node_fs4.default.writeFileSync(output, generateEnvExample(meta2), "utf8");
|
|
548
551
|
process.exit(0);
|
|
549
552
|
}
|
|
550
553
|
const env = readEnvFile(input);
|
|
551
554
|
if (Object.keys(env).length === 0) {
|
|
552
|
-
fail(lang, "
|
|
555
|
+
fail(lang, "META_PARSE_FAILED", [`- ${t(lang, "INIT_INPUT_EMPTY")} ${input}`]);
|
|
553
556
|
}
|
|
554
557
|
const meta = metaFromEnv(env, opts.group ? String(opts.group) : void 0);
|
|
555
558
|
import_node_fs4.default.writeFileSync(output, JSON.stringify(meta, null, 2) + "\n", "utf8");
|
package/dist/cli/index.js
CHANGED
|
@@ -167,6 +167,7 @@ function loadDotEnv(files) {
|
|
|
167
167
|
const list = files?.split(",").map((s) => s.trim()).filter(Boolean) ?? [".env"];
|
|
168
168
|
const loaded = [];
|
|
169
169
|
const skipped = [];
|
|
170
|
+
const merged = {};
|
|
170
171
|
for (const f of list) {
|
|
171
172
|
const abs = resolvePath(f);
|
|
172
173
|
if (!fs3.existsSync(abs)) {
|
|
@@ -175,10 +176,11 @@ function loadDotEnv(files) {
|
|
|
175
176
|
}
|
|
176
177
|
const raw = fs3.readFileSync(abs, "utf8");
|
|
177
178
|
const parsed = dotenv.parse(raw);
|
|
179
|
+
for (const [k, v] of Object.entries(parsed)) merged[k] = v;
|
|
178
180
|
for (const [k, v] of Object.entries(parsed)) process.env[k] = v;
|
|
179
181
|
loaded.push(f);
|
|
180
182
|
}
|
|
181
|
-
return { loaded, skipped };
|
|
183
|
+
return { loaded, skipped, env: merged };
|
|
182
184
|
}
|
|
183
185
|
|
|
184
186
|
// src/cli/lib/mask.ts
|
|
@@ -279,9 +281,9 @@ function registerShow(program2, getLang2) {
|
|
|
279
281
|
|
|
280
282
|
// src/cli/commands/check.ts
|
|
281
283
|
function registerCheck(program2, getLang2) {
|
|
282
|
-
program2.command("check").description("Exit with code 1 if env is invalid (loads dotenv)").option("-c, --config <file>", "Path to env meta json", "env.meta.json").option("--dotenv <list>", "Comma-separated dotenv files (default: .env)", ".env").option("--strict", "Fail if unknown env vars are present").action((opts) => {
|
|
284
|
+
program2.command("check").description("Exit with code 1 if env is invalid (loads dotenv)").option("-c, --config <file>", "Path to env meta json", "env.meta.json").option("--dotenv <list>", "Comma-separated dotenv files (default: .env)", ".env").option("--strict", "Fail if unknown env vars are present (dotenv-only)").action((opts) => {
|
|
283
285
|
const lang = getLang2();
|
|
284
|
-
loadDotEnv(
|
|
286
|
+
const loaded = loadDotEnv(opts.dotenv);
|
|
285
287
|
const { meta } = loadMeta(lang, opts.config);
|
|
286
288
|
const missing = getMissingEnv(meta, process.env);
|
|
287
289
|
if (missing.length) {
|
|
@@ -289,8 +291,8 @@ function registerCheck(program2, getLang2) {
|
|
|
289
291
|
for (const k of missing) console.error(`- ${k}`);
|
|
290
292
|
process.exit(1);
|
|
291
293
|
}
|
|
292
|
-
if (
|
|
293
|
-
const unknown = getUnknownEnv(meta,
|
|
294
|
+
if (opts.strict) {
|
|
295
|
+
const unknown = getUnknownEnv(meta, loaded.env);
|
|
294
296
|
if (unknown.length) {
|
|
295
297
|
console.error(`\u274C ${t(lang, "UNKNOWN_ENV")}`);
|
|
296
298
|
for (const k of unknown) console.error(`- ${k}`);
|
|
@@ -326,18 +328,19 @@ function metaFromEnv(env, defaultGroup) {
|
|
|
326
328
|
return meta;
|
|
327
329
|
}
|
|
328
330
|
function registerInit(program2, getLang2) {
|
|
329
|
-
program2.command("init").description("Initialize env.meta.json from .env.example (or generate .env.example from meta)").option("--input <file>", "Input file (
|
|
331
|
+
program2.command("init").description("Initialize env.meta.json from .env.example (or generate .env.example from meta)").option("--input <file>", "Input file (.env.example or env.meta.json)").option("--output <file>", "Output file (env.meta.json or .env.example)").option("--from-meta", "Generate .env.example from env.meta.json instead").option("--group <name>", "Default group for all vars (when generating meta)").action((opts) => {
|
|
330
332
|
const lang = getLang2();
|
|
331
|
-
const
|
|
332
|
-
const
|
|
333
|
-
|
|
333
|
+
const fromMeta = Boolean(opts.fromMeta);
|
|
334
|
+
const input = String(opts.input ?? (fromMeta ? "env.meta.json" : ".env.example"));
|
|
335
|
+
const output = String(opts.output ?? (fromMeta ? ".env.example" : "env.meta.json"));
|
|
336
|
+
if (fromMeta) {
|
|
334
337
|
const { meta: meta2 } = loadMeta(lang, input);
|
|
335
338
|
fs4.writeFileSync(output, generateEnvExample(meta2), "utf8");
|
|
336
339
|
process.exit(0);
|
|
337
340
|
}
|
|
338
341
|
const env = readEnvFile(input);
|
|
339
342
|
if (Object.keys(env).length === 0) {
|
|
340
|
-
fail(lang, "
|
|
343
|
+
fail(lang, "META_PARSE_FAILED", [`- ${t(lang, "INIT_INPUT_EMPTY")} ${input}`]);
|
|
341
344
|
}
|
|
342
345
|
const meta = metaFromEnv(env, opts.group ? String(opts.group) : void 0);
|
|
343
346
|
fs4.writeFileSync(output, JSON.stringify(meta, null, 2) + "\n", "utf8");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zod-envkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Validate environment variables with Zod and generate .env.example",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "",
|
|
@@ -38,16 +38,24 @@
|
|
|
38
38
|
"README.md",
|
|
39
39
|
"README.RU.md",
|
|
40
40
|
"CHANGELOG.md",
|
|
41
|
-
"LICENSE"
|
|
41
|
+
"LICENSE",
|
|
42
|
+
"package.json"
|
|
42
43
|
],
|
|
43
44
|
"scripts": {
|
|
44
45
|
"build": "rm -rf dist && tsup src/index.ts src/cli/index.ts --format esm,cjs --dts --outDir dist",
|
|
45
46
|
"dev": "tsup src/index.ts src/cli/index.ts --watch --dts --outDir dist",
|
|
46
|
-
"test": "
|
|
47
|
-
"
|
|
48
|
-
"
|
|
47
|
+
"test": "pnpm test:preprod",
|
|
48
|
+
"test:preprod": "pnpm build && vitest run tests/test/preprod",
|
|
49
|
+
"test:preprod:robust": "pnpm build && PREPROD_ITER=30 vitest run tests/test/preprod/30-robustness",
|
|
50
|
+
"test:before-release": "pnpm build && pnpm test:preprod && pnpm test:preprod:robust",
|
|
51
|
+
"ci:smoke": "pnpm build && node dist/cli/index.js --help",
|
|
52
|
+
"ci:docs": "pnpm docs:build",
|
|
53
|
+
"ci:all": "pnpm ci:smoke && pnpm test && pnpm ci:docs",
|
|
54
|
+
"prepublishOnly": "pnpm build && pnpm test",
|
|
55
|
+
"docs:api": "typedoc --tsconfig tsconfig.docs.json --entryPoints src/index.ts",
|
|
49
56
|
"docs:build": "pnpm docs:api && vitepress build docs",
|
|
50
|
-
"docs:dev": "vitepress dev docs"
|
|
57
|
+
"docs:dev": "vitepress dev docs",
|
|
58
|
+
"release": "semantic-release"
|
|
51
59
|
},
|
|
52
60
|
"dependencies": {
|
|
53
61
|
"commander": "^13.1.0",
|
|
@@ -63,7 +71,9 @@
|
|
|
63
71
|
"@semantic-release/release-notes-generator": "^12.1.0",
|
|
64
72
|
"@types/node": "^25.0.10",
|
|
65
73
|
"eslint": "^9.39.2",
|
|
74
|
+
"execa": "^9.6.1",
|
|
66
75
|
"semantic-release": "^22.0.12",
|
|
76
|
+
"tmp-promise": "^3.0.3",
|
|
67
77
|
"tsup": "^8.5.1",
|
|
68
78
|
"typedoc": "^0.28.16",
|
|
69
79
|
"typedoc-plugin-markdown": "^4.9.0",
|