dotenvxjs
Version:
dotenvx is the official Node.js library for .envx files, offering advanced type validation, intelligent interpolation, and conditional logic. It makes environment management safer, more dynamic, and easier to maintain.
5 lines • 8.31 kB
JavaScript
import A from'fs';import J from'path';var s=class extends Error{constructor(n){super(n),this.name="EnvError";}};var S={fileDoesNotExist:e=>`[envx:error] The specified file does not exist: ${e}`,unsupportedType:(e,n)=>`[envx:error] Line ${e+1}: Unsupported type "${n}".`,validator:{invalidNumber:(e,n)=>`Invalid value for "${e}": expected a number but received "${n}".`,invalidBoolean:(e,n)=>`Invalid boolean for "${e}": expected "true" or "false" but received "${n}".`,missingEnumValues:e=>`Enum values are not defined for "${e}". Please specify allowed values.`,invalidEnum:(e,n,r)=>`Invalid enum value for "${e}": expected one of [${r.join(", ")}] but received "${n}".`,invalidEmail:(e,n)=>`Invalid email format for "${e}": received "${n}". Please provide a valid email address.`,invalidUrl:(e,n)=>`Invalid URL format for "${e}": received "${n}". Please provide a valid URL.`,unsupportedType:(e,n)=>`Unsupported type "${n}" specified for "${e}".`,requiredMissing:e=>`Required environment variable "${e}" is missing and has no default value.`},resolver:{circularDependency:e=>`[envx:error] Circular dependency detected while resolving "${e}".
\u2192 Check for variables referencing each other in a loop.`,interpolationFailed:e=>`[envx:error] Failed to resolve variable "${e}". No value found for interpolation.`,requiredMissingInResolve:e=>`[envx:error] Environment variable "${e}" is not set and no default is defined.
\u2192 Define "${e}" in your .envx file or provide a default value in the schema.`},parser:{schemaBlockEmpty:e=>`[envx:error] Line ${e+1}: Schema block name cannot be empty.`,schemaKeyDuplicate:(e,n)=>`[envx:error] Line ${e+1}: Schema key "${n}" is already defined. Duplicate schema definitions are not allowed.`,typeAlreadyDefined:e=>`[envx:error] Line ${e+1}: "type" is already defined for this variable. You cannot redefine it.`,unknownSchemaProperty:(e,n)=>`[envx:error] Line ${e+1}: Unknown schema property "${n}".`,missingDelimiter:e=>`[envx:error] Line ${e+1}: Missing "=" delimiter in assignment.`,variableNameEmpty:e=>`[envx:error] Line ${e+1}: Variable name cannot be empty.`,typeRequiredBeforeDefault:e=>`[envx:error] Line ${e+1}: "type" must be defined before "default".`,defaultMustBeNumber:e=>`[envx:error] Line ${e+1}: Default value must be a valid number.`,defaultMustBeBoolean:e=>`[envx:error] Line ${e+1}: Default value must be "true" or "false".`,defaultNotInEnum:(e,n)=>`[envx:error] Line ${e+1}: Default "${n}" is not in enum values.`,invalidValuesJson:e=>`[envx:error] Line ${e+1}: "values" must be a valid JSON string array.`}};var l={lib:S};var w=["string","number","boolean","enum","email","url"],b={};function x(e){let n=e.split(/\r?\n/),r=[],o={},i=null,t=null,a=[];for(let u=0;u<n.length;u++){let f=n[u],c=f.trim();if(!c||c.startsWith("#"))continue;if(t){c.endsWith('"""')?(a.push(c.slice(0,-3)),r.push({key:t,value:a.join(`
`)}),t=null,a=[]):a.push(f);continue}if(q(c)){i=N(c,u,o);continue}if(i){O(c,u,o[i]);continue}let{key:v,value:p,isMultilineStart:E}=_(c,u);E?(t=v,a.push(p)):r.push({key:v,value:p});}return {env:r,schema:o}}function N(e,n,r){let o=e.slice(1,-1).trim();if(!o)throw new s(l.lib.parser.schemaBlockEmpty(n));if(r[o])throw new s(l.lib.parser.schemaKeyDuplicate(n,o));return r[o]=r[o]??{},o}function O(e,n,r){let[o,i]=$(e,n),t=o.trim(),a=R(i.trim());switch(t){case "required":r.required=a==="true";break;case "type":if(r.type!==void 0)throw new s(l.lib.parser.typeAlreadyDefined(n));if(!w.includes(a))throw new s(l.lib.unsupportedType(n,a));r.type=a;break;case "default":P(a,n,r);break;case "values":try{let u=JSON.parse(a);if(!Array.isArray(u)||!u.every(f=>typeof f=="string"))throw new s(`[envx:error] Line ${n+1}: The parsed "values" field must be an array of strings. Please ensure the JSON array contains only string elements.`);r.values=u;}catch{throw new s(l.lib.parser.invalidValuesJson(n))}break;case "deprecated":r.deprecated=a;break;case "description":r.description=a;break;default:throw new s(l.lib.parser.unknownSchemaProperty(n,t))}}function q(e){return /^\[.*\]$/.test(e)}function P(e,n,r){let{type:o,values:i}=r;if(!o)throw new s(l.lib.parser.typeRequiredBeforeDefault(n));switch(o){case "number":let t=Number(e);if(isNaN(t))throw new s(l.lib.parser.defaultMustBeNumber(n));r.default=t;break;case "boolean":if(e!=="true"&&e!=="false")throw new s(l.lib.parser.defaultMustBeBoolean(n));r.default=e==="true";break;case "enum":if(!i||!i.includes(e))throw new s(l.lib.parser.defaultNotInEnum(n,e));r.default=e;break;default:r.default=e;}}function _(e,n){let[r,o]=$(e,n),i=r.trim();if(!i)throw new s(l.lib.parser.variableNameEmpty(n));let t=o.trim();return t.startsWith('"""')?{key:i,value:t.slice(3),isMultilineStart:true}:{key:i,value:R(t),isMultilineStart:false}}function $(e,n){let r=e.indexOf("=");if(r===-1)throw new s(l.lib.parser.missingDelimiter(n));return [e.slice(0,r),e.slice(r+1)]}function R(e){let n=e[0],r=e[e.length-1];return n==='"'&&r==='"'||n==="'"&&r==="'"?e.slice(1,-1):e}function M(e){if(typeof e=="boolean")return e;if(typeof e=="string"){if(e.toLowerCase()==="true")return true;if(e.toLowerCase()==="false")return false}return typeof e=="number"?e===1:false}var L=e=>/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e);function U(e){return typeof e=="boolean"?"boolean":!isNaN(Number(e))&&e!==""&&e!==null?"number":"string"}function k(e,n){let r={...b,...n};return e.forEach(({key:o,value:i})=>{r[o]||(r[o]={type:U(i),required:false});}),r}function T(e){return {...b,...e||{}}}function V(e,n,r,o){switch(r){case "string":return n;case "number":{let i=Number(n);if(isNaN(i))throw new s(l.lib.validator.invalidNumber(e,n));return i}case "boolean":{let i=n.trim().toLowerCase();if(i==="true")return true;if(i==="false")return false;throw new s(l.lib.validator.invalidBoolean(e,n))}case "enum":{if(!o?.length)throw new s(l.lib.validator.missingEnumValues(e));if(!o.includes(n))throw new s(l.lib.validator.invalidEnum(e,n,o));return n}case "email":{if(!L(n))throw new s(l.lib.validator.invalidEmail(e,n));return n}case "url":try{return new URL(n),n}catch{throw new s(l.lib.validator.invalidUrl(e,n))}default:throw new s(l.lib.validator.unsupportedType(e,r))}}function B(e,n){let r={},o=new Map(e.map(({key:i,value:t})=>[i,t]));for(let i in n){let{type:t,required:a,default:u,values:f}=n[i],c=o.get(i);if(c!==void 0){r[i]=V(i,c,t,t==="enum"?f:void 0);continue}if(u!==void 0){r[i]=V(i,String(u),t,t==="enum"?f:void 0);continue}if(a)throw new s(l.lib.validator.requiredMissing(i))}for(let[i,t]of o.entries())i in r||(r[i]=t);return r}function D(e,n){let r={},o={};for(let t of e)r[t.key]=t.value;if(n)for(let t in n)r[t]===void 0&&n[t].default!==void 0&&(r[t]=n[t].default);let i=new Set;for(let t of Object.keys(r))o[t]=y(r[t],r,o,i,t);if(n){for(let t in n)if(n[t].required&&r[t]===void 0)throw new s(l.lib.resolver.requiredMissingInResolve(t))}return o}function y(e,n,r,o,i){if(typeof e!="string")return e;if(o.has(i))throw new s(l.lib.resolver.circularDependency(i));return o.add(i),G(e)&&(e=j(e,n,r,o)),F(e)&&(e=C(e,n,r,o)),o.delete(i),e}function F(e){return typeof e=="string"&&e.includes("${")}function C(e,n,r,o){return e.replace(/\${([^}]+)}/g,(i,t)=>{let a=t.trim();if(r[a]!==void 0)return String(r[a]);if(n[a]!==void 0)return String(y(n[a],n,r,o,a));throw new s(l.lib.resolver.interpolationFailed(a))})}function G(e){return /^\s*\${[^}]+}(?:\s*(==|!=)\s*"[^"]*")?\s*\?\s*"[^"]*"\s*:\s*"[^"]*"\s*$/.test(e.trim())}function j(e,n,r,o){let t=e.trim().match(/^\s*\${([^}]+)}(?:\s*(==|!=)\s*"([^"]*)")?\s*\?\s*"([^"]*)"\s*:\s*"([^"]*)"\s*$/);if(!t)return e;let[,a,u,f,c,v]=t,p=a.trim(),E=r[p]!==void 0?r[p]:y(n[p],n,r,o,p),h;if(u==="=="||u==="!="){let g=String(E)===f;h=u==="=="?g:!g;}else h=M(E);return h?c:v}function I(e,n){let{env:r,schema:o}=x(e),i=k(r,o),t=n?{...i,...n}:Object.keys(i).length>0?i:void 0,a=D(r,t);return t?{result:B(Object.entries(a).map(([f,c])=>({key:f,value:c})),t),schema:t}:{result:a,schema:t}}function H(e=".envx",n){let r=J.resolve(process.cwd(),e);if(!A.existsSync(r))throw new s(l.lib.fileDoesNotExist(r));let o=A.readFileSync(r,{encoding:"utf-8"}),i=T(n);return I(o,i).result}function Ue(e=".envx",n){let r=H(e,n);return Object.entries(r).forEach(([o,i])=>{process.env[o]=String(i);}),r}export{H as getEnvx,Ue as loadEnvx};//# sourceMappingURL=index.mjs.map
//# sourceMappingURL=index.mjs.map