@geekmidas/envkit 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{EnvironmentBuilder-DHfDXJUm.d.mts → EnvironmentBuilder-jF-b7WQg.d.mts} +1 -1
- package/dist/{EnvironmentBuilder-DHfDXJUm.d.mts.map → EnvironmentBuilder-jF-b7WQg.d.mts.map} +1 -1
- package/dist/EnvironmentBuilder.d.mts +1 -1
- package/dist/{EnvironmentParser-c06agx31.mjs → EnvironmentParser-CBLsPUyQ.mjs} +7 -2
- package/dist/EnvironmentParser-CBLsPUyQ.mjs.map +1 -0
- package/dist/{EnvironmentParser-CY8TosTN.d.mts → EnvironmentParser-CkLfmn4Y.d.mts} +1 -1
- package/dist/{EnvironmentParser-CY8TosTN.d.mts.map → EnvironmentParser-CkLfmn4Y.d.mts.map} +1 -1
- package/dist/{EnvironmentParser-Bt246UeP.cjs → EnvironmentParser-DlWHnhDY.cjs} +7 -2
- package/dist/EnvironmentParser-DlWHnhDY.cjs.map +1 -0
- package/dist/EnvironmentParser-DtOL86NU.d.cts.map +1 -1
- package/dist/EnvironmentParser.cjs +2 -1
- package/dist/EnvironmentParser.d.mts +1 -1
- package/dist/EnvironmentParser.mjs +2 -1
- package/dist/SnifferEnvironmentParser.cjs +2 -1
- package/dist/SnifferEnvironmentParser.cjs.map +1 -1
- package/dist/SnifferEnvironmentParser.d.mts +1 -1
- package/dist/SnifferEnvironmentParser.mjs +2 -1
- package/dist/SnifferEnvironmentParser.mjs.map +1 -1
- package/dist/{SstEnvironmentBuilder-CjURMGjW.d.mts → SstEnvironmentBuilder-BZngSQKQ.d.mts} +2 -2
- package/dist/{SstEnvironmentBuilder-CjURMGjW.d.mts.map → SstEnvironmentBuilder-BZngSQKQ.d.mts.map} +1 -1
- package/dist/{SstEnvironmentBuilder-BEBFSUYr.mjs → SstEnvironmentBuilder-DVB7cJq4.mjs} +1 -1
- package/dist/{SstEnvironmentBuilder-BEBFSUYr.mjs.map → SstEnvironmentBuilder-DVB7cJq4.mjs.map} +1 -1
- package/dist/{SstEnvironmentBuilder-wFnN2M5O.cjs → SstEnvironmentBuilder-jsnqgtcW.cjs} +1 -1
- package/dist/{SstEnvironmentBuilder-wFnN2M5O.cjs.map → SstEnvironmentBuilder-jsnqgtcW.cjs.map} +1 -1
- package/dist/SstEnvironmentBuilder.cjs +1 -1
- package/dist/SstEnvironmentBuilder.d.mts +2 -2
- package/dist/SstEnvironmentBuilder.mjs +1 -1
- package/dist/formatter-Cox0NGxT.d.mts +51 -0
- package/dist/formatter-Cox0NGxT.d.mts.map +1 -0
- package/dist/formatter-D85aIkpd.d.cts +51 -0
- package/dist/formatter-D85aIkpd.d.cts.map +1 -0
- package/dist/formatter-fz8V7x6i.mjs +113 -0
- package/dist/formatter-fz8V7x6i.mjs.map +1 -0
- package/dist/formatter-w8Tsccw4.cjs +125 -0
- package/dist/formatter-w8Tsccw4.cjs.map +1 -0
- package/dist/formatter.cjs +4 -0
- package/dist/formatter.d.cts +2 -0
- package/dist/formatter.d.mts +2 -0
- package/dist/formatter.mjs +3 -0
- package/dist/index.cjs +5 -2
- package/dist/index.d.cts +2 -1
- package/dist/index.d.mts +4 -3
- package/dist/index.mjs +3 -2
- package/dist/sst.cjs +1 -1
- package/dist/sst.d.mts +2 -2
- package/dist/sst.mjs +1 -1
- package/package.json +1 -1
- package/src/EnvironmentParser.ts +7 -2
- package/src/__tests__/formatter.spec.ts +250 -0
- package/src/formatter.ts +147 -0
- package/src/index.ts +5 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/dist/EnvironmentParser-Bt246UeP.cjs.map +0 -1
- package/dist/EnvironmentParser-c06agx31.mjs.map +0 -1
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
|
|
3
|
+
//#region src/formatter.ts
|
|
4
|
+
/**
|
|
5
|
+
* ANSI color codes for terminal output.
|
|
6
|
+
*/
|
|
7
|
+
const colors = {
|
|
8
|
+
reset: "\x1B[0m",
|
|
9
|
+
red: "\x1B[31m",
|
|
10
|
+
yellow: "\x1B[33m",
|
|
11
|
+
cyan: "\x1B[36m",
|
|
12
|
+
dim: "\x1B[2m",
|
|
13
|
+
bold: "\x1B[1m"
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Formats a ZodError into a user-friendly string for development.
|
|
17
|
+
*
|
|
18
|
+
* @param error - The ZodError to format
|
|
19
|
+
* @param options - Formatting options
|
|
20
|
+
* @returns Formatted error message
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* try {
|
|
25
|
+
* config.parse();
|
|
26
|
+
* } catch (error) {
|
|
27
|
+
* if (error instanceof ZodError) {
|
|
28
|
+
* console.error(formatParseError(error));
|
|
29
|
+
* }
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* Output:
|
|
34
|
+
* ```
|
|
35
|
+
* Environment Configuration Failed
|
|
36
|
+
*
|
|
37
|
+
* Missing Variables:
|
|
38
|
+
* DATABASE_URL - Required
|
|
39
|
+
* JWT_SECRET - Required
|
|
40
|
+
*
|
|
41
|
+
* Invalid Values:
|
|
42
|
+
* NODE_ENV = "invalid"
|
|
43
|
+
* Expected: "development" | "staging" | "production"
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
function formatParseError(error, options = {}) {
|
|
47
|
+
const useColors = options.colors ?? (process.stdout?.isTTY && process.env.NO_COLOR == null);
|
|
48
|
+
const c = useColors ? colors : {
|
|
49
|
+
reset: "",
|
|
50
|
+
red: "",
|
|
51
|
+
yellow: "",
|
|
52
|
+
cyan: "",
|
|
53
|
+
dim: "",
|
|
54
|
+
bold: ""
|
|
55
|
+
};
|
|
56
|
+
const missingVars = [];
|
|
57
|
+
const invalidVars = [];
|
|
58
|
+
for (const issue of error.issues) {
|
|
59
|
+
let envName = "";
|
|
60
|
+
if (issue.path.length > 0) envName = String(issue.path[0]);
|
|
61
|
+
else {
|
|
62
|
+
const match = issue.message.match(/Environment variable "([^"]+)"/);
|
|
63
|
+
if (match?.[1]) envName = match[1];
|
|
64
|
+
}
|
|
65
|
+
const received = "received" in issue ? issue.received : void 0;
|
|
66
|
+
const isMissing = issue.code === "invalid_type" && (received === "undefined" || received === "null");
|
|
67
|
+
if (isMissing) missingVars.push({
|
|
68
|
+
name: envName || "Unknown",
|
|
69
|
+
message: cleanMessage(issue.message)
|
|
70
|
+
});
|
|
71
|
+
else invalidVars.push({
|
|
72
|
+
name: envName || "Unknown",
|
|
73
|
+
value: received,
|
|
74
|
+
message: cleanMessage(issue.message)
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
const lines = [];
|
|
78
|
+
lines.push("");
|
|
79
|
+
lines.push(`${c.red}${c.bold}Environment Configuration Failed${c.reset}`);
|
|
80
|
+
lines.push("");
|
|
81
|
+
if (missingVars.length > 0) {
|
|
82
|
+
lines.push(`${c.yellow}Missing Variables:${c.reset}`);
|
|
83
|
+
for (const v of missingVars) lines.push(` ${c.cyan}${v.name}${c.reset} ${c.dim}- Required${c.reset}`);
|
|
84
|
+
lines.push("");
|
|
85
|
+
}
|
|
86
|
+
if (invalidVars.length > 0) {
|
|
87
|
+
lines.push(`${c.yellow}Invalid Values:${c.reset}`);
|
|
88
|
+
for (const v of invalidVars) {
|
|
89
|
+
const valueStr = v.value !== void 0 ? ` = ${JSON.stringify(v.value)}` : "";
|
|
90
|
+
lines.push(` ${c.cyan}${v.name}${c.reset}${valueStr}`);
|
|
91
|
+
lines.push(` ${c.dim}${v.message}${c.reset}`);
|
|
92
|
+
}
|
|
93
|
+
lines.push("");
|
|
94
|
+
}
|
|
95
|
+
return lines.join("\n");
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Cleans up a Zod error message by removing redundant prefixes.
|
|
99
|
+
*/
|
|
100
|
+
function cleanMessage(message) {
|
|
101
|
+
return message.replace(/^Environment variable "[^"]+": /, "");
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Checks if the current environment is development.
|
|
105
|
+
*/
|
|
106
|
+
function isDevelopment() {
|
|
107
|
+
const nodeEnv = process.env.NODE_ENV?.toLowerCase();
|
|
108
|
+
return nodeEnv == null || nodeEnv === "development" || nodeEnv === "dev";
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
//#endregion
|
|
112
|
+
export { formatParseError, isDevelopment };
|
|
113
|
+
//# sourceMappingURL=formatter-fz8V7x6i.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatter-fz8V7x6i.mjs","names":["error: z.ZodError","options: FormatOptions","missingVars: Array<{ name: string; message: string }>","invalidVars: Array<{ name: string; value: unknown; message: string }>","lines: string[]","message: string"],"sources":["../src/formatter.ts"],"sourcesContent":["import { z } from 'zod/v4';\n\n/**\n * Options for formatting parse errors.\n */\nexport interface FormatOptions {\n\t/** Whether to use colors in output. Defaults to auto-detect TTY. */\n\tcolors?: boolean;\n}\n\n/**\n * ANSI color codes for terminal output.\n */\nconst colors = {\n\treset: '\\x1b[0m',\n\tred: '\\x1b[31m',\n\tyellow: '\\x1b[33m',\n\tcyan: '\\x1b[36m',\n\tdim: '\\x1b[2m',\n\tbold: '\\x1b[1m',\n};\n\n/**\n * Formats a ZodError into a user-friendly string for development.\n *\n * @param error - The ZodError to format\n * @param options - Formatting options\n * @returns Formatted error message\n *\n * @example\n * ```typescript\n * try {\n * config.parse();\n * } catch (error) {\n * if (error instanceof ZodError) {\n * console.error(formatParseError(error));\n * }\n * }\n * ```\n *\n * Output:\n * ```\n * Environment Configuration Failed\n *\n * Missing Variables:\n * DATABASE_URL - Required\n * JWT_SECRET - Required\n *\n * Invalid Values:\n * NODE_ENV = \"invalid\"\n * Expected: \"development\" | \"staging\" | \"production\"\n * ```\n */\nexport function formatParseError(\n\terror: z.ZodError,\n\toptions: FormatOptions = {},\n): string {\n\tconst useColors =\n\t\toptions.colors ?? (process.stdout?.isTTY && process.env.NO_COLOR == null);\n\n\tconst c = useColors\n\t\t? colors\n\t\t: { reset: '', red: '', yellow: '', cyan: '', dim: '', bold: '' };\n\n\tconst missingVars: Array<{ name: string; message: string }> = [];\n\tconst invalidVars: Array<{ name: string; value: unknown; message: string }> =\n\t\t[];\n\n\tfor (const issue of error.issues) {\n\t\t// Extract environment variable name from path or message\n\t\tlet envName = '';\n\t\tif (issue.path.length > 0) {\n\t\t\tenvName = String(issue.path[0]);\n\t\t} else {\n\t\t\t// Try to extract from message like 'Environment variable \"NAME\": ...'\n\t\t\tconst match = issue.message.match(/Environment variable \"([^\"]+)\"/);\n\t\t\tif (match?.[1]) {\n\t\t\t\tenvName = match[1];\n\t\t\t}\n\t\t}\n\n\t\t// Determine if this is a missing or invalid value\n\t\t// Use type guard for received property\n\t\tconst received = 'received' in issue ? issue.received : undefined;\n\t\tconst isMissing =\n\t\t\tissue.code === 'invalid_type' &&\n\t\t\t(received === 'undefined' || received === 'null');\n\n\t\tif (isMissing) {\n\t\t\tmissingVars.push({\n\t\t\t\tname: envName || 'Unknown',\n\t\t\t\tmessage: cleanMessage(issue.message),\n\t\t\t});\n\t\t} else {\n\t\t\tinvalidVars.push({\n\t\t\t\tname: envName || 'Unknown',\n\t\t\t\tvalue: received,\n\t\t\t\tmessage: cleanMessage(issue.message),\n\t\t\t});\n\t\t}\n\t}\n\n\tconst lines: string[] = [];\n\n\tlines.push('');\n\tlines.push(\n\t\t`${c.red}${c.bold}Environment Configuration Failed${c.reset}`,\n\t);\n\tlines.push('');\n\n\tif (missingVars.length > 0) {\n\t\tlines.push(`${c.yellow}Missing Variables:${c.reset}`);\n\t\tfor (const v of missingVars) {\n\t\t\tlines.push(` ${c.cyan}${v.name}${c.reset} ${c.dim}- Required${c.reset}`);\n\t\t}\n\t\tlines.push('');\n\t}\n\n\tif (invalidVars.length > 0) {\n\t\tlines.push(`${c.yellow}Invalid Values:${c.reset}`);\n\t\tfor (const v of invalidVars) {\n\t\t\tconst valueStr =\n\t\t\t\tv.value !== undefined ? ` = ${JSON.stringify(v.value)}` : '';\n\t\t\tlines.push(` ${c.cyan}${v.name}${c.reset}${valueStr}`);\n\t\t\tlines.push(` ${c.dim}${v.message}${c.reset}`);\n\t\t}\n\t\tlines.push('');\n\t}\n\n\treturn lines.join('\\n');\n}\n\n/**\n * Cleans up a Zod error message by removing redundant prefixes.\n */\nfunction cleanMessage(message: string): string {\n\t// Remove \"Environment variable \"NAME\": \" prefix if present\n\treturn message.replace(/^Environment variable \"[^\"]+\": /, '');\n}\n\n/**\n * Checks if the current environment is development.\n */\nexport function isDevelopment(): boolean {\n\tconst nodeEnv = process.env.NODE_ENV?.toLowerCase();\n\treturn nodeEnv == null || nodeEnv === 'development' || nodeEnv === 'dev';\n}\n"],"mappings":";;;;;;AAaA,MAAM,SAAS;CACd,OAAO;CACP,KAAK;CACL,QAAQ;CACR,MAAM;CACN,KAAK;CACL,MAAM;AACN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCD,SAAgB,iBACfA,OACAC,UAAyB,CAAE,GAClB;CACT,MAAM,YACL,QAAQ,WAAW,QAAQ,QAAQ,SAAS,QAAQ,IAAI,YAAY;CAErE,MAAM,IAAI,YACP,SACA;EAAE,OAAO;EAAI,KAAK;EAAI,QAAQ;EAAI,MAAM;EAAI,KAAK;EAAI,MAAM;CAAI;CAElE,MAAMC,cAAwD,CAAE;CAChE,MAAMC,cACL,CAAE;AAEH,MAAK,MAAM,SAAS,MAAM,QAAQ;EAEjC,IAAI,UAAU;AACd,MAAI,MAAM,KAAK,SAAS,EACvB,WAAU,OAAO,MAAM,KAAK,GAAG;OACzB;GAEN,MAAM,QAAQ,MAAM,QAAQ,MAAM,iCAAiC;AACnE,OAAI,QAAQ,GACX,WAAU,MAAM;EAEjB;EAID,MAAM,WAAW,cAAc,QAAQ,MAAM;EAC7C,MAAM,YACL,MAAM,SAAS,mBACd,aAAa,eAAe,aAAa;AAE3C,MAAI,UACH,aAAY,KAAK;GAChB,MAAM,WAAW;GACjB,SAAS,aAAa,MAAM,QAAQ;EACpC,EAAC;MAEF,aAAY,KAAK;GAChB,MAAM,WAAW;GACjB,OAAO;GACP,SAAS,aAAa,MAAM,QAAQ;EACpC,EAAC;CAEH;CAED,MAAMC,QAAkB,CAAE;AAE1B,OAAM,KAAK,GAAG;AACd,OAAM,MACJ,EAAE,EAAE,IAAI,EAAE,EAAE,KAAK,kCAAkC,EAAE,MAAM,EAC5D;AACD,OAAM,KAAK,GAAG;AAEd,KAAI,YAAY,SAAS,GAAG;AAC3B,QAAM,MAAM,EAAE,EAAE,OAAO,oBAAoB,EAAE,MAAM,EAAE;AACrD,OAAK,MAAM,KAAK,YACf,OAAM,MAAM,IAAI,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,MAAM,GAAG,EAAE,IAAI,YAAY,EAAE,MAAM,EAAE;AAE1E,QAAM,KAAK,GAAG;CACd;AAED,KAAI,YAAY,SAAS,GAAG;AAC3B,QAAM,MAAM,EAAE,EAAE,OAAO,iBAAiB,EAAE,MAAM,EAAE;AAClD,OAAK,MAAM,KAAK,aAAa;GAC5B,MAAM,WACL,EAAE,oBAAuB,KAAK,KAAK,UAAU,EAAE,MAAM,CAAC,IAAI;AAC3D,SAAM,MAAM,IAAI,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE;AACvD,SAAM,MAAM,MAAM,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE;EAChD;AACD,QAAM,KAAK,GAAG;CACd;AAED,QAAO,MAAM,KAAK,KAAK;AACvB;;;;AAKD,SAAS,aAAaC,SAAyB;AAE9C,QAAO,QAAQ,QAAQ,mCAAmC,GAAG;AAC7D;;;;AAKD,SAAgB,gBAAyB;CACxC,MAAM,UAAU,QAAQ,IAAI,UAAU,aAAa;AACnD,QAAO,WAAW,QAAQ,YAAY,iBAAiB,YAAY;AACnE"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
const require_chunk = require('./chunk-CUT6urMc.cjs');
|
|
2
|
+
const zod_v4 = require_chunk.__toESM(require("zod/v4"));
|
|
3
|
+
|
|
4
|
+
//#region src/formatter.ts
|
|
5
|
+
/**
|
|
6
|
+
* ANSI color codes for terminal output.
|
|
7
|
+
*/
|
|
8
|
+
const colors = {
|
|
9
|
+
reset: "\x1B[0m",
|
|
10
|
+
red: "\x1B[31m",
|
|
11
|
+
yellow: "\x1B[33m",
|
|
12
|
+
cyan: "\x1B[36m",
|
|
13
|
+
dim: "\x1B[2m",
|
|
14
|
+
bold: "\x1B[1m"
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Formats a ZodError into a user-friendly string for development.
|
|
18
|
+
*
|
|
19
|
+
* @param error - The ZodError to format
|
|
20
|
+
* @param options - Formatting options
|
|
21
|
+
* @returns Formatted error message
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* try {
|
|
26
|
+
* config.parse();
|
|
27
|
+
* } catch (error) {
|
|
28
|
+
* if (error instanceof ZodError) {
|
|
29
|
+
* console.error(formatParseError(error));
|
|
30
|
+
* }
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* Output:
|
|
35
|
+
* ```
|
|
36
|
+
* Environment Configuration Failed
|
|
37
|
+
*
|
|
38
|
+
* Missing Variables:
|
|
39
|
+
* DATABASE_URL - Required
|
|
40
|
+
* JWT_SECRET - Required
|
|
41
|
+
*
|
|
42
|
+
* Invalid Values:
|
|
43
|
+
* NODE_ENV = "invalid"
|
|
44
|
+
* Expected: "development" | "staging" | "production"
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
function formatParseError(error, options = {}) {
|
|
48
|
+
const useColors = options.colors ?? (process.stdout?.isTTY && process.env.NO_COLOR == null);
|
|
49
|
+
const c = useColors ? colors : {
|
|
50
|
+
reset: "",
|
|
51
|
+
red: "",
|
|
52
|
+
yellow: "",
|
|
53
|
+
cyan: "",
|
|
54
|
+
dim: "",
|
|
55
|
+
bold: ""
|
|
56
|
+
};
|
|
57
|
+
const missingVars = [];
|
|
58
|
+
const invalidVars = [];
|
|
59
|
+
for (const issue of error.issues) {
|
|
60
|
+
let envName = "";
|
|
61
|
+
if (issue.path.length > 0) envName = String(issue.path[0]);
|
|
62
|
+
else {
|
|
63
|
+
const match = issue.message.match(/Environment variable "([^"]+)"/);
|
|
64
|
+
if (match?.[1]) envName = match[1];
|
|
65
|
+
}
|
|
66
|
+
const received = "received" in issue ? issue.received : void 0;
|
|
67
|
+
const isMissing = issue.code === "invalid_type" && (received === "undefined" || received === "null");
|
|
68
|
+
if (isMissing) missingVars.push({
|
|
69
|
+
name: envName || "Unknown",
|
|
70
|
+
message: cleanMessage(issue.message)
|
|
71
|
+
});
|
|
72
|
+
else invalidVars.push({
|
|
73
|
+
name: envName || "Unknown",
|
|
74
|
+
value: received,
|
|
75
|
+
message: cleanMessage(issue.message)
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
const lines = [];
|
|
79
|
+
lines.push("");
|
|
80
|
+
lines.push(`${c.red}${c.bold}Environment Configuration Failed${c.reset}`);
|
|
81
|
+
lines.push("");
|
|
82
|
+
if (missingVars.length > 0) {
|
|
83
|
+
lines.push(`${c.yellow}Missing Variables:${c.reset}`);
|
|
84
|
+
for (const v of missingVars) lines.push(` ${c.cyan}${v.name}${c.reset} ${c.dim}- Required${c.reset}`);
|
|
85
|
+
lines.push("");
|
|
86
|
+
}
|
|
87
|
+
if (invalidVars.length > 0) {
|
|
88
|
+
lines.push(`${c.yellow}Invalid Values:${c.reset}`);
|
|
89
|
+
for (const v of invalidVars) {
|
|
90
|
+
const valueStr = v.value !== void 0 ? ` = ${JSON.stringify(v.value)}` : "";
|
|
91
|
+
lines.push(` ${c.cyan}${v.name}${c.reset}${valueStr}`);
|
|
92
|
+
lines.push(` ${c.dim}${v.message}${c.reset}`);
|
|
93
|
+
}
|
|
94
|
+
lines.push("");
|
|
95
|
+
}
|
|
96
|
+
return lines.join("\n");
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Cleans up a Zod error message by removing redundant prefixes.
|
|
100
|
+
*/
|
|
101
|
+
function cleanMessage(message) {
|
|
102
|
+
return message.replace(/^Environment variable "[^"]+": /, "");
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Checks if the current environment is development.
|
|
106
|
+
*/
|
|
107
|
+
function isDevelopment() {
|
|
108
|
+
const nodeEnv = process.env.NODE_ENV?.toLowerCase();
|
|
109
|
+
return nodeEnv == null || nodeEnv === "development" || nodeEnv === "dev";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
//#endregion
|
|
113
|
+
Object.defineProperty(exports, 'formatParseError', {
|
|
114
|
+
enumerable: true,
|
|
115
|
+
get: function () {
|
|
116
|
+
return formatParseError;
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
Object.defineProperty(exports, 'isDevelopment', {
|
|
120
|
+
enumerable: true,
|
|
121
|
+
get: function () {
|
|
122
|
+
return isDevelopment;
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
//# sourceMappingURL=formatter-w8Tsccw4.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatter-w8Tsccw4.cjs","names":["error: z.ZodError","options: FormatOptions","missingVars: Array<{ name: string; message: string }>","invalidVars: Array<{ name: string; value: unknown; message: string }>","lines: string[]","message: string"],"sources":["../src/formatter.ts"],"sourcesContent":["import { z } from 'zod/v4';\n\n/**\n * Options for formatting parse errors.\n */\nexport interface FormatOptions {\n\t/** Whether to use colors in output. Defaults to auto-detect TTY. */\n\tcolors?: boolean;\n}\n\n/**\n * ANSI color codes for terminal output.\n */\nconst colors = {\n\treset: '\\x1b[0m',\n\tred: '\\x1b[31m',\n\tyellow: '\\x1b[33m',\n\tcyan: '\\x1b[36m',\n\tdim: '\\x1b[2m',\n\tbold: '\\x1b[1m',\n};\n\n/**\n * Formats a ZodError into a user-friendly string for development.\n *\n * @param error - The ZodError to format\n * @param options - Formatting options\n * @returns Formatted error message\n *\n * @example\n * ```typescript\n * try {\n * config.parse();\n * } catch (error) {\n * if (error instanceof ZodError) {\n * console.error(formatParseError(error));\n * }\n * }\n * ```\n *\n * Output:\n * ```\n * Environment Configuration Failed\n *\n * Missing Variables:\n * DATABASE_URL - Required\n * JWT_SECRET - Required\n *\n * Invalid Values:\n * NODE_ENV = \"invalid\"\n * Expected: \"development\" | \"staging\" | \"production\"\n * ```\n */\nexport function formatParseError(\n\terror: z.ZodError,\n\toptions: FormatOptions = {},\n): string {\n\tconst useColors =\n\t\toptions.colors ?? (process.stdout?.isTTY && process.env.NO_COLOR == null);\n\n\tconst c = useColors\n\t\t? colors\n\t\t: { reset: '', red: '', yellow: '', cyan: '', dim: '', bold: '' };\n\n\tconst missingVars: Array<{ name: string; message: string }> = [];\n\tconst invalidVars: Array<{ name: string; value: unknown; message: string }> =\n\t\t[];\n\n\tfor (const issue of error.issues) {\n\t\t// Extract environment variable name from path or message\n\t\tlet envName = '';\n\t\tif (issue.path.length > 0) {\n\t\t\tenvName = String(issue.path[0]);\n\t\t} else {\n\t\t\t// Try to extract from message like 'Environment variable \"NAME\": ...'\n\t\t\tconst match = issue.message.match(/Environment variable \"([^\"]+)\"/);\n\t\t\tif (match?.[1]) {\n\t\t\t\tenvName = match[1];\n\t\t\t}\n\t\t}\n\n\t\t// Determine if this is a missing or invalid value\n\t\t// Use type guard for received property\n\t\tconst received = 'received' in issue ? issue.received : undefined;\n\t\tconst isMissing =\n\t\t\tissue.code === 'invalid_type' &&\n\t\t\t(received === 'undefined' || received === 'null');\n\n\t\tif (isMissing) {\n\t\t\tmissingVars.push({\n\t\t\t\tname: envName || 'Unknown',\n\t\t\t\tmessage: cleanMessage(issue.message),\n\t\t\t});\n\t\t} else {\n\t\t\tinvalidVars.push({\n\t\t\t\tname: envName || 'Unknown',\n\t\t\t\tvalue: received,\n\t\t\t\tmessage: cleanMessage(issue.message),\n\t\t\t});\n\t\t}\n\t}\n\n\tconst lines: string[] = [];\n\n\tlines.push('');\n\tlines.push(\n\t\t`${c.red}${c.bold}Environment Configuration Failed${c.reset}`,\n\t);\n\tlines.push('');\n\n\tif (missingVars.length > 0) {\n\t\tlines.push(`${c.yellow}Missing Variables:${c.reset}`);\n\t\tfor (const v of missingVars) {\n\t\t\tlines.push(` ${c.cyan}${v.name}${c.reset} ${c.dim}- Required${c.reset}`);\n\t\t}\n\t\tlines.push('');\n\t}\n\n\tif (invalidVars.length > 0) {\n\t\tlines.push(`${c.yellow}Invalid Values:${c.reset}`);\n\t\tfor (const v of invalidVars) {\n\t\t\tconst valueStr =\n\t\t\t\tv.value !== undefined ? ` = ${JSON.stringify(v.value)}` : '';\n\t\t\tlines.push(` ${c.cyan}${v.name}${c.reset}${valueStr}`);\n\t\t\tlines.push(` ${c.dim}${v.message}${c.reset}`);\n\t\t}\n\t\tlines.push('');\n\t}\n\n\treturn lines.join('\\n');\n}\n\n/**\n * Cleans up a Zod error message by removing redundant prefixes.\n */\nfunction cleanMessage(message: string): string {\n\t// Remove \"Environment variable \"NAME\": \" prefix if present\n\treturn message.replace(/^Environment variable \"[^\"]+\": /, '');\n}\n\n/**\n * Checks if the current environment is development.\n */\nexport function isDevelopment(): boolean {\n\tconst nodeEnv = process.env.NODE_ENV?.toLowerCase();\n\treturn nodeEnv == null || nodeEnv === 'development' || nodeEnv === 'dev';\n}\n"],"mappings":";;;;;;;AAaA,MAAM,SAAS;CACd,OAAO;CACP,KAAK;CACL,QAAQ;CACR,MAAM;CACN,KAAK;CACL,MAAM;AACN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCD,SAAgB,iBACfA,OACAC,UAAyB,CAAE,GAClB;CACT,MAAM,YACL,QAAQ,WAAW,QAAQ,QAAQ,SAAS,QAAQ,IAAI,YAAY;CAErE,MAAM,IAAI,YACP,SACA;EAAE,OAAO;EAAI,KAAK;EAAI,QAAQ;EAAI,MAAM;EAAI,KAAK;EAAI,MAAM;CAAI;CAElE,MAAMC,cAAwD,CAAE;CAChE,MAAMC,cACL,CAAE;AAEH,MAAK,MAAM,SAAS,MAAM,QAAQ;EAEjC,IAAI,UAAU;AACd,MAAI,MAAM,KAAK,SAAS,EACvB,WAAU,OAAO,MAAM,KAAK,GAAG;OACzB;GAEN,MAAM,QAAQ,MAAM,QAAQ,MAAM,iCAAiC;AACnE,OAAI,QAAQ,GACX,WAAU,MAAM;EAEjB;EAID,MAAM,WAAW,cAAc,QAAQ,MAAM;EAC7C,MAAM,YACL,MAAM,SAAS,mBACd,aAAa,eAAe,aAAa;AAE3C,MAAI,UACH,aAAY,KAAK;GAChB,MAAM,WAAW;GACjB,SAAS,aAAa,MAAM,QAAQ;EACpC,EAAC;MAEF,aAAY,KAAK;GAChB,MAAM,WAAW;GACjB,OAAO;GACP,SAAS,aAAa,MAAM,QAAQ;EACpC,EAAC;CAEH;CAED,MAAMC,QAAkB,CAAE;AAE1B,OAAM,KAAK,GAAG;AACd,OAAM,MACJ,EAAE,EAAE,IAAI,EAAE,EAAE,KAAK,kCAAkC,EAAE,MAAM,EAC5D;AACD,OAAM,KAAK,GAAG;AAEd,KAAI,YAAY,SAAS,GAAG;AAC3B,QAAM,MAAM,EAAE,EAAE,OAAO,oBAAoB,EAAE,MAAM,EAAE;AACrD,OAAK,MAAM,KAAK,YACf,OAAM,MAAM,IAAI,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,MAAM,GAAG,EAAE,IAAI,YAAY,EAAE,MAAM,EAAE;AAE1E,QAAM,KAAK,GAAG;CACd;AAED,KAAI,YAAY,SAAS,GAAG;AAC3B,QAAM,MAAM,EAAE,EAAE,OAAO,iBAAiB,EAAE,MAAM,EAAE;AAClD,OAAK,MAAM,KAAK,aAAa;GAC5B,MAAM,WACL,EAAE,oBAAuB,KAAK,KAAK,UAAU,EAAE,MAAM,CAAC,IAAI;AAC3D,SAAM,MAAM,IAAI,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE;AACvD,SAAM,MAAM,MAAM,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE;EAChD;AACD,QAAM,KAAK,GAAG;CACd;AAED,QAAO,MAAM,KAAK,KAAK;AACvB;;;;AAKD,SAAS,aAAaC,SAAyB;AAE9C,QAAO,QAAQ,QAAQ,mCAAmC,GAAG;AAC7D;;;;AAKD,SAAgB,gBAAyB;CACxC,MAAM,UAAU,QAAQ,IAAI,UAAU,aAAa;AACnD,QAAO,WAAW,QAAQ,YAAY,iBAAiB,YAAY;AACnE"}
|
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
const require_EnvironmentBuilder = require('./EnvironmentBuilder-Djr1VsWM.cjs');
|
|
2
|
-
const
|
|
2
|
+
const require_formatter = require('./formatter-w8Tsccw4.cjs');
|
|
3
|
+
const require_EnvironmentParser = require('./EnvironmentParser-DlWHnhDY.cjs');
|
|
3
4
|
|
|
4
5
|
exports.ConfigParser = require_EnvironmentParser.ConfigParser;
|
|
5
6
|
exports.EnvironmentBuilder = require_EnvironmentBuilder.EnvironmentBuilder;
|
|
6
7
|
exports.EnvironmentParser = require_EnvironmentParser.EnvironmentParser;
|
|
7
|
-
exports.environmentCase = require_EnvironmentBuilder.environmentCase;
|
|
8
|
+
exports.environmentCase = require_EnvironmentBuilder.environmentCase;
|
|
9
|
+
exports.formatParseError = require_formatter.formatParseError;
|
|
10
|
+
exports.isDevelopment = require_formatter.isDevelopment;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { EnvRecord, EnvValue, EnvironmentBuilder, EnvironmentBuilderOptions, EnvironmentResolver, InputValue, Resolvers, TypedInputValue, TypedResolvers, environmentCase } from "./EnvironmentBuilder-Xuf2Dd9u.cjs";
|
|
2
2
|
import { ConfigParser, EnvironmentParser } from "./EnvironmentParser-DtOL86NU.cjs";
|
|
3
|
-
|
|
3
|
+
import { FormatOptions, formatParseError, isDevelopment } from "./formatter-D85aIkpd.cjs";
|
|
4
|
+
export { ConfigParser, EnvRecord, EnvValue, EnvironmentBuilder, EnvironmentBuilderOptions, EnvironmentParser, EnvironmentResolver, FormatOptions, InputValue, Resolvers, TypedInputValue, TypedResolvers, environmentCase, formatParseError, isDevelopment };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import { EnvRecord, EnvValue, EnvironmentBuilder, EnvironmentBuilderOptions, EnvironmentResolver, InputValue, Resolvers, TypedInputValue, TypedResolvers, environmentCase } from "./EnvironmentBuilder-
|
|
2
|
-
import { ConfigParser, EnvironmentParser } from "./EnvironmentParser-
|
|
3
|
-
|
|
1
|
+
import { EnvRecord, EnvValue, EnvironmentBuilder, EnvironmentBuilderOptions, EnvironmentResolver, InputValue, Resolvers, TypedInputValue, TypedResolvers, environmentCase } from "./EnvironmentBuilder-jF-b7WQg.mjs";
|
|
2
|
+
import { ConfigParser, EnvironmentParser } from "./EnvironmentParser-CkLfmn4Y.mjs";
|
|
3
|
+
import { FormatOptions, formatParseError, isDevelopment } from "./formatter-Cox0NGxT.mjs";
|
|
4
|
+
export { ConfigParser, EnvRecord, EnvValue, EnvironmentBuilder, EnvironmentBuilderOptions, EnvironmentParser, EnvironmentResolver, FormatOptions, InputValue, Resolvers, TypedInputValue, TypedResolvers, environmentCase, formatParseError, isDevelopment };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EnvironmentBuilder, environmentCase } from "./EnvironmentBuilder-BSuHZm0y.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { formatParseError, isDevelopment } from "./formatter-fz8V7x6i.mjs";
|
|
3
|
+
import { ConfigParser, EnvironmentParser } from "./EnvironmentParser-CBLsPUyQ.mjs";
|
|
3
4
|
|
|
4
|
-
export { ConfigParser, EnvironmentBuilder, EnvironmentParser, environmentCase };
|
|
5
|
+
export { ConfigParser, EnvironmentBuilder, EnvironmentParser, environmentCase, formatParseError, isDevelopment };
|
package/dist/sst.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_EnvironmentBuilder = require('./EnvironmentBuilder-Djr1VsWM.cjs');
|
|
2
|
-
const require_SstEnvironmentBuilder = require('./SstEnvironmentBuilder-
|
|
2
|
+
const require_SstEnvironmentBuilder = require('./SstEnvironmentBuilder-jsnqgtcW.cjs');
|
|
3
3
|
|
|
4
4
|
//#region src/sst.ts
|
|
5
5
|
/**
|
package/dist/sst.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { EnvRecord, EnvValue, EnvironmentBuilderOptions, environmentCase } from "./EnvironmentBuilder-
|
|
2
|
-
import { ApiGatewayV2, Bucket, Function, Postgres, ResourceProcessor, ResourceType, Secret, SnsTopic, SstEnvironmentBuilder, SstResource, Vpc, sstResolvers } from "./SstEnvironmentBuilder-
|
|
1
|
+
import { EnvRecord, EnvValue, EnvironmentBuilderOptions, environmentCase } from "./EnvironmentBuilder-jF-b7WQg.mjs";
|
|
2
|
+
import { ApiGatewayV2, Bucket, Function, Postgres, ResourceProcessor, ResourceType, Secret, SnsTopic, SstEnvironmentBuilder, SstResource, Vpc, sstResolvers } from "./SstEnvironmentBuilder-BZngSQKQ.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/sst.d.ts
|
|
5
5
|
|
package/dist/sst.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { environmentCase } from "./EnvironmentBuilder-BSuHZm0y.mjs";
|
|
2
|
-
import { ResourceType, SstEnvironmentBuilder, sstResolvers } from "./SstEnvironmentBuilder-
|
|
2
|
+
import { ResourceType, SstEnvironmentBuilder, sstResolvers } from "./SstEnvironmentBuilder-DVB7cJq4.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/sst.ts
|
|
5
5
|
/**
|
package/package.json
CHANGED
package/src/EnvironmentParser.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import get from 'lodash.get';
|
|
2
2
|
import set from 'lodash.set';
|
|
3
3
|
import { z } from 'zod/v4';
|
|
4
|
+
import { formatParseError, isDevelopment } from './formatter.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Parses and validates configuration objects against Zod schemas.
|
|
@@ -63,8 +64,12 @@ export class ConfigParser<TResponse extends EmptyObject> {
|
|
|
63
64
|
) as unknown as InferConfig<TResponse>;
|
|
64
65
|
|
|
65
66
|
if (errors.length > 0) {
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
const zodError = new z.ZodError(errors);
|
|
68
|
+
// In development, log a formatted error message before throwing
|
|
69
|
+
if (isDevelopment()) {
|
|
70
|
+
console.error(formatParseError(zodError));
|
|
71
|
+
}
|
|
72
|
+
throw zodError;
|
|
68
73
|
}
|
|
69
74
|
|
|
70
75
|
return parsedConfig;
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { z } from 'zod/v4';
|
|
3
|
+
import { formatParseError, isDevelopment } from '../formatter';
|
|
4
|
+
|
|
5
|
+
// Helper to create test issues - casts through unknown because we're creating test fixtures
|
|
6
|
+
// that may include properties like 'received' which vary by issue type
|
|
7
|
+
function createIssue(issue: Record<string, unknown>): z.core.$ZodIssue {
|
|
8
|
+
return issue as unknown as z.core.$ZodIssue;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
describe('formatParseError', () => {
|
|
12
|
+
it('should format missing variables', () => {
|
|
13
|
+
const error = new z.ZodError([
|
|
14
|
+
createIssue({
|
|
15
|
+
code: 'invalid_type',
|
|
16
|
+
expected: 'string',
|
|
17
|
+
received: 'undefined',
|
|
18
|
+
path: ['DATABASE_URL'],
|
|
19
|
+
message: 'Environment variable "DATABASE_URL": Required',
|
|
20
|
+
}),
|
|
21
|
+
createIssue({
|
|
22
|
+
code: 'invalid_type',
|
|
23
|
+
expected: 'string',
|
|
24
|
+
received: 'undefined',
|
|
25
|
+
path: ['JWT_SECRET'],
|
|
26
|
+
message: 'Environment variable "JWT_SECRET": Required',
|
|
27
|
+
}),
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
const formatted = formatParseError(error, { colors: false });
|
|
31
|
+
|
|
32
|
+
expect(formatted).toContain('Environment Configuration Failed');
|
|
33
|
+
expect(formatted).toContain('Missing Variables:');
|
|
34
|
+
expect(formatted).toContain('DATABASE_URL');
|
|
35
|
+
expect(formatted).toContain('JWT_SECRET');
|
|
36
|
+
expect(formatted).toContain('Required');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should format invalid values', () => {
|
|
40
|
+
const error = new z.ZodError([
|
|
41
|
+
createIssue({
|
|
42
|
+
code: 'invalid_value',
|
|
43
|
+
values: ['development', 'staging', 'production'],
|
|
44
|
+
received: 'invalid',
|
|
45
|
+
path: ['NODE_ENV'],
|
|
46
|
+
message:
|
|
47
|
+
'Invalid enum value. Expected \'development\' | \'staging\' | \'production\', received \'invalid\'',
|
|
48
|
+
}),
|
|
49
|
+
]);
|
|
50
|
+
|
|
51
|
+
const formatted = formatParseError(error, { colors: false });
|
|
52
|
+
|
|
53
|
+
expect(formatted).toContain('Environment Configuration Failed');
|
|
54
|
+
expect(formatted).toContain('Invalid Values:');
|
|
55
|
+
expect(formatted).toContain('NODE_ENV');
|
|
56
|
+
expect(formatted).toContain('invalid');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should handle mixed missing and invalid values', () => {
|
|
60
|
+
const error = new z.ZodError([
|
|
61
|
+
createIssue({
|
|
62
|
+
code: 'invalid_type',
|
|
63
|
+
expected: 'string',
|
|
64
|
+
received: 'undefined',
|
|
65
|
+
path: ['DATABASE_URL'],
|
|
66
|
+
message: 'Environment variable "DATABASE_URL": Required',
|
|
67
|
+
}),
|
|
68
|
+
createIssue({
|
|
69
|
+
code: 'invalid_type',
|
|
70
|
+
expected: 'number',
|
|
71
|
+
received: 'string',
|
|
72
|
+
path: ['PORT'],
|
|
73
|
+
message: 'Expected number, received string',
|
|
74
|
+
}),
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
const formatted = formatParseError(error, { colors: false });
|
|
78
|
+
|
|
79
|
+
expect(formatted).toContain('Missing Variables:');
|
|
80
|
+
expect(formatted).toContain('DATABASE_URL');
|
|
81
|
+
expect(formatted).toContain('Invalid Values:');
|
|
82
|
+
expect(formatted).toContain('PORT');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should clean message prefixes', () => {
|
|
86
|
+
const error = new z.ZodError([
|
|
87
|
+
createIssue({
|
|
88
|
+
code: 'invalid_type',
|
|
89
|
+
expected: 'number',
|
|
90
|
+
received: 'string',
|
|
91
|
+
path: ['PORT'],
|
|
92
|
+
message: 'Environment variable "PORT": Expected number, received string',
|
|
93
|
+
}),
|
|
94
|
+
]);
|
|
95
|
+
|
|
96
|
+
const formatted = formatParseError(error, { colors: false });
|
|
97
|
+
|
|
98
|
+
// Should not duplicate "Environment variable" in output
|
|
99
|
+
expect(formatted).not.toContain(
|
|
100
|
+
'Environment variable "PORT": Environment variable',
|
|
101
|
+
);
|
|
102
|
+
expect(formatted).toContain('Expected number, received string');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should extract env name from message when path is empty', () => {
|
|
106
|
+
const error = new z.ZodError([
|
|
107
|
+
createIssue({
|
|
108
|
+
code: 'invalid_type',
|
|
109
|
+
expected: 'string',
|
|
110
|
+
received: 'undefined',
|
|
111
|
+
path: [],
|
|
112
|
+
message: 'Environment variable "API_KEY": Required',
|
|
113
|
+
}),
|
|
114
|
+
]);
|
|
115
|
+
|
|
116
|
+
const formatted = formatParseError(error, { colors: false });
|
|
117
|
+
|
|
118
|
+
expect(formatted).toContain('API_KEY');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should handle colors option false', () => {
|
|
122
|
+
const error = new z.ZodError([
|
|
123
|
+
createIssue({
|
|
124
|
+
code: 'invalid_type',
|
|
125
|
+
expected: 'string',
|
|
126
|
+
received: 'undefined',
|
|
127
|
+
path: ['TEST'],
|
|
128
|
+
message: 'Required',
|
|
129
|
+
}),
|
|
130
|
+
]);
|
|
131
|
+
|
|
132
|
+
const formatted = formatParseError(error, { colors: false });
|
|
133
|
+
|
|
134
|
+
// Should not contain ANSI codes
|
|
135
|
+
expect(formatted).not.toContain('\x1b[');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should handle colors option true', () => {
|
|
139
|
+
const error = new z.ZodError([
|
|
140
|
+
createIssue({
|
|
141
|
+
code: 'invalid_type',
|
|
142
|
+
expected: 'string',
|
|
143
|
+
received: 'undefined',
|
|
144
|
+
path: ['TEST'],
|
|
145
|
+
message: 'Required',
|
|
146
|
+
}),
|
|
147
|
+
]);
|
|
148
|
+
|
|
149
|
+
const formatted = formatParseError(error, { colors: true });
|
|
150
|
+
|
|
151
|
+
// Should contain ANSI codes
|
|
152
|
+
expect(formatted).toContain('\x1b[');
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe('isDevelopment', () => {
|
|
157
|
+
const originalEnv = process.env.NODE_ENV;
|
|
158
|
+
|
|
159
|
+
afterEach(() => {
|
|
160
|
+
if (originalEnv === undefined) {
|
|
161
|
+
delete process.env.NODE_ENV;
|
|
162
|
+
} else {
|
|
163
|
+
process.env.NODE_ENV = originalEnv;
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('should return true when NODE_ENV is undefined', () => {
|
|
168
|
+
delete process.env.NODE_ENV;
|
|
169
|
+
expect(isDevelopment()).toBe(true);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('should return true when NODE_ENV is development', () => {
|
|
173
|
+
process.env.NODE_ENV = 'development';
|
|
174
|
+
expect(isDevelopment()).toBe(true);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('should return true when NODE_ENV is dev', () => {
|
|
178
|
+
process.env.NODE_ENV = 'dev';
|
|
179
|
+
expect(isDevelopment()).toBe(true);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('should return true when NODE_ENV is DEVELOPMENT (case insensitive)', () => {
|
|
183
|
+
process.env.NODE_ENV = 'DEVELOPMENT';
|
|
184
|
+
expect(isDevelopment()).toBe(true);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('should return false when NODE_ENV is production', () => {
|
|
188
|
+
process.env.NODE_ENV = 'production';
|
|
189
|
+
expect(isDevelopment()).toBe(false);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('should return false when NODE_ENV is staging', () => {
|
|
193
|
+
process.env.NODE_ENV = 'staging';
|
|
194
|
+
expect(isDevelopment()).toBe(false);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should return false when NODE_ENV is test', () => {
|
|
198
|
+
process.env.NODE_ENV = 'test';
|
|
199
|
+
expect(isDevelopment()).toBe(false);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
describe('ConfigParser with dev formatting', () => {
|
|
204
|
+
const originalEnv = process.env.NODE_ENV;
|
|
205
|
+
|
|
206
|
+
beforeEach(() => {
|
|
207
|
+
vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
afterEach(() => {
|
|
211
|
+
vi.restoreAllMocks();
|
|
212
|
+
if (originalEnv === undefined) {
|
|
213
|
+
delete process.env.NODE_ENV;
|
|
214
|
+
} else {
|
|
215
|
+
process.env.NODE_ENV = originalEnv;
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('should log formatted error in development mode', async () => {
|
|
220
|
+
process.env.NODE_ENV = 'development';
|
|
221
|
+
|
|
222
|
+
const { EnvironmentParser } = await import('../EnvironmentParser');
|
|
223
|
+
const parser = new EnvironmentParser({});
|
|
224
|
+
const config = parser.create((get) => ({
|
|
225
|
+
dbUrl: get('DATABASE_URL').string(),
|
|
226
|
+
}));
|
|
227
|
+
|
|
228
|
+
expect(() => config.parse()).toThrow();
|
|
229
|
+
expect(console.error).toHaveBeenCalled();
|
|
230
|
+
|
|
231
|
+
const errorOutput = (console.error as any).mock.calls[0][0];
|
|
232
|
+
expect(errorOutput).toContain('Environment Configuration Failed');
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('should not log formatted error in production mode', async () => {
|
|
236
|
+
process.env.NODE_ENV = 'production';
|
|
237
|
+
|
|
238
|
+
// Need to reimport to pick up the new NODE_ENV
|
|
239
|
+
vi.resetModules();
|
|
240
|
+
const { EnvironmentParser } = await import('../EnvironmentParser');
|
|
241
|
+
|
|
242
|
+
const parser = new EnvironmentParser({});
|
|
243
|
+
const config = parser.create((get) => ({
|
|
244
|
+
dbUrl: get('DATABASE_URL').string(),
|
|
245
|
+
}));
|
|
246
|
+
|
|
247
|
+
expect(() => config.parse()).toThrow();
|
|
248
|
+
expect(console.error).not.toHaveBeenCalled();
|
|
249
|
+
});
|
|
250
|
+
});
|