zod-envkit 1.3.0 → 1.3.1

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,3 +1,29 @@
1
+ ## [1.3.1](https://github.com/nxtxe/zod-envkit/compare/v1.3.0...v1.3.1) (2026-03-19)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **cli:** improve env error grouping and hints ([d098a70](https://github.com/nxtxe/zod-envkit/commit/d098a70683f8028b7be275bd16a580f4fccb4938))
7
+
8
+ ## [1.3.1](https://github.com/nxtxe/zod-envkit/compare/v1.3.0...v1.3.1) (2026-03-xx)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * Improve `zod-envkit check` error output by grouping issues:
14
+ * clearly separate missing required variables and unknown variables (in strict mode)
15
+ * use a single `ENV_INVALID` header with structured sections instead of multiple disjoint messages
16
+ * Add actionable hints for env meta issues:
17
+ * on `META_PARSE_FAILED`, show which file failed and suggest `npx zod-envkit generate -c env.meta.json`
18
+ * on `META_NOT_FOUND` (and no `.env.example`), suggest generating `env.meta.json` from CLI
19
+ * Clarify strict mode behavior in unknown-variable messages:
20
+ * `UNKNOWN_ENV` now explicitly states that only dotenv-loaded keys are checked in `--strict` mode
21
+ * Keep CLI contracts stable:
22
+ * exit codes unchanged
23
+ * `zod-envkit show` table format unchanged
24
+ * `formatZodError` output shape unchanged
25
+ * no JSON structures were modified
26
+
1
27
  # [1.3.0](https://github.com/nxtxe/zod-envkit/compare/v1.2.3...v1.3.0) (2026-03-04)
2
28
 
3
29
 
@@ -29,6 +29,7 @@ var import_commander = require("commander");
29
29
  // src/messages.ts
