env-typed-checker 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +225 -190
- package/dist/{chunk-L5DK6LRX.mjs → chunk-CCJEPMPN.mjs} +55 -5
- package/dist/chunk-CCJEPMPN.mjs.map +1 -0
- package/dist/cli/index.js +187 -23
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +134 -20
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.d.mts +11 -3
- package/dist/index.d.ts +11 -3
- package/dist/index.js +54 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +2 -2
- package/dist/chunk-L5DK6LRX.mjs.map +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -143,6 +143,33 @@ function coerceDefault(kind, def) {
|
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
|
+
function normalizeMeta(schemaValue) {
|
|
147
|
+
let description = void 0;
|
|
148
|
+
if (hasOwn(schemaValue, "description")) {
|
|
149
|
+
const d = schemaValue.description;
|
|
150
|
+
if (d !== void 0 && typeof d !== "string") {
|
|
151
|
+
throw new Error(`"description" must be a string if provided`);
|
|
152
|
+
}
|
|
153
|
+
description = d;
|
|
154
|
+
}
|
|
155
|
+
let example = void 0;
|
|
156
|
+
if (hasOwn(schemaValue, "example")) {
|
|
157
|
+
const ex = schemaValue.example;
|
|
158
|
+
if (ex !== void 0 && typeof ex !== "string") {
|
|
159
|
+
throw new Error(`"example" must be a string if provided`);
|
|
160
|
+
}
|
|
161
|
+
example = ex;
|
|
162
|
+
}
|
|
163
|
+
let secret = false;
|
|
164
|
+
if (hasOwn(schemaValue, "secret")) {
|
|
165
|
+
const s = schemaValue.secret;
|
|
166
|
+
if (s !== void 0 && typeof s !== "boolean") {
|
|
167
|
+
throw new Error(`"secret" must be a boolean if provided`);
|
|
168
|
+
}
|
|
169
|
+
secret = s === true;
|
|
170
|
+
}
|
|
171
|
+
return { description, example, secret };
|
|
172
|
+
}
|
|
146
173
|
function normalizeSpec(schemaValue) {
|
|
147
174
|
if (typeof schemaValue === "string") {
|
|
148
175
|
const optional = schemaValue.endsWith("?");
|
|
@@ -160,12 +187,17 @@ function normalizeSpec(schemaValue) {
|
|
|
160
187
|
`Unsupported type "${schemaValue}". Supported: string, number, boolean, json, url, email (optional with ?)`
|
|
161
188
|
);
|
|
162
189
|
}
|
|
163
|
-
return {
|
|
190
|
+
return {
|
|
191
|
+
kind: base,
|
|
192
|
+
optional,
|
|
193
|
+
secret: false
|
|
194
|
+
};
|
|
164
195
|
}
|
|
165
196
|
if (!schemaValue || typeof schemaValue !== "object" || Array.isArray(schemaValue)) {
|
|
166
197
|
throw new Error("Schema value must be a string or object spec.");
|
|
167
198
|
}
|
|
168
199
|
const t = schemaValue.type;
|
|
200
|
+
const meta = normalizeMeta(schemaValue);
|
|
169
201
|
const primitiveAllowed = [
|
|
170
202
|
"string",
|
|
171
203
|
"number",
|
|
@@ -177,7 +209,12 @@ function normalizeSpec(schemaValue) {
|
|
|
177
209
|
if (primitiveAllowed.includes(t)) {
|
|
178
210
|
const optional = !!schemaValue.optional;
|
|
179
211
|
const defaultValue = hasOwn(schemaValue, "default") ? coerceDefault(t, schemaValue.default) : void 0;
|
|
180
|
-
return {
|
|
212
|
+
return {
|
|
213
|
+
kind: t,
|
|
214
|
+
optional,
|
|
215
|
+
defaultValue,
|
|
216
|
+
...meta
|
|
217
|
+
};
|
|
181
218
|
}
|
|
182
219
|
if (t === "enum") {
|
|
183
220
|
const values = schemaValue.values;
|
|
@@ -198,7 +235,13 @@ function normalizeSpec(schemaValue) {
|
|
|
198
235
|
}
|
|
199
236
|
defaultValue = def;
|
|
200
237
|
}
|
|
201
|
-
return {
|
|
238
|
+
return {
|
|
239
|
+
kind: "enum",
|
|
240
|
+
optional,
|
|
241
|
+
values,
|
|
242
|
+
defaultValue,
|
|
243
|
+
...meta
|
|
244
|
+
};
|
|
202
245
|
}
|
|
203
246
|
if (t === "regex") {
|
|
204
247
|
const pattern = schemaValue.pattern;
|
|
@@ -228,7 +271,14 @@ function normalizeSpec(schemaValue) {
|
|
|
228
271
|
}
|
|
229
272
|
defaultValue = def;
|
|
230
273
|
}
|
|
231
|
-
return {
|
|
274
|
+
return {
|
|
275
|
+
kind: "regex",
|
|
276
|
+
optional,
|
|
277
|
+
re,
|
|
278
|
+
display,
|
|
279
|
+
defaultValue,
|
|
280
|
+
...meta
|
|
281
|
+
};
|
|
232
282
|
}
|
|
233
283
|
throw new Error(
|
|
234
284
|
`Unsupported object spec type "${String(t)}". Supported: primitives (string/number/boolean/json/url/email), enum, regex`
|
|
@@ -296,8 +346,18 @@ function envDoctor(schema, options = {}) {
|
|
|
296
346
|
}
|
|
297
347
|
|
|
298
348
|
// src/cli/cli.ts
|
|
349
|
+
function isCheckOutputFormat(value) {
|
|
350
|
+
return value === "pretty" || value === "json" || value === "github";
|
|
351
|
+
}
|
|
299
352
|
function parseArgs(argv) {
|
|
300
|
-
const out = {
|
|
353
|
+
const out = {
|
|
354
|
+
checkFormat: "pretty",
|
|
355
|
+
useDotenv: true,
|
|
356
|
+
useDefaults: true,
|
|
357
|
+
commentTypes: false,
|
|
358
|
+
commentDocs: false,
|
|
359
|
+
exampleValues: false
|
|
360
|
+
};
|
|
301
361
|
const [cmd, ...rest] = argv;
|
|
302
362
|
out.cmd = cmd;
|
|
303
363
|
for (let i = 0; i < rest.length; i++) {
|
|
@@ -306,7 +366,15 @@ function parseArgs(argv) {
|
|
|
306
366
|
else if (a.startsWith("--schema=")) out.schemaPath = a.split("=", 2)[1];
|
|
307
367
|
else if (a === "--env-file") out.envFile = rest[++i];
|
|
308
368
|
else if (a.startsWith("--env-file=")) out.envFile = a.split("=", 2)[1];
|
|
309
|
-
else if (a === "--
|
|
369
|
+
else if (a === "--format") {
|
|
370
|
+
const f = rest[++i];
|
|
371
|
+
if (isCheckOutputFormat(f)) out.checkFormat = f;
|
|
372
|
+
else out.invalidCheckFormat = String(f);
|
|
373
|
+
} else if (a.startsWith("--format=")) {
|
|
374
|
+
const f = a.slice("--format=".length);
|
|
375
|
+
if (isCheckOutputFormat(f)) out.checkFormat = f;
|
|
376
|
+
else out.invalidCheckFormat = f;
|
|
377
|
+
} else if (a === "--no-dotenv") out.useDotenv = false;
|
|
310
378
|
else if (a === "--out") out.outFile = rest[++i];
|
|
311
379
|
else if (a.startsWith("--out=")) out.outFile = a.split("=", 2)[1];
|
|
312
380
|
else if (a === "--mode") {
|
|
@@ -317,6 +385,8 @@ function parseArgs(argv) {
|
|
|
317
385
|
if (m === "update" || m === "create") out.mode = m;
|
|
318
386
|
} else if (a === "--no-defaults") out.useDefaults = false;
|
|
319
387
|
else if (a === "--comment-types") out.commentTypes = true;
|
|
388
|
+
else if (a === "--comment-docs") out.commentDocs = true;
|
|
389
|
+
else if (a === "--example-values") out.exampleValues = true;
|
|
320
390
|
}
|
|
321
391
|
return out;
|
|
322
392
|
}
|
|
@@ -371,7 +441,22 @@ function formatEnvLine(key, value, comment) {
|
|
|
371
441
|
const safe = needsQuoting(value) ? JSON.stringify(value) : value;
|
|
372
442
|
return comment ? `${key}=${safe} # ${comment}` : `${key}=${safe}`;
|
|
373
443
|
}
|
|
374
|
-
function
|
|
444
|
+
function normalizeCommentText(s) {
|
|
445
|
+
return s.replace(/\r?\n+/g, " ").replace(/\s+/g, " ").trim();
|
|
446
|
+
}
|
|
447
|
+
function getDocCommentLines(description, example) {
|
|
448
|
+
const lines = [];
|
|
449
|
+
if (typeof description === "string") {
|
|
450
|
+
const d = normalizeCommentText(description);
|
|
451
|
+
if (d) lines.push(`# ${d}`);
|
|
452
|
+
}
|
|
453
|
+
if (typeof example === "string") {
|
|
454
|
+
const ex = normalizeCommentText(example);
|
|
455
|
+
if (ex) lines.push(`# example: ${ex}`);
|
|
456
|
+
}
|
|
457
|
+
return lines;
|
|
458
|
+
}
|
|
459
|
+
function runGenerate(schema, outPath, mode, useDefaults, commentTypes, commentDocs, exampleValues, io) {
|
|
375
460
|
const absOut = import_node_path.default.resolve(process.cwd(), outPath);
|
|
376
461
|
if (mode === "create" && import_node_fs.default.existsSync(absOut)) {
|
|
377
462
|
io.error(`Refusing to overwrite existing file: ${outPath}`);
|
|
@@ -380,45 +465,100 @@ function runGenerate(schema, outPath, mode, useDefaults, commentTypes, io) {
|
|
|
380
465
|
const existing = mode === "update" ? readEnvFile(absOut) : {};
|
|
381
466
|
const keys = Object.keys(schema).sort();
|
|
382
467
|
const linesToAdd = [];
|
|
468
|
+
let addedVars = 0;
|
|
383
469
|
for (const key of keys) {
|
|
384
470
|
if (existing[key] !== void 0) continue;
|
|
385
471
|
const spec = schema[key];
|
|
472
|
+
const ns = normalizeSpec(spec);
|
|
386
473
|
const typeLabel = commentTypes ? getTypeLabel(spec) : void 0;
|
|
387
474
|
let val = "";
|
|
388
|
-
if (
|
|
389
|
-
|
|
475
|
+
if (exampleValues && ns.example !== void 0) {
|
|
476
|
+
val = ns.example;
|
|
477
|
+
} else if (useDefaults) {
|
|
390
478
|
if (ns.defaultValue !== void 0) {
|
|
391
479
|
val = defaultToEnvString(ns.defaultValue);
|
|
392
480
|
}
|
|
393
481
|
}
|
|
482
|
+
if (commentDocs) {
|
|
483
|
+
linesToAdd.push(...getDocCommentLines(ns.description, ns.example));
|
|
484
|
+
}
|
|
394
485
|
linesToAdd.push(formatEnvLine(key, val, typeLabel));
|
|
486
|
+
addedVars++;
|
|
395
487
|
}
|
|
396
|
-
if (
|
|
488
|
+
if (addedVars === 0) {
|
|
397
489
|
io.log("\u2705 No missing variables. Nothing to generate.");
|
|
398
490
|
return 0;
|
|
399
491
|
}
|
|
400
492
|
if (mode === "create") {
|
|
401
493
|
import_node_fs.default.writeFileSync(absOut, linesToAdd.join("\n") + "\n", "utf8");
|
|
402
|
-
io.log(`\u2705 Created ${outPath} with ${
|
|
494
|
+
io.log(`\u2705 Created ${outPath} with ${addedVars} variables.`);
|
|
403
495
|
return 0;
|
|
404
496
|
}
|
|
405
497
|
const prefix = import_node_fs.default.existsSync(absOut) ? "\n" : "";
|
|
406
498
|
import_node_fs.default.appendFileSync(absOut, prefix + linesToAdd.join("\n") + "\n", "utf8");
|
|
407
|
-
io.log(
|
|
408
|
-
`\u2705 Updated ${outPath}. Added ${linesToAdd.length} missing variables.`
|
|
409
|
-
);
|
|
499
|
+
io.log(`\u2705 Updated ${outPath}. Added ${addedVars} missing variables.`);
|
|
410
500
|
return 0;
|
|
411
501
|
}
|
|
502
|
+
function redactIssueMessage(message) {
|
|
503
|
+
const sep = ", got ";
|
|
504
|
+
const i = message.indexOf(sep);
|
|
505
|
+
if (i >= 0) {
|
|
506
|
+
return `${message.slice(0, i)}, got "<redacted>"`;
|
|
507
|
+
}
|
|
508
|
+
return `invalid value, got "<redacted>"`;
|
|
509
|
+
}
|
|
510
|
+
function isSecretKey(schema, key) {
|
|
511
|
+
const spec = schema[key];
|
|
512
|
+
if (spec === void 0) return false;
|
|
513
|
+
try {
|
|
514
|
+
const ns = normalizeSpec(spec);
|
|
515
|
+
return ns.secret === true;
|
|
516
|
+
} catch {
|
|
517
|
+
return false;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
function getIssueMessage(issue, schema) {
|
|
521
|
+
const redact = issue.kind === "invalid" && isSecretKey(schema, issue.key);
|
|
522
|
+
return redact ? redactIssueMessage(issue.message) : issue.message;
|
|
523
|
+
}
|
|
524
|
+
function escapeGithubCommandData(value) {
|
|
525
|
+
return value.replace(/%/g, "%25").replace(/\r/g, "%0D").replace(/\n/g, "%0A");
|
|
526
|
+
}
|
|
527
|
+
function escapeGithubCommandProperty(value) {
|
|
528
|
+
return escapeGithubCommandData(value).replace(/:/g, "%3A").replace(/,/g, "%2C");
|
|
529
|
+
}
|
|
530
|
+
function formatValidationErrorForCli(err, schema, format) {
|
|
531
|
+
const issues = err.issues.map((issue) => {
|
|
532
|
+
const message = getIssueMessage(issue, schema);
|
|
533
|
+
return { key: issue.key, kind: issue.kind, message };
|
|
534
|
+
});
|
|
535
|
+
if (format === "github") {
|
|
536
|
+
const title = escapeGithubCommandProperty("ENV validation");
|
|
537
|
+
return issues.map(
|
|
538
|
+
(issue) => `::error title=${title}::${escapeGithubCommandData(`${issue.key}: ${issue.message}`)}`
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
if (format === "json") {
|
|
542
|
+
return [JSON.stringify({ error: "ENV validation failed", issues })];
|
|
543
|
+
}
|
|
544
|
+
const header = "ENV validation failed";
|
|
545
|
+
const lines = issues.map((issue) => `- ${issue.key}: ${issue.message}`);
|
|
546
|
+
return [[header, ...lines].join("\n")];
|
|
547
|
+
}
|
|
412
548
|
function runCli(argv, io = console) {
|
|
413
549
|
const {
|
|
414
550
|
cmd,
|
|
415
551
|
schemaPath,
|
|
416
552
|
envFile,
|
|
553
|
+
checkFormat,
|
|
554
|
+
invalidCheckFormat,
|
|
417
555
|
useDotenv,
|
|
418
556
|
outFile,
|
|
419
557
|
mode,
|
|
420
558
|
useDefaults,
|
|
421
|
-
commentTypes
|
|
559
|
+
commentTypes,
|
|
560
|
+
commentDocs,
|
|
561
|
+
exampleValues
|
|
422
562
|
} = parseArgs(argv);
|
|
423
563
|
if (!cmd || cmd === "help" || cmd === "--help" || cmd === "-h") {
|
|
424
564
|
io.log(
|
|
@@ -426,17 +566,20 @@ function runCli(argv, io = console) {
|
|
|
426
566
|
"env-typed-checker",
|
|
427
567
|
"",
|
|
428
568
|
"Usage:",
|
|
429
|
-
" env-typed-checker check --schema <file> [--env-file <file>] [--no-dotenv]",
|
|
430
|
-
" env-typed-checker generate --schema <file> [--out <file>] [--mode update|create] [--no-defaults] [--comment-types]",
|
|
569
|
+
" env-typed-checker check --schema <file> [--env-file <file>] [--no-dotenv] [--format pretty|json|github]",
|
|
570
|
+
" env-typed-checker generate --schema <file> [--out <file>] [--mode update|create] [--no-defaults] [--comment-types] [--comment-docs] [--example-values]",
|
|
431
571
|
"",
|
|
432
572
|
"Options:",
|
|
433
573
|
" --schema <file> Path to schema JSON (required)",
|
|
434
574
|
" --env-file <file> Env file path (default: .env) [check]",
|
|
435
575
|
" --no-dotenv Do not load env file; use process.env only [check]",
|
|
576
|
+
" --format <name> check output format: pretty|json|github (default: pretty) [check]",
|
|
436
577
|
" --out <file> Output env file (default: .env) [generate]",
|
|
437
578
|
" --mode update|create update appends missing keys; create fails if file exists (default: update)",
|
|
438
579
|
" --no-defaults do not write schema defaults; write empty placeholders [generate]",
|
|
439
580
|
" --comment-types add inline comments with type info [generate]",
|
|
581
|
+
" --comment-docs add description/example comments above each key [generate]",
|
|
582
|
+
" --example-values use schema example values when generating missing keys [generate]",
|
|
440
583
|
"",
|
|
441
584
|
"Exit codes:",
|
|
442
585
|
" 0 = OK, 1 = validation failed, 2 = CLI error"
|
|
@@ -448,26 +591,47 @@ function runCli(argv, io = console) {
|
|
|
448
591
|
io.error("Missing required option: --schema <file>");
|
|
449
592
|
return 2;
|
|
450
593
|
}
|
|
594
|
+
if (cmd === "check" && invalidCheckFormat !== void 0) {
|
|
595
|
+
io.error(
|
|
596
|
+
`Invalid value for --format: "${invalidCheckFormat}". Expected: pretty, json, github`
|
|
597
|
+
);
|
|
598
|
+
return 2;
|
|
599
|
+
}
|
|
451
600
|
try {
|
|
452
601
|
const schema = loadSchema(schemaPath);
|
|
453
602
|
if (cmd === "check") {
|
|
454
603
|
const env = buildEnv(useDotenv, envFile);
|
|
455
|
-
|
|
604
|
+
try {
|
|
605
|
+
envDoctor(schema, { loadDotEnv: false, env });
|
|
606
|
+
} catch (e) {
|
|
607
|
+
if (e instanceof EnvDoctorError) {
|
|
608
|
+
for (const line of formatValidationErrorForCli(e, schema, checkFormat)) {
|
|
609
|
+
io.error(line);
|
|
610
|
+
}
|
|
611
|
+
return 1;
|
|
612
|
+
}
|
|
613
|
+
throw e;
|
|
614
|
+
}
|
|
456
615
|
io.log("\u2705 Environment is valid.");
|
|
457
616
|
return 0;
|
|
458
617
|
}
|
|
459
618
|
if (cmd === "generate") {
|
|
460
619
|
const out = outFile ?? ".env";
|
|
461
620
|
const m = mode ?? "update";
|
|
462
|
-
return runGenerate(
|
|
621
|
+
return runGenerate(
|
|
622
|
+
schema,
|
|
623
|
+
out,
|
|
624
|
+
m,
|
|
625
|
+
useDefaults,
|
|
626
|
+
commentTypes,
|
|
627
|
+
commentDocs,
|
|
628
|
+
exampleValues,
|
|
629
|
+
io
|
|
630
|
+
);
|
|
463
631
|
}
|
|
464
632
|
io.error(`Unknown command: ${cmd}`);
|
|
465
633
|
return 2;
|
|
466
634
|
} catch (e) {
|
|
467
|
-
if (e instanceof EnvDoctorError) {
|
|
468
|
-
io.error(e.message);
|
|
469
|
-
return 1;
|
|
470
|
-
}
|
|
471
635
|
io.error(e instanceof Error ? e.message : String(e));
|
|
472
636
|
return 2;
|
|
473
637
|
}
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/index.ts","../../src/cli/cli.ts","../../src/core/envDoctor.ts","../../src/errors/EnvDoctorError.ts","../../src/validators/primitives.ts","../../src/core/runner.ts"],"sourcesContent":["export { runCli } from \"./cli\";\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport * as dotenv from \"dotenv\";\nimport { envDoctor, EnvDoctorError } from \"../index\";\nimport { normalizeSpec } from \"../validators/primitives\";\nimport type { EnvDoctorSchema, EnvSchemaValue } from \"../types\";\n\ntype Io = {\n log: (msg: string) => void;\n error: (msg: string) => void;\n};\n\nfunction parseArgs(argv: string[]) {\n const out: {\n cmd?: string;\n schemaPath?: string;\n envFile?: string;\n useDotenv: boolean;\n\n // generate options\n outFile?: string;\n mode?: \"update\" | \"create\";\n useDefaults: boolean;\n commentTypes: boolean;\n } = { useDotenv: true, useDefaults: true, commentTypes: false };\n\n const [cmd, ...rest] = argv;\n out.cmd = cmd;\n\n for (let i = 0; i < rest.length; i++) {\n const a = rest[i];\n\n if (a === \"--schema\") out.schemaPath = rest[++i];\n else if (a.startsWith(\"--schema=\")) out.schemaPath = a.split(\"=\", 2)[1];\n else if (a === \"--env-file\") out.envFile = rest[++i];\n else if (a.startsWith(\"--env-file=\")) out.envFile = a.split(\"=\", 2)[1];\n else if (a === \"--no-dotenv\") out.useDotenv = false;\n // generate flags\n else if (a === \"--out\") out.outFile = rest[++i];\n else if (a.startsWith(\"--out=\")) out.outFile = a.split(\"=\", 2)[1];\n else if (a === \"--mode\") {\n const m = rest[++i];\n if (m === \"update\" || m === \"create\") out.mode = m;\n } else if (a.startsWith(\"--mode=\")) {\n const m = a.split(\"=\", 2)[1];\n if (m === \"update\" || m === \"create\") out.mode = m;\n } else if (a === \"--no-defaults\") out.useDefaults = false;\n else if (a === \"--comment-types\") out.commentTypes = true;\n }\n\n return out;\n}\n\nfunction loadSchema(schemaPath: string): EnvDoctorSchema {\n const abs = path.resolve(process.cwd(), schemaPath);\n const raw = fs.readFileSync(abs, \"utf8\");\n\n // Let JSON.parse throw with its native message; CLI catches and prints it\n const json = JSON.parse(raw);\n\n if (!json || typeof json !== \"object\" || Array.isArray(json)) {\n throw new Error(\"Schema must be a JSON object of key -> spec.\");\n }\n\n // Validate using the same core logic (reduces CLI branch complexity a lot)\n for (const [k, v] of Object.entries(json)) {\n try {\n normalizeSpec(v as EnvSchemaValue);\n } catch (e) {\n throw new Error(`Invalid schema value for \"${k}\": ${String(e)}`);\n }\n }\n\n return json as EnvDoctorSchema;\n}\n\nfunction buildEnv(\n useDotenv: boolean,\n envFile?: string,\n): Record<string, string | undefined> {\n const env: Record<string, string | undefined> = { ...process.env };\n\n if (!useDotenv) return env;\n\n const p = envFile ?? \".env\";\n const envAbsPath = path.resolve(process.cwd(), p);\n\n if (fs.existsSync(envAbsPath)) {\n const fileRaw = fs.readFileSync(envAbsPath, \"utf8\");\n const parsed = dotenv.parse(fileRaw);\n Object.assign(env, parsed); // env-file overrides shell env\n }\n\n return env;\n}\n\n// -------- generate helpers --------\n\nfunction readEnvFile(filePath: string): Record<string, string> {\n if (!fs.existsSync(filePath)) return {};\n // allow \"export KEY=...\" lines too\n const raw = fs.readFileSync(filePath, \"utf8\").replace(/^export\\s+/gm, \"\");\n return dotenv.parse(raw);\n}\n\nfunction getTypeLabel(spec: EnvSchemaValue): string {\n // loadSchema() already validates specs are string or object\n if (typeof spec === \"string\") return spec;\n return (spec as any).type as string;\n}\n\nfunction defaultToEnvString(x: unknown): string {\n if (x === undefined || x === null) return \"\";\n if (typeof x === \"string\") return x;\n if (typeof x === \"number\" || typeof x === \"boolean\") return String(x);\n\n // Schema defaults come from JSON => JSON.stringify won't throw and won't return undefined\n return JSON.stringify(x) as string;\n}\n\nfunction needsQuoting(v: string): boolean {\n if (v === \"\") return true;\n return /[\\s#\\n\"']/.test(v);\n}\n\nfunction formatEnvLine(key: string, value: string, comment?: string): string {\n const safe = needsQuoting(value) ? JSON.stringify(value) : value;\n return comment ? `${key}=${safe} # ${comment}` : `${key}=${safe}`;\n}\n\nfunction runGenerate(\n schema: EnvDoctorSchema,\n outPath: string,\n mode: \"update\" | \"create\",\n useDefaults: boolean,\n commentTypes: boolean,\n io: Io,\n): number {\n const absOut = path.resolve(process.cwd(), outPath);\n\n if (mode === \"create\" && fs.existsSync(absOut)) {\n io.error(`Refusing to overwrite existing file: ${outPath}`);\n return 2;\n }\n\n const existing = mode === \"update\" ? readEnvFile(absOut) : {};\n const keys = Object.keys(schema).sort();\n\n const linesToAdd: string[] = [];\n\n for (const key of keys) {\n if (existing[key] !== undefined) continue; // do not overwrite existing values (even empty)\n\n const spec = schema[key];\n const typeLabel = commentTypes ? getTypeLabel(spec) : undefined;\n\n let val = \"\";\n\n if (useDefaults) {\n const ns = normalizeSpec(spec);\n if (ns.defaultValue !== undefined) {\n val = defaultToEnvString(ns.defaultValue);\n }\n }\n\n linesToAdd.push(formatEnvLine(key, val, typeLabel));\n }\n\n if (linesToAdd.length === 0) {\n io.log(\"✅ No missing variables. Nothing to generate.\");\n return 0;\n }\n\n if (mode === \"create\") {\n fs.writeFileSync(absOut, linesToAdd.join(\"\\n\") + \"\\n\", \"utf8\");\n io.log(`✅ Created ${outPath} with ${linesToAdd.length} variables.`);\n return 0;\n }\n\n // update mode: append\n const prefix = fs.existsSync(absOut) ? \"\\n\" : \"\";\n fs.appendFileSync(absOut, prefix + linesToAdd.join(\"\\n\") + \"\\n\", \"utf8\");\n io.log(\n `✅ Updated ${outPath}. Added ${linesToAdd.length} missing variables.`,\n );\n return 0;\n}\n\n// -------- main --------\n\nexport function runCli(argv: string[], io: Io = console): number {\n const {\n cmd,\n schemaPath,\n envFile,\n useDotenv,\n outFile,\n mode,\n useDefaults,\n commentTypes,\n } = parseArgs(argv);\n\n if (!cmd || cmd === \"help\" || cmd === \"--help\" || cmd === \"-h\") {\n io.log(\n [\n \"env-typed-checker\",\n \"\",\n \"Usage:\",\n \" env-typed-checker check --schema <file> [--env-file <file>] [--no-dotenv]\",\n \" env-typed-checker generate --schema <file> [--out <file>] [--mode update|create] [--no-defaults] [--comment-types]\",\n \"\",\n \"Options:\",\n \" --schema <file> Path to schema JSON (required)\",\n \" --env-file <file> Env file path (default: .env) [check]\",\n \" --no-dotenv Do not load env file; use process.env only [check]\",\n \" --out <file> Output env file (default: .env) [generate]\",\n \" --mode update|create update appends missing keys; create fails if file exists (default: update)\",\n \" --no-defaults do not write schema defaults; write empty placeholders [generate]\",\n \" --comment-types add inline comments with type info [generate]\",\n \"\",\n \"Exit codes:\",\n \" 0 = OK, 1 = validation failed, 2 = CLI error\",\n ].join(\"\\n\"),\n );\n return 0;\n }\n\n if (!schemaPath) {\n io.error(\"Missing required option: --schema <file>\");\n return 2;\n }\n\n try {\n const schema = loadSchema(schemaPath);\n\n if (cmd === \"check\") {\n const env = buildEnv(useDotenv, envFile);\n envDoctor(schema, { loadDotEnv: false, env });\n io.log(\"✅ Environment is valid.\");\n return 0;\n }\n\n if (cmd === \"generate\") {\n const out = outFile ?? \".env\";\n const m = mode ?? \"update\";\n return runGenerate(schema, out, m, useDefaults, commentTypes, io);\n }\n\n io.error(`Unknown command: ${cmd}`);\n return 2;\n } catch (e) {\n if (e instanceof EnvDoctorError) {\n io.error(e.message);\n return 1;\n }\n io.error(e instanceof Error ? e.message : String(e));\n return 2;\n }\n}\n","import * as dotenv from \"dotenv\";\nimport { validateAndParse } from \"./runner\";\nimport type { EnvDoctorOptions, EnvDoctorResult, EnvDoctorSchema } from \"../types\";\n\nexport type { EnvDoctorOptions, EnvDoctorSchema } from \"../types\";\nexport { EnvDoctorError } from \"../errors/EnvDoctorError\";\n\nexport function envDoctor<TSchema extends EnvDoctorSchema>(\n schema: TSchema,\n options: EnvDoctorOptions = {}\n): EnvDoctorResult<TSchema> {\n const { loadDotEnv = true, env = process.env } = options;\n\n if (loadDotEnv) dotenv.config();\n\n return validateAndParse(schema, env);\n}\n","export type EnvDoctorIssue =\n | { key: string; kind: \"missing\"; message: string }\n | { key: string; kind: \"invalid\"; message: string };\n\nexport class EnvDoctorError extends Error {\n public readonly issues: EnvDoctorIssue[];\n\n constructor(issues: EnvDoctorIssue[]) {\n const header = \"ENV validation failed\";\n const lines = issues.map((i) => `- ${i.key}: ${i.message}`);\n super([header, ...lines].join(\"\\n\"));\n this.name = \"EnvDoctorError\";\n this.issues = issues;\n }\n}\n","import type { EnvBaseType, EnvSchemaValue } from \"../types\";\n\nconst EMAIL_RE = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\nexport function parseByType(type: EnvBaseType, raw: string): unknown {\n switch (type) {\n case \"string\":\n return raw;\n\n case \"number\": {\n const n = Number(raw.trim());\n if (!Number.isFinite(n)) throw new Error(`expected number, got \"${raw}\"`);\n return n;\n }\n\n case \"boolean\": {\n const v = raw.trim().toLowerCase();\n if ([\"true\", \"1\", \"yes\", \"y\", \"on\"].includes(v)) return true;\n if ([\"false\", \"0\", \"no\", \"n\", \"off\"].includes(v)) return false;\n throw new Error(\n `expected boolean (true/false/1/0/yes/no/on/off), got \"${raw}\"`,\n );\n }\n\n case \"json\": {\n try {\n return JSON.parse(raw);\n } catch {\n throw new Error(`expected json, got \"${raw}\"`);\n }\n }\n\n case \"url\": {\n try {\n new URL(raw);\n return raw;\n } catch {\n throw new Error(`expected url, got \"${raw}\"`);\n }\n }\n\n case \"email\": {\n const s = raw.trim();\n if (!EMAIL_RE.test(s)) {\n throw new Error(`expected email, got \"${raw}\"`);\n }\n return s;\n }\n\n default: {\n throw new Error(`unsupported primitive type: ${String(type)}`);\n }\n }\n}\n\nfunction hasOwn(obj: unknown, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(obj, key);\n}\n\n/**\n * Type-check + normalize a default value into the right runtime type.\n * - Allows string defaults for number/boolean (parsed)\n * - Validates url/email defaults\n * - json defaults can be any value\n */\nfunction coerceDefault(kind: EnvBaseType, def: unknown): unknown {\n if (def === undefined) return undefined;\n\n switch (kind) {\n case \"string\": {\n if (typeof def !== \"string\")\n throw new Error(`default for string must be a string`);\n return def;\n }\n\n case \"number\": {\n if (typeof def === \"number\") {\n if (!Number.isFinite(def))\n throw new Error(`default for number must be finite`);\n return def;\n }\n if (typeof def === \"string\") return parseByType(\"number\", def);\n throw new Error(`default for number must be number or string`);\n }\n\n case \"boolean\": {\n if (typeof def === \"boolean\") return def;\n if (typeof def === \"string\") return parseByType(\"boolean\", def);\n throw new Error(`default for boolean must be boolean or string`);\n }\n\n case \"json\": {\n // allow any JSON-ish value\n return def;\n }\n\n case \"url\": {\n if (typeof def !== \"string\")\n throw new Error(`default for url must be a string`);\n return parseByType(\"url\", def);\n }\n\n case \"email\": {\n if (typeof def !== \"string\")\n throw new Error(`default for email must be a string`);\n return parseByType(\"email\", def);\n }\n\n /* c8 ignore next */\n default: {\n throw new Error(`unsupported default kind: ${String(kind)}`);\n }\n }\n}\n\n/** Normalized spec used by the runner */\nexport type NormalizedSpec =\n | { kind: EnvBaseType; optional: boolean; defaultValue?: unknown }\n | {\n kind: \"enum\";\n optional: boolean;\n values: readonly string[];\n defaultValue?: string;\n }\n | {\n kind: \"regex\";\n optional: boolean;\n re: RegExp;\n display: string;\n defaultValue?: string;\n };\n\nexport function normalizeSpec(schemaValue: EnvSchemaValue): NormalizedSpec {\n // --------------------\n // String style: \"number?\" etc.\n // --------------------\n if (typeof schemaValue === \"string\") {\n const optional = schemaValue.endsWith(\"?\");\n const base = optional ? schemaValue.slice(0, -1) : schemaValue;\n\n const allowed: readonly EnvBaseType[] = [\n \"string\",\n \"number\",\n \"boolean\",\n \"json\",\n \"url\",\n \"email\",\n ];\n\n if (!allowed.includes(base as EnvBaseType)) {\n throw new Error(\n `Unsupported type \"${schemaValue}\". Supported: string, number, boolean, json, url, email (optional with ?)`,\n );\n }\n\n return { kind: base as EnvBaseType, optional };\n }\n\n // --------------------\n // Object style\n // --------------------\n if (\n !schemaValue ||\n typeof schemaValue !== \"object\" ||\n Array.isArray(schemaValue)\n ) {\n throw new Error(\"Schema value must be a string or object spec.\");\n }\n\n const t = (schemaValue as any).type;\n\n // --------------------\n // Primitive object spec: { type: \"number\", optional?: true, default?: ... }\n // --------------------\n const primitiveAllowed: readonly EnvBaseType[] = [\n \"string\",\n \"number\",\n \"boolean\",\n \"json\",\n \"url\",\n \"email\",\n ];\n\n if (primitiveAllowed.includes(t)) {\n const optional = !!(schemaValue as any).optional;\n const defaultValue = hasOwn(schemaValue, \"default\")\n ? coerceDefault(t as EnvBaseType, (schemaValue as any).default)\n : undefined;\n\n return { kind: t as EnvBaseType, optional, defaultValue };\n }\n\n // --------------------\n // Enum: { type: \"enum\", values: [...], optional?: true, default?: \"dev\" }\n // --------------------\n if (t === \"enum\") {\n const values = (schemaValue as any).values;\n if (\n !Array.isArray(values) ||\n values.length === 0 ||\n !values.every((v: any) => typeof v === \"string\")\n ) {\n throw new Error(`enum spec requires \"values\": string[] (non-empty)`);\n }\n\n const optional = !!(schemaValue as any).optional;\n\n let defaultValue: string | undefined = undefined;\n if (hasOwn(schemaValue, \"default\")) {\n const def = (schemaValue as any).default;\n if (typeof def !== \"string\") {\n throw new Error(`default for enum must be a string`);\n }\n if (!values.includes(def)) {\n throw new Error(\n `default \"${def}\" must be one of [${values.join(\", \")}]`,\n );\n }\n defaultValue = def;\n }\n\n return { kind: \"enum\", optional, values, defaultValue };\n }\n\n // --------------------\n // Regex: { type: \"regex\", pattern: \"...\", flags?: \"...\", optional?: true, default?: \"abc\" }\n // --------------------\n if (t === \"regex\") {\n const pattern = (schemaValue as any).pattern;\n const flags = (schemaValue as any).flags;\n\n if (typeof pattern !== \"string\" || pattern.length === 0) {\n throw new Error(`regex spec requires \"pattern\": string`);\n }\n if (flags !== undefined && typeof flags !== \"string\") {\n throw new Error(`regex spec \"flags\" must be a string if provided`);\n }\n\n let re: RegExp;\n try {\n re = new RegExp(pattern, flags);\n } catch (e) {\n throw new Error(`invalid regex: ${String(e)}`);\n }\n\n const display = `/${pattern}/${flags ?? \"\"}`;\n const optional = !!(schemaValue as any).optional;\n\n let defaultValue: string | undefined = undefined;\n if (hasOwn(schemaValue, \"default\")) {\n const def = (schemaValue as any).default;\n if (typeof def !== \"string\") {\n throw new Error(`default for regex must be a string`);\n }\n if (!re.test(def)) {\n throw new Error(`default \"${def}\" does not match ${display}`);\n }\n defaultValue = def;\n }\n\n return { kind: \"regex\", optional, re, display, defaultValue };\n }\n\n throw new Error(\n `Unsupported object spec type \"${String(t)}\". Supported: primitives (string/number/boolean/json/url/email), enum, regex`,\n );\n}\n","import { EnvDoctorError, type EnvDoctorIssue } from \"../errors/EnvDoctorError\";\nimport { normalizeSpec, parseByType } from \"../validators/primitives\";\nimport type { EnvDoctorResult, EnvDoctorSchema } from \"../types\";\n\nexport function validateAndParse<TSchema extends EnvDoctorSchema>(\n schema: TSchema,\n env: Record<string, string | undefined>,\n): EnvDoctorResult<TSchema> {\n const issues: EnvDoctorIssue[] = [];\n const out: Record<string, unknown> = {};\n\n for (const [key, schemaValue] of Object.entries(schema)) {\n let spec: ReturnType<typeof normalizeSpec>;\n\n try {\n spec = normalizeSpec(schemaValue);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n issues.push({ key, kind: \"invalid\", message: msg });\n continue;\n }\n\n const raw = env[key];\n\n // treat undefined or empty string as \"missing\"\n if (raw === undefined || raw === \"\") {\n if (spec.defaultValue !== undefined) {\n out[key] = spec.defaultValue;\n } else if (spec.optional) {\n out[key] = undefined;\n } else {\n issues.push({\n key,\n kind: \"missing\",\n message: \"missing required environment variable\",\n });\n }\n continue;\n }\n\n try {\n if (spec.kind === \"enum\") {\n if (!spec.values.includes(raw)) {\n throw new Error(\n `expected one of [${spec.values.join(\", \")}], got \"${raw}\"`,\n );\n }\n out[key] = raw;\n } else if (spec.kind === \"regex\") {\n if (!spec.re.test(raw)) {\n throw new Error(`does not match ${spec.display}`);\n }\n out[key] = raw;\n } else {\n // primitives: string/number/boolean/json/url/email\n out[key] = parseByType(spec.kind, raw);\n }\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n issues.push({ key, kind: \"invalid\", message: msg });\n }\n }\n\n if (issues.length > 0) throw new EnvDoctorError(issues);\n\n return out as EnvDoctorResult<TSchema>;\n}\n"],"mappings":"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","names":["dotenv","path","fs"]}
|
|
1
|
+
{"version":3,"sources":["../../src/cli/index.ts","../../src/cli/cli.ts","../../src/core/envDoctor.ts","../../src/errors/EnvDoctorError.ts","../../src/validators/primitives.ts","../../src/core/runner.ts"],"sourcesContent":["export { runCli } from \"./cli\";\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport * as dotenv from \"dotenv\";\nimport { envDoctor, EnvDoctorError } from \"../index\";\nimport { normalizeSpec } from \"../validators/primitives\";\nimport type { EnvDoctorSchema, EnvSchemaValue } from \"../types\";\n\ntype Io = {\n log: (msg: string) => void;\n error: (msg: string) => void;\n};\n\ntype CheckOutputFormat = \"pretty\" | \"json\" | \"github\";\n\nfunction isCheckOutputFormat(value: unknown): value is CheckOutputFormat {\n return value === \"pretty\" || value === \"json\" || value === \"github\";\n}\n\nfunction parseArgs(argv: string[]) {\n const out: {\n cmd?: string;\n schemaPath?: string;\n envFile?: string;\n checkFormat: CheckOutputFormat;\n invalidCheckFormat?: string;\n useDotenv: boolean;\n\n // generate options\n outFile?: string;\n mode?: \"update\" | \"create\";\n useDefaults: boolean;\n commentTypes: boolean;\n commentDocs: boolean;\n exampleValues: boolean;\n } = {\n checkFormat: \"pretty\",\n useDotenv: true,\n useDefaults: true,\n commentTypes: false,\n commentDocs: false,\n exampleValues: false,\n };\n\n const [cmd, ...rest] = argv;\n out.cmd = cmd;\n\n for (let i = 0; i < rest.length; i++) {\n const a = rest[i];\n\n if (a === \"--schema\") out.schemaPath = rest[++i];\n else if (a.startsWith(\"--schema=\")) out.schemaPath = a.split(\"=\", 2)[1];\n else if (a === \"--env-file\") out.envFile = rest[++i];\n else if (a.startsWith(\"--env-file=\")) out.envFile = a.split(\"=\", 2)[1];\n else if (a === \"--format\") {\n const f = rest[++i];\n if (isCheckOutputFormat(f)) out.checkFormat = f;\n else out.invalidCheckFormat = String(f);\n } else if (a.startsWith(\"--format=\")) {\n const f = a.slice(\"--format=\".length);\n if (isCheckOutputFormat(f)) out.checkFormat = f;\n else out.invalidCheckFormat = f;\n }\n else if (a === \"--no-dotenv\") out.useDotenv = false;\n // generate flags\n else if (a === \"--out\") out.outFile = rest[++i];\n else if (a.startsWith(\"--out=\")) out.outFile = a.split(\"=\", 2)[1];\n else if (a === \"--mode\") {\n const m = rest[++i];\n if (m === \"update\" || m === \"create\") out.mode = m;\n } else if (a.startsWith(\"--mode=\")) {\n const m = a.split(\"=\", 2)[1];\n if (m === \"update\" || m === \"create\") out.mode = m;\n } else if (a === \"--no-defaults\") out.useDefaults = false;\n else if (a === \"--comment-types\") out.commentTypes = true;\n else if (a === \"--comment-docs\") out.commentDocs = true;\n else if (a === \"--example-values\") out.exampleValues = true;\n }\n\n return out;\n}\n\nfunction loadSchema(schemaPath: string): EnvDoctorSchema {\n const abs = path.resolve(process.cwd(), schemaPath);\n const raw = fs.readFileSync(abs, \"utf8\");\n\n // Let JSON.parse throw with its native message; CLI catches and prints it\n const json = JSON.parse(raw);\n\n if (!json || typeof json !== \"object\" || Array.isArray(json)) {\n throw new Error(\"Schema must be a JSON object of key -> spec.\");\n }\n\n // Validate using the same core logic (reduces CLI branch complexity a lot)\n for (const [k, v] of Object.entries(json)) {\n try {\n normalizeSpec(v as EnvSchemaValue);\n } catch (e) {\n throw new Error(`Invalid schema value for \"${k}\": ${String(e)}`);\n }\n }\n\n return json as EnvDoctorSchema;\n}\n\nfunction buildEnv(\n useDotenv: boolean,\n envFile?: string,\n): Record<string, string | undefined> {\n const env: Record<string, string | undefined> = { ...process.env };\n\n if (!useDotenv) return env;\n\n const p = envFile ?? \".env\";\n const envAbsPath = path.resolve(process.cwd(), p);\n\n if (fs.existsSync(envAbsPath)) {\n const fileRaw = fs.readFileSync(envAbsPath, \"utf8\");\n const parsed = dotenv.parse(fileRaw);\n Object.assign(env, parsed); // env-file overrides shell env\n }\n\n return env;\n}\n\n// -------- generate helpers --------\n\nfunction readEnvFile(filePath: string): Record<string, string> {\n if (!fs.existsSync(filePath)) return {};\n // allow \"export KEY=...\" lines too\n const raw = fs.readFileSync(filePath, \"utf8\").replace(/^export\\s+/gm, \"\");\n return dotenv.parse(raw);\n}\n\nfunction getTypeLabel(spec: EnvSchemaValue): string {\n // loadSchema() already validates specs are string or object\n if (typeof spec === \"string\") return spec;\n return (spec as any).type as string;\n}\n\nfunction defaultToEnvString(x: unknown): string {\n if (x === undefined || x === null) return \"\";\n if (typeof x === \"string\") return x;\n if (typeof x === \"number\" || typeof x === \"boolean\") return String(x);\n\n // Schema defaults come from JSON => JSON.stringify won't throw and won't return undefined\n return JSON.stringify(x) as string;\n}\n\nfunction needsQuoting(v: string): boolean {\n if (v === \"\") return true;\n return /[\\s#\\n\"']/.test(v);\n}\n\nfunction formatEnvLine(key: string, value: string, comment?: string): string {\n const safe = needsQuoting(value) ? JSON.stringify(value) : value;\n return comment ? `${key}=${safe} # ${comment}` : `${key}=${safe}`;\n}\n\nfunction normalizeCommentText(s: string): string {\n return s\n .replace(/\\r?\\n+/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\nfunction getDocCommentLines(description?: string, example?: string): string[] {\n const lines: string[] = [];\n\n if (typeof description === \"string\") {\n const d = normalizeCommentText(description);\n if (d) lines.push(`# ${d}`);\n }\n\n if (typeof example === \"string\") {\n const ex = normalizeCommentText(example);\n if (ex) lines.push(`# example: ${ex}`);\n }\n\n return lines;\n}\n\nfunction runGenerate(\n schema: EnvDoctorSchema,\n outPath: string,\n mode: \"update\" | \"create\",\n useDefaults: boolean,\n commentTypes: boolean,\n commentDocs: boolean,\n exampleValues: boolean,\n io: Io,\n): number {\n const absOut = path.resolve(process.cwd(), outPath);\n\n if (mode === \"create\" && fs.existsSync(absOut)) {\n io.error(`Refusing to overwrite existing file: ${outPath}`);\n return 2;\n }\n\n const existing = mode === \"update\" ? readEnvFile(absOut) : {};\n const keys = Object.keys(schema).sort();\n\n const linesToAdd: string[] = [];\n let addedVars = 0;\n\n for (const key of keys) {\n if (existing[key] !== undefined) continue; // do not overwrite existing values (even empty)\n\n const spec = schema[key];\n const ns = normalizeSpec(spec);\n const typeLabel = commentTypes ? getTypeLabel(spec) : undefined;\n\n let val = \"\";\n\n if (exampleValues && ns.example !== undefined) {\n val = ns.example;\n } else if (useDefaults) {\n if (ns.defaultValue !== undefined) {\n val = defaultToEnvString(ns.defaultValue);\n }\n }\n\n if (commentDocs) {\n linesToAdd.push(...getDocCommentLines(ns.description, ns.example));\n }\n\n linesToAdd.push(formatEnvLine(key, val, typeLabel));\n addedVars++;\n }\n\n if (addedVars === 0) {\n io.log(\"✅ No missing variables. Nothing to generate.\");\n return 0;\n }\n\n if (mode === \"create\") {\n fs.writeFileSync(absOut, linesToAdd.join(\"\\n\") + \"\\n\", \"utf8\");\n io.log(`✅ Created ${outPath} with ${addedVars} variables.`);\n return 0;\n }\n\n // update mode: append\n const prefix = fs.existsSync(absOut) ? \"\\n\" : \"\";\n fs.appendFileSync(absOut, prefix + linesToAdd.join(\"\\n\") + \"\\n\", \"utf8\");\n io.log(`✅ Updated ${outPath}. Added ${addedVars} missing variables.`);\n return 0;\n}\n\nfunction redactIssueMessage(message: string): string {\n const sep = \", got \";\n const i = message.indexOf(sep);\n if (i >= 0) {\n return `${message.slice(0, i)}, got \"<redacted>\"`;\n }\n return `invalid value, got \"<redacted>\"`;\n}\n\nfunction isSecretKey(schema: EnvDoctorSchema, key: string): boolean {\n const spec = schema[key];\n if (spec === undefined) return false;\n\n try {\n const ns = normalizeSpec(spec);\n return ns.secret === true;\n } catch {\n return false;\n }\n}\n\nfunction getIssueMessage(\n issue: EnvDoctorError[\"issues\"][number],\n schema: EnvDoctorSchema,\n): string {\n const redact = issue.kind === \"invalid\" && isSecretKey(schema, issue.key);\n return redact ? redactIssueMessage(issue.message) : issue.message;\n}\n\nfunction escapeGithubCommandData(value: string): string {\n return value.replace(/%/g, \"%25\").replace(/\\r/g, \"%0D\").replace(/\\n/g, \"%0A\");\n}\n\nfunction escapeGithubCommandProperty(value: string): string {\n return escapeGithubCommandData(value)\n .replace(/:/g, \"%3A\")\n .replace(/,/g, \"%2C\");\n}\n\nfunction formatValidationErrorForCli(\n err: EnvDoctorError,\n schema: EnvDoctorSchema,\n format: CheckOutputFormat,\n): string[] {\n const issues = err.issues.map((issue) => {\n const message = getIssueMessage(issue, schema);\n return { key: issue.key, kind: issue.kind, message };\n });\n\n if (format === \"github\") {\n const title = escapeGithubCommandProperty(\"ENV validation\");\n return issues.map(\n (issue) =>\n `::error title=${title}::${escapeGithubCommandData(`${issue.key}: ${issue.message}`)}`,\n );\n }\n\n if (format === \"json\") {\n return [JSON.stringify({ error: \"ENV validation failed\", issues })];\n }\n\n const header = \"ENV validation failed\";\n const lines = issues.map((issue) => `- ${issue.key}: ${issue.message}`);\n return [[header, ...lines].join(\"\\n\")];\n}\n\n// -------- main --------\n\nexport function runCli(argv: string[], io: Io = console): number {\n const {\n cmd,\n schemaPath,\n envFile,\n checkFormat,\n invalidCheckFormat,\n useDotenv,\n outFile,\n mode,\n useDefaults,\n commentTypes,\n commentDocs,\n exampleValues,\n } = parseArgs(argv);\n\n if (!cmd || cmd === \"help\" || cmd === \"--help\" || cmd === \"-h\") {\n io.log(\n [\n \"env-typed-checker\",\n \"\",\n \"Usage:\",\n \" env-typed-checker check --schema <file> [--env-file <file>] [--no-dotenv] [--format pretty|json|github]\",\n \" env-typed-checker generate --schema <file> [--out <file>] [--mode update|create] [--no-defaults] [--comment-types] [--comment-docs] [--example-values]\",\n \"\",\n \"Options:\",\n \" --schema <file> Path to schema JSON (required)\",\n \" --env-file <file> Env file path (default: .env) [check]\",\n \" --no-dotenv Do not load env file; use process.env only [check]\",\n \" --format <name> check output format: pretty|json|github (default: pretty) [check]\",\n \" --out <file> Output env file (default: .env) [generate]\",\n \" --mode update|create update appends missing keys; create fails if file exists (default: update)\",\n \" --no-defaults do not write schema defaults; write empty placeholders [generate]\",\n \" --comment-types add inline comments with type info [generate]\",\n \" --comment-docs add description/example comments above each key [generate]\",\n \" --example-values use schema example values when generating missing keys [generate]\",\n \"\",\n \"Exit codes:\",\n \" 0 = OK, 1 = validation failed, 2 = CLI error\",\n ].join(\"\\n\"),\n );\n return 0;\n }\n\n if (!schemaPath) {\n io.error(\"Missing required option: --schema <file>\");\n return 2;\n }\n\n if (cmd === \"check\" && invalidCheckFormat !== undefined) {\n io.error(\n `Invalid value for --format: \"${invalidCheckFormat}\". Expected: pretty, json, github`,\n );\n return 2;\n }\n\n try {\n const schema = loadSchema(schemaPath);\n\n if (cmd === \"check\") {\n const env = buildEnv(useDotenv, envFile);\n try {\n envDoctor(schema, { loadDotEnv: false, env });\n } catch (e) {\n if (e instanceof EnvDoctorError) {\n for (const line of formatValidationErrorForCli(e, schema, checkFormat)) {\n io.error(line);\n }\n return 1;\n }\n throw e;\n }\n io.log(\"✅ Environment is valid.\");\n return 0;\n }\n\n if (cmd === \"generate\") {\n const out = outFile ?? \".env\";\n const m = mode ?? \"update\";\n return runGenerate(\n schema,\n out,\n m,\n useDefaults,\n commentTypes,\n commentDocs,\n exampleValues,\n io,\n );\n }\n\n io.error(`Unknown command: ${cmd}`);\n return 2;\n } catch (e) {\n io.error(e instanceof Error ? e.message : String(e));\n return 2;\n }\n}\n","import * as dotenv from \"dotenv\";\nimport { validateAndParse } from \"./runner\";\nimport type { EnvDoctorOptions, EnvDoctorResult, EnvDoctorSchema } from \"../types\";\n\nexport type { EnvDoctorOptions, EnvDoctorSchema } from \"../types\";\nexport { EnvDoctorError } from \"../errors/EnvDoctorError\";\n\nexport function envDoctor<TSchema extends EnvDoctorSchema>(\n schema: TSchema,\n options: EnvDoctorOptions = {}\n): EnvDoctorResult<TSchema> {\n const { loadDotEnv = true, env = process.env } = options;\n\n if (loadDotEnv) dotenv.config();\n\n return validateAndParse(schema, env);\n}\n","export type EnvDoctorIssue =\n | { key: string; kind: \"missing\"; message: string }\n | { key: string; kind: \"invalid\"; message: string };\n\nexport class EnvDoctorError extends Error {\n public readonly issues: EnvDoctorIssue[];\n\n constructor(issues: EnvDoctorIssue[]) {\n const header = \"ENV validation failed\";\n const lines = issues.map((i) => `- ${i.key}: ${i.message}`);\n super([header, ...lines].join(\"\\n\"));\n this.name = \"EnvDoctorError\";\n this.issues = issues;\n }\n}\n","import type { EnvBaseType, EnvSchemaValue } from \"../types\";\n\nconst EMAIL_RE = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\nexport function parseByType(type: EnvBaseType, raw: string): unknown {\n switch (type) {\n case \"string\":\n return raw;\n\n case \"number\": {\n const n = Number(raw.trim());\n if (!Number.isFinite(n)) throw new Error(`expected number, got \"${raw}\"`);\n return n;\n }\n\n case \"boolean\": {\n const v = raw.trim().toLowerCase();\n if ([\"true\", \"1\", \"yes\", \"y\", \"on\"].includes(v)) return true;\n if ([\"false\", \"0\", \"no\", \"n\", \"off\"].includes(v)) return false;\n throw new Error(\n `expected boolean (true/false/1/0/yes/no/on/off), got \"${raw}\"`,\n );\n }\n\n case \"json\": {\n try {\n return JSON.parse(raw);\n } catch {\n throw new Error(`expected json, got \"${raw}\"`);\n }\n }\n\n case \"url\": {\n try {\n new URL(raw);\n return raw;\n } catch {\n throw new Error(`expected url, got \"${raw}\"`);\n }\n }\n\n case \"email\": {\n const s = raw.trim();\n if (!EMAIL_RE.test(s)) {\n throw new Error(`expected email, got \"${raw}\"`);\n }\n return s;\n }\n\n default: {\n throw new Error(`unsupported primitive type: ${String(type)}`);\n }\n }\n}\n\nfunction hasOwn(obj: unknown, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(obj, key);\n}\n\n/**\n * Type-check + normalize a default value into the right runtime type.\n * - Allows string defaults for number/boolean (parsed)\n * - Validates url/email defaults\n * - json defaults can be any value\n */\nfunction coerceDefault(kind: EnvBaseType, def: unknown): unknown {\n if (def === undefined) return undefined;\n\n switch (kind) {\n case \"string\": {\n if (typeof def !== \"string\")\n throw new Error(`default for string must be a string`);\n return def;\n }\n\n case \"number\": {\n if (typeof def === \"number\") {\n if (!Number.isFinite(def))\n throw new Error(`default for number must be finite`);\n return def;\n }\n if (typeof def === \"string\") return parseByType(\"number\", def);\n throw new Error(`default for number must be number or string`);\n }\n\n case \"boolean\": {\n if (typeof def === \"boolean\") return def;\n if (typeof def === \"string\") return parseByType(\"boolean\", def);\n throw new Error(`default for boolean must be boolean or string`);\n }\n\n case \"json\": {\n // allow any JSON-ish value\n return def;\n }\n\n case \"url\": {\n if (typeof def !== \"string\")\n throw new Error(`default for url must be a string`);\n return parseByType(\"url\", def);\n }\n\n case \"email\": {\n if (typeof def !== \"string\")\n throw new Error(`default for email must be a string`);\n return parseByType(\"email\", def);\n }\n\n /* c8 ignore next */\n default: {\n throw new Error(`unsupported default kind: ${String(kind)}`);\n }\n }\n}\n\n/** Normalized spec used by the runner */\nexport type NormalizedSpec =\n | {\n kind: EnvBaseType;\n optional: boolean;\n defaultValue?: unknown;\n description?: string;\n example?: string;\n secret: boolean;\n }\n | {\n kind: \"enum\";\n optional: boolean;\n values: readonly string[];\n defaultValue?: string;\n description?: string;\n example?: string;\n secret: boolean;\n }\n | {\n kind: \"regex\";\n optional: boolean;\n re: RegExp;\n display: string;\n defaultValue?: string;\n description?: string;\n example?: string;\n secret: boolean;\n };\n\nfunction normalizeMeta(schemaValue: Record<string, unknown>): {\n description?: string;\n example?: string;\n secret: boolean;\n} {\n let description: string | undefined = undefined;\n if (hasOwn(schemaValue, \"description\")) {\n const d = schemaValue.description;\n if (d !== undefined && typeof d !== \"string\") {\n throw new Error(`\"description\" must be a string if provided`);\n }\n description = d;\n }\n\n let example: string | undefined = undefined;\n if (hasOwn(schemaValue, \"example\")) {\n const ex = schemaValue.example;\n if (ex !== undefined && typeof ex !== \"string\") {\n throw new Error(`\"example\" must be a string if provided`);\n }\n example = ex;\n }\n\n let secret = false;\n if (hasOwn(schemaValue, \"secret\")) {\n const s = schemaValue.secret;\n if (s !== undefined && typeof s !== \"boolean\") {\n throw new Error(`\"secret\" must be a boolean if provided`);\n }\n secret = s === true;\n }\n\n return { description, example, secret };\n}\n\nexport function normalizeSpec(schemaValue: EnvSchemaValue): NormalizedSpec {\n // --------------------\n // String style: \"number?\" etc.\n // --------------------\n if (typeof schemaValue === \"string\") {\n const optional = schemaValue.endsWith(\"?\");\n const base = optional ? schemaValue.slice(0, -1) : schemaValue;\n\n const allowed: readonly EnvBaseType[] = [\n \"string\",\n \"number\",\n \"boolean\",\n \"json\",\n \"url\",\n \"email\",\n ];\n\n if (!allowed.includes(base as EnvBaseType)) {\n throw new Error(\n `Unsupported type \"${schemaValue}\". Supported: string, number, boolean, json, url, email (optional with ?)`,\n );\n }\n\n return {\n kind: base as EnvBaseType,\n optional,\n secret: false,\n };\n }\n\n // --------------------\n // Object style\n // --------------------\n if (\n !schemaValue ||\n typeof schemaValue !== \"object\" ||\n Array.isArray(schemaValue)\n ) {\n throw new Error(\"Schema value must be a string or object spec.\");\n }\n\n const t = (schemaValue as any).type;\n const meta = normalizeMeta(schemaValue as Record<string, unknown>);\n\n // --------------------\n // Primitive object spec: { type: \"number\", optional?: true, default?: ... }\n // --------------------\n const primitiveAllowed: readonly EnvBaseType[] = [\n \"string\",\n \"number\",\n \"boolean\",\n \"json\",\n \"url\",\n \"email\",\n ];\n\n if (primitiveAllowed.includes(t)) {\n const optional = !!(schemaValue as any).optional;\n const defaultValue = hasOwn(schemaValue, \"default\")\n ? coerceDefault(t as EnvBaseType, (schemaValue as any).default)\n : undefined;\n\n return {\n kind: t as EnvBaseType,\n optional,\n defaultValue,\n ...meta,\n };\n }\n\n // --------------------\n // Enum: { type: \"enum\", values: [...], optional?: true, default?: \"dev\" }\n // --------------------\n if (t === \"enum\") {\n const values = (schemaValue as any).values;\n if (\n !Array.isArray(values) ||\n values.length === 0 ||\n !values.every((v: any) => typeof v === \"string\")\n ) {\n throw new Error(`enum spec requires \"values\": string[] (non-empty)`);\n }\n\n const optional = !!(schemaValue as any).optional;\n\n let defaultValue: string | undefined = undefined;\n if (hasOwn(schemaValue, \"default\")) {\n const def = (schemaValue as any).default;\n if (typeof def !== \"string\") {\n throw new Error(`default for enum must be a string`);\n }\n if (!values.includes(def)) {\n throw new Error(\n `default \"${def}\" must be one of [${values.join(\", \")}]`,\n );\n }\n defaultValue = def;\n }\n\n return {\n kind: \"enum\",\n optional,\n values,\n defaultValue,\n ...meta,\n };\n }\n\n // --------------------\n // Regex: { type: \"regex\", pattern: \"...\", flags?: \"...\", optional?: true, default?: \"abc\" }\n // --------------------\n if (t === \"regex\") {\n const pattern = (schemaValue as any).pattern;\n const flags = (schemaValue as any).flags;\n\n if (typeof pattern !== \"string\" || pattern.length === 0) {\n throw new Error(`regex spec requires \"pattern\": string`);\n }\n if (flags !== undefined && typeof flags !== \"string\") {\n throw new Error(`regex spec \"flags\" must be a string if provided`);\n }\n\n let re: RegExp;\n try {\n re = new RegExp(pattern, flags);\n } catch (e) {\n throw new Error(`invalid regex: ${String(e)}`);\n }\n\n const display = `/${pattern}/${flags ?? \"\"}`;\n const optional = !!(schemaValue as any).optional;\n\n let defaultValue: string | undefined = undefined;\n if (hasOwn(schemaValue, \"default\")) {\n const def = (schemaValue as any).default;\n if (typeof def !== \"string\") {\n throw new Error(`default for regex must be a string`);\n }\n if (!re.test(def)) {\n throw new Error(`default \"${def}\" does not match ${display}`);\n }\n defaultValue = def;\n }\n\n return {\n kind: \"regex\",\n optional,\n re,\n display,\n defaultValue,\n ...meta,\n };\n }\n\n throw new Error(\n `Unsupported object spec type \"${String(t)}\". Supported: primitives (string/number/boolean/json/url/email), enum, regex`,\n );\n}\n","import { EnvDoctorError, type EnvDoctorIssue } from \"../errors/EnvDoctorError\";\nimport { normalizeSpec, parseByType } from \"../validators/primitives\";\nimport type { EnvDoctorResult, EnvDoctorSchema } from \"../types\";\n\nexport function validateAndParse<TSchema extends EnvDoctorSchema>(\n schema: TSchema,\n env: Record<string, string | undefined>,\n): EnvDoctorResult<TSchema> {\n const issues: EnvDoctorIssue[] = [];\n const out: Record<string, unknown> = {};\n\n for (const [key, schemaValue] of Object.entries(schema)) {\n let spec: ReturnType<typeof normalizeSpec>;\n\n try {\n spec = normalizeSpec(schemaValue);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n issues.push({ key, kind: \"invalid\", message: msg });\n continue;\n }\n\n const raw = env[key];\n\n // treat undefined or empty string as \"missing\"\n if (raw === undefined || raw === \"\") {\n if (spec.defaultValue !== undefined) {\n out[key] = spec.defaultValue;\n } else if (spec.optional) {\n out[key] = undefined;\n } else {\n issues.push({\n key,\n kind: \"missing\",\n message: \"missing required environment variable\",\n });\n }\n continue;\n }\n\n try {\n if (spec.kind === \"enum\") {\n if (!spec.values.includes(raw)) {\n throw new Error(\n `expected one of [${spec.values.join(\", \")}], got \"${raw}\"`,\n );\n }\n out[key] = raw;\n } else if (spec.kind === \"regex\") {\n if (!spec.re.test(raw)) {\n throw new Error(`does not match ${spec.display}`);\n }\n out[key] = raw;\n } else {\n // primitives: string/number/boolean/json/url/email\n out[key] = parseByType(spec.kind, raw);\n }\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n issues.push({ key, kind: \"invalid\", message: msg });\n }\n }\n\n if (issues.length > 0) throw new EnvDoctorError(issues);\n\n return out as EnvDoctorResult<TSchema>;\n}\n"],"mappings":"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","names":["dotenv","path","fs"]}
|