UNPKG

envfortress

Version:

Turn your .env into a fortress—type-checked, secure, and effortless for React, Vite, and Node.

1 lines 21.2 kB
{"version":3,"sources":["../src/index.ts"],"names":["envUtils","z","val","normalized","num","port","schema","parsed","separator","s","minLength","parseEnvExample","exampleFilePath","existsSync","content","readFileSync","requiredVars","line","trimmed","key","error","validateAgainstExample","env","exampleVars","allowMissingInProduction","missingVars","requiredVar","varName","enhanceZodError","schemas","enhancedIssues","issue","enhancedMessage","options","validateSchema","schemaObj","strict","filteredEnv","_envCache","_envCacheKey","createEnv","config","envKey","client","server","exampleFile","debug","onValidationError","clientParsed","serverParsed","clientKeys","serverKeys","overlap","allIssues","combinedError","merged","createPublicEnv","validated","_clearEnvCacheForTests","index_default"],"mappings":"gFAiCO,IAAMA,CAAAA,CAAW,CAKtB,OAAA,CAAS,IAAMC,CAAAA,CAAE,MAAA,EAAO,CAAE,SAAA,CAAWC,CAAAA,EAAgB,CACnD,IAAMC,CAAAA,CAAaD,CAAAA,CAAI,WAAA,EAAY,CAAE,IAAA,EAAK,CAC1C,GAAI,CAAC,MAAA,CAAQ,GAAA,CAAK,KAAA,CAAO,IAAI,CAAA,CAAE,QAAA,CAASC,CAAU,CAAA,CAAG,OAAO,KAAA,CAC5D,GAAI,CAAC,OAAA,CAAS,GAAA,CAAK,IAAA,CAAM,KAAK,EAAE,QAAA,CAASA,CAAU,CAAA,CAAG,OAAO,MAAA,CAC7D,MAAM,IAAI,KAAA,CAAM,CAAA,uBAAA,EAA0BD,CAAG,CAAA,6DAAA,CAA+D,CAC9G,CAAC,CAAA,CAKD,MAAA,CAAQ,IAAMD,EAAE,MAAA,EAAO,CAAE,SAAA,CAAWC,CAAAA,EAAgB,CAClD,IAAME,CAAAA,CAAM,MAAA,CAAOF,CAAG,CAAA,CACtB,GAAI,KAAA,CAAME,CAAG,CAAA,CACX,MAAM,IAAI,KAAA,CAAM,CAAA,gBAAA,EAAmBF,CAAG,CAAA,CAAE,CAAA,CAE1C,OAAOE,CACT,CAAC,CAAA,CAKD,OAAA,CAAS,IAAMH,CAAAA,CAAE,MAAA,EAAO,CAAE,SAAA,CAAWC,CAAAA,EAAgB,CACnD,IAAME,CAAAA,CAAM,QAAA,CAASF,CAAAA,CAAK,EAAE,CAAA,CAC5B,GAAI,KAAA,CAAME,CAAG,CAAA,CACX,MAAM,IAAI,KAAA,CAAM,CAAA,iBAAA,EAAoBF,CAAG,CAAA,CAAE,EAE3C,OAAOE,CACT,CAAC,CAAA,CAKD,GAAA,CAAK,IAAMH,CAAAA,CAAE,MAAA,EAAO,CAAE,GAAA,EAAI,CAK1B,KAAA,CAAO,IAAMA,CAAAA,CAAE,MAAA,EAAO,CAAE,OAAM,CAK9B,IAAA,CAAM,IAAMA,CAAAA,CAAE,MAAA,EAAO,CAAE,SAAA,CAAWC,CAAAA,EAAgB,CAChD,IAAMG,CAAAA,CAAO,QAAA,CAASH,CAAAA,CAAK,EAAE,CAAA,CAC7B,GAAI,KAAA,CAAMG,CAAI,CAAA,EAAKA,CAAAA,CAAO,CAAA,EAAKA,CAAAA,CAAO,KAAA,CACpC,MAAM,IAAI,KAAA,CAAM,CAAA,qBAAA,EAAwBH,CAAG,CAAA,6BAAA,CAA+B,CAAA,CAE5E,OAAOG,CACT,CAAC,CAAA,CAKD,IAAA,CAAUC,CAAAA,EAA0BL,CAAAA,CAAE,MAAA,EAAO,CAAE,SAAA,CAAWC,CAAAA,EAAgB,CACxE,GAAI,CACF,IAAMK,CAAAA,CAAS,IAAA,CAAK,KAAA,CAAML,CAAG,EAC7B,OAAII,CAAAA,CACKA,CAAAA,CAAO,KAAA,CAAMC,CAAM,CAAA,CAErBA,CACT,CAAA,KAAgB,CACd,MAAM,IAAI,KAAA,CAAM,CAAA,cAAA,EAAiBL,CAAG,CAAA,CAAE,CACxC,CACF,CAAC,CAAA,CAKD,IAAA,CAAM,CAACM,CAAAA,CAAY,GAAA,GAAQP,CAAAA,CAAE,MAAA,EAAO,CAAE,SAAA,CAAWC,CAAAA,EAC/CA,CAAAA,CAAI,KAAA,CAAMM,CAAS,CAAA,CAAE,GAAA,CAAIC,CAAAA,EAAKA,CAAAA,CAAE,IAAA,EAAM,CAAA,CAAE,MAAA,CAAO,OAAO,CACxD,CAAA,CAKA,MAAA,CAAQ,CAACC,CAAAA,CAAY,EAAA,GAAOT,CAAAA,CAAE,MAAA,EAAO,CAAE,IAAIS,CAAAA,CAAW,CACpD,OAAA,CAAS,CAAA,wBAAA,EAA2BA,CAAS,CAAA,gBAAA,CAC/C,CAAC,CACH,EAKA,SAASC,CAAAA,CAAgBC,CAAAA,CAAsC,CAC7D,GAAI,CAACC,UAAAA,CAAWD,CAAe,CAAA,CAC7B,OAAO,IAAI,GAAA,CAGb,GAAI,CACF,IAAME,CAAAA,CAAUC,YAAAA,CAAaH,CAAAA,CAAiB,OAAO,CAAA,CAC/CI,CAAAA,CAAe,IAAI,GAAA,CAEzB,OAAAF,EAAQ,KAAA,CAAM;AAAA,CAAI,EAAE,OAAA,CAAQG,CAAAA,EAAQ,CAClC,IAAMC,CAAAA,CAAUD,EAAK,IAAA,EAAK,CAC1B,GAAIC,CAAAA,EAAW,CAACA,EAAQ,UAAA,CAAW,GAAG,GAAK,CAACA,CAAAA,CAAQ,WAAW,IAAI,CAAA,CAAG,CACpE,GAAM,CAACC,CAAG,CAAA,CAAID,EAAQ,KAAA,CAAM,GAAG,EAC3BC,CAAAA,EAAO,CAACA,EAAI,QAAA,CAAS,GAAG,GAC1BH,CAAAA,CAAa,GAAA,CAAIG,EAAI,IAAA,EAAM,EAE/B,CACF,CAAC,CAAA,CAEMH,CACT,OAASI,CAAAA,CAAO,CACd,eAAQ,IAAA,CAAK,CAAA,6BAAA,EAAsBR,CAAe,CAAA,CAAA,CAAA,CAAKQ,CAAK,EACrD,IAAI,GACb,CACF,CAKA,SAASC,EACPC,CAAAA,CACAC,CAAAA,CACAC,EACM,CACN,IAAMC,EAAwB,EAAC,CAE/B,QAAWC,CAAAA,IAAeH,CAAAA,CACpB,CAACD,CAAAA,CAAII,CAAW,GAAK,CAACF,CAAAA,EACxBC,EAAY,IAAA,CAAKC,CAAW,EAIhC,GAAID,CAAAA,CAAY,OAAS,CAAA,CASvB,MARc,IAAI,KAAA,CAChB,CAAA;AAAA,EACGA,EAAY,GAAA,CAAIE,CAAAA,EAAW,OAAOA,CAAO,CAAA,CAAE,EAAE,IAAA,CAAK;AAAA,CAAI,CAAC;;AAAA;AAAA;AAAA;AAAA,gEAAA,CAK5D,CAGJ,CAKA,SAASC,CAAAA,CAAgBR,EAAmBS,CAAAA,CAAgC,CAC1E,IAAMC,CAAAA,CAAiBV,EAAM,MAAA,CAAO,GAAA,CAAIW,CAAAA,EAAS,CAE/C,IAAMJ,CAAAA,CAAU,KAAA,CAAM,OAAA,CAAQI,CAAAA,CAAM,IAAI,CAAA,CAAIA,CAAAA,CAAM,IAAA,CAAK,IAAA,CAAK,GAAG,CAAA,CAAI,SAAA,CAE/DC,CAAAA,CAAkBD,CAAAA,CAAM,QAGxBA,CAAAA,CAAM,OAAA,CAAQ,QAAA,CAAS,aAAa,EACtCC,CAAAA,EAAmB;AAAA,8DAAA,CAAA,CACVD,CAAAA,CAAM,OAAA,CAAQ,QAAA,CAAS,WAAW,EAC3CC,CAAAA,EAAmB;AAAA,8CAAA,CAAA,CACVD,CAAAA,CAAM,OAAA,CAAQ,QAAA,CAAS,gBAAgB,EAChDC,CAAAA,EAAmB;AAAA,gEAAA,CAAA,CACVD,CAAAA,CAAM,OAAA,CAAQ,QAAA,CAAS,cAAc,EAC9CC,CAAAA,EAAmB;AAAA,oCAAA,CAAA,CACVD,CAAAA,CAAM,OAAA,CAAQ,QAAA,CAAS,mBAAmB,EACnDC,CAAAA,EAAmB;AAAA,yEAAA,CAAA,CACVD,CAAAA,CAAM,OAAA,CAAQ,QAAA,CAAS,eAAe,EAC/CC,CAAAA,EAAmB;AAAA,iEAAA,CAAA,CACVD,CAAAA,CAAM,OAAA,CAAQ,QAAA,CAAS,iBAAiB,EACjDC,CAAAA,EAAmB;AAAA,qEAAA,CAAA,CACVD,CAAAA,CAAM,OAAA,CAAQ,QAAA,CAAS,gBAAgB,EAChDC,CAAAA,EAAmB;AAAA,iCAAA,CAAA,CACVD,CAAAA,CAAM,OAAA,CAAQ,QAAA,CAAS,iBAAiB,EACjDC,CAAAA,EAAmB;AAAA,kCAAA,CAAA,CACVD,CAAAA,CAAM,OAAA,CAAQ,QAAA,CAAS,cAAc,IAC9CC,CAAAA,EAAmB;AAAA,oDAAA,CAAA,CAAA,CAIrB,IAAM1B,CAAAA,CAASuB,CAAAA,CAAQF,CAAO,CAAA,CAC9B,GAAIrB,GAAUA,CAAAA,YAAkBL,CAAAA,CAAE,OAAA,CAAS,CACzC,IAAMgC,CAAAA,CAAU3B,CAAAA,CAAO,SAAS,IAAA,CAAK,IAAI,EACzC0B,CAAAA,EAAmB;AAAA,0BAAA,EAAwBC,CAAO,GACpD,CAGA,OAAIF,EAAM,OAAA,CAAQ,QAAA,CAAS,iBAAiB,CAAA,GAC1CC,CAAAA,EAAmB;AAAA,6FAAA,CAAA,CAAA,CAGd,CACL,GAAGD,CAAAA,CACH,OAAA,CAASC,CACX,CACF,CAAC,CAAA,CAED,OAAO,IAAI/B,CAAAA,CAAE,SAAS6B,CAAc,CACtC,CAKA,SAASI,CAAAA,CACPC,CAAAA,CACAb,EACAc,CAAAA,CACiF,CAEjF,IAAMC,CAAAA,CAAmC,EAAC,CAC1C,QAAWlB,CAAAA,IAAO,MAAA,CAAO,IAAA,CAAKgB,CAAS,CAAA,CACjCb,CAAAA,CAAIH,CAAG,CAAA,GAAM,MAAA,GACfkB,CAAAA,CAAYlB,CAAG,CAAA,CAAIG,CAAAA,CAAIH,CAAG,CAAA,CAAA,CAM9B,IAAMZ,CAAAA,CADSN,CAAAA,CAAE,MAAA,CAAOkC,CAAS,EACX,SAAA,CAAUE,CAAW,CAAA,CAC3C,OAAI9B,CAAAA,CAAO,OAAA,CACF,CAAE,OAAA,CAAS,IAAA,CAAM,IAAA,CAAMA,CAAAA,CAAO,IAAuB,CAAA,CAErD,CAAE,OAAA,CAAS,KAAA,CAAO,KAAA,CAAOA,CAAAA,CAAO,KAAM,CAEjD,CAEA,IAAI+B,CAAAA,CAAiB,IAAA,CACjBC,CAAAA,CAA8B,IAAA,CAQ3B,SAASC,EAIdC,CAAAA,CACAR,CAAAA,CAAsB,EAAC,CACK,CAC5B,IAAMS,EAAS,IAAA,CAAK,SAAA,CAAU,OAAA,CAAQ,GAAG,CAAA,CACzC,GAAIJ,GAAaC,CAAAA,GAAiBG,CAAAA,CAChC,OAAOJ,CAAAA,CAGT,GAAM,CAAE,OAAAK,CAAAA,CAAQ,MAAA,CAAAC,CAAO,CAAA,CAAIH,CAAAA,CACrB,CACJ,YAAAI,CAAAA,CAAc,cAAA,CACd,wBAAA,CAAArB,CAAAA,CAA2B,KAAA,CAC3B,MAAA,CAAAY,EAAS,KAAA,CACT,KAAA,CAAAU,EAAQ,KAAA,CACR,iBAAA,CAAAC,EAAqB3B,CAAAA,EAAU,CAC7B,OAAA,CAAQ,KAAA,CAAM,qDAAA,CAAkDA,CAAAA,CAAM,MAAM,CAAA,CAC5E,OAAA,CAAQ,IAAA,CAAK,CAAC,EAChB,CACF,EAAIa,CAAAA,CAGEV,CAAAA,CAAcZ,CAAAA,CAAgBkC,CAAW,CAAA,CAmB/C,GAfE,CAACrB,CAAAA,EACD,OAAA,CAAQ,GAAA,CAAI,QAAA,GAAa,YAAA,EACzBD,CAAAA,CAAY,KAAO,CAAA,EAEnB,OAAA,CAAQ,IAAA,CACN,uJACF,CAAA,CAIEA,CAAAA,CAAY,KAAO,CAAA,EACrBF,CAAAA,CAAuB,OAAA,CAAQ,GAAA,CAAKE,CAAAA,CAAaC,CAAwB,EAIvE,OAAA,CAAQ,GAAA,CAAI,QAAA,GAAa,YAAA,CAC3B,IAAA,IAAWL,CAAAA,IAAO,QAAQ,GAAA,CACpB,OAAO,OAAA,CAAQ,GAAA,CAAIA,CAAG,CAAA,CAAM,KAC9B,OAAA,CAAQ,IAAA,CAAK,CAAA,0BAAA,EAAmBA,CAAG,CAAA,eAAA,CAAiB,CAAA,CAM1D,IAAM6B,CAAAA,CAAed,CAAAA,CAAeS,CAAAA,CAAQ,OAAA,CAAQ,GAAW,EACzDM,CAAAA,CAAef,CAAAA,CAAeU,CAAAA,CAAQ,OAAA,CAAQ,GAAW,EAGzDM,CAAAA,CAAa,MAAA,CAAO,IAAA,CAAKP,CAAM,CAAA,CAC/BQ,CAAAA,CAAa,OAAO,IAAA,CAAKP,CAAM,CAAA,CAC/BQ,CAAAA,CAAUF,CAAAA,CAAW,MAAA,CAAO/B,GAAOgC,CAAAA,CAAW,QAAA,CAAShC,CAAG,CAAC,CAAA,CAEjE,GAAIiC,EAAQ,MAAA,CAAS,CAAA,CAMnB,MALc,IAAI,KAAA,CAChB,CAAA;AAAA,EACGA,EAAQ,GAAA,CAAIjC,CAAAA,EAAO,OAAOA,CAAG,CAAA,6CAAA,CAA+C,EAAE,IAAA,CAAK;AAAA,CAAI,CAAC;;AAAA,+EAAA,CAE7F,CAAA,CAKF,GAAI6B,CAAAA,CAAa,OAAA,GAAY,KAAA,EAASC,CAAAA,CAAa,OAAA,GAAY,KAAA,CAAO,CACpE,IAAMI,CAAAA,CAAY,CAChB,GAAIL,CAAAA,CAAa,OAAA,CAAU,EAAC,CAAIA,CAAAA,CAAa,KAAA,CAAM,MAAA,CACnD,GAAIC,CAAAA,CAAa,OAAA,CAAU,EAAC,CAAIA,CAAAA,CAAa,KAAA,CAAM,MACrD,CAAA,CACMK,CAAAA,CAAgB,IAAIrD,CAAAA,CAAE,QAAA,CAASoD,CAAS,CAAA,CAC9C,OAAAN,CAAAA,CAAkBnB,CAAAA,CAAgB0B,CAAAA,CAAe,CAAE,GAAGX,CAAAA,CAAQ,GAAGC,CAAO,CAAC,CAAC,CAAA,CACnE,EACT,CAGA,IAAMW,CAAAA,CAAS,CACb,GAAGP,CAAAA,CAAa,IAAA,CAChB,GAAGC,CAAAA,CAAa,IAClB,CAAA,CAGA,OAAIH,CAAAA,EAAS,OAAA,CAAQ,GAAA,CAAI,QAAA,GAAa,YAAA,EACpC,OAAA,CAAQ,GAAA,CAAI,2BAAA,CAA6BS,CAAM,CAAA,CAGjDjB,CAAAA,CAAYiB,CAAAA,CACZhB,CAAAA,CAAeG,CAAAA,CACRa,CACT,CAQO,SAASC,CAAAA,CACdlD,CAAAA,CACA2B,CAAAA,CAAsB,EAAC,CACD,CACtB,IAAMS,CAAAA,CAAS,IAAA,CAAK,SAAA,CAAU,OAAA,CAAQ,GAAG,CAAA,CACzC,GAAIJ,CAAAA,EAAaC,CAAAA,GAAiBG,CAAAA,CAChC,OAAOJ,CAAAA,CAET,IAAMmB,CAAAA,CAAYjB,CAAAA,CAAU,CAAE,MAAA,CAAQlC,CAAAA,CAAQ,MAAA,CAAQ,EAAG,CAAA,CAAG2B,CAAO,CAAA,CACnE,OAAAK,CAAAA,CAAYmB,CAAAA,CACZlB,CAAAA,CAAeG,CAAAA,CACRe,CACT,CAEO,SAASC,CAAAA,EAAyB,CACvCpB,CAAAA,CAAY,IAAA,CACZC,CAAAA,CAAe,KACjB,KAKOoB,CAAAA,CAAQnB","file":"index.mjs","sourcesContent":["import { z } from 'zod'\nimport { readFileSync, existsSync } from 'fs'\n\n// Type alias for cleaner code\ntype EnvSchema = Record<string, z.ZodTypeAny>\n\nexport interface EnvConfig<TClient extends EnvSchema, TServer extends EnvSchema> {\n client: TClient\n server: TServer\n}\n\nexport interface EnvOptions {\n exampleFile?: string\n allowMissingInProduction?: boolean\n onValidationError?: (error: z.ZodError) => void\n strict?: boolean\n debug?: boolean\n}\n\ntype InferClient<T extends EnvSchema> = {\n [K in keyof T]: z.infer<T[K]>\n}\n\ntype InferServer<T extends EnvSchema> = {\n [K in keyof T]: z.infer<T[K]>\n}\n\ntype InferEnv<TClient extends EnvSchema, TServer extends EnvSchema> = \n InferClient<TClient> & InferServer<TServer>\n\n/**\n * Zod 4 utility functions for common environment variable patterns\n */\nexport const envUtils = {\n /**\n * Creates a boolean environment variable parser with improved Zod 4 validation\n * Accepts 'true', 'false', '1', '0', 'yes', 'no' (case insensitive)\n */\n boolean: () => z.string().transform((val: string) => {\n const normalized = val.toLowerCase().trim()\n if (['true', '1', 'yes', 'on'].includes(normalized)) return true\n if (['false', '0', 'no', 'off'].includes(normalized)) return false\n throw new Error(`Invalid boolean value: ${val}. Expected 'true'/'false', '1'/'0', 'yes'/'no', or 'on'/'off'`)\n }),\n\n /**\n * Creates a number environment variable parser with Zod 4's improved validation\n */\n number: () => z.string().transform((val: string) => {\n const num = Number(val)\n if (isNaN(num)) {\n throw new Error(`Invalid number: ${val}`)\n }\n return num\n }),\n\n /**\n * Creates an integer environment variable parser\n */\n integer: () => z.string().transform((val: string) => {\n const num = parseInt(val, 10)\n if (isNaN(num)) {\n throw new Error(`Invalid integer: ${val}`)\n }\n return num\n }),\n\n /**\n * Creates a URL environment variable parser with Zod 4's improved URL validation\n */\n url: () => z.string().url(),\n\n /**\n * Creates an email environment variable parser with Zod 4's improved email validation\n */\n email: () => z.string().email(),\n\n /**\n * Creates a port number parser (1-65535)\n */\n port: () => z.string().transform((val: string) => {\n const port = parseInt(val, 10)\n if (isNaN(port) || port < 1 || port > 65535) {\n throw new Error(`Invalid port number: ${val}. Must be between 1 and 65535`)\n }\n return port\n }),\n\n /**\n * Creates a JSON environment variable parser with Zod 4's improved error handling\n */\n json: <T>(schema?: z.ZodType<T>) => z.string().transform((val: string) => {\n try {\n const parsed = JSON.parse(val)\n if (schema) {\n return schema.parse(parsed)\n }\n return parsed\n } catch (error) {\n throw new Error(`Invalid JSON: ${val}`)\n }\n }),\n\n /**\n * Creates a comma-separated list parser\n */\n list: (separator = ',') => z.string().transform((val: string) => \n val.split(separator).map(s => s.trim()).filter(Boolean)\n ),\n\n /**\n * Creates a secret key parser with minimum length validation\n */\n secret: (minLength = 32) => z.string().min(minLength, {\n message: `Secret must be at least ${minLength} characters long`\n }),\n}\n\n/**\n * Parses .env.example file and returns a set of required variable names\n */\nfunction parseEnvExample(exampleFilePath: string): Set<string> {\n if (!existsSync(exampleFilePath)) {\n return new Set()\n }\n\n try {\n const content = readFileSync(exampleFilePath, 'utf-8')\n const requiredVars = new Set<string>()\n\n content.split('\\n').forEach(line => {\n const trimmed = line.trim()\n if (trimmed && !trimmed.startsWith('#') && !trimmed.startsWith('//')) {\n const [key] = trimmed.split('=')\n if (key && !key.includes(' ')) {\n requiredVars.add(key.trim())\n }\n }\n })\n\n return requiredVars\n } catch (error) {\n console.warn(`⚠️ Could not read ${exampleFilePath}:`, error)\n return new Set()\n }\n}\n\n/**\n * Validates that all required variables from .env.example are present\n */\nfunction validateAgainstExample(\n env: Record<string, string | undefined>,\n exampleVars: Set<string>,\n allowMissingInProduction: boolean\n): void {\n const missingVars: string[] = []\n \n for (const requiredVar of exampleVars) {\n if (!env[requiredVar] && !allowMissingInProduction) {\n missingVars.push(requiredVar)\n }\n }\n\n if (missingVars.length > 0) {\n const error = new Error(\n `❌ Missing required environment variables:\\n` +\n `${missingVars.map(varName => ` - ${varName}`).join('\\n')}\\n\\n` +\n `💡 To fix this:\\n` +\n ` 1. Copy .env.example to .env\\n` +\n ` 2. Fill in the missing values\\n` +\n ` 3. Or set allowMissingInProduction: true if these are optional`\n )\n throw error\n }\n}\n\n/**\n * Enhances Zod 4 error messages with more helpful context\n */\nfunction enhanceZodError(error: z.ZodError, schemas: EnvSchema): z.ZodError {\n const enhancedIssues = error.issues.map(issue => {\n // Handle nested paths for complex validation\n const varName = Array.isArray(issue.path) ? issue.path.join('.') : 'unknown'\n \n let enhancedMessage = issue.message\n \n // Add helpful hints based on error code and message (Zod 4 has improved error messages)\n if (issue.message.includes('Invalid URL')) {\n enhancedMessage += `\\n💡 Expected a valid URL (e.g., https://api.example.com)`\n } else if (issue.message.includes('too small')) {\n enhancedMessage += `\\n💡 Check the minimum length requirement`\n } else if (issue.message.includes('Invalid option')) {\n enhancedMessage += `\\n💡 Check the allowed values for this environment variable`\n } else if (issue.message.includes('Invalid JSON')) {\n enhancedMessage += `\\n💡 Expected valid JSON format`\n } else if (issue.message.includes('unrecognized keys')) {\n enhancedMessage += `\\n💡 Remove unknown environment variables or add them to your schema`\n } else if (issue.message.includes('Invalid email')) {\n enhancedMessage += `\\n💡 Expected a valid email address (e.g., user@example.com)`\n } else if (issue.message.includes('Invalid boolean')) {\n enhancedMessage += `\\n💡 Expected 'true'/'false', '1'/'0', 'yes'/'no', or 'on'/'off'`\n } else if (issue.message.includes('Invalid number')) {\n enhancedMessage += `\\n💡 Expected a valid number`\n } else if (issue.message.includes('Invalid integer')) {\n enhancedMessage += `\\n💡 Expected a valid integer`\n } else if (issue.message.includes('Invalid port')) {\n enhancedMessage += `\\n💡 Expected a port number between 1 and 65535`\n }\n \n // Add enum options if available (Zod 4 has improved enum handling)\n const schema = schemas[varName]\n if (schema && schema instanceof z.ZodEnum) {\n const options = schema.options?.join(', ')\n enhancedMessage += `\\n💡 Allowed values: ${options}`\n }\n \n // Add transform hints for common patterns\n if (issue.message.includes('Expected string')) {\n enhancedMessage += `\\n💡 Environment variables are always strings. Use transforms to convert to other types.`\n }\n \n return {\n ...issue,\n message: enhancedMessage\n }\n })\n \n return new z.ZodError(enhancedIssues)\n}\n\n/**\n * Validates a schema and returns the parsed result or collects issues\n */\nfunction validateSchema<T extends EnvSchema>(\n schemaObj: T,\n env: Record<string, any>,\n strict: boolean\n): { success: true; data: InferClient<T> } | { success: false; error: z.ZodError } {\n // Only include keys that are actually set (not undefined)\n const filteredEnv: Record<string, any> = {}\n for (const key of Object.keys(schemaObj)) {\n if (env[key] !== undefined) {\n filteredEnv[key] = env[key]\n }\n // If not present or undefined, omit the key so Zod applies default/optional\n }\n \n const schema = z.object(schemaObj)\n const parsed = schema.safeParse(filteredEnv)\n if (parsed.success) {\n return { success: true, data: parsed.data as InferClient<T> }\n } else {\n return { success: false, error: parsed.error }\n }\n}\n\nlet _envCache: any = null\nlet _envCacheKey: string | null = null\n\n/**\n * Creates a type-safe environment variable validator with support for nested objects and transforms\n * @param config - Configuration object with client and server schemas\n * @param options - Optional configuration for validation behavior\n * @returns Validated environment variables with full type inference\n */\nexport function createEnv<\n TClient extends EnvSchema,\n TServer extends EnvSchema\n>(\n config: EnvConfig<TClient, TServer>,\n options: EnvOptions = {}\n): InferEnv<TClient, TServer> {\n const envKey = JSON.stringify(process.env)\n if (_envCache && _envCacheKey === envKey) {\n return _envCache\n }\n\n const { client, server } = config\n const { \n exampleFile = '.env.example', \n allowMissingInProduction = false,\n strict = false,\n debug = false,\n onValidationError = (error) => {\n console.error('❌ [envfortress] Invalid environment variables:', error.issues)\n process.exit(1)\n }\n } = options\n\n // Parse .env.example if it exists\n const exampleVars = parseEnvExample(exampleFile)\n \n // Warn about production enforcement\n if (\n !allowMissingInProduction &&\n process.env.NODE_ENV === 'production' &&\n exampleVars.size > 0\n ) {\n console.warn(\n '⚠️ You are enforcing .env.example validation in production. Consider setting `allowMissingInProduction: true` if this causes deploy issues.'\n )\n }\n \n // Validate against .env.example first\n if (exampleVars.size > 0) {\n validateAgainstExample(process.env, exampleVars, allowMissingInProduction)\n }\n\n // Warn on undefined process.env keys in development\n if (process.env.NODE_ENV !== 'production') {\n for (const key in process.env) {\n if (typeof process.env[key] === 'undefined') {\n console.warn(`⚠️ process.env[\"${key}\"] is undefined`)\n }\n }\n }\n\n // Validate client and server schemas separately using helper\n const clientParsed = validateSchema(client, process.env, strict)\n const serverParsed = validateSchema(server, process.env, strict)\n\n // Check for overlap between client and server schemas\n const clientKeys = Object.keys(client)\n const serverKeys = Object.keys(server)\n const overlap = clientKeys.filter(key => serverKeys.includes(key))\n \n if (overlap.length > 0) {\n const error = new Error(\n `❌ Environment variable overlap detected:\\n` +\n `${overlap.map(key => ` - ${key} is defined in both client and server schemas`).join('\\n')}\\n\\n` +\n `💡 Each variable should only be defined in one schema (client or server)`\n )\n throw error\n }\n\n // Handle validation errors\n if (clientParsed.success === false || serverParsed.success === false) {\n const allIssues = [\n ...(clientParsed.success ? [] : clientParsed.error.issues),\n ...(serverParsed.success ? [] : serverParsed.error.issues),\n ]\n const combinedError = new z.ZodError(allIssues)\n onValidationError(enhanceZodError(combinedError, { ...client, ...server }))\n return {} as InferEnv<TClient, TServer>\n }\n\n // Merge client and server data\n const merged = {\n ...clientParsed.data,\n ...serverParsed.data,\n } as InferEnv<TClient, TServer>\n\n // Debug log if enabled\n if (debug && process.env.NODE_ENV !== 'production') {\n console.log('[envfortress] Parsed env:', merged)\n }\n\n _envCache = merged\n _envCacheKey = envKey\n return merged\n}\n\n/**\n * Creates a public-only environment validator (for client-side only)\n * @param schema - Schema for public environment variables\n * @param options - Optional configuration\n * @returns Validated environment variables\n */\nexport function createPublicEnv<TClient extends EnvSchema>(\n schema: TClient,\n options: EnvOptions = {}\n): InferClient<TClient> {\n const envKey = JSON.stringify(process.env)\n if (_envCache && _envCacheKey === envKey) {\n return _envCache as InferClient<TClient>\n }\n const validated = createEnv({ client: schema, server: {} }, options) as InferClient<TClient>\n _envCache = validated\n _envCacheKey = envKey\n return validated\n}\n\nexport function _clearEnvCacheForTests() {\n _envCache = null;\n _envCacheKey = null;\n}\n\n// Re-export Zod and inferred types for convenience\nexport { z }\nexport type { InferClient, InferServer, InferEnv, EnvSchema }\nexport default createEnv\n"]}