zod-envkit 1.2.3 → 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 +39 -4
- package/dist/{chunk-RI23O6XK.js → chunk-J4V5ODI6.js} +14 -5
- package/dist/cli/index.cjs +120 -16
- package/dist/cli/index.js +107 -12
- package/dist/index.cjs +16 -6
- package/dist/index.d.cts +22 -4
- package/dist/index.d.ts +22 -4
- package/dist/index.js +3 -2
- package/package.json +5 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,36 @@
|
|
|
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
|
+
|
|
27
|
+
# [1.3.0](https://github.com/nxtxe/zod-envkit/compare/v1.2.3...v1.3.0) (2026-03-04)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
### Features
|
|
31
|
+
|
|
32
|
+
* polish examples and install docs for 1.3.0 ([182ed34](https://github.com/nxtxe/zod-envkit/commit/182ed349f60c2e7cacb002f12c3a3642001eb19e))
|
|
33
|
+
|
|
1
34
|
## [1.2.3](https://github.com/nxtxe/zod-envkit/compare/v1.2.2...v1.2.3) (2026-02-19)
|
|
2
35
|
|
|
3
36
|
|
|
@@ -20,10 +53,6 @@
|
|
|
20
53
|
|
|
21
54
|
* strengthen CLI + env contract guarantees ([d516fbb](https://github.com/nxtxe/zod-envkit/commit/d516fbb4c8248745f7d74c1f07defaf4529c33ec))
|
|
22
55
|
|
|
23
|
-
# Changelog
|
|
24
|
-
|
|
25
|
-
All notable changes to this project will be documented in this file.
|
|
26
|
-
This project follows [Semantic Versioning](https://semver.org/).
|
|
27
56
|
|
|
28
57
|
## [1.2.0] – 2026-02-16
|
|
29
58
|
|
|
@@ -223,3 +252,9 @@ This project follows [Semantic Versioning](https://semver.org/).
|
|
|
223
252
|
* Small, framework-agnostic core
|
|
224
253
|
|
|
225
254
|
[1.0.0]: https://www.npmjs.com/package/zod-envkit/v/1.0.0
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
# Changelog
|
|
258
|
+
|
|
259
|
+
All notable changes to this project will be documented in this file.
|
|
260
|
+
This project follows [Semantic Versioning](https://semver.org/).
|
|
@@ -57,7 +57,7 @@ function generateEnvExample(meta) {
|
|
|
57
57
|
function buildRows(entries) {
|
|
58
58
|
return entries.map(([key, m]) => {
|
|
59
59
|
const req = m.required === false ? "no" : "yes";
|
|
60
|
-
const dep = m.deprecated ? "\u26A0\uFE0F" : "";
|
|
60
|
+
const dep = m.deprecated === true ? "\u26A0\uFE0F" : typeof m.deprecated === "string" ? `\u26A0\uFE0F ${m.deprecated}` : "";
|
|
61
61
|
const link = m.link ? m.link : "";
|
|
62
62
|
return {
|
|
63
63
|
Key: key,
|
|
@@ -182,16 +182,25 @@ function getUnknownEnv(meta, env = process.env) {
|
|
|
182
182
|
return unknown;
|
|
183
183
|
}
|
|
184
184
|
var SECRET_PATTERNS = [
|
|
185
|
-
(k) => k.includes("TOKEN"),
|
|
186
185
|
(k) => k.includes("SECRET"),
|
|
187
186
|
(k) => k.includes("PASSWORD"),
|
|
187
|
+
(k) => k.includes("PASS"),
|
|
188
|
+
(k) => k.includes("PWD"),
|
|
188
189
|
(k) => k.includes("PRIVATE"),
|
|
189
190
|
(k) => k.includes("API_KEY"),
|
|
190
|
-
(k) => k.endsWith("_KEY")
|
|
191
|
+
(k) => k.endsWith("_KEY"),
|
|
192
|
+
(k) => k === "KEY",
|
|
193
|
+
(k) => k === "API",
|
|
194
|
+
(k) => k.includes("TOKEN"),
|
|
195
|
+
(k) => k.includes("JWT"),
|
|
196
|
+
(k) => k.includes("SESSION"),
|
|
197
|
+
(k) => k.includes("CREDENTIAL"),
|
|
198
|
+
(k) => k.includes("CREDS"),
|
|
199
|
+
(k) => k.includes("DATABASE_URL") || k === "DB_URL",
|
|
200
|
+
(k) => k.includes("CONNECTION_STRING")
|
|
191
201
|
];
|
|
192
202
|
function isSecretKey(key) {
|
|
193
|
-
|
|
194
|
-
return SECRET_PATTERNS.some((fn) => fn(k));
|
|
203
|
+
return SECRET_PATTERNS.some((fn) => fn(key.toUpperCase()));
|
|
195
204
|
}
|
|
196
205
|
function checkEnv(meta, env = process.env) {
|
|
197
206
|
const missing = getMissingEnv(meta, env);
|
package/dist/cli/index.cjs
CHANGED
|
@@ -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,13 +39,20 @@ 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",
|
|
45
|
-
INIT_INPUT_EMPTY: "Input env file is empty or not found:"
|
|
46
|
+
INIT_INPUT_EMPTY: "Input env file is empty or not found:",
|
|
47
|
+
SCHEMA_LOAD_FAILED: "Failed to load schema file:",
|
|
48
|
+
SCHEMA_NOT_OBJECT: "Schema file must export a Zod object (z.object(...)).",
|
|
49
|
+
SCHEMA_VARS_NOT_IN_META: "Schema variables not listed in env.meta.json:",
|
|
50
|
+
META_VARS_NOT_IN_SCHEMA: "env.meta.json variables not in schema:",
|
|
51
|
+
SCHEMA_HINT_ADD_TO_META: "Hint: add these keys to env.meta.json for docs and CLI.",
|
|
52
|
+
META_HINT_SYNC_SCHEMA: "Hint: add these to your Zod schema or remove from env.meta.json."
|
|
46
53
|
},
|
|
47
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.",
|
|
48
56
|
META_NOT_FOUND: "\u0424\u0430\u0439\u043B env.meta.json \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D.",
|
|
49
57
|
META_TRIED: "\u041F\u0440\u043E\u0431\u043E\u0432\u0430\u043B\u0438:",
|
|
50
58
|
META_TIP: "\u041F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0430:",
|
|
@@ -54,11 +62,17 @@ var messages = {
|
|
|
54
62
|
GENERATED: "\u0421\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u043E: {example}, {docs}",
|
|
55
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.",
|
|
56
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:",
|
|
57
|
-
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):",
|
|
58
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",
|
|
59
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",
|
|
60
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",
|
|
61
|
-
INIT_INPUT_EMPTY: "\u0424\u0430\u0439\u043B \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u043F\u0443\u0441\u0442 \u0438\u043B\u0438 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D:"
|
|
69
|
+
INIT_INPUT_EMPTY: "\u0424\u0430\u0439\u043B \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u043F\u0443\u0441\u0442 \u0438\u043B\u0438 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D:",
|
|
70
|
+
SCHEMA_LOAD_FAILED: "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0444\u0430\u0439\u043B \u0441\u0445\u0435\u043C\u044B:",
|
|
71
|
+
SCHEMA_NOT_OBJECT: "\u0424\u0430\u0439\u043B \u0441\u0445\u0435\u043C\u044B \u0434\u043E\u043B\u0436\u0435\u043D \u044D\u043A\u0441\u043F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C Zod object (z.object(...)).",
|
|
72
|
+
SCHEMA_VARS_NOT_IN_META: "\u041F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 \u0441\u0445\u0435\u043C\u044B \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044E\u0442 \u0432 env.meta.json:",
|
|
73
|
+
META_VARS_NOT_IN_SCHEMA: "\u041F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 env.meta.json \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044E\u0442 \u0432 \u0441\u0445\u0435\u043C\u0435:",
|
|
74
|
+
SCHEMA_HINT_ADD_TO_META: "\u041F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0430: \u0434\u043E\u0431\u0430\u0432\u044C\u0442\u0435 \u044D\u0442\u0438 \u043A\u043B\u044E\u0447\u0438 \u0432 env.meta.json \u0434\u043B\u044F \u0434\u043E\u043A\u043E\u0432 \u0438 CLI.",
|
|
75
|
+
META_HINT_SYNC_SCHEMA: "\u041F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0430: \u0434\u043E\u0431\u0430\u0432\u044C\u0442\u0435 \u0438\u0445 \u0432 Zod-\u0441\u0445\u0435\u043C\u0443 \u0438\u043B\u0438 \u0443\u0434\u0430\u043B\u0438\u0442\u0435 \u0438\u0437 env.meta.json."
|
|
62
76
|
}
|
|
63
77
|
};
|
|
64
78
|
|
|
@@ -148,7 +162,12 @@ function loadMeta(lang, configFile) {
|
|
|
148
162
|
const raw = import_node_fs.default.readFileSync(found, "utf8");
|
|
149
163
|
return { meta: JSON.parse(raw), configPath: found };
|
|
150
164
|
} catch {
|
|
151
|
-
fail(lang, "META_PARSE_FAILED", [
|
|
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
|
+
]);
|
|
152
171
|
}
|
|
153
172
|
}
|
|
154
173
|
const examplePath = import_node_path.default.resolve(process.cwd(), ".env.example");
|
|
@@ -163,7 +182,7 @@ function loadMeta(lang, configFile) {
|
|
|
163
182
|
...candidates.map((p) => `- ${p}`),
|
|
164
183
|
"",
|
|
165
184
|
t(lang, "META_TIP"),
|
|
166
|
-
" npx zod-envkit
|
|
185
|
+
" Run: npx zod-envkit generate -c env.meta.json"
|
|
167
186
|
]);
|
|
168
187
|
}
|
|
169
188
|
|
|
@@ -226,7 +245,7 @@ function generateEnvExample(meta) {
|
|
|
226
245
|
function buildRows(entries) {
|
|
227
246
|
return entries.map(([key, m]) => {
|
|
228
247
|
const req = m.required === false ? "no" : "yes";
|
|
229
|
-
const dep = m.deprecated ? "\u26A0\uFE0F" : "";
|
|
248
|
+
const dep = m.deprecated === true ? "\u26A0\uFE0F" : typeof m.deprecated === "string" ? `\u26A0\uFE0F ${m.deprecated}` : "";
|
|
230
249
|
const link = m.link ? m.link : "";
|
|
231
250
|
return {
|
|
232
251
|
Key: key,
|
|
@@ -422,16 +441,25 @@ function getUnknownEnv(meta, env = process.env) {
|
|
|
422
441
|
return unknown;
|
|
423
442
|
}
|
|
424
443
|
var SECRET_PATTERNS = [
|
|
425
|
-
(k) => k.includes("TOKEN"),
|
|
426
444
|
(k) => k.includes("SECRET"),
|
|
427
445
|
(k) => k.includes("PASSWORD"),
|
|
446
|
+
(k) => k.includes("PASS"),
|
|
447
|
+
(k) => k.includes("PWD"),
|
|
428
448
|
(k) => k.includes("PRIVATE"),
|
|
429
449
|
(k) => k.includes("API_KEY"),
|
|
430
|
-
(k) => k.endsWith("_KEY")
|
|
450
|
+
(k) => k.endsWith("_KEY"),
|
|
451
|
+
(k) => k === "KEY",
|
|
452
|
+
(k) => k === "API",
|
|
453
|
+
(k) => k.includes("TOKEN"),
|
|
454
|
+
(k) => k.includes("JWT"),
|
|
455
|
+
(k) => k.includes("SESSION"),
|
|
456
|
+
(k) => k.includes("CREDENTIAL"),
|
|
457
|
+
(k) => k.includes("CREDS"),
|
|
458
|
+
(k) => k.includes("DATABASE_URL") || k === "DB_URL",
|
|
459
|
+
(k) => k.includes("CONNECTION_STRING")
|
|
431
460
|
];
|
|
432
461
|
function isSecretKey(key) {
|
|
433
|
-
|
|
434
|
-
return SECRET_PATTERNS.some((fn) => fn(k));
|
|
462
|
+
return SECRET_PATTERNS.some((fn) => fn(key.toUpperCase()));
|
|
435
463
|
}
|
|
436
464
|
|
|
437
465
|
// src/cli/lib/mask.ts
|
|
@@ -554,20 +582,96 @@ function registerShow(program2, getLang2) {
|
|
|
554
582
|
});
|
|
555
583
|
}
|
|
556
584
|
|
|
585
|
+
// src/cli/lib/schema.ts
|
|
586
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
587
|
+
var import_node_module = require("module");
|
|
588
|
+
var import_node_url = require("url");
|
|
589
|
+
var require2 = (0, import_node_module.createRequire)(import_node_path3.default.join(process.cwd(), "package.json"));
|
|
590
|
+
async function loadSchemaFile(filePath, lang) {
|
|
591
|
+
const absolutePath = import_node_path3.default.isAbsolute(filePath) ? filePath : import_node_path3.default.resolve(process.cwd(), filePath);
|
|
592
|
+
let mod;
|
|
593
|
+
try {
|
|
594
|
+
const url = (0, import_node_url.pathToFileURL)(absolutePath).href;
|
|
595
|
+
mod = await import(url);
|
|
596
|
+
} catch {
|
|
597
|
+
try {
|
|
598
|
+
mod = require2(absolutePath);
|
|
599
|
+
} catch (e) {
|
|
600
|
+
fail(lang, "SCHEMA_LOAD_FAILED", [`- ${absolutePath}`, String(e)]);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
const raw = mod != null && typeof mod === "object" ? mod.default ?? mod : void 0;
|
|
604
|
+
const schema = raw != null && typeof raw === "object" ? raw.default ?? raw.schema ?? raw : void 0;
|
|
605
|
+
if (schema == null) {
|
|
606
|
+
fail(lang, "SCHEMA_NOT_OBJECT", [`- ${absolutePath}`]);
|
|
607
|
+
}
|
|
608
|
+
const shape = schema != null && typeof schema === "object" && "shape" in schema && typeof schema.shape === "object" ? schema.shape : void 0;
|
|
609
|
+
if (shape == null || !Object.keys(shape).length) {
|
|
610
|
+
fail(lang, "SCHEMA_NOT_OBJECT", [`- ${absolutePath}`]);
|
|
611
|
+
}
|
|
612
|
+
return { keys: Object.keys(shape) };
|
|
613
|
+
}
|
|
614
|
+
|
|
557
615
|
// src/cli/commands/check.ts
|
|
558
616
|
function registerCheck(program2, getLang2) {
|
|
559
|
-
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)").
|
|
617
|
+
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)").option(
|
|
618
|
+
"--schema <file>",
|
|
619
|
+
"Path to JS file exporting Zod object; run schema\u2194meta consistency check"
|
|
620
|
+
).option(
|
|
621
|
+
"--schema-mode <mode>",
|
|
622
|
+
"Schema\u2194meta consistency: warn (report, exit 0) or strict (report, exit 1)",
|
|
623
|
+
"strict"
|
|
624
|
+
).action(async (opts) => {
|
|
560
625
|
const lang = getLang2();
|
|
561
626
|
const loaded = loadDotEnv(opts.dotenv);
|
|
562
627
|
const { meta } = loadMeta(lang, opts.config);
|
|
628
|
+
const sections = [];
|
|
563
629
|
const missing = getMissingEnv(meta, process.env);
|
|
564
630
|
if (missing.length) {
|
|
565
|
-
|
|
631
|
+
sections.push(t(lang, "MISSING_ENV"));
|
|
632
|
+
missing.forEach((k) => sections.push(`- ${k}`));
|
|
633
|
+
sections.push("");
|
|
566
634
|
}
|
|
567
635
|
if (opts.strict) {
|
|
568
636
|
const unknown = getUnknownEnv(meta, loaded.env);
|
|
569
637
|
if (unknown.length) {
|
|
570
|
-
|
|
638
|
+
sections.push(t(lang, "UNKNOWN_ENV"));
|
|
639
|
+
unknown.forEach((k) => sections.push(`- ${k}`));
|
|
640
|
+
sections.push("");
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
if (sections.length) {
|
|
644
|
+
fail(lang, "ENV_INVALID", sections);
|
|
645
|
+
}
|
|
646
|
+
if (opts.schema) {
|
|
647
|
+
const schemaMode = String(opts.schemaMode ?? "strict").toLowerCase();
|
|
648
|
+
if (schemaMode !== "warn" && schemaMode !== "strict") {
|
|
649
|
+
fail(lang, "INVALID_FORMAT", ["--schema-mode must be warn or strict"]);
|
|
650
|
+
}
|
|
651
|
+
const { keys: schemaKeys } = await loadSchemaFile(opts.schema, lang);
|
|
652
|
+
const metaKeys = new Set(Object.keys(meta));
|
|
653
|
+
const inSchemaNotMeta = schemaKeys.filter((k) => !metaKeys.has(k));
|
|
654
|
+
const inMetaNotSchema = [...metaKeys].filter((k) => !schemaKeys.includes(k));
|
|
655
|
+
const hasMismatch = inSchemaNotMeta.length > 0 || inMetaNotSchema.length > 0;
|
|
656
|
+
if (hasMismatch) {
|
|
657
|
+
const lines = [];
|
|
658
|
+
if (inSchemaNotMeta.length) {
|
|
659
|
+
lines.push(`\u274C ${t(lang, "SCHEMA_VARS_NOT_IN_META")}`);
|
|
660
|
+
inSchemaNotMeta.forEach((k) => lines.push(`- ${k}`));
|
|
661
|
+
lines.push(` ${t(lang, "SCHEMA_HINT_ADD_TO_META")}`);
|
|
662
|
+
lines.push("");
|
|
663
|
+
}
|
|
664
|
+
if (inMetaNotSchema.length) {
|
|
665
|
+
lines.push(`\u274C ${t(lang, "META_VARS_NOT_IN_SCHEMA")}`);
|
|
666
|
+
inMetaNotSchema.sort((a, b) => a.localeCompare(b)).forEach((k) => lines.push(`- ${k}`));
|
|
667
|
+
lines.push(` ${t(lang, "META_HINT_SYNC_SCHEMA")}`);
|
|
668
|
+
}
|
|
669
|
+
const out = lines.join("\n");
|
|
670
|
+
if (schemaMode === "strict") {
|
|
671
|
+
console.error(out);
|
|
672
|
+
process.exit(1);
|
|
673
|
+
}
|
|
674
|
+
console.warn(out);
|
|
571
675
|
}
|
|
572
676
|
}
|
|
573
677
|
console.log(`\u2705 ${t(lang, "ENV_OK")}`);
|
|
@@ -577,10 +681,10 @@ function registerCheck(program2, getLang2) {
|
|
|
577
681
|
|
|
578
682
|
// src/cli/commands/init.ts
|
|
579
683
|
var import_node_fs4 = __toESM(require("fs"), 1);
|
|
580
|
-
var
|
|
684
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
581
685
|
var import_dotenv5 = __toESM(require("dotenv"), 1);
|
|
582
686
|
function readEnvFile(file) {
|
|
583
|
-
const abs =
|
|
687
|
+
const abs = import_node_path4.default.resolve(process.cwd(), file);
|
|
584
688
|
if (!import_node_fs4.default.existsSync(abs)) return {};
|
|
585
689
|
const raw = import_node_fs4.default.readFileSync(abs, "utf8");
|
|
586
690
|
return import_dotenv5.default.parse(raw);
|
package/dist/cli/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
getMissingEnv,
|
|
6
6
|
getUnknownEnv,
|
|
7
7
|
isSecretKey
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-J4V5ODI6.js";
|
|
9
9
|
|
|
10
10
|
// src/cli/index.ts
|
|
11
11
|
import { Command } from "commander";
|
|
@@ -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,13 +23,20 @@ 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",
|
|
29
|
-
INIT_INPUT_EMPTY: "Input env file is empty or not found:"
|
|
30
|
+
INIT_INPUT_EMPTY: "Input env file is empty or not found:",
|
|
31
|
+
SCHEMA_LOAD_FAILED: "Failed to load schema file:",
|
|
32
|
+
SCHEMA_NOT_OBJECT: "Schema file must export a Zod object (z.object(...)).",
|
|
33
|
+
SCHEMA_VARS_NOT_IN_META: "Schema variables not listed in env.meta.json:",
|
|
34
|
+
META_VARS_NOT_IN_SCHEMA: "env.meta.json variables not in schema:",
|
|
35
|
+
SCHEMA_HINT_ADD_TO_META: "Hint: add these keys to env.meta.json for docs and CLI.",
|
|
36
|
+
META_HINT_SYNC_SCHEMA: "Hint: add these to your Zod schema or remove from env.meta.json."
|
|
30
37
|
},
|
|
31
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.",
|
|
32
40
|
META_NOT_FOUND: "\u0424\u0430\u0439\u043B env.meta.json \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D.",
|
|
33
41
|
META_TRIED: "\u041F\u0440\u043E\u0431\u043E\u0432\u0430\u043B\u0438:",
|
|
34
42
|
META_TIP: "\u041F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0430:",
|
|
@@ -38,11 +46,17 @@ var messages = {
|
|
|
38
46
|
GENERATED: "\u0421\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u043E: {example}, {docs}",
|
|
39
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.",
|
|
40
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:",
|
|
41
|
-
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):",
|
|
42
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",
|
|
43
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",
|
|
44
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",
|
|
45
|
-
INIT_INPUT_EMPTY: "\u0424\u0430\u0439\u043B \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u043F\u0443\u0441\u0442 \u0438\u043B\u0438 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D:"
|
|
53
|
+
INIT_INPUT_EMPTY: "\u0424\u0430\u0439\u043B \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u043F\u0443\u0441\u0442 \u0438\u043B\u0438 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D:",
|
|
54
|
+
SCHEMA_LOAD_FAILED: "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0444\u0430\u0439\u043B \u0441\u0445\u0435\u043C\u044B:",
|
|
55
|
+
SCHEMA_NOT_OBJECT: "\u0424\u0430\u0439\u043B \u0441\u0445\u0435\u043C\u044B \u0434\u043E\u043B\u0436\u0435\u043D \u044D\u043A\u0441\u043F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C Zod object (z.object(...)).",
|
|
56
|
+
SCHEMA_VARS_NOT_IN_META: "\u041F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 \u0441\u0445\u0435\u043C\u044B \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044E\u0442 \u0432 env.meta.json:",
|
|
57
|
+
META_VARS_NOT_IN_SCHEMA: "\u041F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 env.meta.json \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044E\u0442 \u0432 \u0441\u0445\u0435\u043C\u0435:",
|
|
58
|
+
SCHEMA_HINT_ADD_TO_META: "\u041F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0430: \u0434\u043E\u0431\u0430\u0432\u044C\u0442\u0435 \u044D\u0442\u0438 \u043A\u043B\u044E\u0447\u0438 \u0432 env.meta.json \u0434\u043B\u044F \u0434\u043E\u043A\u043E\u0432 \u0438 CLI.",
|
|
59
|
+
META_HINT_SYNC_SCHEMA: "\u041F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0430: \u0434\u043E\u0431\u0430\u0432\u044C\u0442\u0435 \u0438\u0445 \u0432 Zod-\u0441\u0445\u0435\u043C\u0443 \u0438\u043B\u0438 \u0443\u0434\u0430\u043B\u0438\u0442\u0435 \u0438\u0437 env.meta.json."
|
|
46
60
|
}
|
|
47
61
|
};
|
|
48
62
|
|
|
@@ -132,7 +146,12 @@ function loadMeta(lang, configFile) {
|
|
|
132
146
|
const raw = fs.readFileSync(found, "utf8");
|
|
133
147
|
return { meta: JSON.parse(raw), configPath: found };
|
|
134
148
|
} catch {
|
|
135
|
-
fail(lang, "META_PARSE_FAILED", [
|
|
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
|
+
]);
|
|
136
155
|
}
|
|
137
156
|
}
|
|
138
157
|
const examplePath = path.resolve(process.cwd(), ".env.example");
|
|
@@ -147,7 +166,7 @@ function loadMeta(lang, configFile) {
|
|
|
147
166
|
...candidates.map((p) => `- ${p}`),
|
|
148
167
|
"",
|
|
149
168
|
t(lang, "META_TIP"),
|
|
150
|
-
" npx zod-envkit
|
|
169
|
+
" Run: npx zod-envkit generate -c env.meta.json"
|
|
151
170
|
]);
|
|
152
171
|
}
|
|
153
172
|
|
|
@@ -342,20 +361,96 @@ function registerShow(program2, getLang2) {
|
|
|
342
361
|
});
|
|
343
362
|
}
|
|
344
363
|
|
|
364
|
+
// src/cli/lib/schema.ts
|
|
365
|
+
import path3 from "path";
|
|
366
|
+
import { createRequire } from "module";
|
|
367
|
+
import { pathToFileURL } from "url";
|
|
368
|
+
var require2 = createRequire(path3.join(process.cwd(), "package.json"));
|
|
369
|
+
async function loadSchemaFile(filePath, lang) {
|
|
370
|
+
const absolutePath = path3.isAbsolute(filePath) ? filePath : path3.resolve(process.cwd(), filePath);
|
|
371
|
+
let mod;
|
|
372
|
+
try {
|
|
373
|
+
const url = pathToFileURL(absolutePath).href;
|
|
374
|
+
mod = await import(url);
|
|
375
|
+
} catch {
|
|
376
|
+
try {
|
|
377
|
+
mod = require2(absolutePath);
|
|
378
|
+
} catch (e) {
|
|
379
|
+
fail(lang, "SCHEMA_LOAD_FAILED", [`- ${absolutePath}`, String(e)]);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
const raw = mod != null && typeof mod === "object" ? mod.default ?? mod : void 0;
|
|
383
|
+
const schema = raw != null && typeof raw === "object" ? raw.default ?? raw.schema ?? raw : void 0;
|
|
384
|
+
if (schema == null) {
|
|
385
|
+
fail(lang, "SCHEMA_NOT_OBJECT", [`- ${absolutePath}`]);
|
|
386
|
+
}
|
|
387
|
+
const shape = schema != null && typeof schema === "object" && "shape" in schema && typeof schema.shape === "object" ? schema.shape : void 0;
|
|
388
|
+
if (shape == null || !Object.keys(shape).length) {
|
|
389
|
+
fail(lang, "SCHEMA_NOT_OBJECT", [`- ${absolutePath}`]);
|
|
390
|
+
}
|
|
391
|
+
return { keys: Object.keys(shape) };
|
|
392
|
+
}
|
|
393
|
+
|
|
345
394
|
// src/cli/commands/check.ts
|
|
346
395
|
function registerCheck(program2, getLang2) {
|
|
347
|
-
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)").
|
|
396
|
+
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)").option(
|
|
397
|
+
"--schema <file>",
|
|
398
|
+
"Path to JS file exporting Zod object; run schema\u2194meta consistency check"
|
|
399
|
+
).option(
|
|
400
|
+
"--schema-mode <mode>",
|
|
401
|
+
"Schema\u2194meta consistency: warn (report, exit 0) or strict (report, exit 1)",
|
|
402
|
+
"strict"
|
|
403
|
+
).action(async (opts) => {
|
|
348
404
|
const lang = getLang2();
|
|
349
405
|
const loaded = loadDotEnv(opts.dotenv);
|
|
350
406
|
const { meta } = loadMeta(lang, opts.config);
|
|
407
|
+
const sections = [];
|
|
351
408
|
const missing = getMissingEnv(meta, process.env);
|
|
352
409
|
if (missing.length) {
|
|
353
|
-
|
|
410
|
+
sections.push(t(lang, "MISSING_ENV"));
|
|
411
|
+
missing.forEach((k) => sections.push(`- ${k}`));
|
|
412
|
+
sections.push("");
|
|
354
413
|
}
|
|
355
414
|
if (opts.strict) {
|
|
356
415
|
const unknown = getUnknownEnv(meta, loaded.env);
|
|
357
416
|
if (unknown.length) {
|
|
358
|
-
|
|
417
|
+
sections.push(t(lang, "UNKNOWN_ENV"));
|
|
418
|
+
unknown.forEach((k) => sections.push(`- ${k}`));
|
|
419
|
+
sections.push("");
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
if (sections.length) {
|
|
423
|
+
fail(lang, "ENV_INVALID", sections);
|
|
424
|
+
}
|
|
425
|
+
if (opts.schema) {
|
|
426
|
+
const schemaMode = String(opts.schemaMode ?? "strict").toLowerCase();
|
|
427
|
+
if (schemaMode !== "warn" && schemaMode !== "strict") {
|
|
428
|
+
fail(lang, "INVALID_FORMAT", ["--schema-mode must be warn or strict"]);
|
|
429
|
+
}
|
|
430
|
+
const { keys: schemaKeys } = await loadSchemaFile(opts.schema, lang);
|
|
431
|
+
const metaKeys = new Set(Object.keys(meta));
|
|
432
|
+
const inSchemaNotMeta = schemaKeys.filter((k) => !metaKeys.has(k));
|
|
433
|
+
const inMetaNotSchema = [...metaKeys].filter((k) => !schemaKeys.includes(k));
|
|
434
|
+
const hasMismatch = inSchemaNotMeta.length > 0 || inMetaNotSchema.length > 0;
|
|
435
|
+
if (hasMismatch) {
|
|
436
|
+
const lines = [];
|
|
437
|
+
if (inSchemaNotMeta.length) {
|
|
438
|
+
lines.push(`\u274C ${t(lang, "SCHEMA_VARS_NOT_IN_META")}`);
|
|
439
|
+
inSchemaNotMeta.forEach((k) => lines.push(`- ${k}`));
|
|
440
|
+
lines.push(` ${t(lang, "SCHEMA_HINT_ADD_TO_META")}`);
|
|
441
|
+
lines.push("");
|
|
442
|
+
}
|
|
443
|
+
if (inMetaNotSchema.length) {
|
|
444
|
+
lines.push(`\u274C ${t(lang, "META_VARS_NOT_IN_SCHEMA")}`);
|
|
445
|
+
inMetaNotSchema.sort((a, b) => a.localeCompare(b)).forEach((k) => lines.push(`- ${k}`));
|
|
446
|
+
lines.push(` ${t(lang, "META_HINT_SYNC_SCHEMA")}`);
|
|
447
|
+
}
|
|
448
|
+
const out = lines.join("\n");
|
|
449
|
+
if (schemaMode === "strict") {
|
|
450
|
+
console.error(out);
|
|
451
|
+
process.exit(1);
|
|
452
|
+
}
|
|
453
|
+
console.warn(out);
|
|
359
454
|
}
|
|
360
455
|
}
|
|
361
456
|
console.log(`\u2705 ${t(lang, "ENV_OK")}`);
|
|
@@ -365,10 +460,10 @@ function registerCheck(program2, getLang2) {
|
|
|
365
460
|
|
|
366
461
|
// src/cli/commands/init.ts
|
|
367
462
|
import fs4 from "fs";
|
|
368
|
-
import
|
|
463
|
+
import path4 from "path";
|
|
369
464
|
import dotenv3 from "dotenv";
|
|
370
465
|
function readEnvFile(file) {
|
|
371
|
-
const abs =
|
|
466
|
+
const abs = path4.resolve(process.cwd(), file);
|
|
372
467
|
if (!fs4.existsSync(abs)) return {};
|
|
373
468
|
const raw = fs4.readFileSync(abs, "utf8");
|
|
374
469
|
return dotenv3.parse(raw);
|
package/dist/index.cjs
CHANGED
|
@@ -92,7 +92,7 @@ function generateEnvExample(meta) {
|
|
|
92
92
|
function buildRows(entries) {
|
|
93
93
|
return entries.map(([key, m]) => {
|
|
94
94
|
const req = m.required === false ? "no" : "yes";
|
|
95
|
-
const dep = m.deprecated ? "\u26A0\uFE0F" : "";
|
|
95
|
+
const dep = m.deprecated === true ? "\u26A0\uFE0F" : typeof m.deprecated === "string" ? `\u26A0\uFE0F ${m.deprecated}` : "";
|
|
96
96
|
const link = m.link ? m.link : "";
|
|
97
97
|
return {
|
|
98
98
|
Key: key,
|
|
@@ -217,16 +217,25 @@ function getUnknownEnv(meta, env = process.env) {
|
|
|
217
217
|
return unknown;
|
|
218
218
|
}
|
|
219
219
|
var SECRET_PATTERNS = [
|
|
220
|
-
(k) => k.includes("TOKEN"),
|
|
221
220
|
(k) => k.includes("SECRET"),
|
|
222
221
|
(k) => k.includes("PASSWORD"),
|
|
222
|
+
(k) => k.includes("PASS"),
|
|
223
|
+
(k) => k.includes("PWD"),
|
|
223
224
|
(k) => k.includes("PRIVATE"),
|
|
224
225
|
(k) => k.includes("API_KEY"),
|
|
225
|
-
(k) => k.endsWith("_KEY")
|
|
226
|
+
(k) => k.endsWith("_KEY"),
|
|
227
|
+
(k) => k === "KEY",
|
|
228
|
+
(k) => k === "API",
|
|
229
|
+
(k) => k.includes("TOKEN"),
|
|
230
|
+
(k) => k.includes("JWT"),
|
|
231
|
+
(k) => k.includes("SESSION"),
|
|
232
|
+
(k) => k.includes("CREDENTIAL"),
|
|
233
|
+
(k) => k.includes("CREDS"),
|
|
234
|
+
(k) => k.includes("DATABASE_URL") || k === "DB_URL",
|
|
235
|
+
(k) => k.includes("CONNECTION_STRING")
|
|
226
236
|
];
|
|
227
237
|
function isSecretKey(key) {
|
|
228
|
-
|
|
229
|
-
return SECRET_PATTERNS.some((fn) => fn(k));
|
|
238
|
+
return SECRET_PATTERNS.some((fn) => fn(key.toUpperCase()));
|
|
230
239
|
}
|
|
231
240
|
function checkEnv(meta, env = process.env) {
|
|
232
241
|
const missing = getMissingEnv(meta, env);
|
|
@@ -253,7 +262,8 @@ function formatZodError(err) {
|
|
|
253
262
|
return pa.localeCompare(pb);
|
|
254
263
|
}).map((issue) => {
|
|
255
264
|
const path = issue.path.length ? issue.path.join(".") : "(root)";
|
|
256
|
-
|
|
265
|
+
const msg = issue.message.trim() || "(validation failed)";
|
|
266
|
+
return `- ${path}: ${msg}`;
|
|
257
267
|
}).join("\n");
|
|
258
268
|
}
|
|
259
269
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/index.d.cts
CHANGED
|
@@ -6,6 +6,8 @@ import { z } from 'zod';
|
|
|
6
6
|
* This file is part of the stable public API: it powers the CLI generators,
|
|
7
7
|
* and is intended to be reusable by consumers.
|
|
8
8
|
*
|
|
9
|
+
* Stable in 1.2.
|
|
10
|
+
*
|
|
9
11
|
* @public
|
|
10
12
|
* @since 1.0.0
|
|
11
13
|
*/
|
|
@@ -40,10 +42,11 @@ type EnvMetaEntry = {
|
|
|
40
42
|
default?: string;
|
|
41
43
|
/**
|
|
42
44
|
* Mark variable as deprecated in docs.
|
|
45
|
+
* Use `true` for a generic warning (⚠️) or a string to show an explanation (e.g. "Use FOO instead").
|
|
43
46
|
*
|
|
44
47
|
* @since 1.1.0
|
|
45
48
|
*/
|
|
46
|
-
deprecated?: boolean;
|
|
49
|
+
deprecated?: boolean | string;
|
|
47
50
|
/**
|
|
48
51
|
* Version when the variable was introduced (documentation only).
|
|
49
52
|
*
|
|
@@ -142,6 +145,9 @@ declare function generateEnvExample(meta: EnvMeta): string;
|
|
|
142
145
|
*/
|
|
143
146
|
declare function generateEnvDocs(meta: EnvMeta, opts?: GenerateDocsOptions): string;
|
|
144
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Env validation helpers. Stable in 1.2.
|
|
150
|
+
*/
|
|
145
151
|
/**
|
|
146
152
|
* Result of validating an env object against {@link EnvMeta}.
|
|
147
153
|
*
|
|
@@ -175,7 +181,8 @@ declare function getUnknownEnv(meta: EnvMeta, env?: NodeJS.ProcessEnv): string[]
|
|
|
175
181
|
/**
|
|
176
182
|
* Detect whether an env key name looks like a secret.
|
|
177
183
|
*
|
|
178
|
-
* Used by the CLI to mask values (
|
|
184
|
+
* Used by the CLI to mask values (e.g. SECRET, PASSWORD, TOKEN, *_KEY, connection strings).
|
|
185
|
+
* Matching is case-insensitive (key is normalized to uppercase).
|
|
179
186
|
*
|
|
180
187
|
* @public
|
|
181
188
|
* @since 1.2.0
|
|
@@ -241,6 +248,9 @@ type LoadEnvFail = {
|
|
|
241
248
|
* - returns `{ ok: false, error }` by default
|
|
242
249
|
* - throws `ZodError` if `opts.throwOnError === true`
|
|
243
250
|
*
|
|
251
|
+
* @remarks Load dotenv (e.g. `import "dotenv/config"`) before calling so `process.env` is populated.
|
|
252
|
+
* @see {@link mustLoadEnv} — fail-fast variant that returns env directly.
|
|
253
|
+
*
|
|
244
254
|
* @example
|
|
245
255
|
* ```ts
|
|
246
256
|
* const result = loadEnv(EnvSchema);
|
|
@@ -260,6 +270,9 @@ declare function loadEnv<T extends z.ZodTypeAny>(schema: T, opts?: LoadEnvOption
|
|
|
260
270
|
*
|
|
261
271
|
* Equivalent to: `loadEnv(schema, { throwOnError: true })` but returns typed env directly.
|
|
262
272
|
*
|
|
273
|
+
* @remarks Load dotenv (e.g. `import "dotenv/config"`) before calling so `process.env` is populated.
|
|
274
|
+
* @see {@link loadEnv} — returns a result object instead of throwing.
|
|
275
|
+
*
|
|
263
276
|
* @example
|
|
264
277
|
* ```ts
|
|
265
278
|
* export const env = mustLoadEnv(EnvSchema);
|
|
@@ -274,8 +287,13 @@ declare function mustLoadEnv<T extends z.ZodTypeAny>(schema: T): z.infer<T>;
|
|
|
274
287
|
/**
|
|
275
288
|
* Format `ZodError` into a human-friendly multi-line message (one issue per line).
|
|
276
289
|
*
|
|
277
|
-
* Output format (stable in 1.x):
|
|
278
|
-
*
|
|
290
|
+
* Output format (stable in 1.x): `path: message`
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* ```ts
|
|
294
|
+
* const result = loadEnv(EnvSchema);
|
|
295
|
+
* if (!result.ok) console.error(formatZodError(result.error));
|
|
296
|
+
* ```
|
|
279
297
|
*
|
|
280
298
|
* @public
|
|
281
299
|
* @since 1.0.0
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ import { z } from 'zod';
|
|
|
6
6
|
* This file is part of the stable public API: it powers the CLI generators,
|
|
7
7
|
* and is intended to be reusable by consumers.
|
|
8
8
|
*
|
|
9
|
+
* Stable in 1.2.
|
|
10
|
+
*
|
|
9
11
|
* @public
|
|
10
12
|
* @since 1.0.0
|
|
11
13
|
*/
|
|
@@ -40,10 +42,11 @@ type EnvMetaEntry = {
|
|
|
40
42
|
default?: string;
|
|
41
43
|
/**
|
|
42
44
|
* Mark variable as deprecated in docs.
|
|
45
|
+
* Use `true` for a generic warning (⚠️) or a string to show an explanation (e.g. "Use FOO instead").
|
|
43
46
|
*
|
|
44
47
|
* @since 1.1.0
|
|
45
48
|
*/
|
|
46
|
-
deprecated?: boolean;
|
|
49
|
+
deprecated?: boolean | string;
|
|
47
50
|
/**
|
|
48
51
|
* Version when the variable was introduced (documentation only).
|
|
49
52
|
*
|
|
@@ -142,6 +145,9 @@ declare function generateEnvExample(meta: EnvMeta): string;
|
|
|
142
145
|
*/
|
|
143
146
|
declare function generateEnvDocs(meta: EnvMeta, opts?: GenerateDocsOptions): string;
|
|
144
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Env validation helpers. Stable in 1.2.
|
|
150
|
+
*/
|
|
145
151
|
/**
|
|
146
152
|
* Result of validating an env object against {@link EnvMeta}.
|
|
147
153
|
*
|
|
@@ -175,7 +181,8 @@ declare function getUnknownEnv(meta: EnvMeta, env?: NodeJS.ProcessEnv): string[]
|
|
|
175
181
|
/**
|
|
176
182
|
* Detect whether an env key name looks like a secret.
|
|
177
183
|
*
|
|
178
|
-
* Used by the CLI to mask values (
|
|
184
|
+
* Used by the CLI to mask values (e.g. SECRET, PASSWORD, TOKEN, *_KEY, connection strings).
|
|
185
|
+
* Matching is case-insensitive (key is normalized to uppercase).
|
|
179
186
|
*
|
|
180
187
|
* @public
|
|
181
188
|
* @since 1.2.0
|
|
@@ -241,6 +248,9 @@ type LoadEnvFail = {
|
|
|
241
248
|
* - returns `{ ok: false, error }` by default
|
|
242
249
|
* - throws `ZodError` if `opts.throwOnError === true`
|
|
243
250
|
*
|
|
251
|
+
* @remarks Load dotenv (e.g. `import "dotenv/config"`) before calling so `process.env` is populated.
|
|
252
|
+
* @see {@link mustLoadEnv} — fail-fast variant that returns env directly.
|
|
253
|
+
*
|
|
244
254
|
* @example
|
|
245
255
|
* ```ts
|
|
246
256
|
* const result = loadEnv(EnvSchema);
|
|
@@ -260,6 +270,9 @@ declare function loadEnv<T extends z.ZodTypeAny>(schema: T, opts?: LoadEnvOption
|
|
|
260
270
|
*
|
|
261
271
|
* Equivalent to: `loadEnv(schema, { throwOnError: true })` but returns typed env directly.
|
|
262
272
|
*
|
|
273
|
+
* @remarks Load dotenv (e.g. `import "dotenv/config"`) before calling so `process.env` is populated.
|
|
274
|
+
* @see {@link loadEnv} — returns a result object instead of throwing.
|
|
275
|
+
*
|
|
263
276
|
* @example
|
|
264
277
|
* ```ts
|
|
265
278
|
* export const env = mustLoadEnv(EnvSchema);
|
|
@@ -274,8 +287,13 @@ declare function mustLoadEnv<T extends z.ZodTypeAny>(schema: T): z.infer<T>;
|
|
|
274
287
|
/**
|
|
275
288
|
* Format `ZodError` into a human-friendly multi-line message (one issue per line).
|
|
276
289
|
*
|
|
277
|
-
* Output format (stable in 1.x):
|
|
278
|
-
*
|
|
290
|
+
* Output format (stable in 1.x): `path: message`
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* ```ts
|
|
294
|
+
* const result = loadEnv(EnvSchema);
|
|
295
|
+
* if (!result.ok) console.error(formatZodError(result.error));
|
|
296
|
+
* ```
|
|
279
297
|
*
|
|
280
298
|
* @public
|
|
281
299
|
* @since 1.0.0
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
getUnknownEnv,
|
|
7
7
|
isSecretKey,
|
|
8
8
|
sortMetaEntries
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-J4V5ODI6.js";
|
|
10
10
|
|
|
11
11
|
// src/index.ts
|
|
12
12
|
function loadEnv(schema, opts) {
|
|
@@ -27,7 +27,8 @@ function formatZodError(err) {
|
|
|
27
27
|
return pa.localeCompare(pb);
|
|
28
28
|
}).map((issue) => {
|
|
29
29
|
const path = issue.path.length ? issue.path.join(".") : "(root)";
|
|
30
|
-
|
|
30
|
+
const msg = issue.message.trim() || "(validation failed)";
|
|
31
|
+
return `- ${path}: ${msg}`;
|
|
31
32
|
}).join("\n");
|
|
32
33
|
}
|
|
33
34
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zod-envkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Validate environment variables with Zod and generate .env.example",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "",
|
|
@@ -53,8 +53,9 @@
|
|
|
53
53
|
"ci:all": "pnpm ci:smoke && pnpm test && pnpm ci:docs",
|
|
54
54
|
"prepublishOnly": "pnpm build && pnpm test",
|
|
55
55
|
"docs:api": "typedoc --tsconfig tsconfig.docs.json --entryPoints src/index.ts",
|
|
56
|
-
"docs:build": "pnpm docs:api && vitepress build docs",
|
|
57
|
-
"docs:dev": "vitepress dev docs",
|
|
56
|
+
"docs:build": "pnpm docs:api && node scripts/copy-changelog-to-docs.cjs && vitepress build docs",
|
|
57
|
+
"docs:dev": "node scripts/copy-changelog-to-docs.cjs && vitepress dev docs",
|
|
58
|
+
"docs:links": "find docs -name '*.md' -not -path '*/.vitepress/*' -exec pnpm exec markdown-link-check -c docs/.markdown-link-check.json {} \\;",
|
|
58
59
|
"release": "semantic-release"
|
|
59
60
|
},
|
|
60
61
|
"dependencies": {
|
|
@@ -72,6 +73,7 @@
|
|
|
72
73
|
"@types/node": "^25.0.10",
|
|
73
74
|
"eslint": "^9.39.2",
|
|
74
75
|
"execa": "^9.6.1",
|
|
76
|
+
"markdown-link-check": "3.10.0",
|
|
75
77
|
"semantic-release": "^22.0.12",
|
|
76
78
|
"tmp-promise": "^3.0.3",
|
|
77
79
|
"tsup": "^8.5.1",
|