UNPKG

env-schema-checker

Version:

A zero-config, type-safe, schema-driven .env validator with automatic error reporting, autocomplete, and runtime checks

138 lines (136 loc) 3.91 kB
import { config } from 'dotenv'; import { z } from 'zod'; // src/index.ts function createZodSchema(schema) { const zodSchema = {}; for (const [key, value] of Object.entries(schema)) { if (value instanceof z.ZodType) { zodSchema[key] = value; } else if (Array.isArray(value)) { zodSchema[key] = z.enum(value); } else { switch (value) { case "string": zodSchema[key] = z.string(); break; case "number": zodSchema[key] = z.string().refine((val) => !isNaN(Number(val)), { message: "Invalid number format" }).transform((val) => Number(val)); break; case "boolean": zodSchema[key] = z.enum(["true", "false"]).transform((val) => val === "true"); break; case "array": zodSchema[key] = z.string().refine((val) => { try { const parsed = JSON.parse(val); return Array.isArray(parsed); } catch (e) { return val.includes(","); } }, { message: "Must be a JSON array or comma-separated values" }).transform((val) => { try { const parsed = JSON.parse(val); if (Array.isArray(parsed)) return parsed; } catch (e) { } return val.split(","); }); break; case "object": zodSchema[key] = z.string().transform((val) => { const parsed = JSON.parse(val); if (typeof parsed !== "object" || Array.isArray(parsed) || parsed === null) { throw new Error("Must be a JSON object"); } return parsed; }); break; default: zodSchema[key] = z.string(); } } } return z.object(zodSchema); } function loadEnv(options) { const { schema, path, encoding, override, debug } = options; if (path) { try { const result = config({ path, encoding, override, debug }); if (result.error) { throw result.error; } } catch (error) { const nodeError = error; if (nodeError.code !== "ENOENT") { throw error; } } } const zodSchema = createZodSchema(schema); const validationResult = zodSchema.safeParse(process.env); if (!validationResult.success) { const errors = validationResult.error.errors.map((err) => ({ key: err.path.join("."), message: err.message, value: process.env[err.path[0]] })); return { success: false, errors, env: {} }; } return { success: true, env: validationResult.data }; } function generateTypes(options) { const { schema, outputPath } = options; let typeDefinitions = "declare global {\n namespace NodeJS {\n interface ProcessEnv {\n"; for (const [key, value] of Object.entries(schema)) { let type; if (value instanceof z.ZodType) { type = "string"; } else if (Array.isArray(value)) { type = value.map((v) => `'${v}'`).join(" | "); } else { switch (value) { case "number": type = "number"; break; case "boolean": type = "boolean"; break; case "array": type = "string[]"; break; case "object": type = "Record<string, unknown>"; break; default: type = "string"; } } typeDefinitions += ` ${key}: ${type}; `; } typeDefinitions += " }\n }\n}\n\nexport {};"; return typeDefinitions; } function validateEnv(schema) { return loadEnv({ schema }); } function parseEnvFile(path) { const result = config({ path }); if (result.error) { throw result.error; } return result.parsed || {}; } export { generateTypes, loadEnv, parseEnvFile, validateEnv }; //# sourceMappingURL=index.mjs.map //# sourceMappingURL=index.mjs.map