@stacksjs/env 0.72.57 → 0.72.61

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/cli.js CHANGED
@@ -1,25 +1,25 @@
1
1
  // @bun
2
- import{createCipheriv as m,createDecipheriv as n,createHash as b,createPrivateKey as Af,createPublicKey as v,diffieHellman as o,generateKeyPairSync as s,hkdfSync as i,randomBytes as N}from"crypto";var K="x25519-public:",k="x25519-private:",F="encrypted:v2:",d="encrypted:",y=Buffer.from("stacks-env:v2","utf8"),p="Environment decryption failed: authentication or format error";function l(g){if(g.length===32)return g;return b("sha256").update(g).digest()}function Mf(g,E){let f=N(16),$=m("aes-256-gcm",l(E),f);return{ciphertext:Buffer.concat([$.update(g,"utf8"),$.final()]).toString("hex"),iv:f.toString("hex"),authTag:$.getAuthTag().toString("hex")}}function _f(g,E,f,$){try{let B=n("aes-256-gcm",l(E),Buffer.from(f,"hex"));return B.setAuthTag(Buffer.from($,"hex")),Buffer.concat([B.update(Buffer.from(g,"hex")),B.final()]).toString("utf8")}catch{throw Error("Decryption failed: authentication or format error")}}function x(g){return g.toString("base64url")}function h(g,E,f){if(typeof g!=="string"||g.length>0&&!/^[A-Za-z0-9_-]+$/.test(g))throw Error(`${E} is not canonical base64url`);let $=Buffer.from(g,"base64url");if(x($)!==g)throw Error(`${E} is not canonical base64url`);if(f!==void 0&&$.length!==f)throw Error(`${E} has an invalid length`);return $}function Tf(g){if(!g.startsWith(K))throw Error("Legacy or invalid public key. Run buddy env:rotate before creating new ciphertext.");let E=v({key:h(g.slice(K.length),"public key"),format:"der",type:"spki"});if(E.asymmetricKeyType!=="x25519")throw Error("Public key is not X25519");return E}function If(g){if(!g.startsWith(k))throw Error("Private key is not a version 2 X25519 key");let E=Af({key:h(g.slice(k.length),"private key"),format:"der",type:"pkcs8"});if(E.asymmetricKeyType!=="x25519")throw Error("Private key is not X25519");return E}function a(g){return Buffer.from(`stacks-env:v2;${g.epk};${g.salt};${g.nonce}`,"utf8")}function M(){let{publicKey:g,privateKey:E}=s("x25519"),f=g.export({format:"der",type:"spki"}),$=E.export({format:"der",type:"pkcs8"});return{publicKey:`${K}${x(f)}`,privateKey:`${k}${x($)}`}}function S(g,E){let f=Tf(E),$=s("x25519"),B=x($.publicKey.export({format:"der",type:"spki"})),w=N(16),I=N(12),D=o({privateKey:$.privateKey,publicKey:f}),_=Buffer.from(i("sha256",D,w,y,32)),T={epk:B,salt:x(w),nonce:x(I)};try{let A=m("aes-256-gcm",_,I);A.setAAD(a(T));let X=Buffer.concat([A.update(g,"utf8"),A.final()]),O={v:2,...T,ciphertext:x(X),tag:x(A.getAuthTag())};return`${F}${x(Buffer.from(JSON.stringify(O),"utf8"))}`}finally{D.fill(0),_.fill(0)}}function Df(g){let E=h(g.slice(F.length),"envelope").toString("utf8"),f=JSON.parse(E);if(!f||typeof f!=="object"||Array.isArray(f))throw Error("envelope is not an object");let $=Object.keys(f).sort(),B=["ciphertext","epk","nonce","salt","tag","v"];if($.length!==B.length||$.some((w,I)=>w!==B[I]))throw Error("envelope fields are invalid");if(f.v!==2)throw Error("envelope version is unsupported");for(let w of["epk","salt","nonce","ciphertext","tag"])if(typeof f[w]!=="string")throw Error(`${w} is invalid`);return f}function Xf(g,E){try{let f=Df(g),$=If(E),B=v({key:h(f.epk,"ephemeral public key"),format:"der",type:"spki"});if(B.asymmetricKeyType!=="x25519")throw Error("ephemeral key is not X25519");let w=h(f.salt,"salt",16),I=h(f.nonce,"nonce",12),D=h(f.ciphertext,"ciphertext"),_=h(f.tag,"tag",16),T=o({privateKey:$,publicKey:B}),A=Buffer.from(i("sha256",T,w,y,32));try{let X=n("aes-256-gcm",A,I);return X.setAAD(a(f)),X.setAuthTag(_),Buffer.concat([X.update(D),X.final()]).toString("utf8")}finally{T.fill(0),A.fill(0)}}catch{throw Error(p)}}function Of(g,E){try{if(!/^[0-9a-f]{64}$/.test(E))throw Error("invalid legacy private key");let f=Buffer.from(g.slice(d.length),"base64");if(f.length<32)throw Error("invalid legacy payload");let $=b("sha256").update(Buffer.from(E,"hex")).digest(),B=b("sha256").update($).digest();return _f(f.subarray(32).toString("hex"),B,f.subarray(0,16).toString("hex"),f.subarray(16,32).toString("hex"))}catch{throw Error(p)}}function Cf(g){return g.startsWith(d)&&!g.startsWith(F)}function C(g,E){if(!g.startsWith(d))return g;return g.startsWith(F)?Xf(g,E):Of(g,E)}function Ff(g,E,f){return S(C(g,E),f)}function Sf(g){let E=g.match(/^DOTENV_PRIVATE_KEY_(.+)$/);return E&&E[1]?E[1].toLowerCase():""}function Pf(g=""){return g?process.env[`DOTENV_PRIVATE_KEY_${g.toUpperCase()}`]:process.env.DOTENV_PRIVATE_KEY}import{spawnSync as Rf}from"child_process";import{readFileSync as Yf}from"fs";import{arch as Hf,hostname as Wf,platform as e,release as jf,userInfo as Gf}from"os";import{basename as Jf,dirname as Uf,resolve as zf}from"path";function L(g,E={}){let f={},$=[],B=[],w=g.split(`
3
- `);for(let I of w){let D=I.trim();if(!D||D.startsWith("#"))continue;if(D.startsWith("DOTENV_PUBLIC_KEY=")){let X=D.match(/^DOTENV_PUBLIC_KEY=["']?([^"'\n]+)["']?/);if(X&&X[1]!==void 0)f.DOTENV_PUBLIC_KEY=X[1];continue}let _=D.match(/^([^=]+)=(.*)$/);if(!_||_[1]===void 0||_[2]===void 0)continue;let T=_[1].trim(),A=_[2].trim();if(A.startsWith('"')&&A.endsWith('"')||A.startsWith("'")&&A.endsWith("'")){if(A=A.slice(1,-1),A.includes("\\n"))A=A.replace(/\\n/g,`
4
- `)}if(A.startsWith("encrypted:")||A.startsWith("enc:")){if(!E.privateKey){B.push(T);continue}try{let X=A.startsWith("enc:")?`encrypted:${A.slice(4)}`:A;A=C(X,E.privateKey)}catch(X){$.push(`Failed to decrypt ${T}: ${X instanceof Error?X.message:"Unknown error"}`)}}A=Zf(A,{...E.processEnv||process.env,...f}),A=hf(A),f[T]=A}return{parsed:f,errors:$,skippedEncrypted:B}}function Zf(g,E){return g.replace(/\$\{([^}]+)\}/g,(f,$)=>{let B=$.match(/^([^:\-+]+)(:-|-)(.+)$/);if(B){let[,I,D,_]=B,T=E[I];if(D===":-")return T||_;else return T!==void 0?T:_}let w=$.match(/^([^:\-+]+)(:?\+)(.+)$/);if(w){let[,I,D,_]=w,T=E[I];if(D===":+")return T?_:"";else return T!==void 0?_:""}return E[$]||""})}var Qf=new Set(["date","hostname","whoami","uname","pwd","echo","printf","cat","basename","dirname"]);function xf(g){let E=[],f="",$=null,B=!1;for(let w of g.trim()){if(B){f+=w,B=!1;continue}if(w==="\\"&&$!=="'"){B=!0;continue}if($){if(w===$)$=null;else f+=w;continue}if(w==='"'||w==="'"){$=w;continue}if(/\s/.test(w)){if(f)E.push(f),f="";continue}f+=w}if(B||$)throw Error("Command substitution contains an unterminated escape or quote");if(f)E.push(f);return E}function Vf(g){let[E,...f]=g,$=f.some((B)=>B.startsWith("-"));if(E==="echo"&&!$)return f.join(" ");if(E==="printf"&&f.length===1&&!f[0]?.includes("%"))return f[0];if(E==="hostname"&&f.length===0)return Wf();if(E==="whoami"&&f.length===0)return Gf().username;if(E==="pwd"&&f.length===0)return process.cwd();if(E==="basename"&&f.length===1)return Jf(f[0]??"");if(E==="dirname"&&f.length===1)return Uf(f[0]??"");if(E==="cat"&&f.length>0&&!$)return f.map((B)=>Yf(zf(process.cwd(),B),"utf8")).join("");if(E==="uname"){let B=f[0]??"-s";if(f.length<=1&&B==="-m")return Hf();if(f.length<=1&&B==="-r")return jf();if(f.length<=1&&B==="-s")return{aix:"AIX",android:"Android",cygwin:"CYGWIN",darwin:"Darwin",freebsd:"FreeBSD",haiku:"Haiku",linux:"Linux",netbsd:"NetBSD",openbsd:"OpenBSD",sunos:"SunOS",win32:"Windows_NT"}[e()]??e()}return}function hf(g){return g.replace(/\$\(([^)]+)\)/g,(E,f)=>{try{let $=xf(f),B=$[0];if(!B||!Qf.has(B))return console.warn(`[env] Blocked command substitution for disallowed command: ${B}`),"";let w=Vf($);if(w!==void 0)return w.trim();let I=Rf(B,$.slice(1),{encoding:"utf8",env:process.env});if(I.status===0)return I.stdout.trim();let D=I.stderr.trim();console.warn(`[env] Command substitution failed (exit code ${I.status??"unknown"}): ${B}${D?`: ${D}`:""}`)}catch($){console.warn(`[env] Command substitution error: ${$ instanceof Error?$.message:String($)}`)}return""})}async function df(g,E={}){let f={},$=[],B=[];for(let w of g)try{let I=Bun.file(w);if(!I.size)continue;let D=await I.text(),{parsed:_,errors:T,skippedEncrypted:A}=L(D,E);$.push(...T),B.push(...A);for(let[X,O]of Object.entries(_)){if(!E.overload&&f[X]!==void 0)continue;f[X]=O}}catch(I){if(I?.code==="ENOENT")continue;$.push(`Failed to read ${w}: ${I instanceof Error?I.message:String(I)}`)}return{parsed:f,errors:$,skippedEncrypted:B}}import{existsSync as Z,readFileSync as z,renameSync as t,rmSync as ff,writeFileSync as Q}from"fs";import{basename as u,dirname as gf,resolve as U}from"path";function of(g,E){if(g)return g;let f=String(E??"").trim();if(!f||f==="development"||f==="dev"||f==="local")return"";if(!/^[\w.-]+$/.test(f))return"";return`.env.${f}`}function Ef(g,E,f,$){let B=["#/-------------------[DOTENV_PUBLIC_KEY]--------------------/","#/ versioned X25519 encryption for .env files /","#/ [how it works](https://stacksjs.com/encryption) /","#/----------------------------------------------------------/",`${f}="${E}"`,""],w=!1;for(let I of g.split(`
5
- `)){let D=I.trim();if(D.startsWith("#/"))continue;if(D.startsWith("DOTENV_PUBLIC_KEY"))continue;if(!D&&!w)continue;if(!D||D.startsWith("#")){w=!0,B.push(I);continue}w=!0;let _=D.match(/^([^=]+)=(.*)$/);if(!_||_[1]===void 0||_[2]===void 0){B.push(I);continue}let T=_[1].trim(),A=_[2].trim();if(A.startsWith('"')&&A.endsWith('"')||A.startsWith("'")&&A.endsWith("'"))A=A.slice(1,-1);if((!$.key||T.includes($.key))&&(!$.excludeKey||!T.includes($.excludeKey))&&!A.startsWith("encrypted:"))A=S(A,E);B.push(`${T}="${A}"`)}return B.join(`
6
- `)}function P(g){let f=((g||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase();return{publicKeyName:f?`DOTENV_PUBLIC_KEY_${f}`:"DOTENV_PUBLIC_KEY",privateKeyName:f?`DOTENV_PRIVATE_KEY_${f}`:"DOTENV_PRIVATE_KEY"}}function Bf(g,E){let f,$=!1;for(let B of g.split(`
7
- `)){let w=B.trim();if(!w||w.startsWith("#"))continue;let I=w.match(/^([^=]+)=(.*)$/);if(!I?.[1]||I[2]===void 0)continue;let D=I[1].trim(),_=I[2].trim().replace(/^["']|["']$/g,"");if(D===E)f=_;else if(_.startsWith("encrypted:")||_.startsWith("enc:"))$=!0}return f&&$?f:void 0}function sf(g={}){let E=g.cwd||process.cwd(),f=U(E,g.file||".env"),$=U(E,g.keysFile||".env.keys");if(!Z(f))return{success:!1,error:`File not found: ${f}`};try{let B,w,I=Bf(z(f,"utf-8"),P(g.file).publicKeyName);if(I)B=I;else if(Z($)){let T=z($,"utf-8"),{parsed:A}=L(T),{publicKeyName:X,privateKeyName:O}=P(g.file);if(B=A[X]||"",w=A[O]||"",!B||!w){let H=M();B=H.publicKey,w=H.privateKey;let Y=`
8
- ${X}="${B}"
9
- ${O}="${w}"
10
- `;Q($,T+Y,"utf-8")}}else{let T=M();B=T.publicKey,w=T.privateKey;let{publicKeyName:A,privateKeyName:X}=P(g.file),O=`# .env.keys - Keep this file secure and never commit to source control
11
- ${A}="${B}"
12
- ${X}="${w}"
13
- `;Q($,O,"utf-8")}let D=z(f,"utf-8"),_=Ef(D,B,P(g.file).publicKeyName,g);if(g.stdout)return{success:!0,output:_};return Q(f,_,"utf-8"),{success:!0,output:`\u2714 encrypted (${g.file||".env"})
14
- \u2714 key added to ${g.keysFile||".env.keys"}`}}catch(B){return{success:!1,error:`Failed to encrypt: ${B instanceof Error?B.message:"Unknown error"}`}}}function Lf(g={}){let E=g.cwd||process.cwd(),f=U(E,g.file||".env"),$=U(E,g.keysFile||".env.keys");if(!Z(f))return{success:!1,error:`File not found: ${f}`};if(!Z($))return{success:!1,error:`Keys file not found: ${$}`};try{let B=z($,"utf-8"),{parsed:w}=L(B),_=((g.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),T=_?`DOTENV_PRIVATE_KEY_${_}`:"DOTENV_PRIVATE_KEY",A=w[T];if(!A)return{success:!1,error:`Private key not found: ${T}`};let X=[A];if(T!=="DOTENV_PRIVATE_KEY"&&w.DOTENV_PRIVATE_KEY&&w.DOTENV_PRIVATE_KEY!==A)X.push(w.DOTENV_PRIVATE_KEY);let H=z(f,"utf-8").split(`
15
- `),Y=[];for(let V of H){let W=V.trim();if(!W||W.startsWith("#")){Y.push(V);continue}if(W.startsWith("DOTENV_PUBLIC_KEY"))continue;let j=W.match(/^([^=]+)=(.*)$/);if(!j||j[1]===void 0||j[2]===void 0){Y.push(V);continue}let G=j[1].trim(),R=j[2].trim();if(R.startsWith('"')&&R.endsWith('"')||R.startsWith("'")&&R.endsWith("'"))R=R.slice(1,-1);let q=R.startsWith("encrypted:");if(g.key&&!G.includes(g.key))q=!1;if(q){let c,r;for(let $f of X)try{c=C(R,$f);break}catch(wf){r=wf}if(c===void 0)throw r instanceof Error?r:Error(`Unable to decrypt ${G}`);R=c}Y.push(`${G}="${R}"`)}let J=Y.join(`
16
- `);if(g.stdout)return{success:!0,output:J};return Q(f,J,"utf-8"),{success:!0,output:`\u2714 decrypted (${g.file||".env"})`}}catch(B){return{success:!1,error:`Failed to decrypt: ${B instanceof Error?B.message:"Unknown error"}`}}}function yf(g,E,f={}){let $=f.cwd||process.cwd(),B=U($,f.file||".env"),w=U($,f.keysFile||".env.keys");try{let I="";if(Z(B))I=z(B,"utf-8");let D=I.split(`
17
- `),_=!1,T,O=((f.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),H=O?`DOTENV_PUBLIC_KEY_${O}`:"DOTENV_PUBLIC_KEY",Y=O?`DOTENV_PRIVATE_KEY_${O}`:"DOTENV_PRIVATE_KEY";for(let W of D){let j=W.trim();if(j.startsWith(`${H}=`)){let G=j.match(/^[^=]+=["']?([^"'\n]+)["']?/);if(G)T=G[1];break}}if(T){let W=!1;if(Z(w)){let j=z(w,"utf-8"),{parsed:G}=L(j);W=Boolean(G[Y])}if(!W&&!process.env[Y]&&!Bf(I,H))T=void 0}if(!f.plain&&!T){let W=M();T=W.publicKey;let j="";if(Z(w))j=z(w,"utf-8");j+=`
18
- ${H}="${W.publicKey}"
19
- ${Y}="${W.privateKey}"
20
- `,Q(w,j,"utf-8");for(let G=D.length-1;G>=0;G--){let R=D[G];if(R!==void 0&&R.trim().startsWith(`${H}=`))D.splice(G,1)}D.unshift(`${H}="${T}"`)}let J=E;if(!f.plain&&T)J=S(E,T);for(let W=0;W<D.length;W++){let j=D[W];if(j===void 0)continue;if(j.trim().startsWith(`${g}=`)){D[W]=`${g}="${J}"`,_=!0;break}}if(!_)D.push(`${g}="${J}"`);let V=D.join(`
21
- `);return Q(B,V,"utf-8"),{success:!0,output:`set ${g}${f.plain?"":" with encryption"} (${f.file||".env"})`}}catch(I){return{success:!1,error:`Failed to set: ${I instanceof Error?I.message:"Unknown error"}`}}}function pf(g,E={}){let f=E.cwd||process.cwd(),$=U(f,E.file||".env");if(!Z($))return{success:!1,error:`File not found: ${$}`};try{let B,w=U(f,E.keysFile||".env.keys"),_=((E.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),T=_?`DOTENV_PRIVATE_KEY_${_}`:"DOTENV_PRIVATE_KEY";if(Z(w)){let Y=z(w,"utf-8"),{parsed:J}=L(Y);B=J[T]}if(!B)B=process.env[T];let A=z($,"utf-8"),{parsed:X}=L(A,{privateKey:B});if(g&&!E.all){let Y=X[g];if(Y===void 0)return{success:!1,error:`Key not found: ${g}`};return{success:!0,output:Y}}let O=E.all?{...process.env,...X}:X,H;switch(E.format){case"shell":H=Object.entries(O).map(([Y,J])=>`${Y}=${J}`).join(" ");break;case"eval":H=Object.entries(O).map(([Y,J])=>`${Y}="${J}"`).join(`
22
- `);break;case"json":default:H=E.prettyPrint?JSON.stringify(O,null,2):JSON.stringify(O);break}return{success:!0,output:H}}catch(B){return{success:!1,error:`Failed to get: ${B instanceof Error?B.message:"Unknown error"}`}}}function lf(g,E={}){let f=E.cwd||process.cwd(),$=U(f,E.keysFile||".env.keys");if(!Z($))return{success:!1,error:`Keys file not found: ${$}`};try{let B=z($,"utf-8"),{parsed:w}=L(B),_=((E.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),T=_?`DOTENV_PUBLIC_KEY_${_}`:"DOTENV_PUBLIC_KEY",A=_?`DOTENV_PRIVATE_KEY_${_}`:"DOTENV_PRIVATE_KEY";if(g){let H=w[g];if(!H)return{success:!1,error:`Key not found: ${g}`};return{success:!0,output:H}}let X={[T]:w[T],[A]:w[A]};return{success:!0,output:E.format==="shell"?`${T}=${X[T]} ${A}=${X[A]}`:JSON.stringify(X)}}catch(B){return{success:!1,error:`Failed to get keypair: ${B instanceof Error?B.message:"Unknown error"}`}}}function af(g={}){let E=Lf({file:g.file,keysFile:g.keysFile,stdout:!0,cwd:g.cwd});if(!E.success)return E;let f=g.cwd||process.cwd(),$=U(f,g.file||".env"),B=U(f,g.keysFile||".env.keys");if(E.output===void 0)return{success:!1,error:"Rotation produced no decrypted content"};let w=M(),I=z($),D=z(B),_=D.toString("utf8").split(`
23
- `),T=g.file||".env",A=u(T).replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),X=A?`DOTENV_PUBLIC_KEY_${A}`:"DOTENV_PUBLIC_KEY",O=A?`DOTENV_PRIVATE_KEY_${A}`:"DOTENV_PRIVATE_KEY",H=!1,Y=!1;for(let R=0;R<_.length;R++){let q=_[R];if(q===void 0)continue;if(q.startsWith(`${X}=`))_[R]=`${X}="${w.publicKey}"`,H=!0;else if(q.startsWith(`${O}=`))_[R]=`${O}="${w.privateKey}"`,Y=!0}if(!H)_.push(`${X}="${w.publicKey}"`);if(!Y)_.push(`${O}="${w.privateKey}"`);let J=`${_.join(`
2
+ import{createCipheriv as j,createDecipheriv as X,createHash as R,createPrivateKey as ce,createPublicKey as H,diffieHellman as J,generateKeyPairSync as M,hkdfSync as z,randomBytes as W}from"crypto";var S="x25519-public:",Y="x25519-private:",D="encrypted:v2:",A="encrypted:",G=Buffer.from("stacks-env:v2","utf8"),q="Environment decryption failed: authentication or format error";function Z(t){if(t.length===32)return t;return R("sha256").update(t).digest()}function Te(t,n){let e=W(16),s=j("aes-256-gcm",Z(n),e);return{ciphertext:Buffer.concat([s.update(t,"utf8"),s.final()]).toString("hex"),iv:e.toString("hex"),authTag:s.getAuthTag().toString("hex")}}function ae(t,n,e,s){try{let r=X("aes-256-gcm",Z(n),Buffer.from(e,"hex"));return r.setAuthTag(Buffer.from(s,"hex")),Buffer.concat([r.update(Buffer.from(t,"hex")),r.final()]).toString("utf8")}catch{throw Error("Decryption failed: authentication or format error")}}function _(t){return t.toString("base64url")}function P(t,n,e){if(typeof t!=="string"||t.length>0&&!/^[A-Za-z0-9_-]+$/.test(t))throw Error(`${n} is not canonical base64url`);let s=Buffer.from(t,"base64url");if(_(s)!==t)throw Error(`${n} is not canonical base64url`);if(e!==void 0&&s.length!==e)throw Error(`${n} has an invalid length`);return s}function ue(t){if(!t.startsWith(S))throw Error("Legacy or invalid public key. Run buddy env:rotate before creating new ciphertext.");let n=H({key:P(t.slice(S.length),"public key"),format:"der",type:"spki"});if(n.asymmetricKeyType!=="x25519")throw Error("Public key is not X25519");return n}function fe(t){if(!t.startsWith(Y))throw Error("Private key is not a version 2 X25519 key");let n=ce({key:P(t.slice(Y.length),"private key"),format:"der",type:"pkcs8"});if(n.asymmetricKeyType!=="x25519")throw Error("Private key is not X25519");return n}function Q(t){return Buffer.from(`stacks-env:v2;${t.epk};${t.salt};${t.nonce}`,"utf8")}function T(){let{publicKey:t,privateKey:n}=M("x25519"),e=t.export({format:"der",type:"spki"}),s=n.export({format:"der",type:"pkcs8"});return{publicKey:`${S}${_(e)}`,privateKey:`${Y}${_(s)}`}}function F(t,n){let e=ue(n),s=M("x25519"),r=_(s.publicKey.export({format:"der",type:"spki"})),i=W(16),u=W(12),f=J({privateKey:s.privateKey,publicKey:e}),c=Buffer.from(z("sha256",f,i,G,32)),a={epk:r,salt:_(i),nonce:_(u)};try{let o=j("aes-256-gcm",c,u);o.setAAD(Q(a));let l=Buffer.concat([o.update(t,"utf8"),o.final()]),p={v:2,...a,ciphertext:_(l),tag:_(o.getAuthTag())};return`${D}${_(Buffer.from(JSON.stringify(p),"utf8"))}`}finally{f.fill(0),c.fill(0)}}function le(t){let n=P(t.slice(D.length),"envelope").toString("utf8"),e=JSON.parse(n);if(!e||typeof e!=="object"||Array.isArray(e))throw Error("envelope is not an object");let s=Object.keys(e).sort(),r=["ciphertext","epk","nonce","salt","tag","v"];if(s.length!==r.length||s.some((i,u)=>i!==r[u]))throw Error("envelope fields are invalid");if(e.v!==2)throw Error("envelope version is unsupported");for(let i of["epk","salt","nonce","ciphertext","tag"])if(typeof e[i]!=="string")throw Error(`${i} is invalid`);return e}function pe(t,n){try{let e=le(t),s=fe(n),r=H({key:P(e.epk,"ephemeral public key"),format:"der",type:"spki"});if(r.asymmetricKeyType!=="x25519")throw Error("ephemeral key is not X25519");let i=P(e.salt,"salt",16),u=P(e.nonce,"nonce",12),f=P(e.ciphertext,"ciphertext"),c=P(e.tag,"tag",16),a=J({privateKey:s,publicKey:r}),o=Buffer.from(z("sha256",a,i,G,32));try{let l=X("aes-256-gcm",o,u);return l.setAAD(Q(e)),l.setAuthTag(c),Buffer.concat([l.update(f),l.final()]).toString("utf8")}finally{a.fill(0),o.fill(0)}}catch{throw Error(q)}}function de(t,n){try{if(!/^[0-9a-f]{64}$/.test(n))throw Error("invalid legacy private key");let e=Buffer.from(t.slice(A.length),"base64");if(e.length<32)throw Error("invalid legacy payload");let s=R("sha256").update(Buffer.from(n,"hex")).digest(),r=R("sha256").update(s).digest();return ae(e.subarray(32).toString("hex"),r,e.subarray(0,16).toString("hex"),e.subarray(16,32).toString("hex"))}catch{throw Error(q)}}function Oe(t){return t.startsWith(A)&&!t.startsWith(D)}function O(t,n){if(!t.startsWith(A))return t;return t.startsWith(D)?pe(t,n):de(t,n)}function De(t,n,e){return F(O(t,n),e)}function Fe(t){let n=t.match(/^DOTENV_PRIVATE_KEY_(.+)$/);return n&&n[1]?n[1].toLowerCase():""}function Ce(t=""){return t?process.env[`DOTENV_PRIVATE_KEY_${t.toUpperCase()}`]:process.env.DOTENV_PRIVATE_KEY}import{spawnSync as ye}from"child_process";import{readFileSync as ge}from"fs";import{arch as he,hostname as me,platform as ee,release as ve,userInfo as Ee}from"os";import{basename as $e,dirname as ke,resolve as Ke}from"path";function x(t,n={}){let e={},s=[],r=[],i=t.split(`
3
+ `);for(let u of i){let f=u.trim();if(!f||f.startsWith("#"))continue;if(f.startsWith("DOTENV_PUBLIC_KEY=")){let l=f.match(/^DOTENV_PUBLIC_KEY=["']?([^"'\n]+)["']?/);if(l&&l[1]!==void 0)e.DOTENV_PUBLIC_KEY=l[1];continue}let c=f.match(/^([^=]+)=(.*)$/);if(!c||c[1]===void 0||c[2]===void 0)continue;let a=c[1].trim(),o=c[2].trim();if(o.startsWith('"')&&o.endsWith('"')||o.startsWith("'")&&o.endsWith("'")){if(o=o.slice(1,-1),o.includes("\\n"))o=o.replace(/\\n/g,`
4
+ `)}if(o.startsWith("encrypted:")||o.startsWith("enc:")){if(!n.privateKey){r.push(a);continue}try{let l=o.startsWith("enc:")?`encrypted:${o.slice(4)}`:o;o=O(l,n.privateKey)}catch(l){s.push(`Failed to decrypt ${a}: ${l instanceof Error?l.message:"Unknown error"}`)}}o=we(o,{...n.processEnv||process.env,...e}),o=Pe(o),e[a]=o}return{parsed:e,errors:s,skippedEncrypted:r}}function we(t,n){return t.replace(/\$\{([^}]+)\}/g,(e,s)=>{let r=s.match(/^([^:\-+]+)(:-|-)(.+)$/);if(r){let[,u,f,c]=r,a=n[u];if(f===":-")return a||c;else return a!==void 0?a:c}let i=s.match(/^([^:\-+]+)(:?\+)(.+)$/);if(i){let[,u,f,c]=i,a=n[u];if(f===":+")return a?c:"";else return a!==void 0?c:""}return n[s]||""})}var be=new Set(["date","hostname","whoami","uname","pwd","echo","printf","cat","basename","dirname"]);function _e(t){let n=[],e="",s=null,r=!1;for(let i of t.trim()){if(r){e+=i,r=!1;continue}if(i==="\\"&&s!=="'"){r=!0;continue}if(s){if(i===s)s=null;else e+=i;continue}if(i==='"'||i==="'"){s=i;continue}if(/\s/.test(i)){if(e)n.push(e),e="";continue}e+=i}if(r||s)throw Error("Command substitution contains an unterminated escape or quote");if(e)n.push(e);return n}function Ne(t){let[n,...e]=t,s=e.some((r)=>r.startsWith("-"));if(n==="echo"&&!s)return e.join(" ");if(n==="printf"&&e.length===1&&!e[0]?.includes("%"))return e[0];if(n==="hostname"&&e.length===0)return me();if(n==="whoami"&&e.length===0)return Ee().username;if(n==="pwd"&&e.length===0)return process.cwd();if(n==="basename"&&e.length===1)return $e(e[0]??"");if(n==="dirname"&&e.length===1)return ke(e[0]??"");if(n==="cat"&&e.length>0&&!s)return e.map((r)=>ge(Ke(process.cwd(),r),"utf8")).join("");if(n==="uname"){let r=e[0]??"-s";if(e.length<=1&&r==="-m")return he();if(e.length<=1&&r==="-r")return ve();if(e.length<=1&&r==="-s")return{aix:"AIX",android:"Android",cygwin:"CYGWIN",darwin:"Darwin",freebsd:"FreeBSD",haiku:"Haiku",linux:"Linux",netbsd:"NetBSD",openbsd:"OpenBSD",sunos:"SunOS",win32:"Windows_NT"}[ee()]??ee()}return}function Pe(t){return t.replace(/\$\(([^)]+)\)/g,(n,e)=>{try{let s=_e(e),r=s[0];if(!r||!be.has(r))return console.warn(`[env] Blocked command substitution for disallowed command: ${r}`),"";let i=Ne(s);if(i!==void 0)return i.trim();let u=ye(r,s.slice(1),{encoding:"utf8",env:process.env});if(u.status===0)return u.stdout.trim();let f=u.stderr.trim();console.warn(`[env] Command substitution failed (exit code ${u.status??"unknown"}): ${r}${f?`: ${f}`:""}`)}catch(s){console.warn(`[env] Command substitution error: ${s instanceof Error?s.message:String(s)}`)}return""})}async function Ae(t,n={}){let e={},s=[],r=[];for(let i of t)try{let u=Bun.file(i);if(!u.size)continue;let f=await u.text(),{parsed:c,errors:a,skippedEncrypted:o}=x(f,n);s.push(...a),r.push(...o);for(let[l,p]of Object.entries(c)){if(!n.overload&&e[l]!==void 0)continue;e[l]=p}}catch(u){if(u?.code==="ENOENT")continue;s.push(`Failed to read ${i}: ${u instanceof Error?u.message:String(u)}`)}return{parsed:e,errors:s,skippedEncrypted:r}}import{existsSync as w,readFileSync as K,renameSync as U,rmSync as te,writeFileSync as b}from"fs";import{basename as L,dirname as ne,resolve as k}from"path";function Je(t,n){if(t)return t;let e=String(n??"").trim();if(!e||e==="development"||e==="dev"||e==="local")return"";if(!/^[\w.-]+$/.test(e))return"";return`.env.${e}`}function re(t,n,e,s){let r=["#/-------------------[DOTENV_PUBLIC_KEY]--------------------/","#/ versioned X25519 encryption for .env files /","#/ [how it works](https://stacksjs.com/encryption) /","#/----------------------------------------------------------/",`${e}="${n}"`,""],i=!1;for(let u of t.split(`
5
+ `)){let f=u.trim();if(f.startsWith("#/"))continue;if(f.startsWith("DOTENV_PUBLIC_KEY"))continue;if(!f&&!i)continue;if(!f||f.startsWith("#")){i=!0,r.push(u);continue}i=!0;let c=f.match(/^([^=]+)=(.*)$/);if(!c||c[1]===void 0||c[2]===void 0){r.push(u);continue}let a=c[1].trim(),o=c[2].trim();if(o.startsWith('"')&&o.endsWith('"')||o.startsWith("'")&&o.endsWith("'"))o=o.slice(1,-1);if((!s.key||a.includes(s.key))&&(!s.excludeKey||!a.includes(s.excludeKey))&&!o.startsWith("encrypted:"))o=F(o,n);r.push(`${a}="${o}"`)}return r.join(`
6
+ `)}function C(t){let e=((t||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase();return{publicKeyName:e?`DOTENV_PUBLIC_KEY_${e}`:"DOTENV_PUBLIC_KEY",privateKeyName:e?`DOTENV_PRIVATE_KEY_${e}`:"DOTENV_PRIVATE_KEY"}}function se(t,n){let e,s=!1;for(let r of t.split(`
7
+ `)){let i=r.trim();if(!i||i.startsWith("#"))continue;let u=i.match(/^([^=]+)=(.*)$/);if(!u?.[1]||u[2]===void 0)continue;let f=u[1].trim(),c=u[2].trim().replace(/^["']|["']$/g,"");if(f===n)e=c;else if(c.startsWith("encrypted:")||c.startsWith("enc:"))s=!0}return e&&s?e:void 0}function Me(t={}){let n=t.cwd||process.cwd(),e=k(n,t.file||".env"),s=k(n,t.keysFile||".env.keys");if(!w(e))return{success:!1,error:`File not found: ${e}`};try{let r,i,u=se(K(e,"utf-8"),C(t.file).publicKeyName);if(u)r=u;else if(w(s)){let a=K(s,"utf-8"),{parsed:o}=x(a),{publicKeyName:l,privateKeyName:p}=C(t.file);if(r=o[l]||"",i=o[p]||"",!r||!i){let g=T();r=g.publicKey,i=g.privateKey;let y=`
8
+ ${l}="${r}"
9
+ ${p}="${i}"
10
+ `;b(s,a+y,"utf-8")}}else{let a=T();r=a.publicKey,i=a.privateKey;let{publicKeyName:o,privateKeyName:l}=C(t.file),p=`# .env.keys - Keep this file secure and never commit to source control
11
+ ${o}="${r}"
12
+ ${l}="${i}"
13
+ `;b(s,p,"utf-8")}let f=K(e,"utf-8"),c=re(f,r,C(t.file).publicKeyName,t);if(t.stdout)return{success:!0,output:c};return b(e,c,"utf-8"),{success:!0,output:`\u2714 encrypted (${t.file||".env"})
14
+ \u2714 key added to ${t.keysFile||".env.keys"}`}}catch(r){return{success:!1,error:`Failed to encrypt: ${r instanceof Error?r.message:"Unknown error"}`}}}function xe(t={}){let n=t.cwd||process.cwd(),e=k(n,t.file||".env"),s=k(n,t.keysFile||".env.keys");if(!w(e))return{success:!1,error:`File not found: ${e}`};if(!w(s))return{success:!1,error:`Keys file not found: ${s}`};try{let r=K(s,"utf-8"),{parsed:i}=x(r),c=((t.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),a=c?`DOTENV_PRIVATE_KEY_${c}`:"DOTENV_PRIVATE_KEY",o=i[a];if(!o)return{success:!1,error:`Private key not found: ${a}`};let l=[o];if(a!=="DOTENV_PRIVATE_KEY"&&i.DOTENV_PRIVATE_KEY&&i.DOTENV_PRIVATE_KEY!==o)l.push(i.DOTENV_PRIVATE_KEY);let g=K(e,"utf-8").split(`
15
+ `),y=[];for(let N of g){let h=N.trim();if(!h||h.startsWith("#")){y.push(N);continue}if(h.startsWith("DOTENV_PUBLIC_KEY"))continue;let m=h.match(/^([^=]+)=(.*)$/);if(!m||m[1]===void 0||m[2]===void 0){y.push(N);continue}let v=m[1].trim(),d=m[2].trim();if(d.startsWith('"')&&d.endsWith('"')||d.startsWith("'")&&d.endsWith("'"))d=d.slice(1,-1);let V=d.startsWith("encrypted:");if(t.key&&!v.includes(t.key))V=!1;if(V){let B,I;for(let ie of l)try{B=O(d,ie);break}catch(oe){I=oe}if(B===void 0)throw I instanceof Error?I:Error(`Unable to decrypt ${v}`);d=B}y.push(`${v}="${d}"`)}let E=y.join(`
16
+ `);if(t.stdout)return{success:!0,output:E};return b(e,E,"utf-8"),{success:!0,output:`\u2714 decrypted (${t.file||".env"})`}}catch(r){return{success:!1,error:`Failed to decrypt: ${r instanceof Error?r.message:"Unknown error"}`}}}function ze(t,n,e={}){let s=e.cwd||process.cwd(),r=k(s,e.file||".env"),i=k(s,e.keysFile||".env.keys");try{let u="";if(w(r))u=K(r,"utf-8");let f=u.split(`
17
+ `),c=!1,a,p=((e.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),g=p?`DOTENV_PUBLIC_KEY_${p}`:"DOTENV_PUBLIC_KEY",y=p?`DOTENV_PRIVATE_KEY_${p}`:"DOTENV_PRIVATE_KEY";for(let h of f){let m=h.trim();if(m.startsWith(`${g}=`)){let v=m.match(/^[^=]+=["']?([^"'\n]+)["']?/);if(v)a=v[1];break}}if(a){let h=!1;if(w(i)){let m=K(i,"utf-8"),{parsed:v}=x(m);h=Boolean(v[y])}if(!h&&!process.env[y]&&!se(u,g))a=void 0}if(!e.plain&&!a){let h=T();a=h.publicKey;let m="";if(w(i))m=K(i,"utf-8");m+=`
18
+ ${g}="${h.publicKey}"
19
+ ${y}="${h.privateKey}"
20
+ `,b(i,m,"utf-8");for(let v=f.length-1;v>=0;v--){let d=f[v];if(d!==void 0&&d.trim().startsWith(`${g}=`))f.splice(v,1)}f.unshift(`${g}="${a}"`)}let E=n;if(!e.plain&&a)E=F(n,a);for(let h=0;h<f.length;h++){let m=f[h];if(m===void 0)continue;if(m.trim().startsWith(`${t}=`)){f[h]=`${t}="${E}"`,c=!0;break}}if(!c)f.push(`${t}="${E}"`);let N=f.join(`
21
+ `);return b(r,N,"utf-8"),{success:!0,output:`set ${t}${e.plain?"":" with encryption"} (${e.file||".env"})`}}catch(u){return{success:!1,error:`Failed to set: ${u instanceof Error?u.message:"Unknown error"}`}}}function Ge(t,n={}){let e=n.cwd||process.cwd(),s=k(e,n.file||".env");if(!w(s))return{success:!1,error:`File not found: ${s}`};try{let r,i=k(e,n.keysFile||".env.keys"),c=((n.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),a=c?`DOTENV_PRIVATE_KEY_${c}`:"DOTENV_PRIVATE_KEY";if(w(i)){let y=K(i,"utf-8"),{parsed:E}=x(y);r=E[a]}if(!r)r=process.env[a];let o=K(s,"utf-8"),{parsed:l}=x(o,{privateKey:r});if(t&&!n.all){let y=l[t];if(y===void 0)return{success:!1,error:`Key not found: ${t}`};return{success:!0,output:y}}let p=n.all?{...process.env,...l}:l,g;switch(n.format){case"shell":g=Object.entries(p).map(([y,E])=>`${y}=${E}`).join(" ");break;case"eval":g=Object.entries(p).map(([y,E])=>`${y}="${E}"`).join(`
22
+ `);break;case"json":default:g=n.prettyPrint?JSON.stringify(p,null,2):JSON.stringify(p);break}return{success:!0,output:g}}catch(r){return{success:!1,error:`Failed to get: ${r instanceof Error?r.message:"Unknown error"}`}}}function qe(t,n={}){let e=n.cwd||process.cwd(),s=k(e,n.keysFile||".env.keys");if(!w(s))return{success:!1,error:`Keys file not found: ${s}`};try{let r=K(s,"utf-8"),{parsed:i}=x(r),c=((n.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),a=c?`DOTENV_PUBLIC_KEY_${c}`:"DOTENV_PUBLIC_KEY",o=c?`DOTENV_PRIVATE_KEY_${c}`:"DOTENV_PRIVATE_KEY";if(t){let g=i[t];if(!g)return{success:!1,error:`Key not found: ${t}`};return{success:!0,output:g}}let l={[a]:i[a],[o]:i[o]};return{success:!0,output:n.format==="shell"?`${a}=${l[a]} ${o}=${l[o]}`:JSON.stringify(l)}}catch(r){return{success:!1,error:`Failed to get keypair: ${r instanceof Error?r.message:"Unknown error"}`}}}function Ze(t={}){let n=xe({file:t.file,keysFile:t.keysFile,stdout:!0,cwd:t.cwd});if(!n.success)return n;let e=t.cwd||process.cwd(),s=k(e,t.file||".env"),r=k(e,t.keysFile||".env.keys");if(n.output===void 0)return{success:!1,error:"Rotation produced no decrypted content"};let i=T(),u=K(s),f=K(r),c=f.toString("utf8").split(`
23
+ `),a=t.file||".env",o=L(a).replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),l=o?`DOTENV_PUBLIC_KEY_${o}`:"DOTENV_PUBLIC_KEY",p=o?`DOTENV_PRIVATE_KEY_${o}`:"DOTENV_PRIVATE_KEY",g=!1,y=!1;for(let d=0;d<c.length;d++){let V=c[d];if(V===void 0)continue;if(V.startsWith(`${l}=`))c[d]=`${l}="${i.publicKey}"`,g=!0;else if(V.startsWith(`${p}=`))c[d]=`${p}="${i.privateKey}"`,y=!0}if(!g)c.push(`${l}="${i.publicKey}"`);if(!y)c.push(`${p}="${i.privateKey}"`);let E=`${c.join(`
24
24
  `).replace(/\n+$/,"")}
25
- `,V=Ef(E.output,w.publicKey,X,g),W=`${process.pid}-${Date.now()}`,j=U(gf($),`.${u($)}.${W}.rotate`),G=U(gf(B),`.${u(B)}.${W}.rotate`);try{if(Q(j,V,{encoding:"utf8",mode:384}),Q(G,J,{encoding:"utf8",mode:384}),g.stdout)return t(G,B),{success:!0,output:V};t(j,$);try{t(G,B)}catch(R){throw Q($,I),R}return{success:!0,output:`\u2714 rotated (${g.file||".env"}) to encrypted:v2`}}catch(R){if(!Z(B))Q(B,D,{mode:384});return{success:!1,error:`Failed to rotate without exposing plaintext: ${R instanceof Error?R.message:"Unknown error"}`}}finally{ff(j,{force:!0}),ff(G,{force:!0})}}export{yf as setEnv,af as rotateKeypair,Bf as reusableEnvPublicKey,of as resolveEnvFile,lf as getKeypair,pf as getEnv,P as envKeyNames,sf as encryptEnv,Lf as decryptEnv};
25
+ `,N=re(n.output,i.publicKey,l,t),h=`${process.pid}-${Date.now()}`,m=k(ne(s),`.${L(s)}.${h}.rotate`),v=k(ne(r),`.${L(r)}.${h}.rotate`);try{if(b(m,N,{encoding:"utf8",mode:384}),b(v,E,{encoding:"utf8",mode:384}),t.stdout)return U(v,r),{success:!0,output:N};U(m,s);try{U(v,r)}catch(d){throw b(s,u),d}return{success:!0,output:`\u2714 rotated (${t.file||".env"}) to encrypted:v2`}}catch(d){if(!w(r))b(r,f,{mode:384});return{success:!1,error:`Failed to rotate without exposing plaintext: ${d instanceof Error?d.message:"Unknown error"}`}}finally{te(m,{force:!0}),te(v,{force:!0})}}export{xe as decryptEnv,Me as encryptEnv,C as envKeyNames,Ge as getEnv,qe as getKeypair,Je as resolveEnvFile,se as reusableEnvPublicKey,Ze as rotateKeypair,ze as setEnv};
package/dist/crypto.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import{createCipheriv as x,createDecipheriv as w,createHash as l,createPrivateKey as S,createPublicKey as k,diffieHellman as b,generateKeyPairSync as K,hkdfSync as B,randomBytes as d}from"crypto";var E="x25519-public:",m="x25519-private:",y="encrypted:v2:",v="encrypted:",P=Buffer.from("stacks-env:v2","utf8"),A="Environment decryption failed: authentication or format error";function T(e){if(e.length===32)return e;return l("sha256").update(e).digest()}function Y(e,t){let r=d(16),n=x("aes-256-gcm",T(t),r);return{ciphertext:Buffer.concat([n.update(e,"utf8"),n.final()]).toString("hex"),iv:r.toString("hex"),authTag:n.getAuthTag().toString("hex")}}function R(e,t,r,n){try{let i=w("aes-256-gcm",T(t),Buffer.from(r,"hex"));return i.setAuthTag(Buffer.from(n,"hex")),Buffer.concat([i.update(Buffer.from(e,"hex")),i.final()]).toString("utf8")}catch{throw Error("Decryption failed: authentication or format error")}}function s(e){return e.toString("base64url")}function c(e,t,r){if(typeof e!=="string"||e.length>0&&!/^[A-Za-z0-9_-]+$/.test(e))throw Error(`${t} is not canonical base64url`);let n=Buffer.from(e,"base64url");if(s(n)!==e)throw Error(`${t} is not canonical base64url`);if(r!==void 0&&n.length!==r)throw Error(`${t} has an invalid length`);return n}function V(e){if(!e.startsWith(E))throw Error("Legacy or invalid public key. Run buddy env:rotate before creating new ciphertext.");let t=k({key:c(e.slice(E.length),"public key"),format:"der",type:"spki"});if(t.asymmetricKeyType!=="x25519")throw Error("Public key is not X25519");return t}function D(e){if(!e.startsWith(m))throw Error("Private key is not a version 2 X25519 key");let t=S({key:c(e.slice(m.length),"private key"),format:"der",type:"pkcs8"});if(t.asymmetricKeyType!=="x25519")throw Error("Private key is not X25519");return t}function _(e){return Buffer.from(`stacks-env:v2;${e.epk};${e.salt};${e.nonce}`,"utf8")}function H(){let{publicKey:e,privateKey:t}=K("x25519"),r=e.export({format:"der",type:"spki"}),n=t.export({format:"der",type:"pkcs8"});return{publicKey:`${E}${s(r)}`,privateKey:`${m}${s(n)}`}}function I(e,t){let r=V(t),n=K("x25519"),i=s(n.publicKey.export({format:"der",type:"spki"})),o=d(16),f=d(12),g=b({privateKey:n.privateKey,publicKey:r}),h=Buffer.from(B("sha256",g,o,P,32)),u={epk:i,salt:s(o),nonce:s(f)};try{let a=x("aes-256-gcm",h,f);a.setAAD(_(u));let p=Buffer.concat([a.update(e,"utf8"),a.final()]),$={v:2,...u,ciphertext:s(p),tag:s(a.getAuthTag())};return`${y}${s(Buffer.from(JSON.stringify($),"utf8"))}`}finally{g.fill(0),h.fill(0)}}function O(e){let t=c(e.slice(y.length),"envelope").toString("utf8"),r=JSON.parse(t);if(!r||typeof r!=="object"||Array.isArray(r))throw Error("envelope is not an object");let n=Object.keys(r).sort(),i=["ciphertext","epk","nonce","salt","tag","v"];if(n.length!==i.length||n.some((o,f)=>o!==i[f]))throw Error("envelope fields are invalid");if(r.v!==2)throw Error("envelope version is unsupported");for(let o of["epk","salt","nonce","ciphertext","tag"])if(typeof r[o]!=="string")throw Error(`${o} is invalid`);return r}function F(e,t){try{let r=O(e),n=D(t),i=k({key:c(r.epk,"ephemeral public key"),format:"der",type:"spki"});if(i.asymmetricKeyType!=="x25519")throw Error("ephemeral key is not X25519");let o=c(r.salt,"salt",16),f=c(r.nonce,"nonce",12),g=c(r.ciphertext,"ciphertext"),h=c(r.tag,"tag",16),u=b({privateKey:n,publicKey:i}),a=Buffer.from(B("sha256",u,o,P,32));try{let p=w("aes-256-gcm",a,f);return p.setAAD(_(r)),p.setAuthTag(h),Buffer.concat([p.update(g),p.final()]).toString("utf8")}finally{u.fill(0),a.fill(0)}}catch{throw Error(A)}}function N(e,t){try{if(!/^[0-9a-f]{64}$/.test(t))throw Error("invalid legacy private key");let r=Buffer.from(e.slice(v.length),"base64");if(r.length<32)throw Error("invalid legacy payload");let n=l("sha256").update(Buffer.from(t,"hex")).digest(),i=l("sha256").update(n).digest();return R(r.subarray(32).toString("hex"),i,r.subarray(0,16).toString("hex"),r.subarray(16,32).toString("hex"))}catch{throw Error(A)}}function L(e){return e.startsWith(v)&&!e.startsWith(y)}function X(e,t){if(!e.startsWith(v))return e;return e.startsWith(y)?F(e,t):N(e,t)}function W(e,t,r){return I(X(e,t),r)}function j(e){let t=e.match(/^DOTENV_PRIVATE_KEY_(.+)$/);return t&&t[1]?t[1].toLowerCase():""}function z(e=""){return e?process.env[`DOTENV_PRIVATE_KEY_${e.toUpperCase()}`]:process.env.DOTENV_PRIVATE_KEY}export{j as parseEnvFromKey,W as migrateEncryptedValue,L as isLegacyEncryptedValue,z as getPrivateKey,H as generateKeypair,I as encryptValue,X as decryptValue,Y as aesEncrypt,R as aesDecrypt};
2
+ import{createCipheriv as x,createDecipheriv as w,createHash as l,createPrivateKey as S,createPublicKey as k,diffieHellman as K,generateKeyPairSync as b,hkdfSync as B,randomBytes as d}from"crypto";var E="x25519-public:",m="x25519-private:",h="encrypted:v2:",v="encrypted:",P=Buffer.from("stacks-env:v2","utf8"),T="Environment decryption failed: authentication or format error";function _(t){if(t.length===32)return t;return l("sha256").update(t).digest()}function L(t,e){let r=d(16),n=x("aes-256-gcm",_(e),r);return{ciphertext:Buffer.concat([n.update(t,"utf8"),n.final()]).toString("hex"),iv:r.toString("hex"),authTag:n.getAuthTag().toString("hex")}}function V(t,e,r,n){try{let i=w("aes-256-gcm",_(e),Buffer.from(r,"hex"));return i.setAuthTag(Buffer.from(n,"hex")),Buffer.concat([i.update(Buffer.from(t,"hex")),i.final()]).toString("utf8")}catch{throw Error("Decryption failed: authentication or format error")}}function s(t){return t.toString("base64url")}function c(t,e,r){if(typeof t!=="string"||t.length>0&&!/^[A-Za-z0-9_-]+$/.test(t))throw Error(`${e} is not canonical base64url`);let n=Buffer.from(t,"base64url");if(s(n)!==t)throw Error(`${e} is not canonical base64url`);if(r!==void 0&&n.length!==r)throw Error(`${e} has an invalid length`);return n}function D(t){if(!t.startsWith(E))throw Error("Legacy or invalid public key. Run buddy env:rotate before creating new ciphertext.");let e=k({key:c(t.slice(E.length),"public key"),format:"der",type:"spki"});if(e.asymmetricKeyType!=="x25519")throw Error("Public key is not X25519");return e}function I(t){if(!t.startsWith(m))throw Error("Private key is not a version 2 X25519 key");let e=S({key:c(t.slice(m.length),"private key"),format:"der",type:"pkcs8"});if(e.asymmetricKeyType!=="x25519")throw Error("Private key is not X25519");return e}function A(t){return Buffer.from(`stacks-env:v2;${t.epk};${t.salt};${t.nonce}`,"utf8")}function W(){let{publicKey:t,privateKey:e}=b("x25519"),r=t.export({format:"der",type:"spki"}),n=e.export({format:"der",type:"pkcs8"});return{publicKey:`${E}${s(r)}`,privateKey:`${m}${s(n)}`}}function O(t,e){let r=D(e),n=b("x25519"),i=s(n.publicKey.export({format:"der",type:"spki"})),o=d(16),f=d(12),g=K({privateKey:n.privateKey,publicKey:r}),y=Buffer.from(B("sha256",g,o,P,32)),u={epk:i,salt:s(o),nonce:s(f)};try{let a=x("aes-256-gcm",y,f);a.setAAD(A(u));let p=Buffer.concat([a.update(t,"utf8"),a.final()]),R={v:2,...u,ciphertext:s(p),tag:s(a.getAuthTag())};return`${h}${s(Buffer.from(JSON.stringify(R),"utf8"))}`}finally{g.fill(0),y.fill(0)}}function N(t){let e=c(t.slice(h.length),"envelope").toString("utf8"),r=JSON.parse(e);if(!r||typeof r!=="object"||Array.isArray(r))throw Error("envelope is not an object");let n=Object.keys(r).sort(),i=["ciphertext","epk","nonce","salt","tag","v"];if(n.length!==i.length||n.some((o,f)=>o!==i[f]))throw Error("envelope fields are invalid");if(r.v!==2)throw Error("envelope version is unsupported");for(let o of["epk","salt","nonce","ciphertext","tag"])if(typeof r[o]!=="string")throw Error(`${o} is invalid`);return r}function X(t,e){try{let r=N(t),n=I(e),i=k({key:c(r.epk,"ephemeral public key"),format:"der",type:"spki"});if(i.asymmetricKeyType!=="x25519")throw Error("ephemeral key is not X25519");let o=c(r.salt,"salt",16),f=c(r.nonce,"nonce",12),g=c(r.ciphertext,"ciphertext"),y=c(r.tag,"tag",16),u=K({privateKey:n,publicKey:i}),a=Buffer.from(B("sha256",u,o,P,32));try{let p=w("aes-256-gcm",a,f);return p.setAAD(A(r)),p.setAuthTag(y),Buffer.concat([p.update(g),p.final()]).toString("utf8")}finally{u.fill(0),a.fill(0)}}catch{throw Error(T)}}function F(t,e){try{if(!/^[0-9a-f]{64}$/.test(e))throw Error("invalid legacy private key");let r=Buffer.from(t.slice(v.length),"base64");if(r.length<32)throw Error("invalid legacy payload");let n=l("sha256").update(Buffer.from(e,"hex")).digest(),i=l("sha256").update(n).digest();return V(r.subarray(32).toString("hex"),i,r.subarray(0,16).toString("hex"),r.subarray(16,32).toString("hex"))}catch{throw Error(T)}}function H(t){return t.startsWith(v)&&!t.startsWith(h)}function Y(t,e){if(!t.startsWith(v))return t;return t.startsWith(h)?X(t,e):F(t,e)}function j(t,e,r){return O(Y(t,e),r)}function z(t){let e=t.match(/^DOTENV_PRIVATE_KEY_(.+)$/);return e&&e[1]?e[1].toLowerCase():""}function J(t=""){return t?process.env[`DOTENV_PRIVATE_KEY_${t.toUpperCase()}`]:process.env.DOTENV_PRIVATE_KEY}export{V as aesDecrypt,L as aesEncrypt,Y as decryptValue,O as encryptValue,W as generateKeypair,J as getPrivateKey,H as isLegacyEncryptedValue,j as migrateEncryptedValue,z as parseEnvFromKey};
package/dist/index.js CHANGED
@@ -1,28 +1,28 @@
1
1
  // @bun
2
- import{createCipheriv as B2,createDecipheriv as $2,createHash as y,createPrivateKey as x2,createPublicKey as X2,diffieHellman as D2,generateKeyPairSync as H2,hkdfSync as _2,randomBytes as v}from"crypto";var n="x25519-public:",o="x25519-private:",P="encrypted:v2:",p="encrypted:",A2=Buffer.from("stacks-env:v2","utf8"),W2="Environment decryption failed: authentication or format error";function Y2(f){if(f.length===32)return f;return y("sha256").update(f).digest()}function Qf(f,g){let B=v(16),X=B2("aes-256-gcm",Y2(g),B);return{ciphertext:Buffer.concat([X.update(f,"utf8"),X.final()]).toString("hex"),iv:B.toString("hex"),authTag:X.getAuthTag().toString("hex")}}function F2(f,g,B,X){try{let $=$2("aes-256-gcm",Y2(g),Buffer.from(B,"hex"));return $.setAuthTag(Buffer.from(X,"hex")),Buffer.concat([$.update(Buffer.from(f,"hex")),$.final()]).toString("utf8")}catch{throw Error("Decryption failed: authentication or format error")}}function C(f){return f.toString("base64url")}function S(f,g,B){if(typeof f!=="string"||f.length>0&&!/^[A-Za-z0-9_-]+$/.test(f))throw Error(`${g} is not canonical base64url`);let X=Buffer.from(f,"base64url");if(C(X)!==f)throw Error(`${g} is not canonical base64url`);if(B!==void 0&&X.length!==B)throw Error(`${g} has an invalid length`);return X}function S2(f){if(!f.startsWith(n))throw Error("Legacy or invalid public key. Run buddy env:rotate before creating new ciphertext.");let g=X2({key:S(f.slice(n.length),"public key"),format:"der",type:"spki"});if(g.asymmetricKeyType!=="x25519")throw Error("Public key is not X25519");return g}function b2(f){if(!f.startsWith(o))throw Error("Private key is not a version 2 X25519 key");let g=x2({key:S(f.slice(o.length),"private key"),format:"der",type:"pkcs8"});if(g.asymmetricKeyType!=="x25519")throw Error("Private key is not X25519");return g}function j2(f){return Buffer.from(`stacks-env:v2;${f.epk};${f.salt};${f.nonce}`,"utf8")}function K(){let{publicKey:f,privateKey:g}=H2("x25519"),B=f.export({format:"der",type:"spki"}),X=g.export({format:"der",type:"pkcs8"});return{publicKey:`${n}${C(B)}`,privateKey:`${o}${C(X)}`}}function k(f,g){let B=S2(g),X=H2("x25519"),$=C(X.publicKey.export({format:"der",type:"spki"})),D=v(16),H=v(12),W=D2({privateKey:X.privateKey,publicKey:B}),A=Buffer.from(_2("sha256",W,D,A2,32)),Y={epk:$,salt:C(D),nonce:C(H)};try{let _=B2("aes-256-gcm",A,H);_.setAAD(j2(Y));let j=Buffer.concat([_.update(f,"utf8"),_.final()]),O={v:2,...Y,ciphertext:C(j),tag:C(_.getAuthTag())};return`${P}${C(Buffer.from(JSON.stringify(O),"utf8"))}`}finally{W.fill(0),A.fill(0)}}function h2(f){let g=S(f.slice(P.length),"envelope").toString("utf8"),B=JSON.parse(g);if(!B||typeof B!=="object"||Array.isArray(B))throw Error("envelope is not an object");let X=Object.keys(B).sort(),$=["ciphertext","epk","nonce","salt","tag","v"];if(X.length!==$.length||X.some((D,H)=>D!==$[H]))throw Error("envelope fields are invalid");if(B.v!==2)throw Error("envelope version is unsupported");for(let D of["epk","salt","nonce","ciphertext","tag"])if(typeof B[D]!=="string")throw Error(`${D} is invalid`);return B}function K2(f,g){try{let B=h2(f),X=b2(g),$=X2({key:S(B.epk,"ephemeral public key"),format:"der",type:"spki"});if($.asymmetricKeyType!=="x25519")throw Error("ephemeral key is not X25519");let D=S(B.salt,"salt",16),H=S(B.nonce,"nonce",12),W=S(B.ciphertext,"ciphertext"),A=S(B.tag,"tag",16),Y=D2({privateKey:X,publicKey:$}),_=Buffer.from(_2("sha256",Y,D,A2,32));try{let j=$2("aes-256-gcm",_,H);return j.setAAD(j2(B)),j.setAuthTag(A),Buffer.concat([j.update(W),j.final()]).toString("utf8")}finally{Y.fill(0),_.fill(0)}}catch{throw Error(W2)}}function N2(f,g){try{if(!/^[0-9a-f]{64}$/.test(g))throw Error("invalid legacy private key");let B=Buffer.from(f.slice(p.length),"base64");if(B.length<32)throw Error("invalid legacy payload");let X=y("sha256").update(Buffer.from(g,"hex")).digest(),$=y("sha256").update(X).digest();return F2(B.subarray(32).toString("hex"),$,B.subarray(0,16).toString("hex"),B.subarray(16,32).toString("hex"))}catch{throw Error(W2)}}function Rf(f){return f.startsWith(p)&&!f.startsWith(P)}function h(f,g){if(!f.startsWith(p))return f;return f.startsWith(P)?K2(f,g):N2(f,g)}function If(f,g,B){return k(h(f,g),B)}function wf(f){let g=f.match(/^DOTENV_PRIVATE_KEY_(.+)$/);return g&&g[1]?g[1].toLowerCase():""}function i(f=""){return f?process.env[`DOTENV_PRIVATE_KEY_${f.toUpperCase()}`]:process.env.DOTENV_PRIVATE_KEY}import{spawnSync as P2}from"child_process";import{readFileSync as k2}from"fs";import{arch as c2,hostname as m2,platform as G2,release as d2,userInfo as u2}from"os";import{basename as r2,dirname as t2,resolve as y2}from"path";function q(f,g={}){let B={},X=[],$=[],D=f.split(`
3
- `);for(let H of D){let W=H.trim();if(!W||W.startsWith("#"))continue;if(W.startsWith("DOTENV_PUBLIC_KEY=")){let j=W.match(/^DOTENV_PUBLIC_KEY=["']?([^"'\n]+)["']?/);if(j&&j[1]!==void 0)B.DOTENV_PUBLIC_KEY=j[1];continue}let A=W.match(/^([^=]+)=(.*)$/);if(!A||A[1]===void 0||A[2]===void 0)continue;let Y=A[1].trim(),_=A[2].trim();if(_.startsWith('"')&&_.endsWith('"')||_.startsWith("'")&&_.endsWith("'")){if(_=_.slice(1,-1),_.includes("\\n"))_=_.replace(/\\n/g,`
4
- `)}if(_.startsWith("encrypted:")||_.startsWith("enc:")){if(!g.privateKey){$.push(Y);continue}try{let j=_.startsWith("enc:")?`encrypted:${_.slice(4)}`:_;_=h(j,g.privateKey)}catch(j){X.push(`Failed to decrypt ${Y}: ${j instanceof Error?j.message:"Unknown error"}`)}}_=v2(_,{...g.processEnv||process.env,...B}),_=i2(_),B[Y]=_}return{parsed:B,errors:X,skippedEncrypted:$}}function v2(f,g){return f.replace(/\$\{([^}]+)\}/g,(B,X)=>{let $=X.match(/^([^:\-+]+)(:-|-)(.+)$/);if($){let[,H,W,A]=$,Y=g[H];if(W===":-")return Y||A;else return Y!==void 0?Y:A}let D=X.match(/^([^:\-+]+)(:?\+)(.+)$/);if(D){let[,H,W,A]=D,Y=g[H];if(W===":+")return Y?A:"";else return Y!==void 0?A:""}return g[X]||""})}var n2=new Set(["date","hostname","whoami","uname","pwd","echo","printf","cat","basename","dirname"]);function o2(f){let g=[],B="",X=null,$=!1;for(let D of f.trim()){if($){B+=D,$=!1;continue}if(D==="\\"&&X!=="'"){$=!0;continue}if(X){if(D===X)X=null;else B+=D;continue}if(D==='"'||D==="'"){X=D;continue}if(/\s/.test(D)){if(B)g.push(B),B="";continue}B+=D}if($||X)throw Error("Command substitution contains an unterminated escape or quote");if(B)g.push(B);return g}function p2(f){let[g,...B]=f,X=B.some(($)=>$.startsWith("-"));if(g==="echo"&&!X)return B.join(" ");if(g==="printf"&&B.length===1&&!B[0]?.includes("%"))return B[0];if(g==="hostname"&&B.length===0)return m2();if(g==="whoami"&&B.length===0)return u2().username;if(g==="pwd"&&B.length===0)return process.cwd();if(g==="basename"&&B.length===1)return r2(B[0]??"");if(g==="dirname"&&B.length===1)return t2(B[0]??"");if(g==="cat"&&B.length>0&&!X)return B.map(($)=>k2(y2(process.cwd(),$),"utf8")).join("");if(g==="uname"){let $=B[0]??"-s";if(B.length<=1&&$==="-m")return c2();if(B.length<=1&&$==="-r")return d2();if(B.length<=1&&$==="-s")return{aix:"AIX",android:"Android",cygwin:"CYGWIN",darwin:"Darwin",freebsd:"FreeBSD",haiku:"Haiku",linux:"Linux",netbsd:"NetBSD",openbsd:"OpenBSD",sunos:"SunOS",win32:"Windows_NT"}[G2()]??G2()}return}function i2(f){return f.replace(/\$\(([^)]+)\)/g,(g,B)=>{try{let X=o2(B),$=X[0];if(!$||!n2.has($))return console.warn(`[env] Blocked command substitution for disallowed command: ${$}`),"";let D=p2(X);if(D!==void 0)return D.trim();let H=P2($,X.slice(1),{encoding:"utf8",env:process.env});if(H.status===0)return H.stdout.trim();let W=H.stderr.trim();console.warn(`[env] Command substitution failed (exit code ${H.status??"unknown"}): ${$}${W?`: ${W}`:""}`)}catch(X){console.warn(`[env] Command substitution error: ${X instanceof Error?X.message:String(X)}`)}return""})}async function Vf(f,g={}){let B={},X=[],$=[];for(let D of f)try{let H=Bun.file(D);if(!H.size)continue;let W=await H.text(),{parsed:A,errors:Y,skippedEncrypted:_}=q(W,g);X.push(...Y),$.push(..._);for(let[j,O]of Object.entries(A)){if(!g.overload&&B[j]!==void 0)continue;B[j]=O}}catch(H){if(H?.code==="ENOENT")continue;X.push(`Failed to read ${D}: ${H instanceof Error?H.message:String(H)}`)}return{parsed:B,errors:X,skippedEncrypted:$}}import{existsSync as c}from"fs";import{readFileSync as l}from"fs";import{resolve as m}from"path";function N(f){return typeof f==="string"&&(f.startsWith("encrypted:")||f.startsWith("enc:"))}function b(f){if(!f||N(f))return;let g=f.trim().toLowerCase();if(!/^[a-z0-9_-]+$/.test(g))return;if(g==="prod")return"production";if(g==="stage")return"staging";if(g==="dev")return"development";return g}var d=null,u,r;function l2(f,g,B=".env.keys"){let X=m(g,B);if(!c(X))return;try{let{parsed:$}=q(l(X,"utf-8"));if(f){let D=$[`DOTENV_PRIVATE_KEY_${f.toUpperCase()}`];if(D)return D}return $.DOTENV_PRIVATE_KEY}catch{return}}function s(f={}){return b(f.env)||r||b("development")||b(process.env.DOTENV_ENV)||b(process.env.APP_ENV)||"development"}function s2(f={}){let g=s(f),B=f.cwd||process.cwd();if(d===g)return u;let X=i(g);if(!X)X=process.env.DOTENV_PRIVATE_KEY;if(!X)X=l2(g,B);return d=g,u=X,X}function Kf(){d=null,u=void 0,r=void 0}function J2(f,g={}){if(!N(f))return f;let B=s2(g);if(!B)return;try{let X=f.startsWith("enc:")?`encrypted:${f.slice(4)}`:f;return h(X,B)}catch{return}}function O2(f={}){let{path:g=[".env"],overload:B=!1,env:X,privateKey:$,keysFile:D=".env.keys",quiet:H=!1,cwd:W=process.cwd()}=f,A=Array.isArray(g)?g:[g],Y=[],_=0,j=new Set(Object.entries(process.env).filter(([,G])=>G!==void 0&&!N(G)).map(([G])=>G)),O=$;if(!O&&D){let G=m(W,D);if(c(G))try{let J=l(G,"utf-8"),{parsed:w}=q(J);if(X){let T=`DOTENV_PRIVATE_KEY_${X.toUpperCase()}`;O=w[T]}else O=w.DOTENV_PRIVATE_KEY}catch(J){Y.push(`Failed to load keys file: ${J instanceof Error?J.message:"Unknown error"}`)}}if(!O)O=i(X||"");for(let G of A){let J=m(W,G);if(!c(J)){if(!H)Y.push(`File not found: ${J}`);continue}try{let w=l(J,"utf-8"),{parsed:T,errors:I,skippedEncrypted:R}=q(w,{privateKey:O,processEnv:process.env});if(Y.push(...I),R.length>0){for(let F of R)if(N(process.env[F]))delete process.env[F];let Z=X?`DOTENV_PRIVATE_KEY_${X.toUpperCase()}`:"DOTENV_PRIVATE_KEY",z=R.slice(0,5).join(", "),x=R.length>5?`, \u2026 +${R.length-5} more`:"";console.warn(`[env] warning: skipped ${R.length} encrypted value(s) in ${G} (${Z} not set; defaults apply): ${z}${x}`)}let U=0;for(let[Z,z]of Object.entries(T)){if(Z==="DOTENV_PUBLIC_KEY")continue;let x=process.env[Z],F=N(x)&&z!==x;if(B||!j.has(Z)||F)process.env[Z]=z,_++,U++}if(!H&&!process.env.__ENV_LOADED__)console.error(`[env] loaded ${U}/${Object.keys(T).length} variables from ${G}`),process.env.__ENV_LOADED__="1"}catch(w){Y.push(`Failed to load ${J}: ${w instanceof Error?w.message:"Unknown error"}`)}}return{loaded:_,errors:Y}}function a2(f={}){return{name:"env-plugin",setup(g){O2(f)}}}function Nf(f={}){let g=b(f.env)||b("development")||b(process.env.DOTENV_ENV)||b(process.env.APP_ENV)||"development",B=f.cwd||process.cwd();if(r!==g)r=g,d=null,u=void 0;let X=[],$=(W)=>{if(!X.includes(W)&&c(m(B,W)))X.push(W)};$(".env"),$(".env.local");let D=`.env.${g}`,H=`.env.${g}.local`;return $(D),$(H),O2({...f,path:X,env:g})}var kf=a2;import{existsSync as V,readFileSync as M,renameSync as a,rmSync as Z2,writeFileSync as E}from"fs";import{basename as e,dirname as Q2,resolve as L}from"path";function rf(f,g){if(f)return f;let B=String(g??"").trim();if(!B||B==="development"||B==="dev"||B==="local")return"";if(!/^[\w.-]+$/.test(B))return"";return`.env.${B}`}function R2(f,g,B,X){let $=["#/-------------------[DOTENV_PUBLIC_KEY]--------------------/","#/ versioned X25519 encryption for .env files /","#/ [how it works](https://stacksjs.com/encryption) /","#/----------------------------------------------------------/",`${B}="${g}"`,""],D=!1;for(let H of f.split(`
5
- `)){let W=H.trim();if(W.startsWith("#/"))continue;if(W.startsWith("DOTENV_PUBLIC_KEY"))continue;if(!W&&!D)continue;if(!W||W.startsWith("#")){D=!0,$.push(H);continue}D=!0;let A=W.match(/^([^=]+)=(.*)$/);if(!A||A[1]===void 0||A[2]===void 0){$.push(H);continue}let Y=A[1].trim(),_=A[2].trim();if(_.startsWith('"')&&_.endsWith('"')||_.startsWith("'")&&_.endsWith("'"))_=_.slice(1,-1);if((!X.key||Y.includes(X.key))&&(!X.excludeKey||!Y.includes(X.excludeKey))&&!_.startsWith("encrypted:"))_=k(_,g);$.push(`${Y}="${_}"`)}return $.join(`
6
- `)}function t(f){let B=((f||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase();return{publicKeyName:B?`DOTENV_PUBLIC_KEY_${B}`:"DOTENV_PUBLIC_KEY",privateKeyName:B?`DOTENV_PRIVATE_KEY_${B}`:"DOTENV_PRIVATE_KEY"}}function I2(f,g){let B,X=!1;for(let $ of f.split(`
7
- `)){let D=$.trim();if(!D||D.startsWith("#"))continue;let H=D.match(/^([^=]+)=(.*)$/);if(!H?.[1]||H[2]===void 0)continue;let W=H[1].trim(),A=H[2].trim().replace(/^["']|["']$/g,"");if(W===g)B=A;else if(A.startsWith("encrypted:")||A.startsWith("enc:"))X=!0}return B&&X?B:void 0}function tf(f={}){let g=f.cwd||process.cwd(),B=L(g,f.file||".env"),X=L(g,f.keysFile||".env.keys");if(!V(B))return{success:!1,error:`File not found: ${B}`};try{let $,D,H=I2(M(B,"utf-8"),t(f.file).publicKeyName);if(H)$=H;else if(V(X)){let Y=M(X,"utf-8"),{parsed:_}=q(Y),{publicKeyName:j,privateKeyName:O}=t(f.file);if($=_[j]||"",D=_[O]||"",!$||!D){let G=K();$=G.publicKey,D=G.privateKey;let J=`
8
- ${j}="${$}"
9
- ${O}="${D}"
10
- `;E(X,Y+J,"utf-8")}}else{let Y=K();$=Y.publicKey,D=Y.privateKey;let{publicKeyName:_,privateKeyName:j}=t(f.file),O=`# .env.keys - Keep this file secure and never commit to source control
11
- ${_}="${$}"
12
- ${j}="${D}"
13
- `;E(X,O,"utf-8")}let W=M(B,"utf-8"),A=R2(W,$,t(f.file).publicKeyName,f);if(f.stdout)return{success:!0,output:A};return E(B,A,"utf-8"),{success:!0,output:`\u2714 encrypted (${f.file||".env"})
14
- \u2714 key added to ${f.keysFile||".env.keys"}`}}catch($){return{success:!1,error:`Failed to encrypt: ${$ instanceof Error?$.message:"Unknown error"}`}}}function e2(f={}){let g=f.cwd||process.cwd(),B=L(g,f.file||".env"),X=L(g,f.keysFile||".env.keys");if(!V(B))return{success:!1,error:`File not found: ${B}`};if(!V(X))return{success:!1,error:`Keys file not found: ${X}`};try{let $=M(X,"utf-8"),{parsed:D}=q($),A=((f.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),Y=A?`DOTENV_PRIVATE_KEY_${A}`:"DOTENV_PRIVATE_KEY",_=D[Y];if(!_)return{success:!1,error:`Private key not found: ${Y}`};let j=[_];if(Y!=="DOTENV_PRIVATE_KEY"&&D.DOTENV_PRIVATE_KEY&&D.DOTENV_PRIVATE_KEY!==_)j.push(D.DOTENV_PRIVATE_KEY);let G=M(B,"utf-8").split(`
15
- `),J=[];for(let T of G){let I=T.trim();if(!I||I.startsWith("#")){J.push(T);continue}if(I.startsWith("DOTENV_PUBLIC_KEY"))continue;let R=I.match(/^([^=]+)=(.*)$/);if(!R||R[1]===void 0||R[2]===void 0){J.push(T);continue}let U=R[1].trim(),Z=R[2].trim();if(Z.startsWith('"')&&Z.endsWith('"')||Z.startsWith("'")&&Z.endsWith("'"))Z=Z.slice(1,-1);let z=Z.startsWith("encrypted:");if(f.key&&!U.includes(f.key))z=!1;if(z){let x,F;for(let E2 of j)try{x=h(Z,E2);break}catch(C2){F=C2}if(x===void 0)throw F instanceof Error?F:Error(`Unable to decrypt ${U}`);Z=x}J.push(`${U}="${Z}"`)}let w=J.join(`
16
- `);if(f.stdout)return{success:!0,output:w};return E(B,w,"utf-8"),{success:!0,output:`\u2714 decrypted (${f.file||".env"})`}}catch($){return{success:!1,error:`Failed to decrypt: ${$ instanceof Error?$.message:"Unknown error"}`}}}function yf(f,g,B={}){let X=B.cwd||process.cwd(),$=L(X,B.file||".env"),D=L(X,B.keysFile||".env.keys");try{let H="";if(V($))H=M($,"utf-8");let W=H.split(`
17
- `),A=!1,Y,O=((B.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),G=O?`DOTENV_PUBLIC_KEY_${O}`:"DOTENV_PUBLIC_KEY",J=O?`DOTENV_PRIVATE_KEY_${O}`:"DOTENV_PRIVATE_KEY";for(let I of W){let R=I.trim();if(R.startsWith(`${G}=`)){let U=R.match(/^[^=]+=["']?([^"'\n]+)["']?/);if(U)Y=U[1];break}}if(Y){let I=!1;if(V(D)){let R=M(D,"utf-8"),{parsed:U}=q(R);I=Boolean(U[J])}if(!I&&!process.env[J]&&!I2(H,G))Y=void 0}if(!B.plain&&!Y){let I=K();Y=I.publicKey;let R="";if(V(D))R=M(D,"utf-8");R+=`
18
- ${G}="${I.publicKey}"
19
- ${J}="${I.privateKey}"
20
- `,E(D,R,"utf-8");for(let U=W.length-1;U>=0;U--){let Z=W[U];if(Z!==void 0&&Z.trim().startsWith(`${G}=`))W.splice(U,1)}W.unshift(`${G}="${Y}"`)}let w=g;if(!B.plain&&Y)w=k(g,Y);for(let I=0;I<W.length;I++){let R=W[I];if(R===void 0)continue;if(R.trim().startsWith(`${f}=`)){W[I]=`${f}="${w}"`,A=!0;break}}if(!A)W.push(`${f}="${w}"`);let T=W.join(`
21
- `);return E($,T,"utf-8"),{success:!0,output:`set ${f}${B.plain?"":" with encryption"} (${B.file||".env"})`}}catch(H){return{success:!1,error:`Failed to set: ${H instanceof Error?H.message:"Unknown error"}`}}}function vf(f,g={}){let B=g.cwd||process.cwd(),X=L(B,g.file||".env");if(!V(X))return{success:!1,error:`File not found: ${X}`};try{let $,D=L(B,g.keysFile||".env.keys"),A=((g.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),Y=A?`DOTENV_PRIVATE_KEY_${A}`:"DOTENV_PRIVATE_KEY";if(V(D)){let J=M(D,"utf-8"),{parsed:w}=q(J);$=w[Y]}if(!$)$=process.env[Y];let _=M(X,"utf-8"),{parsed:j}=q(_,{privateKey:$});if(f&&!g.all){let J=j[f];if(J===void 0)return{success:!1,error:`Key not found: ${f}`};return{success:!0,output:J}}let O=g.all?{...process.env,...j}:j,G;switch(g.format){case"shell":G=Object.entries(O).map(([J,w])=>`${J}=${w}`).join(" ");break;case"eval":G=Object.entries(O).map(([J,w])=>`${J}="${w}"`).join(`
22
- `);break;case"json":default:G=g.prettyPrint?JSON.stringify(O,null,2):JSON.stringify(O);break}return{success:!0,output:G}}catch($){return{success:!1,error:`Failed to get: ${$ instanceof Error?$.message:"Unknown error"}`}}}function nf(f,g={}){let B=g.cwd||process.cwd(),X=L(B,g.keysFile||".env.keys");if(!V(X))return{success:!1,error:`Keys file not found: ${X}`};try{let $=M(X,"utf-8"),{parsed:D}=q($),A=((g.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),Y=A?`DOTENV_PUBLIC_KEY_${A}`:"DOTENV_PUBLIC_KEY",_=A?`DOTENV_PRIVATE_KEY_${A}`:"DOTENV_PRIVATE_KEY";if(f){let G=D[f];if(!G)return{success:!1,error:`Key not found: ${f}`};return{success:!0,output:G}}let j={[Y]:D[Y],[_]:D[_]};return{success:!0,output:g.format==="shell"?`${Y}=${j[Y]} ${_}=${j[_]}`:JSON.stringify(j)}}catch($){return{success:!1,error:`Failed to get keypair: ${$ instanceof Error?$.message:"Unknown error"}`}}}function of(f={}){let g=e2({file:f.file,keysFile:f.keysFile,stdout:!0,cwd:f.cwd});if(!g.success)return g;let B=f.cwd||process.cwd(),X=L(B,f.file||".env"),$=L(B,f.keysFile||".env.keys");if(g.output===void 0)return{success:!1,error:"Rotation produced no decrypted content"};let D=K(),H=M(X),W=M($),A=W.toString("utf8").split(`
23
- `),Y=f.file||".env",_=e(Y).replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),j=_?`DOTENV_PUBLIC_KEY_${_}`:"DOTENV_PUBLIC_KEY",O=_?`DOTENV_PRIVATE_KEY_${_}`:"DOTENV_PRIVATE_KEY",G=!1,J=!1;for(let Z=0;Z<A.length;Z++){let z=A[Z];if(z===void 0)continue;if(z.startsWith(`${j}=`))A[Z]=`${j}="${D.publicKey}"`,G=!0;else if(z.startsWith(`${O}=`))A[Z]=`${O}="${D.privateKey}"`,J=!0}if(!G)A.push(`${j}="${D.publicKey}"`);if(!J)A.push(`${O}="${D.privateKey}"`);let w=`${A.join(`
2
+ import{createCipheriv as re,createDecipheriv as ie,createHash as G,createPrivateKey as Ce,createPublicKey as se,diffieHellman as oe,generateKeyPairSync as de,hkdfSync as ce,randomBytes as H}from"crypto";var X="x25519-public:",j="x25519-private:",w="encrypted:v2:",Q="encrypted:",ae=Buffer.from("stacks-env:v2","utf8"),ue="Environment decryption failed: authentication or format error";function fe(e){if(e.length===32)return e;return G("sha256").update(e).digest()}function Rn(e,n){let t=H(16),r=re("aes-256-gcm",fe(n),t);return{ciphertext:Buffer.concat([r.update(e,"utf8"),r.final()]).toString("hex"),iv:t.toString("hex"),authTag:r.getAuthTag().toString("hex")}}function be(e,n,t,r){try{let i=ie("aes-256-gcm",fe(n),Buffer.from(t,"hex"));return i.setAuthTag(Buffer.from(r,"hex")),Buffer.concat([i.update(Buffer.from(e,"hex")),i.final()]).toString("utf8")}catch{throw Error("Decryption failed: authentication or format error")}}function P(e){return e.toString("base64url")}function b(e,n,t){if(typeof e!=="string"||e.length>0&&!/^[A-Za-z0-9_-]+$/.test(e))throw Error(`${n} is not canonical base64url`);let r=Buffer.from(e,"base64url");if(P(r)!==e)throw Error(`${n} is not canonical base64url`);if(t!==void 0&&r.length!==t)throw Error(`${n} has an invalid length`);return r}function Le(e){if(!e.startsWith(X))throw Error("Legacy or invalid public key. Run buddy env:rotate before creating new ciphertext.");let n=se({key:b(e.slice(X.length),"public key"),format:"der",type:"spki"});if(n.asymmetricKeyType!=="x25519")throw Error("Public key is not X25519");return n}function Ke(e){if(!e.startsWith(j))throw Error("Private key is not a version 2 X25519 key");let n=Ce({key:b(e.slice(j.length),"private key"),format:"der",type:"pkcs8"});if(n.asymmetricKeyType!=="x25519")throw Error("Private key is not X25519");return n}function Ee(e){return Buffer.from(`stacks-env:v2;${e.epk};${e.salt};${e.nonce}`,"utf8")}function k(){let{publicKey:e,privateKey:n}=de("x25519"),t=e.export({format:"der",type:"spki"}),r=n.export({format:"der",type:"pkcs8"});return{publicKey:`${X}${P(t)}`,privateKey:`${j}${P(r)}`}}function B(e,n){let t=Le(n),r=de("x25519"),i=P(r.publicKey.export({format:"der",type:"spki"})),s=H(16),o=H(12),a=oe({privateKey:r.privateKey,publicKey:t}),c=Buffer.from(ce("sha256",a,s,ae,32)),u={epk:i,salt:P(s),nonce:P(o)};try{let d=re("aes-256-gcm",c,o);d.setAAD(Ee(u));let f=Buffer.concat([d.update(e,"utf8"),d.final()]),p={v:2,...u,ciphertext:P(f),tag:P(d.getAuthTag())};return`${w}${P(Buffer.from(JSON.stringify(p),"utf8"))}`}finally{a.fill(0),c.fill(0)}}function ke(e){let n=b(e.slice(w.length),"envelope").toString("utf8"),t=JSON.parse(n);if(!t||typeof t!=="object"||Array.isArray(t))throw Error("envelope is not an object");let r=Object.keys(t).sort(),i=["ciphertext","epk","nonce","salt","tag","v"];if(r.length!==i.length||r.some((s,o)=>s!==i[o]))throw Error("envelope fields are invalid");if(t.v!==2)throw Error("envelope version is unsupported");for(let s of["epk","salt","nonce","ciphertext","tag"])if(typeof t[s]!=="string")throw Error(`${s} is invalid`);return t}function $e(e,n){try{let t=ke(e),r=Ke(n),i=se({key:b(t.epk,"ephemeral public key"),format:"der",type:"spki"});if(i.asymmetricKeyType!=="x25519")throw Error("ephemeral key is not X25519");let s=b(t.salt,"salt",16),o=b(t.nonce,"nonce",12),a=b(t.ciphertext,"ciphertext"),c=b(t.tag,"tag",16),u=oe({privateKey:r,publicKey:i}),d=Buffer.from(ce("sha256",u,s,ae,32));try{let f=ie("aes-256-gcm",d,o);return f.setAAD(Ee(t)),f.setAuthTag(c),Buffer.concat([f.update(a),f.final()]).toString("utf8")}finally{u.fill(0),d.fill(0)}}catch{throw Error(ue)}}function Ue(e,n){try{if(!/^[0-9a-f]{64}$/.test(n))throw Error("invalid legacy private key");let t=Buffer.from(e.slice(Q.length),"base64");if(t.length<32)throw Error("invalid legacy payload");let r=G("sha256").update(Buffer.from(n,"hex")).digest(),i=G("sha256").update(r).digest();return be(t.subarray(32).toString("hex"),i,t.subarray(0,16).toString("hex"),t.subarray(16,32).toString("hex"))}catch{throw Error(ue)}}function yn(e){return e.startsWith(Q)&&!e.startsWith(w)}function K(e,n){if(!e.startsWith(Q))return e;return e.startsWith(w)?$e(e,n):Ue(e,n)}function Tn(e,n,t){return B(K(e,n),t)}function vn(e){let n=e.match(/^DOTENV_PRIVATE_KEY_(.+)$/);return n&&n[1]?n[1].toLowerCase():""}function q(e=""){return e?process.env[`DOTENV_PRIVATE_KEY_${e.toUpperCase()}`]:process.env.DOTENV_PRIVATE_KEY}import{spawnSync as we}from"child_process";import{readFileSync as Be}from"fs";import{arch as xe,hostname as Ve,platform as le,release as Me,userInfo as Fe}from"os";import{basename as Ye,dirname as We,resolve as Ge}from"path";function O(e,n={}){let t={},r=[],i=[],s=e.split(`
3
+ `);for(let o of s){let a=o.trim();if(!a||a.startsWith("#"))continue;if(a.startsWith("DOTENV_PUBLIC_KEY=")){let f=a.match(/^DOTENV_PUBLIC_KEY=["']?([^"'\n]+)["']?/);if(f&&f[1]!==void 0)t.DOTENV_PUBLIC_KEY=f[1];continue}let c=a.match(/^([^=]+)=(.*)$/);if(!c||c[1]===void 0||c[2]===void 0)continue;let u=c[1].trim(),d=c[2].trim();if(d.startsWith('"')&&d.endsWith('"')||d.startsWith("'")&&d.endsWith("'")){if(d=d.slice(1,-1),d.includes("\\n"))d=d.replace(/\\n/g,`
4
+ `)}if(d.startsWith("encrypted:")||d.startsWith("enc:")){if(!n.privateKey){i.push(u);continue}try{let f=d.startsWith("enc:")?`encrypted:${d.slice(4)}`:d;d=K(f,n.privateKey)}catch(f){r.push(`Failed to decrypt ${u}: ${f instanceof Error?f.message:"Unknown error"}`)}}d=He(d,{...n.processEnv||process.env,...t}),d=qe(d),t[u]=d}return{parsed:t,errors:r,skippedEncrypted:i}}function He(e,n){return e.replace(/\$\{([^}]+)\}/g,(t,r)=>{let i=r.match(/^([^:\-+]+)(:-|-)(.+)$/);if(i){let[,o,a,c]=i,u=n[o];if(a===":-")return u||c;else return u!==void 0?u:c}let s=r.match(/^([^:\-+]+)(:?\+)(.+)$/);if(s){let[,o,a,c]=s,u=n[o];if(a===":+")return u?c:"";else return u!==void 0?c:""}return n[r]||""})}var Xe=new Set(["date","hostname","whoami","uname","pwd","echo","printf","cat","basename","dirname"]);function je(e){let n=[],t="",r=null,i=!1;for(let s of e.trim()){if(i){t+=s,i=!1;continue}if(s==="\\"&&r!=="'"){i=!0;continue}if(r){if(s===r)r=null;else t+=s;continue}if(s==='"'||s==="'"){r=s;continue}if(/\s/.test(s)){if(t)n.push(t),t="";continue}t+=s}if(i||r)throw Error("Command substitution contains an unterminated escape or quote");if(t)n.push(t);return n}function Qe(e){let[n,...t]=e,r=t.some((i)=>i.startsWith("-"));if(n==="echo"&&!r)return t.join(" ");if(n==="printf"&&t.length===1&&!t[0]?.includes("%"))return t[0];if(n==="hostname"&&t.length===0)return Ve();if(n==="whoami"&&t.length===0)return Fe().username;if(n==="pwd"&&t.length===0)return process.cwd();if(n==="basename"&&t.length===1)return Ye(t[0]??"");if(n==="dirname"&&t.length===1)return We(t[0]??"");if(n==="cat"&&t.length>0&&!r)return t.map((i)=>Be(Ge(process.cwd(),i),"utf8")).join("");if(n==="uname"){let i=t[0]??"-s";if(t.length<=1&&i==="-m")return xe();if(t.length<=1&&i==="-r")return Me();if(t.length<=1&&i==="-s")return{aix:"AIX",android:"Android",cygwin:"CYGWIN",darwin:"Darwin",freebsd:"FreeBSD",haiku:"Haiku",linux:"Linux",netbsd:"NetBSD",openbsd:"OpenBSD",sunos:"SunOS",win32:"Windows_NT"}[le()]??le()}return}function qe(e){return e.replace(/\$\(([^)]+)\)/g,(n,t)=>{try{let r=je(t),i=r[0];if(!i||!Xe.has(i))return console.warn(`[env] Blocked command substitution for disallowed command: ${i}`),"";let s=Qe(r);if(s!==void 0)return s.trim();let o=we(i,r.slice(1),{encoding:"utf8",env:process.env});if(o.status===0)return o.stdout.trim();let a=o.stderr.trim();console.warn(`[env] Command substitution failed (exit code ${o.status??"unknown"}): ${i}${a?`: ${a}`:""}`)}catch(r){console.warn(`[env] Command substitution error: ${r instanceof Error?r.message:String(r)}`)}return""})}async function Pn(e,n={}){let t={},r=[],i=[];for(let s of e)try{let o=Bun.file(s);if(!o.size)continue;let a=await o.text(),{parsed:c,errors:u,skippedEncrypted:d}=O(a,n);r.push(...u),i.push(...d);for(let[f,p]of Object.entries(c)){if(!n.overload&&t[f]!==void 0)continue;t[f]=p}}catch(o){if(o?.code==="ENOENT")continue;r.push(`Failed to read ${s}: ${o instanceof Error?o.message:String(o)}`)}return{parsed:t,errors:r,skippedEncrypted:i}}import{existsSync as x}from"fs";import{readFileSync as z}from"fs";import{resolve as V}from"path";function U(e){return typeof e==="string"&&(e.startsWith("encrypted:")||e.startsWith("enc:"))}function L(e){if(!e||U(e))return;let n=e.trim().toLowerCase();if(!/^[a-z0-9_-]+$/.test(n))return;if(n==="prod")return"production";if(n==="stage")return"staging";if(n==="dev")return"development";return n}var M=null,F,Y;function ze(e,n,t=".env.keys"){let r=V(n,t);if(!x(r))return;try{let{parsed:i}=O(z(r,"utf-8"));if(e){let s=i[`DOTENV_PRIVATE_KEY_${e.toUpperCase()}`];if(s)return s}return i.DOTENV_PRIVATE_KEY}catch{return}}function J(e={}){return L(e.env)||Y||L("development")||L(process.env.DOTENV_ENV)||L(process.env.APP_ENV)||"development"}function Je(e={}){let n=J(e),t=e.cwd||process.cwd();if(M===n)return F;let r=q(n);if(!r)r=process.env.DOTENV_PRIVATE_KEY;if(!r)r=ze(n,t);return M=n,F=r,r}function $n(){M=null,F=void 0,Y=void 0}function pe(e,n={}){if(!U(e))return e;let t=Je(n);if(!t)return;try{let r=e.startsWith("enc:")?`encrypted:${e.slice(4)}`:e;return K(r,t)}catch{return}}function ge(e={}){let{path:n=[".env"],overload:t=!1,env:r,privateKey:i,keysFile:s=".env.keys",quiet:o=!1,cwd:a=process.cwd()}=e,c=Array.isArray(n)?n:[n],u=[],d=0,f=new Set(Object.entries(process.env).filter(([,E])=>E!==void 0&&!U(E)).map(([E])=>E)),p=i;if(!p&&s){let E=V(a,s);if(x(E))try{let l=z(E,"utf-8"),{parsed:y}=O(l);if(r){let v=`DOTENV_PRIVATE_KEY_${r.toUpperCase()}`;p=y[v]}else p=y.DOTENV_PRIVATE_KEY}catch(l){u.push(`Failed to load keys file: ${l instanceof Error?l.message:"Unknown error"}`)}}if(!p)p=q(r||"");for(let E of c){let l=V(a,E);if(!x(l)){if(!o)u.push(`File not found: ${l}`);continue}try{let y=z(l,"utf-8"),{parsed:v,errors:R,skippedEncrypted:m}=O(y,{privateKey:p,processEnv:process.env});if(u.push(...R),m.length>0){for(let C of m)if(U(process.env[C]))delete process.env[C];let g=r?`DOTENV_PRIVATE_KEY_${r.toUpperCase()}`:"DOTENV_PRIVATE_KEY",N=m.slice(0,5).join(", "),D=m.length>5?`, \u2026 +${m.length-5} more`:"";console.warn(`[env] warning: skipped ${m.length} encrypted value(s) in ${E} (${g} not set; defaults apply): ${N}${D}`)}let T=0;for(let[g,N]of Object.entries(v)){if(g==="DOTENV_PUBLIC_KEY")continue;let D=process.env[g],C=U(D)&&N!==D;if(t||!f.has(g)||C)process.env[g]=N,d++,T++}if(!o&&!process.env.__ENV_LOADED__)console.error(`[env] loaded ${T}/${Object.keys(v).length} variables from ${E}`),process.env.__ENV_LOADED__="1"}catch(y){u.push(`Failed to load ${l}: ${y instanceof Error?y.message:"Unknown error"}`)}}return{loaded:d,errors:u}}function Ze(e={}){return{name:"env-plugin",setup(n){ge(e)}}}function Un(e={}){let n=L(e.env)||L("development")||L(process.env.DOTENV_ENV)||L(process.env.APP_ENV)||"development",t=e.cwd||process.cwd();if(Y!==n)Y=n,M=null,F=void 0;let r=[],i=(a)=>{if(!r.includes(a)&&x(V(t,a)))r.push(a)};i(".env"),i(".env.local");let s=`.env.${n}`,o=`.env.${n}.local`;return i(s),i(o),ge({...e,path:r,env:n})}var Bn=Ze;import{existsSync as S,readFileSync as I,renameSync as Z,rmSync as _e,writeFileSync as h}from"fs";import{basename as ee,dirname as me,resolve as A}from"path";function Yn(e,n){if(e)return e;let t=String(n??"").trim();if(!t||t==="development"||t==="dev"||t==="local")return"";if(!/^[\w.-]+$/.test(t))return"";return`.env.${t}`}function Re(e,n,t,r){let i=["#/-------------------[DOTENV_PUBLIC_KEY]--------------------/","#/ versioned X25519 encryption for .env files /","#/ [how it works](https://stacksjs.com/encryption) /","#/----------------------------------------------------------/",`${t}="${n}"`,""],s=!1;for(let o of e.split(`
5
+ `)){let a=o.trim();if(a.startsWith("#/"))continue;if(a.startsWith("DOTENV_PUBLIC_KEY"))continue;if(!a&&!s)continue;if(!a||a.startsWith("#")){s=!0,i.push(o);continue}s=!0;let c=a.match(/^([^=]+)=(.*)$/);if(!c||c[1]===void 0||c[2]===void 0){i.push(o);continue}let u=c[1].trim(),d=c[2].trim();if(d.startsWith('"')&&d.endsWith('"')||d.startsWith("'")&&d.endsWith("'"))d=d.slice(1,-1);if((!r.key||u.includes(r.key))&&(!r.excludeKey||!u.includes(r.excludeKey))&&!d.startsWith("encrypted:"))d=B(d,n);i.push(`${u}="${d}"`)}return i.join(`
6
+ `)}function W(e){let t=((e||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase();return{publicKeyName:t?`DOTENV_PUBLIC_KEY_${t}`:"DOTENV_PUBLIC_KEY",privateKeyName:t?`DOTENV_PRIVATE_KEY_${t}`:"DOTENV_PRIVATE_KEY"}}function ye(e,n){let t,r=!1;for(let i of e.split(`
7
+ `)){let s=i.trim();if(!s||s.startsWith("#"))continue;let o=s.match(/^([^=]+)=(.*)$/);if(!o?.[1]||o[2]===void 0)continue;let a=o[1].trim(),c=o[2].trim().replace(/^["']|["']$/g,"");if(a===n)t=c;else if(c.startsWith("encrypted:")||c.startsWith("enc:"))r=!0}return t&&r?t:void 0}function Wn(e={}){let n=e.cwd||process.cwd(),t=A(n,e.file||".env"),r=A(n,e.keysFile||".env.keys");if(!S(t))return{success:!1,error:`File not found: ${t}`};try{let i,s,o=ye(I(t,"utf-8"),W(e.file).publicKeyName);if(o)i=o;else if(S(r)){let u=I(r,"utf-8"),{parsed:d}=O(u),{publicKeyName:f,privateKeyName:p}=W(e.file);if(i=d[f]||"",s=d[p]||"",!i||!s){let E=k();i=E.publicKey,s=E.privateKey;let l=`
8
+ ${f}="${i}"
9
+ ${p}="${s}"
10
+ `;h(r,u+l,"utf-8")}}else{let u=k();i=u.publicKey,s=u.privateKey;let{publicKeyName:d,privateKeyName:f}=W(e.file),p=`# .env.keys - Keep this file secure and never commit to source control
11
+ ${d}="${i}"
12
+ ${f}="${s}"
13
+ `;h(r,p,"utf-8")}let a=I(t,"utf-8"),c=Re(a,i,W(e.file).publicKeyName,e);if(e.stdout)return{success:!0,output:c};return h(t,c,"utf-8"),{success:!0,output:`\u2714 encrypted (${e.file||".env"})
14
+ \u2714 key added to ${e.keysFile||".env.keys"}`}}catch(i){return{success:!1,error:`Failed to encrypt: ${i instanceof Error?i.message:"Unknown error"}`}}}function en(e={}){let n=e.cwd||process.cwd(),t=A(n,e.file||".env"),r=A(n,e.keysFile||".env.keys");if(!S(t))return{success:!1,error:`File not found: ${t}`};if(!S(r))return{success:!1,error:`Keys file not found: ${r}`};try{let i=I(r,"utf-8"),{parsed:s}=O(i),c=((e.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),u=c?`DOTENV_PRIVATE_KEY_${c}`:"DOTENV_PRIVATE_KEY",d=s[u];if(!d)return{success:!1,error:`Private key not found: ${u}`};let f=[d];if(u!=="DOTENV_PRIVATE_KEY"&&s.DOTENV_PRIVATE_KEY&&s.DOTENV_PRIVATE_KEY!==d)f.push(s.DOTENV_PRIVATE_KEY);let E=I(t,"utf-8").split(`
15
+ `),l=[];for(let v of E){let R=v.trim();if(!R||R.startsWith("#")){l.push(v);continue}if(R.startsWith("DOTENV_PUBLIC_KEY"))continue;let m=R.match(/^([^=]+)=(.*)$/);if(!m||m[1]===void 0||m[2]===void 0){l.push(v);continue}let T=m[1].trim(),g=m[2].trim();if(g.startsWith('"')&&g.endsWith('"')||g.startsWith("'")&&g.endsWith("'"))g=g.slice(1,-1);let N=g.startsWith("encrypted:");if(e.key&&!T.includes(e.key))N=!1;if(N){let D,C;for(let Pe of f)try{D=K(g,Pe);break}catch(De){C=De}if(D===void 0)throw C instanceof Error?C:Error(`Unable to decrypt ${T}`);g=D}l.push(`${T}="${g}"`)}let y=l.join(`
16
+ `);if(e.stdout)return{success:!0,output:y};return h(t,y,"utf-8"),{success:!0,output:`\u2714 decrypted (${e.file||".env"})`}}catch(i){return{success:!1,error:`Failed to decrypt: ${i instanceof Error?i.message:"Unknown error"}`}}}function Gn(e,n,t={}){let r=t.cwd||process.cwd(),i=A(r,t.file||".env"),s=A(r,t.keysFile||".env.keys");try{let o="";if(S(i))o=I(i,"utf-8");let a=o.split(`
17
+ `),c=!1,u,p=((t.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),E=p?`DOTENV_PUBLIC_KEY_${p}`:"DOTENV_PUBLIC_KEY",l=p?`DOTENV_PRIVATE_KEY_${p}`:"DOTENV_PRIVATE_KEY";for(let R of a){let m=R.trim();if(m.startsWith(`${E}=`)){let T=m.match(/^[^=]+=["']?([^"'\n]+)["']?/);if(T)u=T[1];break}}if(u){let R=!1;if(S(s)){let m=I(s,"utf-8"),{parsed:T}=O(m);R=Boolean(T[l])}if(!R&&!process.env[l]&&!ye(o,E))u=void 0}if(!t.plain&&!u){let R=k();u=R.publicKey;let m="";if(S(s))m=I(s,"utf-8");m+=`
18
+ ${E}="${R.publicKey}"
19
+ ${l}="${R.privateKey}"
20
+ `,h(s,m,"utf-8");for(let T=a.length-1;T>=0;T--){let g=a[T];if(g!==void 0&&g.trim().startsWith(`${E}=`))a.splice(T,1)}a.unshift(`${E}="${u}"`)}let y=n;if(!t.plain&&u)y=B(n,u);for(let R=0;R<a.length;R++){let m=a[R];if(m===void 0)continue;if(m.trim().startsWith(`${e}=`)){a[R]=`${e}="${y}"`,c=!0;break}}if(!c)a.push(`${e}="${y}"`);let v=a.join(`
21
+ `);return h(i,v,"utf-8"),{success:!0,output:`set ${e}${t.plain?"":" with encryption"} (${t.file||".env"})`}}catch(o){return{success:!1,error:`Failed to set: ${o instanceof Error?o.message:"Unknown error"}`}}}function Hn(e,n={}){let t=n.cwd||process.cwd(),r=A(t,n.file||".env");if(!S(r))return{success:!1,error:`File not found: ${r}`};try{let i,s=A(t,n.keysFile||".env.keys"),c=((n.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),u=c?`DOTENV_PRIVATE_KEY_${c}`:"DOTENV_PRIVATE_KEY";if(S(s)){let l=I(s,"utf-8"),{parsed:y}=O(l);i=y[u]}if(!i)i=process.env[u];let d=I(r,"utf-8"),{parsed:f}=O(d,{privateKey:i});if(e&&!n.all){let l=f[e];if(l===void 0)return{success:!1,error:`Key not found: ${e}`};return{success:!0,output:l}}let p=n.all?{...process.env,...f}:f,E;switch(n.format){case"shell":E=Object.entries(p).map(([l,y])=>`${l}=${y}`).join(" ");break;case"eval":E=Object.entries(p).map(([l,y])=>`${l}="${y}"`).join(`
22
+ `);break;case"json":default:E=n.prettyPrint?JSON.stringify(p,null,2):JSON.stringify(p);break}return{success:!0,output:E}}catch(i){return{success:!1,error:`Failed to get: ${i instanceof Error?i.message:"Unknown error"}`}}}function Xn(e,n={}){let t=n.cwd||process.cwd(),r=A(t,n.keysFile||".env.keys");if(!S(r))return{success:!1,error:`Keys file not found: ${r}`};try{let i=I(r,"utf-8"),{parsed:s}=O(i),c=((n.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),u=c?`DOTENV_PUBLIC_KEY_${c}`:"DOTENV_PUBLIC_KEY",d=c?`DOTENV_PRIVATE_KEY_${c}`:"DOTENV_PRIVATE_KEY";if(e){let E=s[e];if(!E)return{success:!1,error:`Key not found: ${e}`};return{success:!0,output:E}}let f={[u]:s[u],[d]:s[d]};return{success:!0,output:n.format==="shell"?`${u}=${f[u]} ${d}=${f[d]}`:JSON.stringify(f)}}catch(i){return{success:!1,error:`Failed to get keypair: ${i instanceof Error?i.message:"Unknown error"}`}}}function jn(e={}){let n=en({file:e.file,keysFile:e.keysFile,stdout:!0,cwd:e.cwd});if(!n.success)return n;let t=e.cwd||process.cwd(),r=A(t,e.file||".env"),i=A(t,e.keysFile||".env.keys");if(n.output===void 0)return{success:!1,error:"Rotation produced no decrypted content"};let s=k(),o=I(r),a=I(i),c=a.toString("utf8").split(`
23
+ `),u=e.file||".env",d=ee(u).replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),f=d?`DOTENV_PUBLIC_KEY_${d}`:"DOTENV_PUBLIC_KEY",p=d?`DOTENV_PRIVATE_KEY_${d}`:"DOTENV_PRIVATE_KEY",E=!1,l=!1;for(let g=0;g<c.length;g++){let N=c[g];if(N===void 0)continue;if(N.startsWith(`${f}=`))c[g]=`${f}="${s.publicKey}"`,E=!0;else if(N.startsWith(`${p}=`))c[g]=`${p}="${s.privateKey}"`,l=!0}if(!E)c.push(`${f}="${s.publicKey}"`);if(!l)c.push(`${p}="${s.privateKey}"`);let y=`${c.join(`
24
24
  `).replace(/\n+$/,"")}
25
- `,T=R2(g.output,D.publicKey,j,f),I=`${process.pid}-${Date.now()}`,R=L(Q2(X),`.${e(X)}.${I}.rotate`),U=L(Q2($),`.${e($)}.${I}.rotate`);try{if(E(R,T,{encoding:"utf8",mode:384}),E(U,w,{encoding:"utf8",mode:384}),f.stdout)return a(U,$),{success:!0,output:T};a(R,X);try{a(U,$)}catch(Z){throw E(X,H),Z}return{success:!0,output:`\u2714 rotated (${f.file||".env"}) to encrypted:v2`}}catch(Z){if(!V($))E($,W,{mode:384});return{success:!1,error:`Failed to rotate without exposing plaintext: ${Z instanceof Error?Z.message:"Unknown error"}`}}finally{Z2(R,{force:!0}),Z2(U,{force:!0})}}import Q from"process";import{platform as ff}from"os";var w2=typeof Bun<"u",U2=typeof Q<"u"&&Q.versions?.node!==void 0,gf=w2?"bun":U2?"node":"unknown",af={name:gf,version:w2?Bun.version:U2?Q.version:void 0},g2=ff(),ef=g2==="win32",f5=g2==="darwin",g5=g2==="linux",T2=Boolean(Q.stdout?.isTTY),B5=typeof globalThis.window<"u",Bf=Boolean(Q.env.CI||Q.env.CONTINUOUS_INTEGRATION||Q.env.BUILD_NUMBER||Q.env.RUN_ID),$5=Boolean(Q.env.DEBUG||Q.env.VERBOSE||Q.argv.includes("--debug")||Q.argv.includes("--verbose")),$f=Bf||!T2,X5=Boolean(!$f&&(T2||Q.env.COLORTERM||Q.env.FORCE_COLOR||Q.env.TERM&&Q.env.TERM!=="dumb")),f2={github:{name:"GitHub Actions",detected:Boolean(Q.env.GITHUB_ACTIONS)},gitlab:{name:"GitLab CI",detected:Boolean(Q.env.GITLAB_CI)},circle:{name:"CircleCI",detected:Boolean(Q.env.CIRCLECI)},travis:{name:"Travis CI",detected:Boolean(Q.env.TRAVIS)},jenkins:{name:"Jenkins",detected:Boolean(Q.env.JENKINS_URL)},vercel:{name:"Vercel",detected:Boolean(Q.env.VERCEL)},netlify:{name:"Netlify",detected:Boolean(Q.env.NETLIFY)},heroku:{name:"Heroku",detected:Boolean(Q.env.DYNO)},aws:{name:"AWS",detected:Boolean(Q.env.AWS_REGION||Q.env.AWS_LAMBDA_FUNCTION_NAME)},azure:{name:"Azure",detected:Boolean(Q.env.AZURE_HTTP_USER_AGENT)},cloudflare:{name:"Cloudflare",detected:Boolean(Q.env.CF_PAGES)},railway:{name:"Railway",detected:Boolean(Q.env.RAILWAY_ENVIRONMENT)},render:{name:"Render",detected:Boolean(Q.env.RENDER)}},Xf=Object.keys(f2).find((f)=>{let g=f2[f];return g!==void 0&&g.detected})||"unknown",D5=f2[Xf]||{name:"Unknown",detected:!1};import Yf from"process";import{projectPath as jf}from"@stacksjs/path";import q2 from"fs";var L2={APP_ENV:["local","dev","development","staging","prod","production"],DB_CONNECTION:["mysql","sqlite","postgres","singlestore","vitess","dynamodb"],DB_MIGRATE_FRESH:["allow","confirm","disabled"],MAIL_MAILER:["smtp","mailgun","ses","log","sendgrid","mailtrap","capture"],SEARCH_ENGINE_DRIVER:["opensearch","meilisearch","algolia","typesense"],FRONTEND_APP_ENV:["development","staging","production"]};function Df(f){return typeof f==="string"&&(f.startsWith("encrypted:")||f.startsWith("enc:"))}var Hf=/(?:^|_)(?:PASSWORD|PASSWD|SECRET|TOKEN|APIKEY|API_KEY|CREDENTIAL|CREDENTIALS|PRIVATE|SALT|DSN|CERT|CIPHER|SIGNATURE|WEBHOOK)(?:_|$)|(?:^|_)(?:ACCESS|SECRET)_KEY(?:_|$)|_KEY$/,_f=/^DOTENV_(?:PUBLIC|PRIVATE)_KEY/,Af=/^\$\{[^}]+\}$/;function A5(f,g={}){let{placeholders:B={},strict:X=!1}=g,$=[];for(let[D,H]of Object.entries(f)){if(!H||H.trim()==="")continue;if(Df(H))continue;if(_f.test(D))continue;if(Af.test(H.trim()))continue;let W=Hf.test(D),A=Object.prototype.hasOwnProperty.call(B,D)&&B[D]===H;if(W&&!A){$.push({key:D,reason:"plaintext-secret"});continue}if(X)$.push({key:D,reason:"plaintext-value"})}return $}function W5(f){return f.split(`
26
- `).map((g)=>g.trim()).filter(Boolean).filter((g)=>g!==".env.example").filter((g)=>/(?:^|\/)\.env(?:\.|$)/.test(g)).filter((g)=>!g.endsWith(".env.keys"))}function M2(f){return`${f.replace(/[^a-z0-9]/gi,"").toUpperCase()}_`}function Wf(f,g={}){let B=g.self?M2(g.self):void 0,X=(g.tenants??[]).filter((H)=>typeof H==="string"&&H.trim().length>0).map((H)=>({slug:H,prefix:M2(H)})).filter(({prefix:H})=>H!==B).sort((H,W)=>W.prefix.length-H.prefix.length),$={},D={};for(let[H,W]of Object.entries(f)){let A=X.find(({prefix:Y})=>H.startsWith(Y));if(A){(D[A.slug]??={})[H]=W;continue}$[H]=W}return{own:$,foreign:D}}function j5(f,g={}){return Wf(f,g).own}function G5(f){return Object.entries(f.foreign).map(([g,B])=>({tenant:g,keys:Object.keys(B).sort()})).sort((g,B)=>g.tenant.localeCompare(B.tenant))}var z2=new Set;function Gf(f){if(z2.has(f))return;z2.add(f);let g=s();console.warn(`[env] warning: "${f}" is encrypted but no usable private key was found for the "${g}" environment; falling back to its default. Set DOTENV_PRIVATE_KEY_${g.toUpperCase()} or ship .env.keys.`)}var Jf={get:(f,g)=>{let B=f[g];if(typeof B==="string"&&(B.startsWith("encrypted:")||B.startsWith("enc:"))){let $=J2(B);if($===void 0){Gf(g),delete f[g];return}f[g]=$,B=$}let X=["_PORT","_TIMEOUT","_TTL","_SIZE","_LIMIT","_MAX","_MIN","_INTERVAL","_RETRIES","_CONCURRENCY","_WORKERS","_CONNECTIONS"];if(typeof B==="string"&&/^\d+$/.test(B)&&!B.startsWith("0")&&X.some(($)=>g.endsWith($)))return Number(B);if(typeof B==="string"){let $=B.toLowerCase();if($==="true")return!0;if($==="false")return!1}return B}};function Of(){return typeof Bun<"u"?Bun.env:Yf.env}var V2=new Proxy(Of(),Jf);function w5(f,g,B){let X=B?.path||jf(".env"),D=q2.readFileSync(X,"utf-8").split(`
27
- `),H=D.findIndex((Y)=>Y.startsWith(`${f}=`)),A=/[\s"'#$\\]/.test(g)?`"${g.replace(/"/g,"\\\"")}"`:g;if(H!==-1)D[H]=`${f}=${A}`;else D.push(`${f}=${A}`);q2.writeFileSync(X,D.join(`
28
- `))}function U5(f=V2){let g=[];for(let[B,X]of Object.entries(L2)){let $=f[B];if($!==void 0&&$!==""&&!X.includes(String($)))g.push(`${B}="${$}" is not valid. Allowed values: ${X.join(", ")}`)}return g}function T5(f,g=V2){let B=[];for(let X of f){let $=g[X];if($===void 0||$===""||$===null)B.push(X)}if(B.length>0)throw Error(`[env] Missing required environment variable(s): ${B.join(", ")}. Set them in .env or your process environment before booting.`);return g}export{w5 as writeEnv,U5 as validateEnv,W5 as trackedEnvFiles,M2 as tenantEnvPrefix,j5 as stripForeignTenantEnv,yf as setEnv,af as runtimeInfo,gf as runtime,of as rotateKeypair,I2 as reusableEnvPublicKey,s2 as resolvePrivateKey,rf as resolveEnvFile,Kf as resetPrivateKeyCache,T5 as requireEnv,D5 as providerInfo,Xf as provider,Of as process,g2 as platform,A5 as plaintextSecrets,Wf as partitionTenantEnv,wf as parseEnvFromKey,q as parse,If as migrateEncryptedValue,Vf as loadEnvFiles,O2 as loadEnv,ef as isWindows,U2 as isNode,$f as isMinimal,f5 as isMacOS,g5 as isLinux,Rf as isLegacyEncryptedValue,Df as isEncryptedValue,$5 as isDebug,X5 as isColorSupported,Bf as isCI,w2 as isBun,B5 as hasWindow,T2 as hasTTY,i as getPrivateKey,nf as getKeypair,vf as getEnv,K as generateKeypair,G5 as foreignTenantKeys,a2 as envPlugin,t as envKeyNames,L2 as envEnum,V2 as env,k as encryptValue,tf as encryptEnv,h as decryptValue,J2 as decryptEnvValue,e2 as decryptEnv,Nf as autoLoadEnv,Qf as aesEncrypt,F2 as aesDecrypt,s as activeEnvName,Hf as SECRET_NAME_PATTERN};
25
+ `,v=Re(n.output,s.publicKey,f,e),R=`${process.pid}-${Date.now()}`,m=A(me(r),`.${ee(r)}.${R}.rotate`),T=A(me(i),`.${ee(i)}.${R}.rotate`);try{if(h(m,v,{encoding:"utf8",mode:384}),h(T,y,{encoding:"utf8",mode:384}),e.stdout)return Z(T,i),{success:!0,output:v};Z(m,r);try{Z(T,i)}catch(g){throw h(r,o),g}return{success:!0,output:`\u2714 rotated (${e.file||".env"}) to encrypted:v2`}}catch(g){if(!S(i))h(i,a,{mode:384});return{success:!1,error:`Failed to rotate without exposing plaintext: ${g instanceof Error?g.message:"Unknown error"}`}}finally{_e(m,{force:!0}),_e(T,{force:!0})}}import _ from"process";import{platform as nn}from"os";var Te=typeof Bun<"u",ve=typeof _<"u"&&_.versions?.node!==void 0,tn=Te?"bun":ve?"node":"unknown",Jn={name:tn,version:Te?Bun.version:ve?_.version:void 0},te=nn(),Zn=te==="win32",et=te==="darwin",nt=te==="linux",Ae=Boolean(_.stdout?.isTTY),tt=typeof globalThis.window<"u",rn=Boolean(_.env.CI||_.env.CONTINUOUS_INTEGRATION||_.env.BUILD_NUMBER||_.env.RUN_ID),rt=Boolean(_.env.DEBUG||_.env.VERBOSE||_.argv.includes("--debug")||_.argv.includes("--verbose")),sn=rn||!Ae,it=Boolean(!sn&&(Ae||_.env.COLORTERM||_.env.FORCE_COLOR||_.env.TERM&&_.env.TERM!=="dumb")),ne={github:{name:"GitHub Actions",detected:Boolean(_.env.GITHUB_ACTIONS)},gitlab:{name:"GitLab CI",detected:Boolean(_.env.GITLAB_CI)},circle:{name:"CircleCI",detected:Boolean(_.env.CIRCLECI)},travis:{name:"Travis CI",detected:Boolean(_.env.TRAVIS)},jenkins:{name:"Jenkins",detected:Boolean(_.env.JENKINS_URL)},vercel:{name:"Vercel",detected:Boolean(_.env.VERCEL)},netlify:{name:"Netlify",detected:Boolean(_.env.NETLIFY)},heroku:{name:"Heroku",detected:Boolean(_.env.DYNO)},aws:{name:"AWS",detected:Boolean(_.env.AWS_REGION||_.env.AWS_LAMBDA_FUNCTION_NAME)},azure:{name:"Azure",detected:Boolean(_.env.AZURE_HTTP_USER_AGENT)},cloudflare:{name:"Cloudflare",detected:Boolean(_.env.CF_PAGES)},railway:{name:"Railway",detected:Boolean(_.env.RAILWAY_ENVIRONMENT)},render:{name:"Render",detected:Boolean(_.env.RENDER)}},on=Object.keys(ne).find((e)=>{let n=ne[e];return n!==void 0&&n.detected})||"unknown",st=ne[on]||{name:"Unknown",detected:!1};import En from"process";import{projectPath as ln}from"@stacksjs/path";import Ne from"fs";var Ie={APP_ENV:["local","dev","development","staging","prod","production"],DB_CONNECTION:["mysql","sqlite","postgres","singlestore","vitess","dynamodb"],DB_MIGRATE_FRESH:["allow","confirm","disabled"],MAIL_MAILER:["smtp","mailgun","ses","log","sendgrid","mailtrap","capture"],SEARCH_ENGINE_DRIVER:["opensearch","meilisearch","algolia","typesense"],FRONTEND_APP_ENV:["development","staging","production"]};function dn(e){return typeof e==="string"&&(e.startsWith("encrypted:")||e.startsWith("enc:"))}var cn=/(?:^|_)(?:PASSWORD|PASSWD|SECRET|TOKEN|APIKEY|API_KEY|CREDENTIAL|CREDENTIALS|PRIVATE|SALT|DSN|CERT|CIPHER|SIGNATURE|WEBHOOK)(?:_|$)|(?:^|_)(?:ACCESS|SECRET)_KEY(?:_|$)|_KEY$/,an=/^DOTENV_(?:PUBLIC|PRIVATE)_KEY/,un=/^\$\{[^}]+\}$/;function ct(e,n={}){let{placeholders:t={},strict:r=!1}=n,i=[];for(let[s,o]of Object.entries(e)){if(!o||o.trim()==="")continue;if(dn(o))continue;if(an.test(s))continue;if(un.test(o.trim()))continue;let a=cn.test(s),c=Object.prototype.hasOwnProperty.call(t,s)&&t[s]===o;if(a&&!c){i.push({key:s,reason:"plaintext-secret"});continue}if(r)i.push({key:s,reason:"plaintext-value"})}return i}function at(e){return e.split(`
26
+ `).map((n)=>n.trim()).filter(Boolean).filter((n)=>n!==".env.example").filter((n)=>/(?:^|\/)\.env(?:\.|$)/.test(n)).filter((n)=>!n.endsWith(".env.keys"))}function Oe(e){return`${e.replace(/[^a-z0-9]/gi,"").toUpperCase()}_`}function fn(e,n={}){let t=n.self?Oe(n.self):void 0,r=(n.tenants??[]).filter((o)=>typeof o==="string"&&o.trim().length>0).map((o)=>({slug:o,prefix:Oe(o)})).filter(({prefix:o})=>o!==t).sort((o,a)=>a.prefix.length-o.prefix.length),i={},s={};for(let[o,a]of Object.entries(e)){let c=r.find(({prefix:u})=>o.startsWith(u));if(c){(s[c.slug]??={})[o]=a;continue}i[o]=a}return{own:i,foreign:s}}function ft(e,n={}){return fn(e,n).own}function Et(e){return Object.entries(e.foreign).map(([n,t])=>({tenant:n,keys:Object.keys(t).sort()})).sort((n,t)=>n.tenant.localeCompare(t.tenant))}var Se=new Set;function pn(e){if(Se.has(e))return;Se.add(e);let n=J();console.warn(`[env] warning: "${e}" is encrypted but no usable private key was found for the "${n}" environment; falling back to its default. Set DOTENV_PRIVATE_KEY_${n.toUpperCase()} or ship .env.keys.`)}var gn={get:(e,n)=>{let t=e,r=t[n];if(typeof r==="string"&&(r.startsWith("encrypted:")||r.startsWith("enc:"))){let s=pe(r);if(s===void 0){pn(n),delete t[n];return}t[n]=s,r=s}let i=["_PORT","_TIMEOUT","_TTL","_SIZE","_LIMIT","_MAX","_MIN","_INTERVAL","_RETRIES","_CONCURRENCY","_WORKERS","_CONNECTIONS"];if(typeof r==="string"&&/^\d+$/.test(r)&&!r.startsWith("0")&&i.some((s)=>n.endsWith(s)))return Number(r);if(typeof r==="string"){let s=r.toLowerCase();if(s==="true")return!0;if(s==="false")return!1}return r}};function _n(){return typeof Bun<"u"?Bun.env:En.env}var he=new Proxy(_n(),gn);function yt(e,n,t){let r=t?.path||ln(".env"),s=Ne.readFileSync(r,"utf-8").split(`
27
+ `),o=s.findIndex((u)=>u.startsWith(`${e}=`)),c=/[\s"'#$\\]/.test(n)?`"${n.replace(/"/g,"\\\"")}"`:n;if(o!==-1)s[o]=`${e}=${c}`;else s.push(`${e}=${c}`);Ne.writeFileSync(r,s.join(`
28
+ `))}function Tt(e=he){let n=[],t=e;for(let[r,i]of Object.entries(Ie)){let s=t[r];if(s!==void 0&&s!==""&&!i.includes(String(s)))n.push(`${r}="${s}" is not valid. Allowed values: ${i.join(", ")}`)}return n}function vt(e,n=he){let t=[],r=n;for(let i of e){let s=r[i];if(s===void 0||s===""||s===null)t.push(i)}if(t.length>0)throw Error(`[env] Missing required environment variable(s): ${t.join(", ")}. Set them in .env or your process environment before booting.`);return n}export{cn as SECRET_NAME_PATTERN,J as activeEnvName,be as aesDecrypt,Rn as aesEncrypt,Un as autoLoadEnv,en as decryptEnv,pe as decryptEnvValue,K as decryptValue,Wn as encryptEnv,B as encryptValue,he as env,Ie as envEnum,W as envKeyNames,Ze as envPlugin,Et as foreignTenantKeys,k as generateKeypair,Hn as getEnv,Xn as getKeypair,q as getPrivateKey,Ae as hasTTY,tt as hasWindow,Te as isBun,rn as isCI,it as isColorSupported,rt as isDebug,dn as isEncryptedValue,yn as isLegacyEncryptedValue,nt as isLinux,et as isMacOS,sn as isMinimal,ve as isNode,Zn as isWindows,ge as loadEnv,Pn as loadEnvFiles,Tn as migrateEncryptedValue,O as parse,vn as parseEnvFromKey,fn as partitionTenantEnv,ct as plaintextSecrets,te as platform,_n as process,on as provider,st as providerInfo,vt as requireEnv,$n as resetPrivateKeyCache,Yn as resolveEnvFile,Je as resolvePrivateKey,ye as reusableEnvPublicKey,jn as rotateKeypair,tn as runtime,Jn as runtimeInfo,Gn as setEnv,ft as stripForeignTenantEnv,Oe as tenantEnvPrefix,at as trackedEnvFiles,Tt as validateEnv,yt as writeEnv};
package/dist/parser.js CHANGED
@@ -1,4 +1,4 @@
1
1
  // @bun
2
- import{createCipheriv as T,createDecipheriv as $,createHash as K,createPrivateKey as O,createPublicKey as m,diffieHellman as A,generateKeyPairSync as _,hkdfSync as R,randomBytes as k}from"crypto";var B="x25519-public:",d="x25519-private:",w="encrypted:v2:",x="encrypted:",b=Buffer.from("stacks-env:v2","utf8"),D="Environment decryption failed: authentication or format error";function I(n){if(n.length===32)return n;return K("sha256").update(n).digest()}function it(n,r){let t=k(16),i=T("aes-256-gcm",I(r),t);return{ciphertext:Buffer.concat([i.update(n,"utf8"),i.final()]).toString("hex"),iv:t.toString("hex"),authTag:i.getAuthTag().toString("hex")}}function Y(n,r,t,i){try{let e=$("aes-256-gcm",I(r),Buffer.from(t,"hex"));return e.setAuthTag(Buffer.from(i,"hex")),Buffer.concat([e.update(Buffer.from(n,"hex")),e.final()]).toString("utf8")}catch{throw Error("Decryption failed: authentication or format error")}}function p(n){return n.toString("base64url")}function E(n,r,t){if(typeof n!=="string"||n.length>0&&!/^[A-Za-z0-9_-]+$/.test(n))throw Error(`${r} is not canonical base64url`);let i=Buffer.from(n,"base64url");if(p(i)!==n)throw Error(`${r} is not canonical base64url`);if(t!==void 0&&i.length!==t)throw Error(`${r} has an invalid length`);return i}function v(n){if(!n.startsWith(B))throw Error("Legacy or invalid public key. Run buddy env:rotate before creating new ciphertext.");let r=m({key:E(n.slice(B.length),"public key"),format:"der",type:"spki"});if(r.asymmetricKeyType!=="x25519")throw Error("Public key is not X25519");return r}function F(n){if(!n.startsWith(d))throw Error("Private key is not a version 2 X25519 key");let r=O({key:E(n.slice(d.length),"private key"),format:"der",type:"pkcs8"});if(r.asymmetricKeyType!=="x25519")throw Error("Private key is not X25519");return r}function S(n){return Buffer.from(`stacks-env:v2;${n.epk};${n.salt};${n.nonce}`,"utf8")}function et(){let{publicKey:n,privateKey:r}=_("x25519"),t=n.export({format:"der",type:"spki"}),i=r.export({format:"der",type:"pkcs8"});return{publicKey:`${B}${p(t)}`,privateKey:`${d}${p(i)}`}}function H(n,r){let t=v(r),i=_("x25519"),e=p(i.publicKey.export({format:"der",type:"spki"})),o=k(16),c=k(12),s=A({privateKey:i.privateKey,publicKey:t}),y=Buffer.from(R("sha256",s,o,b,32)),g={epk:e,salt:p(o),nonce:p(c)};try{let f=T("aes-256-gcm",y,c);f.setAAD(S(g));let h=Buffer.concat([f.update(n,"utf8"),f.final()]),u={v:2,...g,ciphertext:p(h),tag:p(f.getAuthTag())};return`${w}${p(Buffer.from(JSON.stringify(u),"utf8"))}`}finally{s.fill(0),y.fill(0)}}function N(n){let r=E(n.slice(w.length),"envelope").toString("utf8"),t=JSON.parse(r);if(!t||typeof t!=="object"||Array.isArray(t))throw Error("envelope is not an object");let i=Object.keys(t).sort(),e=["ciphertext","epk","nonce","salt","tag","v"];if(i.length!==e.length||i.some((o,c)=>o!==e[c]))throw Error("envelope fields are invalid");if(t.v!==2)throw Error("envelope version is unsupported");for(let o of["epk","salt","nonce","ciphertext","tag"])if(typeof t[o]!=="string")throw Error(`${o} is invalid`);return t}function W(n,r){try{let t=N(n),i=F(r),e=m({key:E(t.epk,"ephemeral public key"),format:"der",type:"spki"});if(e.asymmetricKeyType!=="x25519")throw Error("ephemeral key is not X25519");let o=E(t.salt,"salt",16),c=E(t.nonce,"nonce",12),s=E(t.ciphertext,"ciphertext"),y=E(t.tag,"tag",16),g=A({privateKey:i,publicKey:e}),f=Buffer.from(R("sha256",g,o,b,32));try{let h=$("aes-256-gcm",f,c);return h.setAAD(S(t)),h.setAuthTag(y),Buffer.concat([h.update(s),h.final()]).toString("utf8")}finally{g.fill(0),f.fill(0)}}catch{throw Error(D)}}function j(n,r){try{if(!/^[0-9a-f]{64}$/.test(r))throw Error("invalid legacy private key");let t=Buffer.from(n.slice(x.length),"base64");if(t.length<32)throw Error("invalid legacy payload");let i=K("sha256").update(Buffer.from(r,"hex")).digest(),e=K("sha256").update(i).digest();return Y(t.subarray(32).toString("hex"),e,t.subarray(0,16).toString("hex"),t.subarray(16,32).toString("hex"))}catch{throw Error(D)}}function ot(n){return n.startsWith(x)&&!n.startsWith(w)}function P(n,r){if(!n.startsWith(x))return n;return n.startsWith(w)?W(n,r):j(n,r)}function ft(n,r,t){return H(P(n,r),t)}function ct(n){let r=n.match(/^DOTENV_PRIVATE_KEY_(.+)$/);return r&&r[1]?r[1].toLowerCase():""}function st(n=""){return n?process.env[`DOTENV_PRIVATE_KEY_${n.toUpperCase()}`]:process.env.DOTENV_PRIVATE_KEY}import{spawnSync as C}from"child_process";import{readFileSync as L}from"fs";import{arch as V,hostname as G,platform as X,release as J,userInfo as U}from"os";import{basename as z,dirname as Z,resolve as Q}from"path";function M(n,r={}){let t={},i=[],e=[],o=n.split(`
3
- `);for(let c of o){let s=c.trim();if(!s||s.startsWith("#"))continue;if(s.startsWith("DOTENV_PUBLIC_KEY=")){let h=s.match(/^DOTENV_PUBLIC_KEY=["']?([^"'\n]+)["']?/);if(h&&h[1]!==void 0)t.DOTENV_PUBLIC_KEY=h[1];continue}let y=s.match(/^([^=]+)=(.*)$/);if(!y||y[1]===void 0||y[2]===void 0)continue;let g=y[1].trim(),f=y[2].trim();if(f.startsWith('"')&&f.endsWith('"')||f.startsWith("'")&&f.endsWith("'")){if(f=f.slice(1,-1),f.includes("\\n"))f=f.replace(/\\n/g,`
4
- `)}if(f.startsWith("encrypted:")||f.startsWith("enc:")){if(!r.privateKey){e.push(g);continue}try{let h=f.startsWith("enc:")?`encrypted:${f.slice(4)}`:f;f=P(h,r.privateKey)}catch(h){i.push(`Failed to decrypt ${g}: ${h instanceof Error?h.message:"Unknown error"}`)}}f=q(f,{...r.processEnv||process.env,...t}),f=nt(f),t[g]=f}return{parsed:t,errors:i,skippedEncrypted:e}}function q(n,r){return n.replace(/\$\{([^}]+)\}/g,(t,i)=>{let e=i.match(/^([^:\-+]+)(:-|-)(.+)$/);if(e){let[,c,s,y]=e,g=r[c];if(s===":-")return g||y;else return g!==void 0?g:y}let o=i.match(/^([^:\-+]+)(:?\+)(.+)$/);if(o){let[,c,s,y]=o,g=r[c];if(s===":+")return g?y:"";else return g!==void 0?y:""}return r[i]||""})}var a=new Set(["date","hostname","whoami","uname","pwd","echo","printf","cat","basename","dirname"]);function l(n){let r=[],t="",i=null,e=!1;for(let o of n.trim()){if(e){t+=o,e=!1;continue}if(o==="\\"&&i!=="'"){e=!0;continue}if(i){if(o===i)i=null;else t+=o;continue}if(o==='"'||o==="'"){i=o;continue}if(/\s/.test(o)){if(t)r.push(t),t="";continue}t+=o}if(e||i)throw Error("Command substitution contains an unterminated escape or quote");if(t)r.push(t);return r}function tt(n){let[r,...t]=n,i=t.some((e)=>e.startsWith("-"));if(r==="echo"&&!i)return t.join(" ");if(r==="printf"&&t.length===1&&!t[0]?.includes("%"))return t[0];if(r==="hostname"&&t.length===0)return G();if(r==="whoami"&&t.length===0)return U().username;if(r==="pwd"&&t.length===0)return process.cwd();if(r==="basename"&&t.length===1)return z(t[0]??"");if(r==="dirname"&&t.length===1)return Z(t[0]??"");if(r==="cat"&&t.length>0&&!i)return t.map((e)=>L(Q(process.cwd(),e),"utf8")).join("");if(r==="uname"){let e=t[0]??"-s";if(t.length<=1&&e==="-m")return V();if(t.length<=1&&e==="-r")return J();if(t.length<=1&&e==="-s")return{aix:"AIX",android:"Android",cygwin:"CYGWIN",darwin:"Darwin",freebsd:"FreeBSD",haiku:"Haiku",linux:"Linux",netbsd:"NetBSD",openbsd:"OpenBSD",sunos:"SunOS",win32:"Windows_NT"}[X()]??X()}return}function nt(n){return n.replace(/\$\(([^)]+)\)/g,(r,t)=>{try{let i=l(t),e=i[0];if(!e||!a.has(e))return console.warn(`[env] Blocked command substitution for disallowed command: ${e}`),"";let o=tt(i);if(o!==void 0)return o.trim();let c=C(e,i.slice(1),{encoding:"utf8",env:process.env});if(c.status===0)return c.stdout.trim();let s=c.stderr.trim();console.warn(`[env] Command substitution failed (exit code ${c.status??"unknown"}): ${e}${s?`: ${s}`:""}`)}catch(i){console.warn(`[env] Command substitution error: ${i instanceof Error?i.message:String(i)}`)}return""})}async function ut(n,r={}){let t={},i=[],e=[];for(let o of n)try{let c=Bun.file(o);if(!c.size)continue;let s=await c.text(),{parsed:y,errors:g,skippedEncrypted:f}=M(s,r);i.push(...g),e.push(...f);for(let[h,u]of Object.entries(y)){if(!r.overload&&t[h]!==void 0)continue;t[h]=u}}catch(c){if(c?.code==="ENOENT")continue;i.push(`Failed to read ${o}: ${c instanceof Error?c.message:String(c)}`)}return{parsed:t,errors:i,skippedEncrypted:e}}export{M as parse,ut as loadEnvFiles};
2
+ import{createCipheriv as k,createDecipheriv as b,createHash as m,createPrivateKey as T,createPublicKey as K,diffieHellman as B,generateKeyPairSync as P,hkdfSync as _,randomBytes as y}from"crypto";var E="x25519-public:",w="x25519-private:",g="encrypted:v2:",v="encrypted:",S=Buffer.from("stacks-env:v2","utf8"),R="Environment decryption failed: authentication or format error";function V(t){if(t.length===32)return t;return m("sha256").update(t).digest()}function ie(t,r){let e=y(16),n=k("aes-256-gcm",V(r),e);return{ciphertext:Buffer.concat([n.update(t,"utf8"),n.final()]).toString("hex"),iv:e.toString("hex"),authTag:n.getAuthTag().toString("hex")}}function D(t,r,e,n){try{let i=b("aes-256-gcm",V(r),Buffer.from(e,"hex"));return i.setAuthTag(Buffer.from(n,"hex")),Buffer.concat([i.update(Buffer.from(t,"hex")),i.final()]).toString("utf8")}catch{throw Error("Decryption failed: authentication or format error")}}function l(t){return t.toString("base64url")}function d(t,r,e){if(typeof t!=="string"||t.length>0&&!/^[A-Za-z0-9_-]+$/.test(t))throw Error(`${r} is not canonical base64url`);let n=Buffer.from(t,"base64url");if(l(n)!==t)throw Error(`${r} is not canonical base64url`);if(e!==void 0&&n.length!==e)throw Error(`${r} has an invalid length`);return n}function A(t){if(!t.startsWith(E))throw Error("Legacy or invalid public key. Run buddy env:rotate before creating new ciphertext.");let r=K({key:d(t.slice(E.length),"public key"),format:"der",type:"spki"});if(r.asymmetricKeyType!=="x25519")throw Error("Public key is not X25519");return r}function W(t){if(!t.startsWith(w))throw Error("Private key is not a version 2 X25519 key");let r=T({key:d(t.slice(w.length),"private key"),format:"der",type:"pkcs8"});if(r.asymmetricKeyType!=="x25519")throw Error("Private key is not X25519");return r}function N(t){return Buffer.from(`stacks-env:v2;${t.epk};${t.salt};${t.nonce}`,"utf8")}function se(){let{publicKey:t,privateKey:r}=P("x25519"),e=t.export({format:"der",type:"spki"}),n=r.export({format:"der",type:"pkcs8"});return{publicKey:`${E}${l(e)}`,privateKey:`${w}${l(n)}`}}function C(t,r){let e=A(r),n=P("x25519"),i=l(n.publicKey.export({format:"der",type:"spki"})),s=y(16),a=y(12),c=B({privateKey:n.privateKey,publicKey:e}),f=Buffer.from(_("sha256",c,s,S,32)),u={epk:i,salt:l(s),nonce:l(a)};try{let o=k("aes-256-gcm",f,a);o.setAAD(N(u));let p=Buffer.concat([o.update(t,"utf8"),o.final()]),h={v:2,...u,ciphertext:l(p),tag:l(o.getAuthTag())};return`${g}${l(Buffer.from(JSON.stringify(h),"utf8"))}`}finally{c.fill(0),f.fill(0)}}function I(t){let r=d(t.slice(g.length),"envelope").toString("utf8"),e=JSON.parse(r);if(!e||typeof e!=="object"||Array.isArray(e))throw Error("envelope is not an object");let n=Object.keys(e).sort(),i=["ciphertext","epk","nonce","salt","tag","v"];if(n.length!==i.length||n.some((s,a)=>s!==i[a]))throw Error("envelope fields are invalid");if(e.v!==2)throw Error("envelope version is unsupported");for(let s of["epk","salt","nonce","ciphertext","tag"])if(typeof e[s]!=="string")throw Error(`${s} is invalid`);return e}function F(t,r){try{let e=I(t),n=W(r),i=K({key:d(e.epk,"ephemeral public key"),format:"der",type:"spki"});if(i.asymmetricKeyType!=="x25519")throw Error("ephemeral key is not X25519");let s=d(e.salt,"salt",16),a=d(e.nonce,"nonce",12),c=d(e.ciphertext,"ciphertext"),f=d(e.tag,"tag",16),u=B({privateKey:n,publicKey:i}),o=Buffer.from(_("sha256",u,s,S,32));try{let p=b("aes-256-gcm",o,a);return p.setAAD(N(e)),p.setAuthTag(f),Buffer.concat([p.update(c),p.final()]).toString("utf8")}finally{u.fill(0),o.fill(0)}}catch{throw Error(R)}}function L(t,r){try{if(!/^[0-9a-f]{64}$/.test(r))throw Error("invalid legacy private key");let e=Buffer.from(t.slice(v.length),"base64");if(e.length<32)throw Error("invalid legacy payload");let n=m("sha256").update(Buffer.from(r,"hex")).digest(),i=m("sha256").update(n).digest();return D(e.subarray(32).toString("hex"),i,e.subarray(0,16).toString("hex"),e.subarray(16,32).toString("hex"))}catch{throw Error(R)}}function oe(t){return t.startsWith(v)&&!t.startsWith(g)}function x(t,r){if(!t.startsWith(v))return t;return t.startsWith(g)?F(t,r):L(t,r)}function ae(t,r,e){return C(x(t,r),e)}function ce(t){let r=t.match(/^DOTENV_PRIVATE_KEY_(.+)$/);return r&&r[1]?r[1].toLowerCase():""}function fe(t=""){return t?process.env[`DOTENV_PRIVATE_KEY_${t.toUpperCase()}`]:process.env.DOTENV_PRIVATE_KEY}import{spawnSync as Y}from"child_process";import{readFileSync as X}from"fs";import{arch as j,hostname as U,platform as O,release as H,userInfo as z}from"os";import{basename as M,dirname as q,resolve as G}from"path";function J(t,r={}){let e={},n=[],i=[],s=t.split(`
3
+ `);for(let a of s){let c=a.trim();if(!c||c.startsWith("#"))continue;if(c.startsWith("DOTENV_PUBLIC_KEY=")){let p=c.match(/^DOTENV_PUBLIC_KEY=["']?([^"'\n]+)["']?/);if(p&&p[1]!==void 0)e.DOTENV_PUBLIC_KEY=p[1];continue}let f=c.match(/^([^=]+)=(.*)$/);if(!f||f[1]===void 0||f[2]===void 0)continue;let u=f[1].trim(),o=f[2].trim();if(o.startsWith('"')&&o.endsWith('"')||o.startsWith("'")&&o.endsWith("'")){if(o=o.slice(1,-1),o.includes("\\n"))o=o.replace(/\\n/g,`
4
+ `)}if(o.startsWith("encrypted:")||o.startsWith("enc:")){if(!r.privateKey){i.push(u);continue}try{let p=o.startsWith("enc:")?`encrypted:${o.slice(4)}`:o;o=x(p,r.privateKey)}catch(p){n.push(`Failed to decrypt ${u}: ${p instanceof Error?p.message:"Unknown error"}`)}}o=Z(o,{...r.processEnv||process.env,...e}),o=re(o),e[u]=o}return{parsed:e,errors:n,skippedEncrypted:i}}function Z(t,r){return t.replace(/\$\{([^}]+)\}/g,(e,n)=>{let i=n.match(/^([^:\-+]+)(:-|-)(.+)$/);if(i){let[,a,c,f]=i,u=r[a];if(c===":-")return u||f;else return u!==void 0?u:f}let s=n.match(/^([^:\-+]+)(:?\+)(.+)$/);if(s){let[,a,c,f]=s,u=r[a];if(c===":+")return u?f:"";else return u!==void 0?f:""}return r[n]||""})}var Q=new Set(["date","hostname","whoami","uname","pwd","echo","printf","cat","basename","dirname"]);function ee(t){let r=[],e="",n=null,i=!1;for(let s of t.trim()){if(i){e+=s,i=!1;continue}if(s==="\\"&&n!=="'"){i=!0;continue}if(n){if(s===n)n=null;else e+=s;continue}if(s==='"'||s==="'"){n=s;continue}if(/\s/.test(s)){if(e)r.push(e),e="";continue}e+=s}if(i||n)throw Error("Command substitution contains an unterminated escape or quote");if(e)r.push(e);return r}function te(t){let[r,...e]=t,n=e.some((i)=>i.startsWith("-"));if(r==="echo"&&!n)return e.join(" ");if(r==="printf"&&e.length===1&&!e[0]?.includes("%"))return e[0];if(r==="hostname"&&e.length===0)return U();if(r==="whoami"&&e.length===0)return z().username;if(r==="pwd"&&e.length===0)return process.cwd();if(r==="basename"&&e.length===1)return M(e[0]??"");if(r==="dirname"&&e.length===1)return q(e[0]??"");if(r==="cat"&&e.length>0&&!n)return e.map((i)=>X(G(process.cwd(),i),"utf8")).join("");if(r==="uname"){let i=e[0]??"-s";if(e.length<=1&&i==="-m")return j();if(e.length<=1&&i==="-r")return H();if(e.length<=1&&i==="-s")return{aix:"AIX",android:"Android",cygwin:"CYGWIN",darwin:"Darwin",freebsd:"FreeBSD",haiku:"Haiku",linux:"Linux",netbsd:"NetBSD",openbsd:"OpenBSD",sunos:"SunOS",win32:"Windows_NT"}[O()]??O()}return}function re(t){return t.replace(/\$\(([^)]+)\)/g,(r,e)=>{try{let n=ee(e),i=n[0];if(!i||!Q.has(i))return console.warn(`[env] Blocked command substitution for disallowed command: ${i}`),"";let s=te(n);if(s!==void 0)return s.trim();let a=Y(i,n.slice(1),{encoding:"utf8",env:process.env});if(a.status===0)return a.stdout.trim();let c=a.stderr.trim();console.warn(`[env] Command substitution failed (exit code ${a.status??"unknown"}): ${i}${c?`: ${c}`:""}`)}catch(n){console.warn(`[env] Command substitution error: ${n instanceof Error?n.message:String(n)}`)}return""})}async function me(t,r={}){let e={},n=[],i=[];for(let s of t)try{let a=Bun.file(s);if(!a.size)continue;let c=await a.text(),{parsed:f,errors:u,skippedEncrypted:o}=J(c,r);n.push(...u),i.push(...o);for(let[p,h]of Object.entries(f)){if(!r.overload&&e[p]!==void 0)continue;e[p]=h}}catch(a){if(a?.code==="ENOENT")continue;n.push(`Failed to read ${s}: ${a instanceof Error?a.message:String(a)}`)}return{parsed:e,errors:n,skippedEncrypted:i}}export{me as loadEnvFiles,J as parse};
package/dist/plugin.js CHANGED
@@ -1,4 +1,4 @@
1
1
  // @bun
2
- import{createCipheriv as q,createDecipheriv as u,createHash as S,createPrivateKey as nt,createPublicKey as P,diffieHellman as p,generateKeyPairSync as z,hkdfSync as N,randomBytes as U}from"crypto";var Z="x25519-public:",Q="x25519-private:",W="encrypted:v2:",V="encrypted:",m=Buffer.from("stacks-env:v2","utf8"),d="Environment decryption failed: authentication or format error";function v(t){if(t.length===32)return t;return S("sha256").update(t).digest()}function Wt(t,n){let f=U(16),i=q("aes-256-gcm",v(n),f);return{ciphertext:Buffer.concat([i.update(t,"utf8"),i.final()]).toString("hex"),iv:f.toString("hex"),authTag:i.getAuthTag().toString("hex")}}function ft(t,n,f,i){try{let r=u("aes-256-gcm",v(n),Buffer.from(f,"hex"));return r.setAuthTag(Buffer.from(i,"hex")),Buffer.concat([r.update(Buffer.from(t,"hex")),r.final()]).toString("utf8")}catch{throw Error("Decryption failed: authentication or format error")}}function _(t){return t.toString("base64url")}function y(t,n,f){if(typeof t!=="string"||t.length>0&&!/^[A-Za-z0-9_-]+$/.test(t))throw Error(`${n} is not canonical base64url`);let i=Buffer.from(t,"base64url");if(_(i)!==t)throw Error(`${n} is not canonical base64url`);if(f!==void 0&&i.length!==f)throw Error(`${n} has an invalid length`);return i}function it(t){if(!t.startsWith(Z))throw Error("Legacy or invalid public key. Run buddy env:rotate before creating new ciphertext.");let n=P({key:y(t.slice(Z.length),"public key"),format:"der",type:"spki"});if(n.asymmetricKeyType!=="x25519")throw Error("Public key is not X25519");return n}function rt(t){if(!t.startsWith(Q))throw Error("Private key is not a version 2 X25519 key");let n=nt({key:y(t.slice(Q.length),"private key"),format:"der",type:"pkcs8"});if(n.asymmetricKeyType!=="x25519")throw Error("Private key is not X25519");return n}function e(t){return Buffer.from(`stacks-env:v2;${t.epk};${t.salt};${t.nonce}`,"utf8")}function jt(){let{publicKey:t,privateKey:n}=z("x25519"),f=t.export({format:"der",type:"spki"}),i=n.export({format:"der",type:"pkcs8"});return{publicKey:`${Z}${_(f)}`,privateKey:`${Q}${_(i)}`}}function gt(t,n){let f=it(n),i=z("x25519"),r=_(i.publicKey.export({format:"der",type:"spki"})),g=U(16),o=U(12),h=p({privateKey:i.privateKey,publicKey:f}),B=Buffer.from(N("sha256",h,g,m,32)),w={epk:r,salt:_(g),nonce:_(o)};try{let c=q("aes-256-gcm",B,o);c.setAAD(e(w));let s=Buffer.concat([c.update(t,"utf8"),c.final()]),$={v:2,...w,ciphertext:_(s),tag:_(c.getAuthTag())};return`${W}${_(Buffer.from(JSON.stringify($),"utf8"))}`}finally{h.fill(0),B.fill(0)}}function ct(t){let n=y(t.slice(W.length),"envelope").toString("utf8"),f=JSON.parse(n);if(!f||typeof f!=="object"||Array.isArray(f))throw Error("envelope is not an object");let i=Object.keys(f).sort(),r=["ciphertext","epk","nonce","salt","tag","v"];if(i.length!==r.length||i.some((g,o)=>g!==r[o]))throw Error("envelope fields are invalid");if(f.v!==2)throw Error("envelope version is unsupported");for(let g of["epk","salt","nonce","ciphertext","tag"])if(typeof f[g]!=="string")throw Error(`${g} is invalid`);return f}function ot(t,n){try{let f=ct(t),i=rt(n),r=P({key:y(f.epk,"ephemeral public key"),format:"der",type:"spki"});if(r.asymmetricKeyType!=="x25519")throw Error("ephemeral key is not X25519");let g=y(f.salt,"salt",16),o=y(f.nonce,"nonce",12),h=y(f.ciphertext,"ciphertext"),B=y(f.tag,"tag",16),w=p({privateKey:i,publicKey:r}),c=Buffer.from(N("sha256",w,g,m,32));try{let s=u("aes-256-gcm",c,o);return s.setAAD(e(f)),s.setAuthTag(B),Buffer.concat([s.update(h),s.final()]).toString("utf8")}finally{w.fill(0),c.fill(0)}}catch{throw Error(d)}}function ht(t,n){try{if(!/^[0-9a-f]{64}$/.test(n))throw Error("invalid legacy private key");let f=Buffer.from(t.slice(V.length),"base64");if(f.length<32)throw Error("invalid legacy payload");let i=S("sha256").update(Buffer.from(n,"hex")).digest(),r=S("sha256").update(i).digest();return ft(f.subarray(32).toString("hex"),r,f.subarray(0,16).toString("hex"),f.subarray(16,32).toString("hex"))}catch{throw Error(d)}}function Ct(t){return t.startsWith(V)&&!t.startsWith(W)}function K(t,n){if(!t.startsWith(V))return t;return t.startsWith(W)?ot(t,n):ht(t,n)}function Gt(t,n,f){return gt(K(t,n),f)}function Jt(t){let n=t.match(/^DOTENV_PRIVATE_KEY_(.+)$/);return n&&n[1]?n[1].toLowerCase():""}function F(t=""){return t?process.env[`DOTENV_PRIVATE_KEY_${t.toUpperCase()}`]:process.env.DOTENV_PRIVATE_KEY}import{spawnSync as wt}from"child_process";import{readFileSync as Bt}from"fs";import{arch as st,hostname as Tt,platform as l,release as $t,userInfo as At}from"os";import{basename as _t,dirname as yt,resolve as Rt}from"path";function D(t,n={}){let f={},i=[],r=[],g=t.split(`
3
- `);for(let o of g){let h=o.trim();if(!h||h.startsWith("#"))continue;if(h.startsWith("DOTENV_PUBLIC_KEY=")){let s=h.match(/^DOTENV_PUBLIC_KEY=["']?([^"'\n]+)["']?/);if(s&&s[1]!==void 0)f.DOTENV_PUBLIC_KEY=s[1];continue}let B=h.match(/^([^=]+)=(.*)$/);if(!B||B[1]===void 0||B[2]===void 0)continue;let w=B[1].trim(),c=B[2].trim();if(c.startsWith('"')&&c.endsWith('"')||c.startsWith("'")&&c.endsWith("'")){if(c=c.slice(1,-1),c.includes("\\n"))c=c.replace(/\\n/g,`
4
- `)}if(c.startsWith("encrypted:")||c.startsWith("enc:")){if(!n.privateKey){r.push(w);continue}try{let s=c.startsWith("enc:")?`encrypted:${c.slice(4)}`:c;c=K(s,n.privateKey)}catch(s){i.push(`Failed to decrypt ${w}: ${s instanceof Error?s.message:"Unknown error"}`)}}c=Et(c,{...n.processEnv||process.env,...f}),c=Dt(c),f[w]=c}return{parsed:f,errors:i,skippedEncrypted:r}}function Et(t,n){return t.replace(/\$\{([^}]+)\}/g,(f,i)=>{let r=i.match(/^([^:\-+]+)(:-|-)(.+)$/);if(r){let[,o,h,B]=r,w=n[o];if(h===":-")return w||B;else return w!==void 0?w:B}let g=i.match(/^([^:\-+]+)(:?\+)(.+)$/);if(g){let[,o,h,B]=g,w=n[o];if(h===":+")return w?B:"";else return w!==void 0?B:""}return n[i]||""})}var bt=new Set(["date","hostname","whoami","uname","pwd","echo","printf","cat","basename","dirname"]);function xt(t){let n=[],f="",i=null,r=!1;for(let g of t.trim()){if(r){f+=g,r=!1;continue}if(g==="\\"&&i!=="'"){r=!0;continue}if(i){if(g===i)i=null;else f+=g;continue}if(g==='"'||g==="'"){i=g;continue}if(/\s/.test(g)){if(f)n.push(f),f="";continue}f+=g}if(r||i)throw Error("Command substitution contains an unterminated escape or quote");if(f)n.push(f);return n}function Kt(t){let[n,...f]=t,i=f.some((r)=>r.startsWith("-"));if(n==="echo"&&!i)return f.join(" ");if(n==="printf"&&f.length===1&&!f[0]?.includes("%"))return f[0];if(n==="hostname"&&f.length===0)return Tt();if(n==="whoami"&&f.length===0)return At().username;if(n==="pwd"&&f.length===0)return process.cwd();if(n==="basename"&&f.length===1)return _t(f[0]??"");if(n==="dirname"&&f.length===1)return yt(f[0]??"");if(n==="cat"&&f.length>0&&!i)return f.map((r)=>Bt(Rt(process.cwd(),r),"utf8")).join("");if(n==="uname"){let r=f[0]??"-s";if(f.length<=1&&r==="-m")return st();if(f.length<=1&&r==="-r")return $t();if(f.length<=1&&r==="-s")return{aix:"AIX",android:"Android",cygwin:"CYGWIN",darwin:"Darwin",freebsd:"FreeBSD",haiku:"Haiku",linux:"Linux",netbsd:"NetBSD",openbsd:"OpenBSD",sunos:"SunOS",win32:"Windows_NT"}[l()]??l()}return}function Dt(t){return t.replace(/\$\(([^)]+)\)/g,(n,f)=>{try{let i=xt(f),r=i[0];if(!r||!bt.has(r))return console.warn(`[env] Blocked command substitution for disallowed command: ${r}`),"";let g=Kt(i);if(g!==void 0)return g.trim();let o=wt(r,i.slice(1),{encoding:"utf8",env:process.env});if(o.status===0)return o.stdout.trim();let h=o.stderr.trim();console.warn(`[env] Command substitution failed (exit code ${o.status??"unknown"}): ${r}${h?`: ${h}`:""}`)}catch(i){console.warn(`[env] Command substitution error: ${i instanceof Error?i.message:String(i)}`)}return""})}async function Ft(t,n={}){let f={},i=[],r=[];for(let g of t)try{let o=Bun.file(g);if(!o.size)continue;let h=await o.text(),{parsed:B,errors:w,skippedEncrypted:c}=D(h,n);i.push(...w),r.push(...c);for(let[s,$]of Object.entries(B)){if(!n.overload&&f[s]!==void 0)continue;f[s]=$}}catch(o){if(o?.code==="ENOENT")continue;i.push(`Failed to read ${g}: ${o instanceof Error?o.message:String(o)}`)}return{parsed:f,errors:i,skippedEncrypted:r}}import{existsSync as j}from"fs";import{readFileSync as M}from"fs";import{resolve as C}from"path";function X(t){return typeof t==="string"&&(t.startsWith("encrypted:")||t.startsWith("enc:"))}function R(t){if(!t||X(t))return;let n=t.trim().toLowerCase();if(!/^[a-z0-9_-]+$/.test(n))return;if(n==="prod")return"production";if(n==="stage")return"staging";if(n==="dev")return"development";return n}var G=null,J,L;function Xt(t,n,f=".env.keys"){let i=C(n,f);if(!j(i))return;try{let{parsed:r}=D(M(i,"utf-8"));if(t){let g=r[`DOTENV_PRIVATE_KEY_${t.toUpperCase()}`];if(g)return g}return r.DOTENV_PRIVATE_KEY}catch{return}}function Ot(t={}){return R(t.env)||L||R("development")||R(process.env.DOTENV_ENV)||R(process.env.APP_ENV)||"development"}function Yt(t={}){let n=Ot(t),f=t.cwd||process.cwd();if(G===n)return J;let i=F(n);if(!i)i=process.env.DOTENV_PRIVATE_KEY;if(!i)i=Xt(n,f);return G=n,J=i,i}function Nt(){G=null,J=void 0,L=void 0}function mt(t,n={}){if(!X(t))return t;let f=Yt(n);if(!f)return;try{let i=t.startsWith("enc:")?`encrypted:${t.slice(4)}`:t;return K(i,f)}catch{return}}function a(t={}){let{path:n=[".env"],overload:f=!1,env:i,privateKey:r,keysFile:g=".env.keys",quiet:o=!1,cwd:h=process.cwd()}=t,B=Array.isArray(n)?n:[n],w=[],c=0,s=new Set(Object.entries(process.env).filter(([,T])=>T!==void 0&&!X(T)).map(([T])=>T)),$=r;if(!$&&g){let T=C(h,g);if(j(T))try{let A=M(T,"utf-8"),{parsed:E}=D(A);if(i){let O=`DOTENV_PRIVATE_KEY_${i.toUpperCase()}`;$=E[O]}else $=E.DOTENV_PRIVATE_KEY}catch(A){w.push(`Failed to load keys file: ${A instanceof Error?A.message:"Unknown error"}`)}}if(!$)$=F(i||"");for(let T of B){let A=C(h,T);if(!j(A)){if(!o)w.push(`File not found: ${A}`);continue}try{let E=M(A,"utf-8"),{parsed:O,errors:tt,skippedEncrypted:b}=D(E,{privateKey:$,processEnv:process.env});if(w.push(...tt),b.length>0){for(let I of b)if(X(process.env[I]))delete process.env[I];let x=i?`DOTENV_PRIVATE_KEY_${i.toUpperCase()}`:"DOTENV_PRIVATE_KEY",Y=b.slice(0,5).join(", "),H=b.length>5?`, \u2026 +${b.length-5} more`:"";console.warn(`[env] warning: skipped ${b.length} encrypted value(s) in ${T} (${x} not set; defaults apply): ${Y}${H}`)}let k=0;for(let[x,Y]of Object.entries(O)){if(x==="DOTENV_PUBLIC_KEY")continue;let H=process.env[x],I=X(H)&&Y!==H;if(f||!s.has(x)||I)process.env[x]=Y,c++,k++}if(!o&&!process.env.__ENV_LOADED__)console.error(`[env] loaded ${k}/${Object.keys(O).length} variables from ${T}`),process.env.__ENV_LOADED__="1"}catch(E){w.push(`Failed to load ${A}: ${E instanceof Error?E.message:"Unknown error"}`)}}return{loaded:c,errors:w}}function Ht(t={}){return{name:"env-plugin",setup(n){a(t)}}}function dt(t={}){let n=R(t.env)||R("development")||R(process.env.DOTENV_ENV)||R(process.env.APP_ENV)||"development",f=t.cwd||process.cwd();if(L!==n)L=n,G=null,J=void 0;let i=[],r=(h)=>{if(!i.includes(h)&&j(C(f,h)))i.push(h)};r(".env"),r(".env.local");let g=`.env.${n}`,o=`.env.${n}.local`;return r(g),r(o),a({...t,path:i,env:n})}var et=Ht;export{Yt as resolvePrivateKey,Nt as resetPrivateKeyCache,a as loadEnv,Ht as envPlugin,et as default,mt as decryptEnvValue,dt as autoLoadEnv,Ot as activeEnvName};
2
+ import{createCipheriv as U,createDecipheriv as j,createHash as R,createPrivateKey as ne,createPublicKey as X,diffieHellman as z,generateKeyPairSync as H,hkdfSync as q,randomBytes as S}from"crypto";var I="x25519-public:",C="x25519-private:",N="encrypted:v2:",F="encrypted:",M=Buffer.from("stacks-env:v2","utf8"),G="Environment decryption failed: authentication or format error";function J(e){if(e.length===32)return e;return R("sha256").update(e).digest()}function Ne(e,t){let n=S(16),r=U("aes-256-gcm",J(t),n);return{ciphertext:Buffer.concat([r.update(e,"utf8"),r.final()]).toString("hex"),iv:n.toString("hex"),authTag:r.getAuthTag().toString("hex")}}function re(e,t,n,r){try{let s=j("aes-256-gcm",J(t),Buffer.from(n,"hex"));return s.setAuthTag(Buffer.from(r,"hex")),Buffer.concat([s.update(Buffer.from(e,"hex")),s.final()]).toString("utf8")}catch{throw Error("Decryption failed: authentication or format error")}}function h(e){return e.toString("base64url")}function y(e,t,n){if(typeof e!=="string"||e.length>0&&!/^[A-Za-z0-9_-]+$/.test(e))throw Error(`${t} is not canonical base64url`);let r=Buffer.from(e,"base64url");if(h(r)!==e)throw Error(`${t} is not canonical base64url`);if(n!==void 0&&r.length!==n)throw Error(`${t} has an invalid length`);return r}function se(e){if(!e.startsWith(I))throw Error("Legacy or invalid public key. Run buddy env:rotate before creating new ciphertext.");let t=X({key:y(e.slice(I.length),"public key"),format:"der",type:"spki"});if(t.asymmetricKeyType!=="x25519")throw Error("Public key is not X25519");return t}function ie(e){if(!e.startsWith(C))throw Error("Private key is not a version 2 X25519 key");let t=ne({key:y(e.slice(C.length),"private key"),format:"der",type:"pkcs8"});if(t.asymmetricKeyType!=="x25519")throw Error("Private key is not X25519");return t}function Z(e){return Buffer.from(`stacks-env:v2;${e.epk};${e.salt};${e.nonce}`,"utf8")}function Oe(){let{publicKey:e,privateKey:t}=H("x25519"),n=e.export({format:"der",type:"spki"}),r=t.export({format:"der",type:"pkcs8"});return{publicKey:`${I}${h(n)}`,privateKey:`${C}${h(r)}`}}function oe(e,t){let n=se(t),r=H("x25519"),s=h(r.publicKey.export({format:"der",type:"spki"})),i=S(16),c=S(12),a=z({privateKey:r.privateKey,publicKey:n}),u=Buffer.from(q("sha256",a,i,M,32)),f={epk:s,salt:h(i),nonce:h(c)};try{let o=U("aes-256-gcm",u,c);o.setAAD(Z(f));let p=Buffer.concat([o.update(e,"utf8"),o.final()]),l={v:2,...f,ciphertext:h(p),tag:h(o.getAuthTag())};return`${N}${h(Buffer.from(JSON.stringify(l),"utf8"))}`}finally{a.fill(0),u.fill(0)}}function ce(e){let t=y(e.slice(N.length),"envelope").toString("utf8"),n=JSON.parse(t);if(!n||typeof n!=="object"||Array.isArray(n))throw Error("envelope is not an object");let r=Object.keys(n).sort(),s=["ciphertext","epk","nonce","salt","tag","v"];if(r.length!==s.length||r.some((i,c)=>i!==s[c]))throw Error("envelope fields are invalid");if(n.v!==2)throw Error("envelope version is unsupported");for(let i of["epk","salt","nonce","ciphertext","tag"])if(typeof n[i]!=="string")throw Error(`${i} is invalid`);return n}function ae(e,t){try{let n=ce(e),r=ie(t),s=X({key:y(n.epk,"ephemeral public key"),format:"der",type:"spki"});if(s.asymmetricKeyType!=="x25519")throw Error("ephemeral key is not X25519");let i=y(n.salt,"salt",16),c=y(n.nonce,"nonce",12),a=y(n.ciphertext,"ciphertext"),u=y(n.tag,"tag",16),f=z({privateKey:r,publicKey:s}),o=Buffer.from(q("sha256",f,i,M,32));try{let p=j("aes-256-gcm",o,c);return p.setAAD(Z(n)),p.setAuthTag(u),Buffer.concat([p.update(a),p.final()]).toString("utf8")}finally{f.fill(0),o.fill(0)}}catch{throw Error(G)}}function fe(e,t){try{if(!/^[0-9a-f]{64}$/.test(t))throw Error("invalid legacy private key");let n=Buffer.from(e.slice(F.length),"base64");if(n.length<32)throw Error("invalid legacy payload");let r=R("sha256").update(Buffer.from(t,"hex")).digest(),s=R("sha256").update(r).digest();return re(n.subarray(32).toString("hex"),s,n.subarray(0,16).toString("hex"),n.subarray(16,32).toString("hex"))}catch{throw Error(G)}}function Te(e){return e.startsWith(F)&&!e.startsWith(N)}function k(e,t){if(!e.startsWith(F))return e;return e.startsWith(N)?ae(e,t):fe(e,t)}function De(e,t,n){return oe(k(e,t),n)}function Be(e){let t=e.match(/^DOTENV_PRIVATE_KEY_(.+)$/);return t&&t[1]?t[1].toLowerCase():""}function W(e=""){return e?process.env[`DOTENV_PRIVATE_KEY_${e.toUpperCase()}`]:process.env.DOTENV_PRIVATE_KEY}import{spawnSync as ue}from"child_process";import{readFileSync as pe}from"fs";import{arch as de,hostname as le,platform as Q,release as ge,userInfo as he}from"os";import{basename as ye,dirname as me,resolve as ve}from"path";function x(e,t={}){let n={},r=[],s=[],i=e.split(`
3
+ `);for(let c of i){let a=c.trim();if(!a||a.startsWith("#"))continue;if(a.startsWith("DOTENV_PUBLIC_KEY=")){let p=a.match(/^DOTENV_PUBLIC_KEY=["']?([^"'\n]+)["']?/);if(p&&p[1]!==void 0)n.DOTENV_PUBLIC_KEY=p[1];continue}let u=a.match(/^([^=]+)=(.*)$/);if(!u||u[1]===void 0||u[2]===void 0)continue;let f=u[1].trim(),o=u[2].trim();if(o.startsWith('"')&&o.endsWith('"')||o.startsWith("'")&&o.endsWith("'")){if(o=o.slice(1,-1),o.includes("\\n"))o=o.replace(/\\n/g,`
4
+ `)}if(o.startsWith("encrypted:")||o.startsWith("enc:")){if(!t.privateKey){s.push(f);continue}try{let p=o.startsWith("enc:")?`encrypted:${o.slice(4)}`:o;o=k(p,t.privateKey)}catch(p){r.push(`Failed to decrypt ${f}: ${p instanceof Error?p.message:"Unknown error"}`)}}o=Ee(o,{...t.processEnv||process.env,...n}),o=_e(o),n[f]=o}return{parsed:n,errors:r,skippedEncrypted:s}}function Ee(e,t){return e.replace(/\$\{([^}]+)\}/g,(n,r)=>{let s=r.match(/^([^:\-+]+)(:-|-)(.+)$/);if(s){let[,c,a,u]=s,f=t[c];if(a===":-")return f||u;else return f!==void 0?f:u}let i=r.match(/^([^:\-+]+)(:?\+)(.+)$/);if(i){let[,c,a,u]=i,f=t[c];if(a===":+")return f?u:"";else return f!==void 0?u:""}return t[r]||""})}var we=new Set(["date","hostname","whoami","uname","pwd","echo","printf","cat","basename","dirname"]);function ke(e){let t=[],n="",r=null,s=!1;for(let i of e.trim()){if(s){n+=i,s=!1;continue}if(i==="\\"&&r!=="'"){s=!0;continue}if(r){if(i===r)r=null;else n+=i;continue}if(i==='"'||i==="'"){r=i;continue}if(/\s/.test(i)){if(n)t.push(n),n="";continue}n+=i}if(s||r)throw Error("Command substitution contains an unterminated escape or quote");if(n)t.push(n);return t}function xe(e){let[t,...n]=e,r=n.some((s)=>s.startsWith("-"));if(t==="echo"&&!r)return n.join(" ");if(t==="printf"&&n.length===1&&!n[0]?.includes("%"))return n[0];if(t==="hostname"&&n.length===0)return le();if(t==="whoami"&&n.length===0)return he().username;if(t==="pwd"&&n.length===0)return process.cwd();if(t==="basename"&&n.length===1)return ye(n[0]??"");if(t==="dirname"&&n.length===1)return me(n[0]??"");if(t==="cat"&&n.length>0&&!r)return n.map((s)=>pe(ve(process.cwd(),s),"utf8")).join("");if(t==="uname"){let s=n[0]??"-s";if(n.length<=1&&s==="-m")return de();if(n.length<=1&&s==="-r")return ge();if(n.length<=1&&s==="-s")return{aix:"AIX",android:"Android",cygwin:"CYGWIN",darwin:"Darwin",freebsd:"FreeBSD",haiku:"Haiku",linux:"Linux",netbsd:"NetBSD",openbsd:"OpenBSD",sunos:"SunOS",win32:"Windows_NT"}[Q()]??Q()}return}function _e(e){return e.replace(/\$\(([^)]+)\)/g,(t,n)=>{try{let r=ke(n),s=r[0];if(!s||!we.has(s))return console.warn(`[env] Blocked command substitution for disallowed command: ${s}`),"";let i=xe(r);if(i!==void 0)return i.trim();let c=ue(s,r.slice(1),{encoding:"utf8",env:process.env});if(c.status===0)return c.stdout.trim();let a=c.stderr.trim();console.warn(`[env] Command substitution failed (exit code ${c.status??"unknown"}): ${s}${a?`: ${a}`:""}`)}catch(r){console.warn(`[env] Command substitution error: ${r instanceof Error?r.message:String(r)}`)}return""})}async function We(e,t={}){let n={},r=[],s=[];for(let i of e)try{let c=Bun.file(i);if(!c.size)continue;let a=await c.text(),{parsed:u,errors:f,skippedEncrypted:o}=x(a,t);r.push(...f),s.push(...o);for(let[p,l]of Object.entries(u)){if(!t.overload&&n[p]!==void 0)continue;n[p]=l}}catch(c){if(c?.code==="ENOENT")continue;r.push(`Failed to read ${i}: ${c instanceof Error?c.message:String(c)}`)}return{parsed:n,errors:r,skippedEncrypted:s}}import{existsSync as O}from"fs";import{readFileSync as L}from"fs";import{resolve as T}from"path";function _(e){return typeof e==="string"&&(e.startsWith("encrypted:")||e.startsWith("enc:"))}function m(e){if(!e||_(e))return;let t=e.trim().toLowerCase();if(!/^[a-z0-9_-]+$/.test(t))return;if(t==="prod")return"production";if(t==="stage")return"staging";if(t==="dev")return"development";return t}var D=null,B,A;function Pe(e,t,n=".env.keys"){let r=T(t,n);if(!O(r))return;try{let{parsed:s}=x(L(r,"utf-8"));if(e){let i=s[`DOTENV_PRIVATE_KEY_${e.toUpperCase()}`];if(i)return i}return s.DOTENV_PRIVATE_KEY}catch{return}}function be(e={}){return m(e.env)||A||m("development")||m(process.env.DOTENV_ENV)||m(process.env.APP_ENV)||"development"}function Ke(e={}){let t=be(e),n=e.cwd||process.cwd();if(D===t)return B;let r=W(t);if(!r)r=process.env.DOTENV_PRIVATE_KEY;if(!r)r=Pe(t,n);return D=t,B=r,r}function He(){D=null,B=void 0,A=void 0}function qe(e,t={}){if(!_(e))return e;let n=Ke(t);if(!n)return;try{let r=e.startsWith("enc:")?`encrypted:${e.slice(4)}`:e;return k(r,n)}catch{return}}function ee(e={}){let{path:t=[".env"],overload:n=!1,env:r,privateKey:s,keysFile:i=".env.keys",quiet:c=!1,cwd:a=process.cwd()}=e,u=Array.isArray(t)?t:[t],f=[],o=0,p=new Set(Object.entries(process.env).filter(([,d])=>d!==void 0&&!_(d)).map(([d])=>d)),l=s;if(!l&&i){let d=T(a,i);if(O(d))try{let g=L(d,"utf-8"),{parsed:v}=x(g);if(r){let P=`DOTENV_PRIVATE_KEY_${r.toUpperCase()}`;l=v[P]}else l=v.DOTENV_PRIVATE_KEY}catch(g){f.push(`Failed to load keys file: ${g instanceof Error?g.message:"Unknown error"}`)}}if(!l)l=W(r||"");for(let d of u){let g=T(a,d);if(!O(g)){if(!c)f.push(`File not found: ${g}`);continue}try{let v=L(g,"utf-8"),{parsed:P,errors:te,skippedEncrypted:E}=x(v,{privateKey:l,processEnv:process.env});if(f.push(...te),E.length>0){for(let V of E)if(_(process.env[V]))delete process.env[V];let w=r?`DOTENV_PRIVATE_KEY_${r.toUpperCase()}`:"DOTENV_PRIVATE_KEY",b=E.slice(0,5).join(", "),K=E.length>5?`, \u2026 +${E.length-5} more`:"";console.warn(`[env] warning: skipped ${E.length} encrypted value(s) in ${d} (${w} not set; defaults apply): ${b}${K}`)}let Y=0;for(let[w,b]of Object.entries(P)){if(w==="DOTENV_PUBLIC_KEY")continue;let K=process.env[w],V=_(K)&&b!==K;if(n||!p.has(w)||V)process.env[w]=b,o++,Y++}if(!c&&!process.env.__ENV_LOADED__)console.error(`[env] loaded ${Y}/${Object.keys(P).length} variables from ${d}`),process.env.__ENV_LOADED__="1"}catch(v){f.push(`Failed to load ${g}: ${v instanceof Error?v.message:"Unknown error"}`)}}return{loaded:o,errors:f}}function $e(e={}){return{name:"env-plugin",setup(t){ee(e)}}}function Me(e={}){let t=m(e.env)||m("development")||m(process.env.DOTENV_ENV)||m(process.env.APP_ENV)||"development",n=e.cwd||process.cwd();if(A!==t)A=t,D=null,B=void 0;let r=[],s=(a)=>{if(!r.includes(a)&&O(T(n,a)))r.push(a)};s(".env"),s(".env.local");let i=`.env.${t}`,c=`.env.${t}.local`;return s(i),s(c),ee({...e,path:r,env:t})}var Je=$e;export{be as activeEnvName,Me as autoLoadEnv,qe as decryptEnvValue,Je as default,$e as envPlugin,ee as loadEnv,He as resetPrivateKeyCache,Ke as resolvePrivateKey};
package/dist/types.d.ts CHANGED
@@ -140,7 +140,100 @@ export declare interface StacksEnv {
140
140
  AUTH_TOKEN_ROTATION: number | undefined
141
141
  AUTH_PASSWORD_RESET_EXPIRE: number | undefined
142
142
  AUTH_PASSWORD_RESET_THROTTLE: number | undefined
143
- [key: string]: string | number | boolean | undefined
143
+ ANTHROPIC_API_KEY: string | undefined
144
+ ANTHROPIC_MAX_TOKENS: string | undefined
145
+ ANTHROPIC_MODEL: string | undefined
146
+ APPLE_CLIENT_ID: string | undefined
147
+ APPLE_KEY_ID: string | undefined
148
+ APPLE_PRIVATE_KEY: string | undefined
149
+ APPLE_REDIRECT_URL: string | undefined
150
+ APPLE_TEAM_ID: string | undefined
151
+ AWS_ENDPOINT: string | undefined
152
+ AWS_USE_PATH_STYLE_ENDPOINT: boolean | undefined
153
+ DB_SSL: string | undefined
154
+ DISCORD_BOT_TOKEN: string | undefined
155
+ DISCORD_MAX_RETRIES: string | undefined
156
+ DISCORD_RETRY_TIMEOUT: string | undefined
157
+ DISCORD_WEBHOOK_URL: string | undefined
158
+ EXPO_ACCESS_TOKEN: string | undefined
159
+ FACEBOOK_CLIENT_ID: string | undefined
160
+ FACEBOOK_CLIENT_SECRET: string | undefined
161
+ FACEBOOK_REDIRECT_URL: string | undefined
162
+ FCM_CLIENT_EMAIL: string | undefined
163
+ FCM_PRIVATE_KEY: string | undefined
164
+ FCM_PROJECT_ID: string | undefined
165
+ FCM_SERVER_KEY: string | undefined
166
+ GITHUB_CLIENT_ID: string | undefined
167
+ GITHUB_CLIENT_SECRET: string | undefined
168
+ GITHUB_REDIRECT_URL: string | undefined
169
+ GOOGLE_CLIENT_ID: string | undefined
170
+ GOOGLE_CLIENT_SECRET: string | undefined
171
+ GOOGLE_REDIRECT_URL: string | undefined
172
+ MAILGUN_API_KEY: string | undefined
173
+ MAILGUN_DOMAIN: string | undefined
174
+ MAILGUN_ENDPOINT: string | undefined
175
+ MAILGUN_MAX_RETRIES: string | undefined
176
+ MAILGUN_RETRY_TIMEOUT: string | undefined
177
+ MAILTRAP_HOST: string | undefined
178
+ MAILTRAP_INBOX_ID: string | undefined
179
+ MAILTRAP_MAX_RETRIES: string | undefined
180
+ MAILTRAP_RETRY_TIMEOUT: string | undefined
181
+ MAILTRAP_TOKEN: string | undefined
182
+ MAIL_MAX_RETRIES: string | undefined
183
+ MAIL_RETRY_TIMEOUT: string | undefined
184
+ OLLAMA_EMBEDDING_MODEL: string | undefined
185
+ OLLAMA_HOST: string | undefined
186
+ OLLAMA_MODEL: string | undefined
187
+ OPENAI_API_KEY: string | undefined
188
+ OPENAI_BASE_URL: string | undefined
189
+ OPENAI_EMBEDDING_MODEL: string | undefined
190
+ OPENAI_MODEL: string | undefined
191
+ PREDICTHQ_DB_PASSWORD: string | undefined
192
+ QUEUE_CONCURRENCY: string | undefined
193
+ QUEUE_DLQ_ENABLED: string | undefined
194
+ QUEUE_DLQ_MAX_RETRIES: string | undefined
195
+ QUEUE_FAILED_DRIVER: string | undefined
196
+ QUEUE_HORIZONTAL_SCALING_ENABLED: string | undefined
197
+ QUEUE_JOBS_PER_WORKER: string | undefined
198
+ QUEUE_LOG_LEVEL: string | undefined
199
+ QUEUE_MAX_WORKERS: string | undefined
200
+ QUEUE_METRICS_ENABLED: string | undefined
201
+ QUEUE_PREFIX: string | undefined
202
+ QUEUE_RATE_LIMIT_DURATION: string | undefined
203
+ QUEUE_RATE_LIMIT_ENABLED: string | undefined
204
+ QUEUE_RATE_LIMIT_MAX: string | undefined
205
+ QUEUE_WORKER_CONCURRENCY: string | undefined
206
+ REDIS_DB: string | undefined
207
+ REDIS_URL: string | undefined
208
+ SENDGRID_API_KEY: string | undefined
209
+ SENDGRID_MAX_RETRIES: string | undefined
210
+ SENDGRID_RETRY_TIMEOUT: string | undefined
211
+ SLACK_APP_ID: string | undefined
212
+ SLACK_BOT_TOKEN: string | undefined
213
+ SLACK_CLIENT_ID: string | undefined
214
+ SLACK_MAX_RETRIES: string | undefined
215
+ SLACK_RETRY_TIMEOUT: string | undefined
216
+ SLACK_SECRET_KEY: string | undefined
217
+ SLACK_WEBHOOK_URL: string | undefined
218
+ SMS_FROM_NUMBER: string | undefined
219
+ SMS_ORIGINATION_NUMBER: string | undefined
220
+ SMS_SENDER_ID: string | undefined
221
+ SQS_PREFIX: string | undefined
222
+ SQS_SUFFIX: string | undefined
223
+ TEAMS_MAX_RETRIES: string | undefined
224
+ TEAMS_RETRY_TIMEOUT: string | undefined
225
+ TEAMS_WEBHOOK_URL: string | undefined
226
+ TWILIO_ACCOUNT_SID: string | undefined
227
+ TWILIO_AUTH_TOKEN: string | undefined
228
+ TWILIO_FROM_NUMBER: string | undefined
229
+ TWILIO_MESSAGING_SERVICE_SID: string | undefined
230
+ TWILIO_VERIFY_SERVICE_SID: string | undefined
231
+ TWITTER_CLIENT_ID: string | undefined
232
+ TWITTER_CLIENT_SECRET: string | undefined
233
+ TWITTER_REDIRECT_URL: string | undefined
234
+ VONAGE_API_KEY: string | undefined
235
+ VONAGE_API_SECRET: string | undefined
236
+ VONAGE_FROM_NUMBER: string | undefined
144
237
  }
145
238
  export declare interface FrontendEnv {
146
239
  FRONTEND_APP_ENV: 'local' | 'development' | 'staging' | 'production'
package/dist/utils.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import b from"process";import{platform as E}from"os";var w=typeof Bun<"u",x=typeof b<"u"&&b.versions?.node!==void 0,F=w?"bun":x?"node":"unknown",O={name:F,version:w?Bun.version:x?b.version:void 0},j=E(),Q=j==="win32",R=j==="darwin",S=j==="linux",z=Boolean(b.stdout?.isTTY),U=typeof globalThis.window<"u",G=Boolean(b.env.CI||b.env.CONTINUOUS_INTEGRATION||b.env.BUILD_NUMBER||b.env.RUN_ID),V=Boolean(b.env.DEBUG||b.env.VERBOSE||b.argv.includes("--debug")||b.argv.includes("--verbose")),H=G||!z,W=Boolean(!H&&(z||b.env.COLORTERM||b.env.FORCE_COLOR||b.env.TERM&&b.env.TERM!=="dumb")),g={github:{name:"GitHub Actions",detected:Boolean(b.env.GITHUB_ACTIONS)},gitlab:{name:"GitLab CI",detected:Boolean(b.env.GITLAB_CI)},circle:{name:"CircleCI",detected:Boolean(b.env.CIRCLECI)},travis:{name:"Travis CI",detected:Boolean(b.env.TRAVIS)},jenkins:{name:"Jenkins",detected:Boolean(b.env.JENKINS_URL)},vercel:{name:"Vercel",detected:Boolean(b.env.VERCEL)},netlify:{name:"Netlify",detected:Boolean(b.env.NETLIFY)},heroku:{name:"Heroku",detected:Boolean(b.env.DYNO)},aws:{name:"AWS",detected:Boolean(b.env.AWS_REGION||b.env.AWS_LAMBDA_FUNCTION_NAME)},azure:{name:"Azure",detected:Boolean(b.env.AZURE_HTTP_USER_AGENT)},cloudflare:{name:"Cloudflare",detected:Boolean(b.env.CF_PAGES)},railway:{name:"Railway",detected:Boolean(b.env.RAILWAY_ENVIRONMENT)},render:{name:"Render",detected:Boolean(b.env.RENDER)}},J=Object.keys(g).find((A)=>{let q=g[A];return q!==void 0&&q.detected})||"unknown",X=g[J]||{name:"Unknown",detected:!1};export{O as runtimeInfo,F as runtime,X as providerInfo,J as provider,j as platform,Q as isWindows,x as isNode,H as isMinimal,R as isMacOS,S as isLinux,V as isDebug,W as isColorSupported,G as isCI,w as isBun,U as hasWindow,z as hasTTY};
2
+ import e from"process";import{platform as s}from"os";var a=typeof Bun<"u",d=typeof e<"u"&&e.versions?.node!==void 0,l=a?"bun":d?"node":"unknown",B={name:l,version:a?Bun.version:d?e.version:void 0},o=s(),b=o==="win32",I=o==="darwin",f=o==="linux",r=Boolean(e.stdout?.isTTY),E=typeof globalThis.window<"u",c=Boolean(e.env.CI||e.env.CONTINUOUS_INTEGRATION||e.env.BUILD_NUMBER||e.env.RUN_ID),R=Boolean(e.env.DEBUG||e.env.VERBOSE||e.argv.includes("--debug")||e.argv.includes("--verbose")),v=c||!r,N=Boolean(!v&&(r||e.env.COLORTERM||e.env.FORCE_COLOR||e.env.TERM&&e.env.TERM!=="dumb")),n={github:{name:"GitHub Actions",detected:Boolean(e.env.GITHUB_ACTIONS)},gitlab:{name:"GitLab CI",detected:Boolean(e.env.GITLAB_CI)},circle:{name:"CircleCI",detected:Boolean(e.env.CIRCLECI)},travis:{name:"Travis CI",detected:Boolean(e.env.TRAVIS)},jenkins:{name:"Jenkins",detected:Boolean(e.env.JENKINS_URL)},vercel:{name:"Vercel",detected:Boolean(e.env.VERCEL)},netlify:{name:"Netlify",detected:Boolean(e.env.NETLIFY)},heroku:{name:"Heroku",detected:Boolean(e.env.DYNO)},aws:{name:"AWS",detected:Boolean(e.env.AWS_REGION||e.env.AWS_LAMBDA_FUNCTION_NAME)},azure:{name:"Azure",detected:Boolean(e.env.AZURE_HTTP_USER_AGENT)},cloudflare:{name:"Cloudflare",detected:Boolean(e.env.CF_PAGES)},railway:{name:"Railway",detected:Boolean(e.env.RAILWAY_ENVIRONMENT)},render:{name:"Render",detected:Boolean(e.env.RENDER)}},u=Object.keys(n).find((i)=>{let t=n[i];return t!==void 0&&t.detected})||"unknown",T=n[u]||{name:"Unknown",detected:!1};export{r as hasTTY,E as hasWindow,a as isBun,c as isCI,N as isColorSupported,R as isDebug,f as isLinux,I as isMacOS,v as isMinimal,d as isNode,b as isWindows,o as platform,u as provider,T as providerInfo,l as runtime,B as runtimeInfo};
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/env",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.72.57",
5
+ "version": "0.72.61",
6
6
  "description": "Stacks env helper methods.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -55,10 +55,10 @@
55
55
  "prepublishOnly": "bun run build"
56
56
  },
57
57
  "dependencies": {
58
- "@stacksjs/path": "0.72.57"
58
+ "@stacksjs/path": "0.72.61"
59
59
  },
60
60
  "devDependencies": {
61
61
  "better-dx": "^0.2.24",
62
- "@stacksjs/validation": "0.72.57"
62
+ "@stacksjs/validation": "0.72.61"
63
63
  }
64
64
  }