30
30
  var messages = {
31
31
  en: {
32
+ ENV_INVALID: "Environment is invalid.",
32
33
  META_NOT_FOUND: "env meta file not found.",
33
34
  META_TRIED: "Tried:",
34
35
  META_TIP: "Tip:",
@@ -38,7 +39,7 @@ var messages = {
38
39
  GENERATED: "Generated: {example}, {docs}",
39
40
  ENV_OK: "Environment looks good.",
40
41
  MISSING_ENV: "Missing required environment variables:",
41
- UNKNOWN_ENV: "Unknown environment variables:",
42
+ UNKNOWN_ENV: "Unknown environment variables (strict mode; only dotenv-loaded keys):",
42
43
  INVALID_FORMAT: "Invalid docs format",
43
44
  INVALID_MASK_MODE: "Invalid mask mode",
44
45
  INVALID_SORT: "Invalid sort mode",
@@ -51,6 +52,7 @@ var messages = {
51
52
  META_HINT_SYNC_SCHEMA: "Hint: add these to your Zod schema or remove from env.meta.json."
52
53
  },
53
54
  ru: {
55
+ ENV_INVALID: "\u041F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u0437\u0430\u0434\u0430\u043D\u044B \u043D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u043E.",
54
56
  META_NOT_FOUND: "\u0424\u0430\u0439\u043B env.meta.json \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D.",
55
57
  META_TRIED: "\u041F\u0440\u043E\u0431\u043E\u0432\u0430\u043B\u0438:",
56
58
  META_TIP: "\u041F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0430:",
@@ -60,7 +62,7 @@ var messages = {
60
62
  GENERATED: "\u0421\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u043E: {example}, {docs}",
61
63
  ENV_OK: "\u041F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u0432 \u043F\u043E\u0440\u044F\u0434\u043A\u0435.",
62
64
  MISSING_ENV: "\u041E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044E\u0442 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u044B\u0435 \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F:",
63
- UNKNOWN_ENV: "\u041E\u0431\u043D\u0430\u0440\u0443\u0436\u0435\u043D\u044B \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u044B\u0435 \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F:",
65
+ UNKNOWN_ENV: "\u041E\u0431\u043D\u0430\u0440\u0443\u0436\u0435\u043D\u044B \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u044B\u0435 \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F (strict; \u0442\u043E\u043B\u044C\u043A\u043E \u0438\u0437 dotenv-\u0444\u0430\u0439\u043B\u043E\u0432):",
64
66
  INVALID_FORMAT: "\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0444\u043E\u0440\u043C\u0430\u0442 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430\u0446\u0438\u0438",
65
67
  INVALID_MASK_MODE: "\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0440\u0435\u0436\u0438\u043C \u043C\u0430\u0441\u043A\u0438\u0440\u043E\u0432\u043A\u0438",
66
68
  INVALID_SORT: "\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0440\u0435\u0436\u0438\u043C \u0441\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u043A\u0438",
@@ -160,7 +162,12 @@ function loadMeta(lang, configFile) {
160
162
  const raw = import_node_fs.default.readFileSync(found, "utf8");
161
163
  return { meta: JSON.parse(raw), configPath: found };
162
164
  } catch {
163
- fail(lang, "META_PARSE_FAILED", [`- ${found}`]);
165
+ fail(lang, "META_PARSE_FAILED", [
166
+ `- ${found}`,
167
+ "",
168
+ t(lang, "META_TIP"),
169
+ " Run: npx zod-envkit generate -c env.meta.json"
170
+ ]);
164
171
  }
165
172
  }
166
173
  const examplePath = import_node_path.default.resolve(process.cwd(), ".env.example");
@@ -175,7 +182,7 @@ function loadMeta(lang, configFile) {
175
182
  ...candidates.map((p) => `- ${p}`),
176
183
  "",
177
184
  t(lang, "META_TIP"),
178
- " npx zod-envkit show -c examples/env.meta.json"
185
+ " Run: npx zod-envkit generate -c env.meta.json"
179
186
  ]);
180
187
  }
181
188
 
@@ -618,16 +625,24 @@ function registerCheck(program2, getLang2) {
618
625
  const lang = getLang2();
619
626
  const loaded = loadDotEnv(opts.dotenv);
620
627
  const { meta } = loadMeta(lang, opts.config);
628
+ const sections = [];
621
629
  const missing = getMissingEnv(meta, process.env);
622
630
  if (missing.length) {
623
- fail(lang, "MISSING_ENV", missing.map((k) => `- ${k}`));
631
+ sections.push(t(lang, "MISSING_ENV"));
632
+ missing.forEach((k) => sections.push(`- ${k}`));
633
+ sections.push("");
624
634
  }
625
635
  if (opts.strict) {
626
636
  const unknown = getUnknownEnv(meta, loaded.env);
627
637
  if (unknown.length) {
628
- fail(lang, "UNKNOWN_ENV", unknown.map((k) => `- ${k}`));
638
+ sections.push(t(lang, "UNKNOWN_ENV"));
639
+ unknown.forEach((k) => sections.push(`- ${k}`));
640
+ sections.push("");
629
641
  }
630
642
  }
643
+ if (sections.length) {
644
+ fail(lang, "ENV_INVALID", sections);
645
+ }
631
646
  if (opts.schema) {
632
647
  const schemaMode = String(opts.schemaMode ?? "strict").toLowerCase();
633
648
  if (schemaMode !== "warn" && schemaMode !== "strict") {
package/dist/cli/index.js CHANGED
@@ -13,6 +13,7 @@ import { Command } from "commander";
13
13
  // src/messages.ts
14
14
  var messages = {
15
15
  en: {
16
+ ENV_INVALID: "Environment is invalid.",
16
17
  META_NOT_FOUND: "env meta file not found.",
17
18
  META_TRIED: "Tried:",
18
19
  META_TIP: "Tip:",
@@ -22,7 +23,7 @@ var messages = {
22
23
  GENERATED: "Generated: {example}, {docs}",
23
24
  ENV_OK: "Environment looks good.",
24
25
  MISSING_ENV: "Missing required environment variables:",
25
- UNKNOWN_ENV: "Unknown environment variables:",
26
+ UNKNOWN_ENV: "Unknown environment variables (strict mode; only dotenv-loaded keys):",
26
27
  INVALID_FORMAT: "Invalid docs format",
27
28
  INVALID_MASK_MODE: "Invalid mask mode",
28
29
  INVALID_SORT: "Invalid sort mode",
@@ -35,6 +36,7 @@ var messages = {
35
36
  META_HINT_SYNC_SCHEMA: "Hint: add these to your Zod schema or remove from env.meta.json."
36
37
  },
37
38
  ru: {
39
+ ENV_INVALID: "\u041F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u0437\u0430\u0434\u0430\u043D\u044B \u043D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u043E.",
38
40
  META_NOT_FOUND: "\u0424\u0430\u0439\u043B env.meta.json \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D.",
39
41
  META_TRIED: "\u041F\u0440\u043E\u0431\u043E\u0432\u0430\u043B\u0438:",
40
42
  META_TIP: "\u041F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0430:",
@@ -44,7 +46,7 @@ var messages = {
44
46
  GENERATED: "\u0421\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u043E: {example}, {docs}",
45
47
  ENV_OK: "\u041F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u0432 \u043F\u043E\u0440\u044F\u0434\u043A\u0435.",
46
48
  MISSING_ENV: "\u041E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044E\u0442 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u044B\u0435 \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F:",
47
- UNKNOWN_ENV: "\u041E\u0431\u043D\u0430\u0440\u0443\u0436\u0435\u043D\u044B \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u044B\u0435 \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F:",
49
+ UNKNOWN_ENV: "\u041E\u0431\u043D\u0430\u0440\u0443\u0436\u0435\u043D\u044B \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u044B\u0435 \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F (strict; \u0442\u043E\u043B\u044C\u043A\u043E \u0438\u0437 dotenv-\u0444\u0430\u0439\u043B\u043E\u0432):",
48
50
  INVALID_FORMAT: "\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0444\u043E\u0440\u043C\u0430\u0442 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430\u0446\u0438\u0438",
49
51
  INVALID_MASK_MODE: "\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0440\u0435\u0436\u0438\u043C \u043C\u0430\u0441\u043A\u0438\u0440\u043E\u0432\u043A\u0438",
50
52
  INVALID_SORT: "\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0440\u0435\u0436\u0438\u043C \u0441\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u043A\u0438",
@@ -144,7 +146,12 @@ function loadMeta(lang, configFile) {
144
146
  const raw = fs.readFileSync(found, "utf8");
145
147
  return { meta: JSON.parse(raw), configPath: found };
146
148
  } catch {
147
- fail(lang, "META_PARSE_FAILED", [`- ${found}`]);
149
+ fail(lang, "META_PARSE_FAILED", [
150
+ `- ${found}`,
151
+ "",
152
+ t(lang, "META_TIP"),
153
+ " Run: npx zod-envkit generate -c env.meta.json"
154
+ ]);
148
155
  }
149
156
  }
150
157
  const examplePath = path.resolve(process.cwd(), ".env.example");
@@ -159,7 +166,7 @@ function loadMeta(lang, configFile) {
159
166
  ...candidates.map((p) => `- ${p}`),
160
167
  "",
161
168
  t(lang, "META_TIP"),
162
- " npx zod-envkit show -c examples/env.meta.json"
169
+ " Run: npx zod-envkit generate -c env.meta.json"
163
170
  ]);
164
171
  }
165
172
 
@@ -397,16 +404,24 @@ function registerCheck(program2, getLang2) {
397
404
  const lang = getLang2();
398
405
  const loaded = loadDotEnv(opts.dotenv);
399
406
  const { meta } = loadMeta(lang, opts.config);
407
+ const sections = [];
400
408
  const missing = getMissingEnv(meta, process.env);
401
409
  if (missing.length) {
402
- fail(lang, "MISSING_ENV", missing.map((k) => `- ${k}`));
410
+ sections.push(t(lang, "MISSING_ENV"));
411
+ missing.forEach((k) => sections.push(`- ${k}`));
412
+ sections.push("");
403
413
  }
404
414
  if (opts.strict) {
405
415
  const unknown = getUnknownEnv(meta, loaded.env);
406
416
  if (unknown.length) {
407
- fail(lang, "UNKNOWN_ENV", unknown.map((k) => `- ${k}`));
417
+ sections.push(t(lang, "UNKNOWN_ENV"));
418
+ unknown.forEach((k) => sections.push(`- ${k}`));
419
+ sections.push("");
408
420
  }
409
421
  }
422
+ if (sections.length) {
423
+ fail(lang, "ENV_INVALID", sections);
424
+ }
410
425
  if (opts.schema) {
411
426
  const schemaMode = String(opts.schemaMode ?? "strict").toLowerCase();
412
427
  if (schemaMode !== "warn" && schemaMode !== "strict") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zod-envkit",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "Validate environment variables with Zod and generate .env.example",
5
5
  "license": "MIT",
6
6
  "author": "",