typenvx
Version:
Typenv is a modern, type-safe alternative to .env files, providing advanced validation, intelligent interpolation, and conditional logic for better environment management.
6 lines • 12.6 kB
JavaScript
import le,{existsSync,readFileSync}from'fs';import F from'path';var s=class extends Error{constructor(n){super(n),this.name="EnvxError";}};var O={fileDoesNotExist:e=>`The specified file does not exist: ${e}`,unsupportedType:(e,n)=>`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=>`Circular dependency detected while resolving "${e}".
\u2192 Check for variables referencing each other in a loop.`,interpolationFailed:(e,n="interpolation")=>`Failed to resolve variable "${e}". No value found for ${n}.`,requiredMissingInResolve:e=>`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=>`Line ${e+1}: Schema block name cannot be empty.`,schemaKeyDuplicate:(e,n)=>`Line ${e+1}: Schema key "${n}" is already defined. Duplicate schema definitions are not allowed.`,typeAlreadyDefined:e=>`Line ${e+1}: "type" is already defined for this variable. You cannot redefine it.`,unknownSchemaProperty:(e,n)=>`Line ${e+1}: Unknown schema property "${n}".`,missingDelimiter:e=>`Line ${e+1}: Missing "=" delimiter in assignment.`,variableNameEmpty:e=>`Line ${e+1}: Variable name cannot be empty.`,typeRequiredBeforeDefault:e=>`Line ${e+1}: "type" must be defined before "default".`,defaultMustBeNumber:e=>`Line ${e+1}: Default value must be a valid number.`,defaultMustBeBoolean:e=>`Line ${e+1}: Default value must be "true" or "false".`,defaultNotInEnum:(e,n)=>`Line ${e+1}: Default "${n}" is not in enum values.`,invalidValuesJson:e=>`Line ${e+1}: "values" must be a valid JSON string array.`}};var c={lib:O,cli:{}};var S=/^\s*\$\{([^}]+)\}(?:\s*(==|!=)\s*(["'])?([^"']+)\3)?\s*\?\s*(["'])?([^"']+?)\5?\s*:\s*(["'])?([^"']+?)\7?\s*$/,P=/^[a-zA-Z0-9_\-\.]+$/,U=/^\[.*\]$/,B=/^[a-zA-Z0-9_\-\.]+$/;function C(e){return P.test(e)}function G(e){return U.test(e)}var q=(e,n)=>{let r=[",",";",":",".","-","?","!","|","\\","/","(",")","{","<",">","`","~","="],t=e.trimEnd().slice(-1);if(r.includes(t))throw new s(`[envx:error] Line ${n+1}: Value ends with a suspicious character (${t}). Please remove unnecessary character at the end.`)};function _(e,n,r){let{type:t,values:i}=r;if(!t)throw new s(c.lib.parser.typeRequiredBeforeDefault(n));switch(t){case "number":let o=Number(e);if(isNaN(o))throw new s(c.lib.parser.defaultMustBeNumber(n));r.default=o;break;case "boolean":if(e!=="true"&&e!=="false")throw new s(c.lib.parser.defaultMustBeBoolean(n));r.default=e==="true";break;case "enum":if(!i||!i.includes(e))throw new s(c.lib.parser.defaultNotInEnum(n,e));r.default=e;break;default:r.default=e;}}function L(e,n){let r=e.indexOf("=");if(r===-1)throw new s(c.lib.parser.missingDelimiter(n));return [e.slice(0,r),e.slice(r+1)]}function $(e){let n=e[0],r=e[e.length-1];return n==='"'&&r==='"'||n==="'"&&r==="'"?e.slice(1,-1):e}function H(e){let n=false,r=false;for(let t=0;t<e.length;t++){let i=e[t];if(i==='"'&&!n)r=!r;else if(i==="'"&&!r)n=!n;else if(i==="#"&&!n&&!r)return t}return -1}function J(e,n,r,t){let i=e.trim();if(i.includes('"""')){let o=i.indexOf('"""'),u=i.slice(0,o);return r.push(u.trimEnd()),t.push({key:n,value:r.join(`
`)}),{multilineKey:null,multilineBuffer:[]}}else return r.push(e),{multilineKey:n,multilineBuffer:r}}var K=["string","number","boolean","enum","email","url"],W={};function X(e,n,r,t){let[i,o]=L(e,n),u=i.trim(),a=$(o.trim());if(u==="description"&&o.trim().startsWith('"""')){let l=[],f=o.trim().slice(3);if(f.endsWith('"""')){r.description=f.slice(0,-3);return}for(l.push(f);t;){let d=t();if(d===null)throw new s(`Line ${n+1}: Unterminated multiline description.`);if(d.endsWith('"""')){l.push(d.slice(0,-3));break}else l.push(d);}r.description=l.join(`
`);return}switch(u){case "required":r.required=a==="true";break;case "type":if(r.type!==void 0)throw new s(c.lib.parser.typeAlreadyDefined(n));if(!K.includes(a))throw new s(c.lib.unsupportedType(n,a));r.type=a;break;case "default":r.type=="enum"&&!r.values?r.__pendingDefault=a:_(a,n,r);break;case "values":try{let l=JSON.parse(a);if(!(Array.isArray(l)&&l.length<=25&&l.every(d=>typeof d=="string"&&C(d))))throw new s(`[envx:error] Line ${n+1}: "values" must be a JSON array (max 25) of safe strings. Allowed characters: a-z, A-Z, 0-9, _, -, .`);r.values=l,r.__pendingDefault&&_(r.__pendingDefault,n,r);}catch{throw new s(c.lib.parser.invalidValuesJson(n))}break;case "deprecated":r.deprecated=a;break;case "description":r.description=a;break;default:throw new s(c.lib.parser.unknownSchemaProperty(n,u))}}function j(e,n,r){let t=e.slice(1,-1).trim();if(!t)throw new s(c.lib.parser.schemaBlockEmpty(n));if(!B.test(t))throw new s(`[envx:error] Line ${n+1}: Schema block name "${t}" contains invalid characters. Only letters, numbers, underscore (_), hyphen (-), and dot (.) are allowed.`);if(r[t])throw new s(c.lib.parser.schemaKeyDuplicate(n,t));return r[t]=r[t]??{},t}function Y(e,n){let[r,t]=L(e,n),i=r.trim();if(!i)throw new s(c.lib.parser.variableNameEmpty(n));let o=t.trim();return o.startsWith('"""')?o.endsWith('"""')&&o.length>6?{key:i,value:o.slice(3,-3),isMultilineStart:false}:{key:i,value:o.slice(3),isMultilineStart:true}:(q(o,n),{key:i,value:$(o),isMultilineStart:false})}function V(e){let n=e.split(/\r?\n/),r=[],t={},i=null,o=null,u=[];for(let a=0;a<n.length;a++){let l=n[a],f=l.trim();if(!f||f.startsWith("#"))continue;if(o){let E=J(l,o,u,r);u=E.multilineBuffer,o=E.multilineKey;continue}let d=H(l);if(d!==-1&&(l=l.slice(0,d)),f=l.trim(),G(f)){if(d>-1&&d>l.indexOf("]"))throw new s(`[envx:error] Line ${a+1}: Schema header line cannot contain inline comments.`);i=j(f,a,t);continue}if(i){X(f,a,t[i],()=>(a++,a>=n.length?null:n[a].trim()));continue}let{key:v,value:g,isMultilineStart:y}=Y(f,a);y?(o=v,u.push(g)):r.push({key:v,value:g});}if(o)throw new s(`[envx:error] End of file reached but multiline value for key "${o}" was not closed with triple quotes (""").`);return {env:r,schema:t}}function h(e,n,r=false,t=false){let i=typeof e=="string"?e.toLowerCase():e,o=i===true||i===false||i==="true"||i==="false"||i===1||i===0;if(t)return o;if(!o){if(r)throw new s(c.lib.validator.invalidBoolean(n||"",e));return false}return i===true||i==="true"||i===1}var z=e=>/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e);function ge(e){return h(e,"",false,true)?"boolean":!isNaN(Number(e))&&e!==""&&e!==null?"number":"string"}function Z(e,n){let r={...W,...n};return e.forEach(({key:t,value:i})=>{r[t]||(r[t]={type:ge(i),required:false});}),r}function M(e){return F.isAbsolute(e)?e:F.resolve(process.cwd(),e)}function ee(e,n,r,t){switch(r){case "string":return n;case "number":{let i=Number(n);if(isNaN(i))throw new s(c.lib.validator.invalidNumber(e,n));return i}case "boolean":return h(n,e,true);case "enum":{if(!t?.length)throw new s(c.lib.validator.missingEnumValues(e));if(!t.includes(n))throw new s(c.lib.validator.invalidEnum(e,n,t));return n}case "email":{if(!z(n))throw new s(c.lib.validator.invalidEmail(e,n));return n}case "url":try{return new URL(n),n}catch{throw new s(c.lib.validator.invalidUrl(e,n))}default:throw new s(c.lib.validator.unsupportedType(e,r))}}function T(e,n){let r={},t=new Map(e.map(({key:i,value:o})=>[i,o]));for(let i in n){let{type:o,required:u,default:a,values:l}=n[i],f=t.get(i);if(f!==void 0){r[i]=ee(i,f,o,o==="enum"?l:void 0);continue}if(a!==void 0){r[i]=ee(i,String(a),o,o==="enum"?l:void 0);continue}if(u)throw new s(c.lib.validator.requiredMissing(i))}for(let[i,o]of t.entries())i in r||(r[i]=o);return r}var ye=new Set(["NODE_ENV","CI","VERCEL_ENV"]);function ne(e){if(ye.has(e)&&typeof process<"u"&&process.env)return process.env[e]}function b(e,n,r){let{env:t,schema:i}=V(e),o=Z(t,i),u=n?{...o,...n}:Object.keys(o).length>0?o:void 0,a=A(t,u),l=Object.fromEntries(Object.entries(a).filter(([f])=>u?.[f]!==void 0));return u&&true?{result:T(Object.entries(l).map(([d,v])=>({key:d,value:v})),u),schema:u,rawEnv:[]}:{result:l,schema:u,rawEnv:t}}function re(e){let n=e.trim();return S.test(n)}function te(e){return typeof e=="string"&&e.includes("${")}function oe(e,n,r,t,i,o="interpolation"){if(r[e]!==void 0)return r[e];if(n[e]!==void 0)return se(n[e],n,r,t,e,i+1);{let u=ne(e);if(u!==void 0)return r[e]=u,u;throw new s(c.lib.resolver.interpolationFailed(e,o))}}function A(e,n){let r={},t={};for(let o of e)r[o.key]=o.value;if(n)for(let o in n)r[o]===void 0&&n[o].default!==void 0&&(r[o]=n[o].default);let i=new Set;for(let o of Object.keys(r))t[o]=se(r[o],r,t,i,o,0);if(n){for(let o in n)if(n[o].required&&r[o]===void 0)throw new s(c.lib.resolver.requiredMissingInResolve(o))}return t}function se(e,n,r,t,i,o=0){if(typeof e!="string")return e;if(t.has(i))throw new s(c.lib.resolver.circularDependency(i));if(o>10)throw new s(`[envx:error] Max resolution depth exceeded while resolving "${i}".`);t.add(i);let a=e,l;for(;typeof a=="string";){if(l=a,re(a)){a=be(a,n,r,t,o+1);continue}if(te(a)){a=Se(a,n,r,t,o+1);continue}if(a===l)break}return t.delete(i),a}function Se(e,n,r,t,i){return e.replace(/\${([^}]+)}/g,(o,u)=>{let a=u.trim();return String(oe(a,n,r,t,i))})}function be(e,n,r,t,i){let u=(e?.trim()).match(S);if(!u)return e;let[,a,l,,f,,d,,v]=u,g=a?.trim(),y=oe(g,n,r,t,i,"ternary"),E=f?.trim();(E?.startsWith('"')&&E?.endsWith('"')||E?.startsWith("'")&&E?.endsWith("'"))&&(E=E.slice(1,-1));let ve=String(y),R;if(l==="=="||l==="!="){let D=ve===E;R=l==="=="?D:!D;}else R=h(y);let he=d.trim(),xe=v.trim();return ie(R?he:xe)}function ie(e){return e==="true"?true:e==="false"?false:isNaN(Number(e))?e:Number(e)}var w="envx.meta.json",N="envx.config.json",ae=".env",I=".envx";function ue(e){let n=e??F.join(process.cwd(),w);if(!le.existsSync(n))throw new Error(`Meta file not found at ${n}`);let r=le.readFileSync(n,"utf-8");try{return JSON.parse(r)}catch(t){throw new Error(`Invalid JSON in ${n}: ${t.message}`)}}var x={input:"./.envx",outputs:{env:"./.env",types:"./types/envx.ts",metaFilePath:"."},overwrite:false};function $e(e){return e&&typeof e=="object"&&typeof e.input=="string"&&(e.outputs===void 0||typeof e.outputs=="object"&&(e.outputs.env===void 0||typeof e.outputs.env=="string")&&(e.outputs.types===void 0||typeof e.outputs.types=="string")&&(e.outputs.metaFilePath===void 0||typeof e.outputs.metaFilePath=="string"))&&(typeof e.overwrite=="boolean"||e.overwrite===void 0)}function ce(){let e=F.resolve(N);if(existsSync(e)){let n=readFileSync(e,"utf-8");try{let r=JSON.parse(n);return $e(r)?{...x,...r,outputs:{...x.outputs,...r.outputs}}:(console.warn("[envx:info:config] Invalid config structure, falling back to default."),x)}catch(r){let t=r instanceof Error?r.message:String(r);return console.warn(`[envx:warn:config] Could not parse ${N}: ${t}`),x}}return x}var Ve=["envx"],Me=e=>{let n=F.basename(e);if(n===".envx"||n.startsWith(".envx.")||n.endsWith(".envx"))return true;let r=F.extname(n).replace(/^\./,"");return !!Ve.includes(r)};function fe(e){if(!Me(e)){let n=F.extname(e).replace(/^\./,"");throw new Error(`[Typenv Error] Unsupported file extension '.${n}'. Please use a '.envx' file or a valid variant such as '.envx.local'.`)}}var k=ce();function de(e){try{if(!le.existsSync(e))throw new s(c.lib.fileDoesNotExist(e));return le.readFileSync(e,"utf-8")}catch(n){let r=n instanceof Error?n.message:String(n);throw new s(`Failed to read file at ${e}: ${r}`)}}function Te(e=ae){let n=F.join(k?.outputs?.metaFilePath||".",w),r=F.resolve(process.cwd(),n),t=k?.outputs?.env||e,i=M(t),o=de(i),u;try{u=ue(r);}catch(l){let f=l instanceof Error?l.message:String(l);throw new s(`Failed to parse meta schema file: ${f}`)}return b(o,u).result}function Ee(e=I,n){let r=k?.input||e,t=M(r);fe(t);let i=de(t);return b(i,n).result}function Ae(e=I,n){let r=Ee(e,n);return Object.entries(r).forEach(([t,i])=>{process.env[t]=String(i);}),r}export{s as EnvxError,Te as getEnv,Ee as getEnvx,Ae as loadEnvx};//# sourceMappingURL=index.mjs.map
//# sourceMappingURL=index.mjs.map