@stacksjs/env 0.70.355 → 0.70.357

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.d.ts CHANGED
@@ -14,6 +14,20 @@
14
14
  * empty string so callers fall through to their own defaults unchanged.
15
15
  */
16
16
  export declare function resolveEnvFile(file?: string, environment?: string): string;
17
+ /**
18
+ * The key names an env file's keypair is stored under: `.env.production` →
19
+ * `DOTENV_{PUBLIC,PRIVATE}_KEY_PRODUCTION`, plain `.env` → the unsuffixed pair.
20
+ *
21
+ * One helper because it was two, and they disagreed. The keys file was written
22
+ * from the basename (`.env` → no suffix, `secrets/.env.production` →
23
+ * `_PRODUCTION`), while the header written INTO the encrypted file was derived
24
+ * from the raw option (`.env` → `_.ENV`, a path → `_SECRETS/.ENV.PRODUCTION`).
25
+ * A file encrypted that way names a public key that does not exist in
26
+ * `.env.keys`, so nothing can find the private half and every value in it is
27
+ * lost — and only for callers who passed `--file` explicitly, which is why it
28
+ * survived: the default path agreed with itself.
29
+ */
30
+ export declare function envKeyNames(file?: string): { publicKeyName: string, privateKeyName: string };
17
31
  /**
18
32
  * Encrypt .env file
19
33
  */
package/dist/cli.js CHANGED
@@ -1,24 +1,24 @@
1
1
  // @bun
2
- import{createCipheriv as d,createDecipheriv as k,createHash as F,createPrivateKey as ff,createPublicKey as u,diffieHellman as m,generateKeyPairSync as n,hkdfSync as i,randomBytes as S}from"crypto";var b="x25519-public:",P="x25519-private:",r="encrypted:v2:",N="encrypted:",o=Buffer.from("stacks-env:v2","utf8"),v="Environment decryption failed: authentication or format error";function s(f){if(f.length===32)return f;return F("sha256").update(f).digest()}function zf(f,E){let g=S(16),$=d("aes-256-gcm",s(E),g);return{ciphertext:Buffer.concat([$.update(f,"utf8"),$.final()]).toString("hex"),iv:g.toString("hex"),authTag:$.getAuthTag().toString("hex")}}function gf(f,E,g,$){try{let B=k("aes-256-gcm",s(E),Buffer.from(g,"hex"));return B.setAuthTag(Buffer.from($,"hex")),Buffer.concat([B.update(Buffer.from(f,"hex")),B.final()]).toString("utf8")}catch{throw Error("Decryption failed: authentication or format error")}}function Q(f){return f.toString("base64url")}function V(f,E,g){if(typeof f!=="string"||f.length>0&&!/^[A-Za-z0-9_-]+$/.test(f))throw Error(`${E} is not canonical base64url`);let $=Buffer.from(f,"base64url");if(Q($)!==f)throw Error(`${E} is not canonical base64url`);if(g!==void 0&&$.length!==g)throw Error(`${E} has an invalid length`);return $}function Ef(f){if(!f.startsWith(b))throw Error("Legacy or invalid public key. Run buddy env:rotate before creating new ciphertext.");let E=u({key:V(f.slice(b.length),"public key"),format:"der",type:"spki"});if(E.asymmetricKeyType!=="x25519")throw Error("Public key is not X25519");return E}function Bf(f){if(!f.startsWith(P))throw Error("Private key is not a version 2 X25519 key");let E=ff({key:V(f.slice(P.length),"private key"),format:"der",type:"pkcs8"});if(E.asymmetricKeyType!=="x25519")throw Error("Private key is not X25519");return E}function y(f){return Buffer.from(`stacks-env:v2;${f.epk};${f.salt};${f.nonce}`,"utf8")}function q(){let{publicKey:f,privateKey:E}=n("x25519"),g=f.export({format:"der",type:"spki"}),$=E.export({format:"der",type:"pkcs8"});return{publicKey:`${b}${Q(g)}`,privateKey:`${P}${Q($)}`}}function C(f,E){let g=Ef(E),$=n("x25519"),B=Q($.publicKey.export({format:"der",type:"spki"})),w=S(16),D=S(12),I=m({privateKey:$.privateKey,publicKey:g}),T=Buffer.from(i("sha256",I,w,o,32)),A={epk:B,salt:Q(w),nonce:Q(D)};try{let _=d("aes-256-gcm",T,D);_.setAAD(y(A));let X=Buffer.concat([_.update(f,"utf8"),_.final()]),H={v:2,...A,ciphertext:Q(X),tag:Q(_.getAuthTag())};return`${r}${Q(Buffer.from(JSON.stringify(H),"utf8"))}`}finally{I.fill(0),T.fill(0)}}function $f(f){let E=V(f.slice(r.length),"envelope").toString("utf8"),g=JSON.parse(E);if(!g||typeof g!=="object"||Array.isArray(g))throw Error("envelope is not an object");let $=Object.keys(g).sort(),B=["ciphertext","epk","nonce","salt","tag","v"];if($.length!==B.length||$.some((w,D)=>w!==B[D]))throw Error("envelope fields are invalid");if(g.v!==2)throw Error("envelope version is unsupported");for(let w of["epk","salt","nonce","ciphertext","tag"])if(typeof g[w]!=="string")throw Error(`${w} is invalid`);return g}function wf(f,E){try{let g=$f(f),$=Bf(E),B=u({key:V(g.epk,"ephemeral public key"),format:"der",type:"spki"});if(B.asymmetricKeyType!=="x25519")throw Error("ephemeral key is not X25519");let w=V(g.salt,"salt",16),D=V(g.nonce,"nonce",12),I=V(g.ciphertext,"ciphertext"),T=V(g.tag,"tag",16),A=m({privateKey:$,publicKey:B}),_=Buffer.from(i("sha256",A,w,o,32));try{let X=k("aes-256-gcm",_,D);return X.setAAD(y(g)),X.setAuthTag(T),Buffer.concat([X.update(I),X.final()]).toString("utf8")}finally{A.fill(0),_.fill(0)}}catch{throw Error(v)}}function Af(f,E){try{if(!/^[0-9a-f]{64}$/.test(E))throw Error("invalid legacy private key");let g=Buffer.from(f.slice(N.length),"base64");if(g.length<32)throw Error("invalid legacy payload");let $=F("sha256").update(Buffer.from(E,"hex")).digest(),B=F("sha256").update($).digest();return gf(g.subarray(32).toString("hex"),B,g.subarray(0,16).toString("hex"),g.subarray(16,32).toString("hex"))}catch{throw Error(v)}}function Zf(f){return f.startsWith(N)&&!f.startsWith(r)}function M(f,E){if(!f.startsWith(N))return f;return f.startsWith(r)?wf(f,E):Af(f,E)}function Qf(f,E,g){return C(M(f,E),g)}function Vf(f){let E=f.match(/^DOTENV_PRIVATE_KEY_(.+)$/);return E&&E[1]?E[1].toLowerCase():""}function Lf(f=""){return f?process.env[`DOTENV_PRIVATE_KEY_${f.toUpperCase()}`]:process.env.DOTENV_PRIVATE_KEY}import{spawnSync as _f}from"child_process";import{readFileSync as Tf}from"fs";import{arch as If,hostname as Df,platform as p,release as Xf,userInfo as Of}from"os";import{basename as Rf,dirname as Yf,resolve as xf}from"path";function L(f,E={}){let g={},$=[],B=[],w=f.split(`
3
- `);for(let D of w){let I=D.trim();if(!I||I.startsWith("#"))continue;if(I.startsWith("DOTENV_PUBLIC_KEY=")){let X=I.match(/^DOTENV_PUBLIC_KEY=["']?([^"'\n]+)["']?/);if(X&&X[1]!==void 0)g.DOTENV_PUBLIC_KEY=X[1];continue}let T=I.match(/^([^=]+)=(.*)$/);if(!T||T[1]===void 0||T[2]===void 0)continue;let A=T[1].trim(),_=T[2].trim();if(_.startsWith('"')&&_.endsWith('"')||_.startsWith("'")&&_.endsWith("'")){if(_=_.slice(1,-1),_.includes("\\n"))_=_.replace(/\\n/g,`
4
- `)}if(_.startsWith("encrypted:")||_.startsWith("enc:")){if(!E.privateKey){B.push(A);continue}try{let X=_.startsWith("enc:")?`encrypted:${_.slice(4)}`:_;_=M(X,E.privateKey)}catch(X){$.push(`Failed to decrypt ${A}: ${X instanceof Error?X.message:"Unknown error"}`)}}_=Hf(_,{...E.processEnv||process.env,...g}),_=Gf(_),g[A]=_}return{parsed:g,errors:$,skippedEncrypted:B}}function Hf(f,E){return f.replace(/\$\{([^}]+)\}/g,(g,$)=>{let B=$.match(/^([^:\-+]+)(:-|-)(.+)$/);if(B){let[,D,I,T]=B,A=E[D];if(I===":-")return A||T;else return A!==void 0?A:T}let w=$.match(/^([^:\-+]+)(:?\+)(.+)$/);if(w){let[,D,I,T]=w,A=E[D];if(I===":+")return A?T:"";else return A!==void 0?T:""}return E[$]||""})}var Wf=new Set(["date","hostname","whoami","uname","pwd","echo","printf","cat","basename","dirname"]);function hf(f){let E=[],g="",$=null,B=!1;for(let w of f.trim()){if(B){g+=w,B=!1;continue}if(w==="\\"&&$!=="'"){B=!0;continue}if($){if(w===$)$=null;else g+=w;continue}if(w==='"'||w==="'"){$=w;continue}if(/\s/.test(w)){if(g)E.push(g),g="";continue}g+=w}if(B||$)throw Error("Command substitution contains an unterminated escape or quote");if(g)E.push(g);return E}function jf(f){let[E,...g]=f,$=g.some((B)=>B.startsWith("-"));if(E==="echo"&&!$)return g.join(" ");if(E==="printf"&&g.length===1&&!g[0]?.includes("%"))return g[0];if(E==="hostname"&&g.length===0)return Df();if(E==="whoami"&&g.length===0)return Of().username;if(E==="pwd"&&g.length===0)return process.cwd();if(E==="basename"&&g.length===1)return Rf(g[0]??"");if(E==="dirname"&&g.length===1)return Yf(g[0]??"");if(E==="cat"&&g.length>0&&!$)return g.map((B)=>Tf(xf(process.cwd(),B),"utf8")).join("");if(E==="uname"){let B=g[0]??"-s";if(g.length<=1&&B==="-m")return If();if(g.length<=1&&B==="-r")return Xf();if(g.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"}[p()]??p()}return}function Gf(f){return f.replace(/\$\(([^)]+)\)/g,(E,g)=>{try{let $=hf(g),B=$[0];if(!B||!Wf.has(B))return console.warn(`[env] Blocked command substitution for disallowed command: ${B}`),"";let w=jf($);if(w!==void 0)return w.trim();let D=_f(B,$.slice(1),{encoding:"utf8",env:process.env});if(D.status===0)return D.stdout.trim();let I=D.stderr.trim();console.warn(`[env] Command substitution failed (exit code ${D.status??"unknown"}): ${B}${I?`: ${I}`:""}`)}catch($){console.warn(`[env] Command substitution error: ${$ instanceof Error?$.message:String($)}`)}return""})}async function Sf(f,E={}){let g={},$=[],B=[];for(let w of f)try{let D=Bun.file(w);if(!D.size)continue;let I=await D.text(),{parsed:T,errors:A,skippedEncrypted:_}=L(I,E);$.push(...A),B.push(..._);for(let[X,H]of Object.entries(T)){if(!E.overload&&g[X]!==void 0)continue;g[X]=H}}catch(D){if(D?.code==="ENOENT")continue;$.push(`Failed to read ${w}: ${D instanceof Error?D.message:String(D)}`)}return{parsed:g,errors:$,skippedEncrypted:B}}import{existsSync as z,readFileSync as U,renameSync as K,rmSync as l,writeFileSync as Z}from"fs";import{basename as t,dirname as a,resolve as J}from"path";function df(f,E){if(f)return f;let g=String(E??"").trim();if(!g||g==="development"||g==="dev"||g==="local")return"";if(!/^[\w.-]+$/.test(g))return"";return`.env.${g}`}function e(f,E,g,$){let B=["#/-------------------[DOTENV_PUBLIC_KEY]--------------------/","#/ versioned X25519 encryption for .env files /","#/ [how it works](https://stacksjs.com/encryption) /","#/----------------------------------------------------------/",`${g}="${E}"`,""];for(let w of f.split(`
5
- `)){let D=w.trim();if(!D||D.startsWith("#")){B.push(w);continue}if(D.startsWith("DOTENV_PUBLIC_KEY"))continue;let I=D.match(/^([^=]+)=(.*)$/);if(!I||I[1]===void 0||I[2]===void 0){B.push(w);continue}let T=I[1].trim(),A=I[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=C(A,E);B.push(`${T}="${A}"`)}return B.join(`
6
- `)}function kf(f={}){let E=f.cwd||process.cwd(),g=J(E,f.file||".env"),$=J(E,f.keysFile||".env.keys");if(!z(g))return{success:!1,error:`File not found: ${g}`};try{let B,w;if(z($)){let _=U($,"utf-8"),{parsed:X}=L(_),x=((f.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),W=x?`DOTENV_PUBLIC_KEY_${x}`:"DOTENV_PUBLIC_KEY",G=x?`DOTENV_PRIVATE_KEY_${x}`:"DOTENV_PRIVATE_KEY";if(B=X[W]||"",w=X[G]||"",!B||!w){let Y=q();B=Y.publicKey,w=Y.privateKey;let h=`
7
- ${W}="${B}"
8
- ${G}="${w}"
9
- `;Z($,_+h,"utf-8")}}else{let _=q();B=_.publicKey,w=_.privateKey;let O=((f.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),x=O?`DOTENV_PUBLIC_KEY_${O}`:"DOTENV_PUBLIC_KEY",W=O?`DOTENV_PRIVATE_KEY_${O}`:"DOTENV_PRIVATE_KEY",G=`# .env.keys - Keep this file secure and never commit to source control
10
- ${x}="${B}"
11
- ${W}="${w}"
12
- `;Z($,G,"utf-8")}let D=U(g,"utf-8"),I=f.file?f.file.replace(/^\.env\./,"").toUpperCase():"",T=I?`DOTENV_PUBLIC_KEY_${I}`:"DOTENV_PUBLIC_KEY",A=e(D,B,T,f);if(f.stdout)return{success:!0,output:A};return Z(g,A,"utf-8"),{success:!0,output:`\u2714 encrypted (${f.file||".env"})
13
- \u2714 key added to ${f.keysFile||".env.keys"}`}}catch(B){return{success:!1,error:`Failed to encrypt: ${B instanceof Error?B.message:"Unknown error"}`}}}function Jf(f={}){let E=f.cwd||process.cwd(),g=J(E,f.file||".env"),$=J(E,f.keysFile||".env.keys");if(!z(g))return{success:!1,error:`File not found: ${g}`};if(!z($))return{success:!1,error:`Keys file not found: ${$}`};try{let B=U($,"utf-8"),{parsed:w}=L(B),T=((f.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),A=T?`DOTENV_PRIVATE_KEY_${T}`:"DOTENV_PRIVATE_KEY",_=w[A];if(!_)return{success:!1,error:`Private key not found: ${A}`};let H=U(g,"utf-8").split(`
14
- `),O=[];for(let W of H){let G=W.trim();if(!G||G.startsWith("#")){O.push(W);continue}if(G.startsWith("DOTENV_PUBLIC_KEY"))continue;let Y=G.match(/^([^=]+)=(.*)$/);if(!Y||Y[1]===void 0||Y[2]===void 0){O.push(W);continue}let h=Y[1].trim(),R=Y[2].trim();if(R.startsWith('"')&&R.endsWith('"')||R.startsWith("'")&&R.endsWith("'"))R=R.slice(1,-1);let j=R.startsWith("encrypted:");if(f.key&&!h.includes(f.key))j=!1;if(j)R=M(R,_);O.push(`${h}="${R}"`)}let x=O.join(`
15
- `);if(f.stdout)return{success:!0,output:x};return Z(g,x,"utf-8"),{success:!0,output:`\u2714 decrypted (${f.file||".env"})`}}catch(B){return{success:!1,error:`Failed to decrypt: ${B instanceof Error?B.message:"Unknown error"}`}}}function uf(f,E,g={}){let $=g.cwd||process.cwd(),B=J($,g.file||".env"),w=J($,g.keysFile||".env.keys");try{let D="";if(z(B))D=U(B,"utf-8");let I=D.split(`
16
- `),T=!1,A,H=((g.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),O=H?`DOTENV_PUBLIC_KEY_${H}`:"DOTENV_PUBLIC_KEY",x=H?`DOTENV_PRIVATE_KEY_${H}`:"DOTENV_PRIVATE_KEY";for(let Y of I){let h=Y.trim();if(h.startsWith(`${O}=`)){let R=h.match(/^[^=]+=["']?([^"'\n]+)["']?/);if(R)A=R[1];break}}if(A){let Y=!1;if(z(w)){let h=U(w,"utf-8"),{parsed:R}=L(h);Y=Boolean(R[x])}if(!Y)A=void 0}if(!g.plain&&!A){let Y=q();A=Y.publicKey;let h="";if(z(w))h=U(w,"utf-8");h+=`
17
- ${O}="${Y.publicKey}"
18
- ${x}="${Y.privateKey}"
19
- `,Z(w,h,"utf-8");for(let R=I.length-1;R>=0;R--){let j=I[R];if(j!==void 0&&j.trim().startsWith(`${O}=`))I.splice(R,1)}I.unshift(`${O}="${A}"`)}let W=E;if(!g.plain&&A)W=C(E,A);for(let Y=0;Y<I.length;Y++){let h=I[Y];if(h===void 0)continue;if(h.trim().startsWith(`${f}=`)){I[Y]=`${f}="${W}"`,T=!0;break}}if(!T)I.push(`${f}="${W}"`);let G=I.join(`
20
- `);return Z(B,G,"utf-8"),{success:!0,output:`set ${f}${g.plain?"":" with encryption"} (${g.file||".env"})`}}catch(D){return{success:!1,error:`Failed to set: ${D instanceof Error?D.message:"Unknown error"}`}}}function mf(f,E={}){let g=E.cwd||process.cwd(),$=J(g,E.file||".env");if(!z($))return{success:!1,error:`File not found: ${$}`};try{let B,w=J(g,E.keysFile||".env.keys"),T=((E.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),A=T?`DOTENV_PRIVATE_KEY_${T}`:"DOTENV_PRIVATE_KEY";if(z(w)){let x=U(w,"utf-8"),{parsed:W}=L(x);B=W[A]}if(!B)B=process.env[A];let _=U($,"utf-8"),{parsed:X}=L(_,{privateKey:B});if(f&&!E.all){let x=X[f];if(x===void 0)return{success:!1,error:`Key not found: ${f}`};return{success:!0,output:x}}let H=E.all?{...process.env,...X}:X,O;switch(E.format){case"shell":O=Object.entries(H).map(([x,W])=>`${x}=${W}`).join(" ");break;case"eval":O=Object.entries(H).map(([x,W])=>`${x}="${W}"`).join(`
21
- `);break;case"json":default:O=E.prettyPrint?JSON.stringify(H,null,2):JSON.stringify(H);break}return{success:!0,output:O}}catch(B){return{success:!1,error:`Failed to get: ${B instanceof Error?B.message:"Unknown error"}`}}}function nf(f,E={}){let g=E.cwd||process.cwd(),$=J(g,E.keysFile||".env.keys");if(!z($))return{success:!1,error:`Keys file not found: ${$}`};try{let B=U($,"utf-8"),{parsed:w}=L(B),T=((E.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),A=T?`DOTENV_PUBLIC_KEY_${T}`:"DOTENV_PUBLIC_KEY",_=T?`DOTENV_PRIVATE_KEY_${T}`:"DOTENV_PRIVATE_KEY";if(f){let O=w[f];if(!O)return{success:!1,error:`Key not found: ${f}`};return{success:!0,output:O}}let X={[A]:w[A],[_]:w[_]};return{success:!0,output:E.format==="shell"?`${A}=${X[A]} ${_}=${X[_]}`:JSON.stringify(X)}}catch(B){return{success:!1,error:`Failed to get keypair: ${B instanceof Error?B.message:"Unknown error"}`}}}function of(f={}){let E=Jf({file:f.file,keysFile:f.keysFile,stdout:!0,cwd:f.cwd});if(!E.success)return E;let g=f.cwd||process.cwd(),$=J(g,f.file||".env"),B=J(g,f.keysFile||".env.keys");if(E.output===void 0)return{success:!1,error:"Rotation produced no decrypted content"};let w=q(),D=U($),I=U(B),T=I.toString("utf8").split(`
22
- `),A=f.file||".env",_=t(A).replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),X=_?`DOTENV_PUBLIC_KEY_${_}`:"DOTENV_PUBLIC_KEY",H=_?`DOTENV_PRIVATE_KEY_${_}`:"DOTENV_PRIVATE_KEY",O=!1,x=!1;for(let j=0;j<T.length;j++){let c=T[j];if(c===void 0)continue;if(c.startsWith(`${X}=`))T[j]=`${X}="${w.publicKey}"`,O=!0;else if(c.startsWith(`${H}=`))T[j]=`${H}="${w.privateKey}"`,x=!0}if(!O)T.push(`${X}="${w.publicKey}"`);if(!x)T.push(`${H}="${w.privateKey}"`);let W=`${T.join(`
2
+ import{createCipheriv as k,createDecipheriv as u,createHash as F,createPrivateKey as gf,createPublicKey as m,diffieHellman as n,generateKeyPairSync as v,hkdfSync as o,randomBytes as S}from"crypto";var b="x25519-public:",P="x25519-private:",C="encrypted:v2:",N="encrypted:",i=Buffer.from("stacks-env:v2","utf8"),s="Environment decryption failed: authentication or format error";function y(f){if(f.length===32)return f;return F("sha256").update(f).digest()}function Zf(f,E){let g=S(16),$=k("aes-256-gcm",y(E),g);return{ciphertext:Buffer.concat([$.update(f,"utf8"),$.final()]).toString("hex"),iv:g.toString("hex"),authTag:$.getAuthTag().toString("hex")}}function Ef(f,E,g,$){try{let B=u("aes-256-gcm",y(E),Buffer.from(g,"hex"));return B.setAuthTag(Buffer.from($,"hex")),Buffer.concat([B.update(Buffer.from(f,"hex")),B.final()]).toString("utf8")}catch{throw Error("Decryption failed: authentication or format error")}}function Q(f){return f.toString("base64url")}function V(f,E,g){if(typeof f!=="string"||f.length>0&&!/^[A-Za-z0-9_-]+$/.test(f))throw Error(`${E} is not canonical base64url`);let $=Buffer.from(f,"base64url");if(Q($)!==f)throw Error(`${E} is not canonical base64url`);if(g!==void 0&&$.length!==g)throw Error(`${E} has an invalid length`);return $}function Bf(f){if(!f.startsWith(b))throw Error("Legacy or invalid public key. Run buddy env:rotate before creating new ciphertext.");let E=m({key:V(f.slice(b.length),"public key"),format:"der",type:"spki"});if(E.asymmetricKeyType!=="x25519")throw Error("Public key is not X25519");return E}function $f(f){if(!f.startsWith(P))throw Error("Private key is not a version 2 X25519 key");let E=gf({key:V(f.slice(P.length),"private key"),format:"der",type:"pkcs8"});if(E.asymmetricKeyType!=="x25519")throw Error("Private key is not X25519");return E}function p(f){return Buffer.from(`stacks-env:v2;${f.epk};${f.salt};${f.nonce}`,"utf8")}function q(){let{publicKey:f,privateKey:E}=v("x25519"),g=f.export({format:"der",type:"spki"}),$=E.export({format:"der",type:"pkcs8"});return{publicKey:`${b}${Q(g)}`,privateKey:`${P}${Q($)}`}}function r(f,E){let g=Bf(E),$=v("x25519"),B=Q($.publicKey.export({format:"der",type:"spki"})),w=S(16),D=S(12),I=n({privateKey:$.privateKey,publicKey:g}),_=Buffer.from(o("sha256",I,w,i,32)),T={epk:B,salt:Q(w),nonce:Q(D)};try{let A=k("aes-256-gcm",_,D);A.setAAD(p(T));let X=Buffer.concat([A.update(f,"utf8"),A.final()]),R={v:2,...T,ciphertext:Q(X),tag:Q(A.getAuthTag())};return`${C}${Q(Buffer.from(JSON.stringify(R),"utf8"))}`}finally{I.fill(0),_.fill(0)}}function wf(f){let E=V(f.slice(C.length),"envelope").toString("utf8"),g=JSON.parse(E);if(!g||typeof g!=="object"||Array.isArray(g))throw Error("envelope is not an object");let $=Object.keys(g).sort(),B=["ciphertext","epk","nonce","salt","tag","v"];if($.length!==B.length||$.some((w,D)=>w!==B[D]))throw Error("envelope fields are invalid");if(g.v!==2)throw Error("envelope version is unsupported");for(let w of["epk","salt","nonce","ciphertext","tag"])if(typeof g[w]!=="string")throw Error(`${w} is invalid`);return g}function Af(f,E){try{let g=wf(f),$=$f(E),B=m({key:V(g.epk,"ephemeral public key"),format:"der",type:"spki"});if(B.asymmetricKeyType!=="x25519")throw Error("ephemeral key is not X25519");let w=V(g.salt,"salt",16),D=V(g.nonce,"nonce",12),I=V(g.ciphertext,"ciphertext"),_=V(g.tag,"tag",16),T=n({privateKey:$,publicKey:B}),A=Buffer.from(o("sha256",T,w,i,32));try{let X=u("aes-256-gcm",A,D);return X.setAAD(p(g)),X.setAuthTag(_),Buffer.concat([X.update(I),X.final()]).toString("utf8")}finally{T.fill(0),A.fill(0)}}catch{throw Error(s)}}function _f(f,E){try{if(!/^[0-9a-f]{64}$/.test(E))throw Error("invalid legacy private key");let g=Buffer.from(f.slice(N.length),"base64");if(g.length<32)throw Error("invalid legacy payload");let $=F("sha256").update(Buffer.from(E,"hex")).digest(),B=F("sha256").update($).digest();return Ef(g.subarray(32).toString("hex"),B,g.subarray(0,16).toString("hex"),g.subarray(16,32).toString("hex"))}catch{throw Error(s)}}function Qf(f){return f.startsWith(N)&&!f.startsWith(C)}function M(f,E){if(!f.startsWith(N))return f;return f.startsWith(C)?Af(f,E):_f(f,E)}function Vf(f,E,g){return r(M(f,E),g)}function Lf(f){let E=f.match(/^DOTENV_PRIVATE_KEY_(.+)$/);return E&&E[1]?E[1].toLowerCase():""}function qf(f=""){return f?process.env[`DOTENV_PRIVATE_KEY_${f.toUpperCase()}`]:process.env.DOTENV_PRIVATE_KEY}import{spawnSync as Tf}from"child_process";import{readFileSync as If}from"fs";import{arch as Df,hostname as Xf,platform as l,release as Of,userInfo as Rf}from"os";import{basename as Yf,dirname as Hf,resolve as Wf}from"path";function L(f,E={}){let g={},$=[],B=[],w=f.split(`
3
+ `);for(let D of w){let I=D.trim();if(!I||I.startsWith("#"))continue;if(I.startsWith("DOTENV_PUBLIC_KEY=")){let X=I.match(/^DOTENV_PUBLIC_KEY=["']?([^"'\n]+)["']?/);if(X&&X[1]!==void 0)g.DOTENV_PUBLIC_KEY=X[1];continue}let _=I.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=M(X,E.privateKey)}catch(X){$.push(`Failed to decrypt ${T}: ${X instanceof Error?X.message:"Unknown error"}`)}}A=hf(A,{...E.processEnv||process.env,...g}),A=Jf(A),g[T]=A}return{parsed:g,errors:$,skippedEncrypted:B}}function hf(f,E){return f.replace(/\$\{([^}]+)\}/g,(g,$)=>{let B=$.match(/^([^:\-+]+)(:-|-)(.+)$/);if(B){let[,D,I,_]=B,T=E[D];if(I===":-")return T||_;else return T!==void 0?T:_}let w=$.match(/^([^:\-+]+)(:?\+)(.+)$/);if(w){let[,D,I,_]=w,T=E[D];if(I===":+")return T?_:"";else return T!==void 0?_:""}return E[$]||""})}var jf=new Set(["date","hostname","whoami","uname","pwd","echo","printf","cat","basename","dirname"]);function xf(f){let E=[],g="",$=null,B=!1;for(let w of f.trim()){if(B){g+=w,B=!1;continue}if(w==="\\"&&$!=="'"){B=!0;continue}if($){if(w===$)$=null;else g+=w;continue}if(w==='"'||w==="'"){$=w;continue}if(/\s/.test(w)){if(g)E.push(g),g="";continue}g+=w}if(B||$)throw Error("Command substitution contains an unterminated escape or quote");if(g)E.push(g);return E}function Gf(f){let[E,...g]=f,$=g.some((B)=>B.startsWith("-"));if(E==="echo"&&!$)return g.join(" ");if(E==="printf"&&g.length===1&&!g[0]?.includes("%"))return g[0];if(E==="hostname"&&g.length===0)return Xf();if(E==="whoami"&&g.length===0)return Rf().username;if(E==="pwd"&&g.length===0)return process.cwd();if(E==="basename"&&g.length===1)return Yf(g[0]??"");if(E==="dirname"&&g.length===1)return Hf(g[0]??"");if(E==="cat"&&g.length>0&&!$)return g.map((B)=>If(Wf(process.cwd(),B),"utf8")).join("");if(E==="uname"){let B=g[0]??"-s";if(g.length<=1&&B==="-m")return Df();if(g.length<=1&&B==="-r")return Of();if(g.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"}[l()]??l()}return}function Jf(f){return f.replace(/\$\(([^)]+)\)/g,(E,g)=>{try{let $=xf(g),B=$[0];if(!B||!jf.has(B))return console.warn(`[env] Blocked command substitution for disallowed command: ${B}`),"";let w=Gf($);if(w!==void 0)return w.trim();let D=Tf(B,$.slice(1),{encoding:"utf8",env:process.env});if(D.status===0)return D.stdout.trim();let I=D.stderr.trim();console.warn(`[env] Command substitution failed (exit code ${D.status??"unknown"}): ${B}${I?`: ${I}`:""}`)}catch($){console.warn(`[env] Command substitution error: ${$ instanceof Error?$.message:String($)}`)}return""})}async function bf(f,E={}){let g={},$=[],B=[];for(let w of f)try{let D=Bun.file(w);if(!D.size)continue;let I=await D.text(),{parsed:_,errors:T,skippedEncrypted:A}=L(I,E);$.push(...T),B.push(...A);for(let[X,R]of Object.entries(_)){if(!E.overload&&g[X]!==void 0)continue;g[X]=R}}catch(D){if(D?.code==="ENOENT")continue;$.push(`Failed to read ${w}: ${D instanceof Error?D.message:String(D)}`)}return{parsed:g,errors:$,skippedEncrypted:B}}import{existsSync as U,readFileSync as J,renameSync as K,rmSync as a,writeFileSync as z}from"fs";import{basename as t,dirname as e,resolve as G}from"path";function kf(f,E){if(f)return f;let g=String(E??"").trim();if(!g||g==="development"||g==="dev"||g==="local")return"";if(!/^[\w.-]+$/.test(g))return"";return`.env.${g}`}function ff(f,E,g,$){let B=["#/-------------------[DOTENV_PUBLIC_KEY]--------------------/","#/ versioned X25519 encryption for .env files /","#/ [how it works](https://stacksjs.com/encryption) /","#/----------------------------------------------------------/",`${g}="${E}"`,""],w=!1;for(let D of f.split(`
5
+ `)){let I=D.trim();if(I.startsWith("#/"))continue;if(I.startsWith("DOTENV_PUBLIC_KEY"))continue;if(!I&&!w)continue;if(!I||I.startsWith("#")){w=!0,B.push(D);continue}w=!0;let _=I.match(/^([^=]+)=(.*)$/);if(!_||_[1]===void 0||_[2]===void 0){B.push(D);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=r(A,E);B.push(`${T}="${A}"`)}return B.join(`
6
+ `)}function d(f){let g=((f||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase();return{publicKeyName:g?`DOTENV_PUBLIC_KEY_${g}`:"DOTENV_PUBLIC_KEY",privateKeyName:g?`DOTENV_PRIVATE_KEY_${g}`:"DOTENV_PRIVATE_KEY"}}function uf(f={}){let E=f.cwd||process.cwd(),g=G(E,f.file||".env"),$=G(E,f.keysFile||".env.keys");if(!U(g))return{success:!1,error:`File not found: ${g}`};try{let B,w;if(U($)){let _=J($,"utf-8"),{parsed:T}=L(_),{publicKeyName:A,privateKeyName:X}=d(f.file);if(B=T[A]||"",w=T[X]||"",!B||!w){let R=q();B=R.publicKey,w=R.privateKey;let Y=`
7
+ ${A}="${B}"
8
+ ${X}="${w}"
9
+ `;z($,_+Y,"utf-8")}}else{let _=q();B=_.publicKey,w=_.privateKey;let{publicKeyName:T,privateKeyName:A}=d(f.file),X=`# .env.keys - Keep this file secure and never commit to source control
10
+ ${T}="${B}"
11
+ ${A}="${w}"
12
+ `;z($,X,"utf-8")}let D=J(g,"utf-8"),I=ff(D,B,d(f.file).publicKeyName,f);if(f.stdout)return{success:!0,output:I};return z(g,I,"utf-8"),{success:!0,output:`\u2714 encrypted (${f.file||".env"})
13
+ \u2714 key added to ${f.keysFile||".env.keys"}`}}catch(B){return{success:!1,error:`Failed to encrypt: ${B instanceof Error?B.message:"Unknown error"}`}}}function Uf(f={}){let E=f.cwd||process.cwd(),g=G(E,f.file||".env"),$=G(E,f.keysFile||".env.keys");if(!U(g))return{success:!1,error:`File not found: ${g}`};if(!U($))return{success:!1,error:`Keys file not found: ${$}`};try{let B=J($,"utf-8"),{parsed:w}=L(B),_=((f.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 R=J(g,"utf-8").split(`
14
+ `),Y=[];for(let x of R){let Z=x.trim();if(!Z||Z.startsWith("#")){Y.push(x);continue}if(Z.startsWith("DOTENV_PUBLIC_KEY"))continue;let H=Z.match(/^([^=]+)=(.*)$/);if(!H||H[1]===void 0||H[2]===void 0){Y.push(x);continue}let W=H[1].trim(),O=H[2].trim();if(O.startsWith('"')&&O.endsWith('"')||O.startsWith("'")&&O.endsWith("'"))O=O.slice(1,-1);let j=O.startsWith("encrypted:");if(f.key&&!W.includes(f.key))j=!1;if(j)O=M(O,A);Y.push(`${W}="${O}"`)}let h=Y.join(`
15
+ `);if(f.stdout)return{success:!0,output:h};return z(g,h,"utf-8"),{success:!0,output:`\u2714 decrypted (${f.file||".env"})`}}catch(B){return{success:!1,error:`Failed to decrypt: ${B instanceof Error?B.message:"Unknown error"}`}}}function mf(f,E,g={}){let $=g.cwd||process.cwd(),B=G($,g.file||".env"),w=G($,g.keysFile||".env.keys");try{let D="";if(U(B))D=J(B,"utf-8");let I=D.split(`
16
+ `),_=!1,T,R=((g.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),Y=R?`DOTENV_PUBLIC_KEY_${R}`:"DOTENV_PUBLIC_KEY",h=R?`DOTENV_PRIVATE_KEY_${R}`:"DOTENV_PRIVATE_KEY";for(let H of I){let W=H.trim();if(W.startsWith(`${Y}=`)){let O=W.match(/^[^=]+=["']?([^"'\n]+)["']?/);if(O)T=O[1];break}}if(T){let H=!1;if(U(w)){let W=J(w,"utf-8"),{parsed:O}=L(W);H=Boolean(O[h])}if(!H)T=void 0}if(!g.plain&&!T){let H=q();T=H.publicKey;let W="";if(U(w))W=J(w,"utf-8");W+=`
17
+ ${Y}="${H.publicKey}"
18
+ ${h}="${H.privateKey}"
19
+ `,z(w,W,"utf-8");for(let O=I.length-1;O>=0;O--){let j=I[O];if(j!==void 0&&j.trim().startsWith(`${Y}=`))I.splice(O,1)}I.unshift(`${Y}="${T}"`)}let x=E;if(!g.plain&&T)x=r(E,T);for(let H=0;H<I.length;H++){let W=I[H];if(W===void 0)continue;if(W.trim().startsWith(`${f}=`)){I[H]=`${f}="${x}"`,_=!0;break}}if(!_)I.push(`${f}="${x}"`);let Z=I.join(`
20
+ `);return z(B,Z,"utf-8"),{success:!0,output:`set ${f}${g.plain?"":" with encryption"} (${g.file||".env"})`}}catch(D){return{success:!1,error:`Failed to set: ${D instanceof Error?D.message:"Unknown error"}`}}}function nf(f,E={}){let g=E.cwd||process.cwd(),$=G(g,E.file||".env");if(!U($))return{success:!1,error:`File not found: ${$}`};try{let B,w=G(g,E.keysFile||".env.keys"),_=((E.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),T=_?`DOTENV_PRIVATE_KEY_${_}`:"DOTENV_PRIVATE_KEY";if(U(w)){let h=J(w,"utf-8"),{parsed:x}=L(h);B=x[T]}if(!B)B=process.env[T];let A=J($,"utf-8"),{parsed:X}=L(A,{privateKey:B});if(f&&!E.all){let h=X[f];if(h===void 0)return{success:!1,error:`Key not found: ${f}`};return{success:!0,output:h}}let R=E.all?{...process.env,...X}:X,Y;switch(E.format){case"shell":Y=Object.entries(R).map(([h,x])=>`${h}=${x}`).join(" ");break;case"eval":Y=Object.entries(R).map(([h,x])=>`${h}="${x}"`).join(`
21
+ `);break;case"json":default:Y=E.prettyPrint?JSON.stringify(R,null,2):JSON.stringify(R);break}return{success:!0,output:Y}}catch(B){return{success:!1,error:`Failed to get: ${B instanceof Error?B.message:"Unknown error"}`}}}function vf(f,E={}){let g=E.cwd||process.cwd(),$=G(g,E.keysFile||".env.keys");if(!U($))return{success:!1,error:`Keys file not found: ${$}`};try{let B=J($,"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(f){let Y=w[f];if(!Y)return{success:!1,error:`Key not found: ${f}`};return{success:!0,output:Y}}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 of(f={}){let E=Uf({file:f.file,keysFile:f.keysFile,stdout:!0,cwd:f.cwd});if(!E.success)return E;let g=f.cwd||process.cwd(),$=G(g,f.file||".env"),B=G(g,f.keysFile||".env.keys");if(E.output===void 0)return{success:!1,error:"Rotation produced no decrypted content"};let w=q(),D=J($),I=J(B),_=I.toString("utf8").split(`
22
+ `),T=f.file||".env",A=t(T).replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),X=A?`DOTENV_PUBLIC_KEY_${A}`:"DOTENV_PUBLIC_KEY",R=A?`DOTENV_PRIVATE_KEY_${A}`:"DOTENV_PRIVATE_KEY",Y=!1,h=!1;for(let j=0;j<_.length;j++){let c=_[j];if(c===void 0)continue;if(c.startsWith(`${X}=`))_[j]=`${X}="${w.publicKey}"`,Y=!0;else if(c.startsWith(`${R}=`))_[j]=`${R}="${w.privateKey}"`,h=!0}if(!Y)_.push(`${X}="${w.publicKey}"`);if(!h)_.push(`${R}="${w.privateKey}"`);let x=`${_.join(`
23
23
  `).replace(/\n+$/,"")}
24
- `,G=e(E.output,w.publicKey,X,f),Y=`${process.pid}-${Date.now()}`,h=J(a($),`.${t($)}.${Y}.rotate`),R=J(a(B),`.${t(B)}.${Y}.rotate`);try{if(Z(h,G,{encoding:"utf8",mode:384}),Z(R,W,{encoding:"utf8",mode:384}),f.stdout)return K(R,B),{success:!0,output:G};K(h,$);try{K(R,B)}catch(j){throw Z($,D),j}return{success:!0,output:`\u2714 rotated (${f.file||".env"}) to encrypted:v2`}}catch(j){if(!z(B))Z(B,I,{mode:384});return{success:!1,error:`Failed to rotate without exposing plaintext: ${j instanceof Error?j.message:"Unknown error"}`}}finally{l(h,{force:!0}),l(R,{force:!0})}}export{uf as setEnv,of as rotateKeypair,df as resolveEnvFile,nf as getKeypair,mf as getEnv,kf as encryptEnv,Jf as decryptEnv};
24
+ `,Z=ff(E.output,w.publicKey,X,f),H=`${process.pid}-${Date.now()}`,W=G(e($),`.${t($)}.${H}.rotate`),O=G(e(B),`.${t(B)}.${H}.rotate`);try{if(z(W,Z,{encoding:"utf8",mode:384}),z(O,x,{encoding:"utf8",mode:384}),f.stdout)return K(O,B),{success:!0,output:Z};K(W,$);try{K(O,B)}catch(j){throw z($,D),j}return{success:!0,output:`\u2714 rotated (${f.file||".env"}) to encrypted:v2`}}catch(j){if(!U(B))z(B,I,{mode:384});return{success:!1,error:`Failed to rotate without exposing plaintext: ${j instanceof Error?j.message:"Unknown error"}`}}finally{a(W,{force:!0}),a(O,{force:!0})}}export{mf as setEnv,of as rotateKeypair,kf as resolveEnvFile,vf as getKeypair,nf as getEnv,d as envKeyNames,uf as encryptEnv,Uf as decryptEnv};
package/dist/index.js CHANGED
@@ -1,27 +1,27 @@
1
1
  // @bun
2
- import{createCipheriv as gf,createDecipheriv as Bf,createHash as t,createPrivateKey as zf,createPublicKey as $f,diffieHellman as Xf,generateKeyPairSync as Df,hkdfSync as Hf,randomBytes as y}from"crypto";var v="x25519-public:",i="x25519-private:",P="encrypted:v2:",n="encrypted:",_f=Buffer.from("stacks-env:v2","utf8"),Af="Environment decryption failed: authentication or format error";function Wf(f){if(f.length===32)return f;return t("sha256").update(f).digest()}function J2(f,g){let B=y(16),X=gf("aes-256-gcm",Wf(g),B);return{ciphertext:Buffer.concat([X.update(f,"utf8"),X.final()]).toString("hex"),iv:B.toString("hex"),authTag:X.getAuthTag().toString("hex")}}function Vf(f,g,B,X){try{let $=Bf("aes-256-gcm",Wf(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 x(f){return f.toString("base64url")}function C(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(x(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 xf(f){if(!f.startsWith(v))throw Error("Legacy or invalid public key. Run buddy env:rotate before creating new ciphertext.");let g=$f({key:C(f.slice(v.length),"public key"),format:"der",type:"spki"});if(g.asymmetricKeyType!=="x25519")throw Error("Public key is not X25519");return g}function Ef(f){if(!f.startsWith(i))throw Error("Private key is not a version 2 X25519 key");let g=zf({key:C(f.slice(i.length),"private key"),format:"der",type:"pkcs8"});if(g.asymmetricKeyType!=="x25519")throw Error("Private key is not X25519");return g}function Yf(f){return Buffer.from(`stacks-env:v2;${f.epk};${f.salt};${f.nonce}`,"utf8")}function S(){let{publicKey:f,privateKey:g}=Df("x25519"),B=f.export({format:"der",type:"spki"}),X=g.export({format:"der",type:"pkcs8"});return{publicKey:`${v}${x(B)}`,privateKey:`${i}${x(X)}`}}function c(f,g){let B=xf(g),X=Df("x25519"),$=x(X.publicKey.export({format:"der",type:"spki"})),D=y(16),H=y(12),A=Xf({privateKey:X.privateKey,publicKey:B}),W=Buffer.from(Hf("sha256",A,D,_f,32)),_={epk:$,salt:x(D),nonce:x(H)};try{let Y=gf("aes-256-gcm",W,H);Y.setAAD(Yf(_));let j=Buffer.concat([Y.update(f,"utf8"),Y.final()]),Z={v:2,..._,ciphertext:x(j),tag:x(Y.getAuthTag())};return`${P}${x(Buffer.from(JSON.stringify(Z),"utf8"))}`}finally{A.fill(0),W.fill(0)}}function Cf(f){let g=C(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 Ff(f,g){try{let B=Cf(f),X=Ef(g),$=$f({key:C(B.epk,"ephemeral public key"),format:"der",type:"spki"});if($.asymmetricKeyType!=="x25519")throw Error("ephemeral key is not X25519");let D=C(B.salt,"salt",16),H=C(B.nonce,"nonce",12),A=C(B.ciphertext,"ciphertext"),W=C(B.tag,"tag",16),_=Xf({privateKey:X,publicKey:$}),Y=Buffer.from(Hf("sha256",_,D,_f,32));try{let j=Bf("aes-256-gcm",Y,H);return j.setAAD(Yf(B)),j.setAuthTag(W),Buffer.concat([j.update(A),j.final()]).toString("utf8")}finally{_.fill(0),Y.fill(0)}}catch{throw Error(Af)}}function bf(f,g){try{if(!/^[0-9a-f]{64}$/.test(g))throw Error("invalid legacy private key");let B=Buffer.from(f.slice(n.length),"base64");if(B.length<32)throw Error("invalid legacy payload");let X=t("sha256").update(Buffer.from(g,"hex")).digest(),$=t("sha256").update(X).digest();return Vf(B.subarray(32).toString("hex"),$,B.subarray(0,16).toString("hex"),B.subarray(16,32).toString("hex"))}catch{throw Error(Af)}}function O2(f){return f.startsWith(n)&&!f.startsWith(P)}function b(f,g){if(!f.startsWith(n))return f;return f.startsWith(P)?Ff(f,g):bf(f,g)}function Z2(f,g,B){return c(b(f,g),B)}function Q2(f){let g=f.match(/^DOTENV_PRIVATE_KEY_(.+)$/);return g&&g[1]?g[1].toLowerCase():""}function o(f=""){return f?process.env[`DOTENV_PRIVATE_KEY_${f.toUpperCase()}`]:process.env.DOTENV_PRIVATE_KEY}import{spawnSync as Sf}from"child_process";import{readFileSync as hf}from"fs";import{arch as Kf,hostname as Nf,platform as jf,release as Pf,userInfo as cf}from"os";import{basename as kf,dirname as mf,resolve as df}from"path";function M(f,g={}){let B={},X=[],$=[],D=f.split(`
3
- `);for(let H of D){let A=H.trim();if(!A||A.startsWith("#"))continue;if(A.startsWith("DOTENV_PUBLIC_KEY=")){let j=A.match(/^DOTENV_PUBLIC_KEY=["']?([^"'\n]+)["']?/);if(j&&j[1]!==void 0)B.DOTENV_PUBLIC_KEY=j[1];continue}let W=A.match(/^([^=]+)=(.*)$/);if(!W||W[1]===void 0||W[2]===void 0)continue;let _=W[1].trim(),Y=W[2].trim();if(Y.startsWith('"')&&Y.endsWith('"')||Y.startsWith("'")&&Y.endsWith("'")){if(Y=Y.slice(1,-1),Y.includes("\\n"))Y=Y.replace(/\\n/g,`
4
- `)}if(Y.startsWith("encrypted:")||Y.startsWith("enc:")){if(!g.privateKey){$.push(_);continue}try{let j=Y.startsWith("enc:")?`encrypted:${Y.slice(4)}`:Y;Y=b(j,g.privateKey)}catch(j){X.push(`Failed to decrypt ${_}: ${j instanceof Error?j.message:"Unknown error"}`)}}Y=uf(Y,{...g.processEnv||process.env,...B}),Y=vf(Y),B[_]=Y}return{parsed:B,errors:X,skippedEncrypted:$}}function uf(f,g){return f.replace(/\$\{([^}]+)\}/g,(B,X)=>{let $=X.match(/^([^:\-+]+)(:-|-)(.+)$/);if($){let[,H,A,W]=$,_=g[H];if(A===":-")return _||W;else return _!==void 0?_:W}let D=X.match(/^([^:\-+]+)(:?\+)(.+)$/);if(D){let[,H,A,W]=D,_=g[H];if(A===":+")return _?W:"";else return _!==void 0?W:""}return g[X]||""})}var rf=new Set(["date","hostname","whoami","uname","pwd","echo","printf","cat","basename","dirname"]);function tf(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 yf(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 Nf();if(g==="whoami"&&B.length===0)return cf().username;if(g==="pwd"&&B.length===0)return process.cwd();if(g==="basename"&&B.length===1)return kf(B[0]??"");if(g==="dirname"&&B.length===1)return mf(B[0]??"");if(g==="cat"&&B.length>0&&!X)return B.map(($)=>hf(df(process.cwd(),$),"utf8")).join("");if(g==="uname"){let $=B[0]??"-s";if(B.length<=1&&$==="-m")return Kf();if(B.length<=1&&$==="-r")return Pf();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"}[jf()]??jf()}return}function vf(f){return f.replace(/\$\(([^)]+)\)/g,(g,B)=>{try{let X=tf(B),$=X[0];if(!$||!rf.has($))return console.warn(`[env] Blocked command substitution for disallowed command: ${$}`),"";let D=yf(X);if(D!==void 0)return D.trim();let H=Sf($,X.slice(1),{encoding:"utf8",env:process.env});if(H.status===0)return H.stdout.trim();let A=H.stderr.trim();console.warn(`[env] Command substitution failed (exit code ${H.status??"unknown"}): ${$}${A?`: ${A}`:""}`)}catch(X){console.warn(`[env] Command substitution error: ${X instanceof Error?X.message:String(X)}`)}return""})}async function M2(f,g={}){let B={},X=[],$=[];for(let D of f)try{let H=Bun.file(D);if(!H.size)continue;let A=await H.text(),{parsed:W,errors:_,skippedEncrypted:Y}=M(A,g);X.push(..._),$.push(...Y);for(let[j,Z]of Object.entries(W)){if(!g.overload&&B[j]!==void 0)continue;B[j]=Z}}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 k}from"fs";import{readFileSync as p}from"fs";import{resolve as m}from"path";function h(f){return typeof f==="string"&&(f.startsWith("encrypted:")||f.startsWith("enc:"))}function F(f){if(!f||h(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 nf(f,g,B=".env.keys"){let X=m(g,B);if(!k(X))return;try{let{parsed:$}=M(p(X,"utf-8"));if(f){let D=$[`DOTENV_PRIVATE_KEY_${f.toUpperCase()}`];if(D)return D}return $.DOTENV_PRIVATE_KEY}catch{return}}function l(f={}){return F(f.env)||r||F("development")||F(process.env.DOTENV_ENV)||F(process.env.APP_ENV)||"development"}function of(f={}){let g=l(f),B=f.cwd||process.cwd();if(d===g)return u;let X=o(g);if(!X)X=process.env.DOTENV_PRIVATE_KEY;if(!X)X=nf(g,B);return d=g,u=X,X}function b2(){d=null,u=void 0,r=void 0}function Gf(f,g={}){if(!h(f))return f;let B=of(g);if(!B)return;try{let X=f.startsWith("enc:")?`encrypted:${f.slice(4)}`:f;return b(X,B)}catch{return}}function Jf(f={}){let{path:g=[".env"],overload:B=!1,env:X,privateKey:$,keysFile:D=".env.keys",quiet:H=!1,cwd:A=process.cwd()}=f,W=Array.isArray(g)?g:[g],_=[],Y=0,j=new Set(Object.entries(process.env).filter(([,G])=>G!==void 0&&!h(G)).map(([G])=>G)),Z=$;if(!Z&&D){let G=m(A,D);if(k(G))try{let J=p(G,"utf-8"),{parsed:R}=M(J);if(X){let T=`DOTENV_PRIVATE_KEY_${X.toUpperCase()}`;Z=R[T]}else Z=R.DOTENV_PRIVATE_KEY}catch(J){_.push(`Failed to load keys file: ${J instanceof Error?J.message:"Unknown error"}`)}}if(!Z)Z=o(X||"");for(let G of W){let J=m(A,G);if(!k(J)){if(!H)_.push(`File not found: ${J}`);continue}try{let R=p(J,"utf-8"),{parsed:T,errors:I,skippedEncrypted:w}=M(R,{privateKey:Z,processEnv:process.env});if(_.push(...I),w.length>0){for(let N of w)if(h(process.env[N]))delete process.env[N];let U=X?`DOTENV_PRIVATE_KEY_${X.toUpperCase()}`:"DOTENV_PRIVATE_KEY",E=w.slice(0,5).join(", "),K=w.length>5?`, \u2026 +${w.length-5} more`:"";console.warn(`[env] warning: skipped ${w.length} encrypted value(s) in ${G} (${U} not set; defaults apply): ${E}${K}`)}let Q=0;for(let[U,E]of Object.entries(T)){if(U==="DOTENV_PUBLIC_KEY")continue;let K=process.env[U],N=h(K)&&E!==K;if(B||!j.has(U)||N)process.env[U]=E,Y++,Q++}if(!H&&!process.env.__ENV_LOADED__)console.error(`[env] loaded ${Q}/${Object.keys(T).length} variables from ${G}`),process.env.__ENV_LOADED__="1"}catch(R){_.push(`Failed to load ${J}: ${R instanceof Error?R.message:"Unknown error"}`)}}return{loaded:Y,errors:_}}function pf(f={}){return{name:"env-plugin",setup(g){Jf(f)}}}function S2(f={}){let g=F(f.env)||F("development")||F(process.env.DOTENV_ENV)||F(process.env.APP_ENV)||"development",B=f.cwd||process.cwd();if(r!==g)r=g,d=null,u=void 0;let X=[],$=(A)=>{if(!X.includes(A)&&k(m(B,A)))X.push(A)};$(".env"),$(".env.local");let D=`.env.${g}`,H=`.env.${g}.local`;return $(D),$(H),Jf({...f,path:X,env:g})}var K2=pf;import{existsSync as z,readFileSync as q,renameSync as s,rmSync as Of,writeFileSync as V}from"fs";import{basename as a,dirname as Zf,resolve as L}from"path";function m2(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 Qf(f,g,B,X){let $=["#/-------------------[DOTENV_PUBLIC_KEY]--------------------/","#/ versioned X25519 encryption for .env files /","#/ [how it works](https://stacksjs.com/encryption) /","#/----------------------------------------------------------/",`${B}="${g}"`,""];for(let D of f.split(`
5
- `)){let H=D.trim();if(!H||H.startsWith("#")){$.push(D);continue}if(H.startsWith("DOTENV_PUBLIC_KEY"))continue;let A=H.match(/^([^=]+)=(.*)$/);if(!A||A[1]===void 0||A[2]===void 0){$.push(D);continue}let W=A[1].trim(),_=A[2].trim();if(_.startsWith('"')&&_.endsWith('"')||_.startsWith("'")&&_.endsWith("'"))_=_.slice(1,-1);if((!X.key||W.includes(X.key))&&(!X.excludeKey||!W.includes(X.excludeKey))&&!_.startsWith("encrypted:"))_=c(_,g);$.push(`${W}="${_}"`)}return $.join(`
6
- `)}function d2(f={}){let g=f.cwd||process.cwd(),B=L(g,f.file||".env"),X=L(g,f.keysFile||".env.keys");if(!z(B))return{success:!1,error:`File not found: ${B}`};try{let $,D;if(z(X)){let Y=q(X,"utf-8"),{parsed:j}=M(Y),J=((f.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),R=J?`DOTENV_PUBLIC_KEY_${J}`:"DOTENV_PUBLIC_KEY",T=J?`DOTENV_PRIVATE_KEY_${J}`:"DOTENV_PRIVATE_KEY";if($=j[R]||"",D=j[T]||"",!$||!D){let I=S();$=I.publicKey,D=I.privateKey;let w=`
7
- ${R}="${$}"
8
- ${T}="${D}"
9
- `;V(X,Y+w,"utf-8")}}else{let Y=S();$=Y.publicKey,D=Y.privateKey;let G=((f.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),J=G?`DOTENV_PUBLIC_KEY_${G}`:"DOTENV_PUBLIC_KEY",R=G?`DOTENV_PRIVATE_KEY_${G}`:"DOTENV_PRIVATE_KEY",T=`# .env.keys - Keep this file secure and never commit to source control
10
- ${J}="${$}"
11
- ${R}="${D}"
12
- `;V(X,T,"utf-8")}let H=q(B,"utf-8"),A=f.file?f.file.replace(/^\.env\./,"").toUpperCase():"",W=A?`DOTENV_PUBLIC_KEY_${A}`:"DOTENV_PUBLIC_KEY",_=Qf(H,$,W,f);if(f.stdout)return{success:!0,output:_};return V(B,_,"utf-8"),{success:!0,output:`\u2714 encrypted (${f.file||".env"})
13
- \u2714 key added to ${f.keysFile||".env.keys"}`}}catch($){return{success:!1,error:`Failed to encrypt: ${$ instanceof Error?$.message:"Unknown error"}`}}}function lf(f={}){let g=f.cwd||process.cwd(),B=L(g,f.file||".env"),X=L(g,f.keysFile||".env.keys");if(!z(B))return{success:!1,error:`File not found: ${B}`};if(!z(X))return{success:!1,error:`Keys file not found: ${X}`};try{let $=q(X,"utf-8"),{parsed:D}=M($),W=((f.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),_=W?`DOTENV_PRIVATE_KEY_${W}`:"DOTENV_PRIVATE_KEY",Y=D[_];if(!Y)return{success:!1,error:`Private key not found: ${_}`};let Z=q(B,"utf-8").split(`
14
- `),G=[];for(let R of Z){let T=R.trim();if(!T||T.startsWith("#")){G.push(R);continue}if(T.startsWith("DOTENV_PUBLIC_KEY"))continue;let I=T.match(/^([^=]+)=(.*)$/);if(!I||I[1]===void 0||I[2]===void 0){G.push(R);continue}let w=I[1].trim(),Q=I[2].trim();if(Q.startsWith('"')&&Q.endsWith('"')||Q.startsWith("'")&&Q.endsWith("'"))Q=Q.slice(1,-1);let U=Q.startsWith("encrypted:");if(f.key&&!w.includes(f.key))U=!1;if(U)Q=b(Q,Y);G.push(`${w}="${Q}"`)}let J=G.join(`
15
- `);if(f.stdout)return{success:!0,output:J};return V(B,J,"utf-8"),{success:!0,output:`\u2714 decrypted (${f.file||".env"})`}}catch($){return{success:!1,error:`Failed to decrypt: ${$ instanceof Error?$.message:"Unknown error"}`}}}function u2(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(z($))H=q($,"utf-8");let A=H.split(`
16
- `),W=!1,_,Z=((B.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),G=Z?`DOTENV_PUBLIC_KEY_${Z}`:"DOTENV_PUBLIC_KEY",J=Z?`DOTENV_PRIVATE_KEY_${Z}`:"DOTENV_PRIVATE_KEY";for(let I of A){let w=I.trim();if(w.startsWith(`${G}=`)){let Q=w.match(/^[^=]+=["']?([^"'\n]+)["']?/);if(Q)_=Q[1];break}}if(_){let I=!1;if(z(D)){let w=q(D,"utf-8"),{parsed:Q}=M(w);I=Boolean(Q[J])}if(!I)_=void 0}if(!B.plain&&!_){let I=S();_=I.publicKey;let w="";if(z(D))w=q(D,"utf-8");w+=`
2
+ import{createCipheriv as B2,createDecipheriv as $2,createHash as t,createPrivateKey as V2,createPublicKey as X2,diffieHellman as D2,generateKeyPairSync as H2,hkdfSync as _2,randomBytes as y}from"crypto";var v="x25519-public:",n="x25519-private:",P="encrypted:v2:",i="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 t("sha256").update(f).digest()}function Jf(f,g){let B=y(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 E2(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 E(f){return f.toString("base64url")}function x(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(E(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 C2(f){if(!f.startsWith(v))throw Error("Legacy or invalid public key. Run buddy env:rotate before creating new ciphertext.");let g=X2({key:x(f.slice(v.length),"public key"),format:"der",type:"spki"});if(g.asymmetricKeyType!=="x25519")throw Error("Public key is not X25519");return g}function x2(f){if(!f.startsWith(n))throw Error("Private key is not a version 2 X25519 key");let g=V2({key:x(f.slice(n.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 S(){let{publicKey:f,privateKey:g}=H2("x25519"),B=f.export({format:"der",type:"spki"}),X=g.export({format:"der",type:"pkcs8"});return{publicKey:`${v}${E(B)}`,privateKey:`${n}${E(X)}`}}function c(f,g){let B=C2(g),X=H2("x25519"),$=E(X.publicKey.export({format:"der",type:"spki"})),D=y(16),H=y(12),W=D2({privateKey:X.privateKey,publicKey:B}),A=Buffer.from(_2("sha256",W,D,A2,32)),Y={epk:$,salt:E(D),nonce:E(H)};try{let _=B2("aes-256-gcm",A,H);_.setAAD(j2(Y));let j=Buffer.concat([_.update(f,"utf8"),_.final()]),J={v:2,...Y,ciphertext:E(j),tag:E(_.getAuthTag())};return`${P}${E(Buffer.from(JSON.stringify(J),"utf8"))}`}finally{W.fill(0),A.fill(0)}}function F2(f){let g=x(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 b2(f,g){try{let B=F2(f),X=x2(g),$=X2({key:x(B.epk,"ephemeral public key"),format:"der",type:"spki"});if($.asymmetricKeyType!=="x25519")throw Error("ephemeral key is not X25519");let D=x(B.salt,"salt",16),H=x(B.nonce,"nonce",12),W=x(B.ciphertext,"ciphertext"),A=x(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 S2(f,g){try{if(!/^[0-9a-f]{64}$/.test(g))throw Error("invalid legacy private key");let B=Buffer.from(f.slice(i.length),"base64");if(B.length<32)throw Error("invalid legacy payload");let X=t("sha256").update(Buffer.from(g,"hex")).digest(),$=t("sha256").update(X).digest();return E2(B.subarray(32).toString("hex"),$,B.subarray(0,16).toString("hex"),B.subarray(16,32).toString("hex"))}catch{throw Error(W2)}}function Of(f){return f.startsWith(i)&&!f.startsWith(P)}function b(f,g){if(!f.startsWith(i))return f;return f.startsWith(P)?b2(f,g):S2(f,g)}function Zf(f,g,B){return c(b(f,g),B)}function Qf(f){let g=f.match(/^DOTENV_PRIVATE_KEY_(.+)$/);return g&&g[1]?g[1].toLowerCase():""}function o(f=""){return f?process.env[`DOTENV_PRIVATE_KEY_${f.toUpperCase()}`]:process.env.DOTENV_PRIVATE_KEY}import{spawnSync as h2}from"child_process";import{readFileSync as K2}from"fs";import{arch as N2,hostname as P2,platform as G2,release as c2,userInfo as k2}from"os";import{basename as m2,dirname as d2,resolve as u2}from"path";function M(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)}`:_;_=b(j,g.privateKey)}catch(j){X.push(`Failed to decrypt ${Y}: ${j instanceof Error?j.message:"Unknown error"}`)}}_=r2(_,{...g.processEnv||process.env,...B}),_=n2(_),B[Y]=_}return{parsed:B,errors:X,skippedEncrypted:$}}function r2(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 t2=new Set(["date","hostname","whoami","uname","pwd","echo","printf","cat","basename","dirname"]);function y2(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 v2(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 P2();if(g==="whoami"&&B.length===0)return k2().username;if(g==="pwd"&&B.length===0)return process.cwd();if(g==="basename"&&B.length===1)return m2(B[0]??"");if(g==="dirname"&&B.length===1)return d2(B[0]??"");if(g==="cat"&&B.length>0&&!X)return B.map(($)=>K2(u2(process.cwd(),$),"utf8")).join("");if(g==="uname"){let $=B[0]??"-s";if(B.length<=1&&$==="-m")return N2();if(B.length<=1&&$==="-r")return c2();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 n2(f){return f.replace(/\$\(([^)]+)\)/g,(g,B)=>{try{let X=y2(B),$=X[0];if(!$||!t2.has($))return console.warn(`[env] Blocked command substitution for disallowed command: ${$}`),"";let D=v2(X);if(D!==void 0)return D.trim();let H=h2($,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 Mf(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:_}=M(W,g);X.push(...Y),$.push(..._);for(let[j,J]of Object.entries(A)){if(!g.overload&&B[j]!==void 0)continue;B[j]=J}}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 k}from"fs";import{readFileSync as p}from"fs";import{resolve as m}from"path";function h(f){return typeof f==="string"&&(f.startsWith("encrypted:")||f.startsWith("enc:"))}function F(f){if(!f||h(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 i2(f,g,B=".env.keys"){let X=m(g,B);if(!k(X))return;try{let{parsed:$}=M(p(X,"utf-8"));if(f){let D=$[`DOTENV_PRIVATE_KEY_${f.toUpperCase()}`];if(D)return D}return $.DOTENV_PRIVATE_KEY}catch{return}}function l(f={}){return F(f.env)||r||F("development")||F(process.env.DOTENV_ENV)||F(process.env.APP_ENV)||"development"}function o2(f={}){let g=l(f),B=f.cwd||process.cwd();if(d===g)return u;let X=o(g);if(!X)X=process.env.DOTENV_PRIVATE_KEY;if(!X)X=i2(g,B);return d=g,u=X,X}function bf(){d=null,u=void 0,r=void 0}function J2(f,g={}){if(!h(f))return f;let B=o2(g);if(!B)return;try{let X=f.startsWith("enc:")?`encrypted:${f.slice(4)}`:f;return b(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&&!h(G)).map(([G])=>G)),J=$;if(!J&&D){let G=m(W,D);if(k(G))try{let Q=p(G,"utf-8"),{parsed:w}=M(Q);if(X){let T=`DOTENV_PRIVATE_KEY_${X.toUpperCase()}`;J=w[T]}else J=w.DOTENV_PRIVATE_KEY}catch(Q){Y.push(`Failed to load keys file: ${Q instanceof Error?Q.message:"Unknown error"}`)}}if(!J)J=o(X||"");for(let G of A){let Q=m(W,G);if(!k(Q)){if(!H)Y.push(`File not found: ${Q}`);continue}try{let w=p(Q,"utf-8"),{parsed:T,errors:I,skippedEncrypted:R}=M(w,{privateKey:J,processEnv:process.env});if(Y.push(...I),R.length>0){for(let N of R)if(h(process.env[N]))delete process.env[N];let U=X?`DOTENV_PRIVATE_KEY_${X.toUpperCase()}`:"DOTENV_PRIVATE_KEY",C=R.slice(0,5).join(", "),K=R.length>5?`, \u2026 +${R.length-5} more`:"";console.warn(`[env] warning: skipped ${R.length} encrypted value(s) in ${G} (${U} not set; defaults apply): ${C}${K}`)}let Z=0;for(let[U,C]of Object.entries(T)){if(U==="DOTENV_PUBLIC_KEY")continue;let K=process.env[U],N=h(K)&&C!==K;if(B||!j.has(U)||N)process.env[U]=C,_++,Z++}if(!H&&!process.env.__ENV_LOADED__)console.error(`[env] loaded ${Z}/${Object.keys(T).length} variables from ${G}`),process.env.__ENV_LOADED__="1"}catch(w){Y.push(`Failed to load ${Q}: ${w instanceof Error?w.message:"Unknown error"}`)}}return{loaded:_,errors:Y}}function p2(f={}){return{name:"env-plugin",setup(g){O2(f)}}}function Sf(f={}){let g=F(f.env)||F("development")||F(process.env.DOTENV_ENV)||F(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)&&k(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=p2;import{existsSync as z,readFileSync as q,renameSync as s,rmSync as Z2,writeFileSync as V}from"fs";import{basename as a,dirname as Q2,resolve as L}from"path";function mf(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:"))_=c(_,g);$.push(`${Y}="${_}"`)}return $.join(`
6
+ `)}function e(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 df(f={}){let g=f.cwd||process.cwd(),B=L(g,f.file||".env"),X=L(g,f.keysFile||".env.keys");if(!z(B))return{success:!1,error:`File not found: ${B}`};try{let $,D;if(z(X)){let A=q(X,"utf-8"),{parsed:Y}=M(A),{publicKeyName:_,privateKeyName:j}=e(f.file);if($=Y[_]||"",D=Y[j]||"",!$||!D){let J=S();$=J.publicKey,D=J.privateKey;let G=`
7
+ ${_}="${$}"
8
+ ${j}="${D}"
9
+ `;V(X,A+G,"utf-8")}}else{let A=S();$=A.publicKey,D=A.privateKey;let{publicKeyName:Y,privateKeyName:_}=e(f.file),j=`# .env.keys - Keep this file secure and never commit to source control
10
+ ${Y}="${$}"
11
+ ${_}="${D}"
12
+ `;V(X,j,"utf-8")}let H=q(B,"utf-8"),W=R2(H,$,e(f.file).publicKeyName,f);if(f.stdout)return{success:!0,output:W};return V(B,W,"utf-8"),{success:!0,output:`\u2714 encrypted (${f.file||".env"})
13
+ \u2714 key added to ${f.keysFile||".env.keys"}`}}catch($){return{success:!1,error:`Failed to encrypt: ${$ instanceof Error?$.message:"Unknown error"}`}}}function l2(f={}){let g=f.cwd||process.cwd(),B=L(g,f.file||".env"),X=L(g,f.keysFile||".env.keys");if(!z(B))return{success:!1,error:`File not found: ${B}`};if(!z(X))return{success:!1,error:`Keys file not found: ${X}`};try{let $=q(X,"utf-8"),{parsed:D}=M($),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=q(B,"utf-8").split(`
14
+ `),G=[];for(let w of J){let T=w.trim();if(!T||T.startsWith("#")){G.push(w);continue}if(T.startsWith("DOTENV_PUBLIC_KEY"))continue;let I=T.match(/^([^=]+)=(.*)$/);if(!I||I[1]===void 0||I[2]===void 0){G.push(w);continue}let R=I[1].trim(),Z=I[2].trim();if(Z.startsWith('"')&&Z.endsWith('"')||Z.startsWith("'")&&Z.endsWith("'"))Z=Z.slice(1,-1);let U=Z.startsWith("encrypted:");if(f.key&&!R.includes(f.key))U=!1;if(U)Z=b(Z,_);G.push(`${R}="${Z}"`)}let Q=G.join(`
15
+ `);if(f.stdout)return{success:!0,output:Q};return V(B,Q,"utf-8"),{success:!0,output:`\u2714 decrypted (${f.file||".env"})`}}catch($){return{success:!1,error:`Failed to decrypt: ${$ instanceof Error?$.message:"Unknown error"}`}}}function uf(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(z($))H=q($,"utf-8");let W=H.split(`
16
+ `),A=!1,Y,J=((B.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),G=J?`DOTENV_PUBLIC_KEY_${J}`:"DOTENV_PUBLIC_KEY",Q=J?`DOTENV_PRIVATE_KEY_${J}`:"DOTENV_PRIVATE_KEY";for(let I of W){let R=I.trim();if(R.startsWith(`${G}=`)){let Z=R.match(/^[^=]+=["']?([^"'\n]+)["']?/);if(Z)Y=Z[1];break}}if(Y){let I=!1;if(z(D)){let R=q(D,"utf-8"),{parsed:Z}=M(R);I=Boolean(Z[Q])}if(!I)Y=void 0}if(!B.plain&&!Y){let I=S();Y=I.publicKey;let R="";if(z(D))R=q(D,"utf-8");R+=`
17
17
  ${G}="${I.publicKey}"
18
- ${J}="${I.privateKey}"
19
- `,V(D,w,"utf-8");for(let Q=A.length-1;Q>=0;Q--){let U=A[Q];if(U!==void 0&&U.trim().startsWith(`${G}=`))A.splice(Q,1)}A.unshift(`${G}="${_}"`)}let R=g;if(!B.plain&&_)R=c(g,_);for(let I=0;I<A.length;I++){let w=A[I];if(w===void 0)continue;if(w.trim().startsWith(`${f}=`)){A[I]=`${f}="${R}"`,W=!0;break}}if(!W)A.push(`${f}="${R}"`);let T=A.join(`
20
- `);return V($,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 r2(f,g={}){let B=g.cwd||process.cwd(),X=L(B,g.file||".env");if(!z(X))return{success:!1,error:`File not found: ${X}`};try{let $,D=L(B,g.keysFile||".env.keys"),W=((g.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),_=W?`DOTENV_PRIVATE_KEY_${W}`:"DOTENV_PRIVATE_KEY";if(z(D)){let J=q(D,"utf-8"),{parsed:R}=M(J);$=R[_]}if(!$)$=process.env[_];let Y=q(X,"utf-8"),{parsed:j}=M(Y,{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 Z=g.all?{...process.env,...j}:j,G;switch(g.format){case"shell":G=Object.entries(Z).map(([J,R])=>`${J}=${R}`).join(" ");break;case"eval":G=Object.entries(Z).map(([J,R])=>`${J}="${R}"`).join(`
21
- `);break;case"json":default:G=g.prettyPrint?JSON.stringify(Z,null,2):JSON.stringify(Z);break}return{success:!0,output:G}}catch($){return{success:!1,error:`Failed to get: ${$ instanceof Error?$.message:"Unknown error"}`}}}function t2(f,g={}){let B=g.cwd||process.cwd(),X=L(B,g.keysFile||".env.keys");if(!z(X))return{success:!1,error:`Keys file not found: ${X}`};try{let $=q(X,"utf-8"),{parsed:D}=M($),W=((g.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),_=W?`DOTENV_PUBLIC_KEY_${W}`:"DOTENV_PUBLIC_KEY",Y=W?`DOTENV_PRIVATE_KEY_${W}`:"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={[_]:D[_],[Y]:D[Y]};return{success:!0,output:g.format==="shell"?`${_}=${j[_]} ${Y}=${j[Y]}`:JSON.stringify(j)}}catch($){return{success:!1,error:`Failed to get keypair: ${$ instanceof Error?$.message:"Unknown error"}`}}}function y2(f={}){let g=lf({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=S(),H=q(X),A=q($),W=A.toString("utf8").split(`
22
- `),_=f.file||".env",Y=a(_).replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),j=Y?`DOTENV_PUBLIC_KEY_${Y}`:"DOTENV_PUBLIC_KEY",Z=Y?`DOTENV_PRIVATE_KEY_${Y}`:"DOTENV_PRIVATE_KEY",G=!1,J=!1;for(let U=0;U<W.length;U++){let E=W[U];if(E===void 0)continue;if(E.startsWith(`${j}=`))W[U]=`${j}="${D.publicKey}"`,G=!0;else if(E.startsWith(`${Z}=`))W[U]=`${Z}="${D.privateKey}"`,J=!0}if(!G)W.push(`${j}="${D.publicKey}"`);if(!J)W.push(`${Z}="${D.privateKey}"`);let R=`${W.join(`
18
+ ${Q}="${I.privateKey}"
19
+ `,V(D,R,"utf-8");for(let Z=W.length-1;Z>=0;Z--){let U=W[Z];if(U!==void 0&&U.trim().startsWith(`${G}=`))W.splice(Z,1)}W.unshift(`${G}="${Y}"`)}let w=g;if(!B.plain&&Y)w=c(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(`
20
+ `);return V($,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 rf(f,g={}){let B=g.cwd||process.cwd(),X=L(B,g.file||".env");if(!z(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(z(D)){let Q=q(D,"utf-8"),{parsed:w}=M(Q);$=w[Y]}if(!$)$=process.env[Y];let _=q(X,"utf-8"),{parsed:j}=M(_,{privateKey:$});if(f&&!g.all){let Q=j[f];if(Q===void 0)return{success:!1,error:`Key not found: ${f}`};return{success:!0,output:Q}}let J=g.all?{...process.env,...j}:j,G;switch(g.format){case"shell":G=Object.entries(J).map(([Q,w])=>`${Q}=${w}`).join(" ");break;case"eval":G=Object.entries(J).map(([Q,w])=>`${Q}="${w}"`).join(`
21
+ `);break;case"json":default:G=g.prettyPrint?JSON.stringify(J,null,2):JSON.stringify(J);break}return{success:!0,output:G}}catch($){return{success:!1,error:`Failed to get: ${$ instanceof Error?$.message:"Unknown error"}`}}}function tf(f,g={}){let B=g.cwd||process.cwd(),X=L(B,g.keysFile||".env.keys");if(!z(X))return{success:!1,error:`Keys file not found: ${X}`};try{let $=q(X,"utf-8"),{parsed:D}=M($),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 yf(f={}){let g=l2({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=S(),H=q(X),W=q($),A=W.toString("utf8").split(`
22
+ `),Y=f.file||".env",_=a(Y).replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),j=_?`DOTENV_PUBLIC_KEY_${_}`:"DOTENV_PUBLIC_KEY",J=_?`DOTENV_PRIVATE_KEY_${_}`:"DOTENV_PRIVATE_KEY",G=!1,Q=!1;for(let U=0;U<A.length;U++){let C=A[U];if(C===void 0)continue;if(C.startsWith(`${j}=`))A[U]=`${j}="${D.publicKey}"`,G=!0;else if(C.startsWith(`${J}=`))A[U]=`${J}="${D.privateKey}"`,Q=!0}if(!G)A.push(`${j}="${D.publicKey}"`);if(!Q)A.push(`${J}="${D.privateKey}"`);let w=`${A.join(`
23
23
  `).replace(/\n+$/,"")}
24
- `,T=Qf(g.output,D.publicKey,j,f),I=`${process.pid}-${Date.now()}`,w=L(Zf(X),`.${a(X)}.${I}.rotate`),Q=L(Zf($),`.${a($)}.${I}.rotate`);try{if(V(w,T,{encoding:"utf8",mode:384}),V(Q,R,{encoding:"utf8",mode:384}),f.stdout)return s(Q,$),{success:!0,output:T};s(w,X);try{s(Q,$)}catch(U){throw V(X,H),U}return{success:!0,output:`\u2714 rotated (${f.file||".env"}) to encrypted:v2`}}catch(U){if(!z($))V($,A,{mode:384});return{success:!1,error:`Failed to rotate without exposing plaintext: ${U instanceof Error?U.message:"Unknown error"}`}}finally{Of(w,{force:!0}),Of(Q,{force:!0})}}import O from"process";import{platform as sf}from"os";var Rf=typeof Bun<"u",If=typeof O<"u"&&O.versions?.node!==void 0,af=Rf?"bun":If?"node":"unknown",o2={name:af,version:Rf?Bun.version:If?O.version:void 0},ff=sf(),p2=ff==="win32",l2=ff==="darwin",s2=ff==="linux",wf=Boolean(O.stdout?.isTTY),a2=typeof globalThis.window<"u",ef=Boolean(O.env.CI||O.env.CONTINUOUS_INTEGRATION||O.env.BUILD_NUMBER||O.env.RUN_ID),e2=Boolean(O.env.DEBUG||O.env.VERBOSE||O.argv.includes("--debug")||O.argv.includes("--verbose")),f2=ef||!wf,f5=Boolean(!f2&&(wf||O.env.COLORTERM||O.env.FORCE_COLOR||O.env.TERM&&O.env.TERM!=="dumb")),e={github:{name:"GitHub Actions",detected:Boolean(O.env.GITHUB_ACTIONS)},gitlab:{name:"GitLab CI",detected:Boolean(O.env.GITLAB_CI)},circle:{name:"CircleCI",detected:Boolean(O.env.CIRCLECI)},travis:{name:"Travis CI",detected:Boolean(O.env.TRAVIS)},jenkins:{name:"Jenkins",detected:Boolean(O.env.JENKINS_URL)},vercel:{name:"Vercel",detected:Boolean(O.env.VERCEL)},netlify:{name:"Netlify",detected:Boolean(O.env.NETLIFY)},heroku:{name:"Heroku",detected:Boolean(O.env.DYNO)},aws:{name:"AWS",detected:Boolean(O.env.AWS_REGION||O.env.AWS_LAMBDA_FUNCTION_NAME)},azure:{name:"Azure",detected:Boolean(O.env.AZURE_HTTP_USER_AGENT)},cloudflare:{name:"Cloudflare",detected:Boolean(O.env.CF_PAGES)},railway:{name:"Railway",detected:Boolean(O.env.RAILWAY_ENVIRONMENT)},render:{name:"Render",detected:Boolean(O.env.RENDER)}},g2=Object.keys(e).find((f)=>{let g=e[f];return g!==void 0&&g.detected})||"unknown",g5=e[g2]||{name:"Unknown",detected:!1};import _2 from"process";import{projectPath as A2}from"@stacksjs/path";import Lf from"fs";var Uf={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 B2(f){return typeof f==="string"&&(f.startsWith("encrypted:")||f.startsWith("enc:"))}var $2=/(?:^|_)(?:PASSWORD|PASSWD|SECRET|TOKEN|APIKEY|API_KEY|CREDENTIAL|CREDENTIALS|PRIVATE|SALT|DSN|CERT|CIPHER|SIGNATURE|WEBHOOK)(?:_|$)|(?:^|_)(?:ACCESS|SECRET)_KEY(?:_|$)|_KEY$/,X2=/^DOTENV_(?:PUBLIC|PRIVATE)_KEY/,D2=/^\$\{[^}]+\}$/;function X5(f,g={}){let{placeholders:B={},strict:X=!1}=g,$=[];for(let[D,H]of Object.entries(f)){if(!H||H.trim()==="")continue;if(B2(H))continue;if(X2.test(D))continue;if(D2.test(H.trim()))continue;let A=$2.test(D),W=Object.prototype.hasOwnProperty.call(B,D)&&B[D]===H;if(A&&!W){$.push({key:D,reason:"plaintext-secret"});continue}if(X)$.push({key:D,reason:"plaintext-value"})}return $}function D5(f){return f.split(`
25
- `).map((g)=>g.trim()).filter(Boolean).filter((g)=>g!==".env.example").filter((g)=>/(?:^|\/)\.env(?:\.|$)/.test(g)).filter((g)=>!g.endsWith(".env.keys"))}function Tf(f){return`${f.replace(/[^a-z0-9]/gi,"").toUpperCase()}_`}function H2(f,g={}){let B=g.self?Tf(g.self):void 0,X=(g.tenants??[]).filter((H)=>typeof H==="string"&&H.trim().length>0).map((H)=>({slug:H,prefix:Tf(H)})).filter(({prefix:H})=>H!==B).sort((H,A)=>A.prefix.length-H.prefix.length),$={},D={};for(let[H,A]of Object.entries(f)){let W=X.find(({prefix:_})=>H.startsWith(_));if(W){(D[W.slug]??={})[H]=A;continue}$[H]=A}return{own:$,foreign:D}}function _5(f,g={}){return H2(f,g).own}function A5(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 Mf=new Set;function W2(f){if(Mf.has(f))return;Mf.add(f);let g=l();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 Y2={get:(f,g)=>{let B=f[g];if(typeof B==="string"&&(B.startsWith("encrypted:")||B.startsWith("enc:"))){let $=Gf(B);if($===void 0){W2(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 j2(){return typeof Bun<"u"?Bun.env:_2.env}var qf=new Proxy(j2(),Y2);function Z5(f,g,B){let X=B?.path||A2(".env"),D=Lf.readFileSync(X,"utf-8").split(`
26
- `),H=D.findIndex((_)=>_.startsWith(`${f}=`)),W=/[\s"'#$\\]/.test(g)?`"${g.replace(/"/g,"\\\"")}"`:g;if(H!==-1)D[H]=`${f}=${W}`;else D.push(`${f}=${W}`);Lf.writeFileSync(X,D.join(`
27
- `))}function Q5(f=qf){let g=[];for(let[B,X]of Object.entries(Uf)){let $=f[B];if($!==void 0&&$!==""&&!X.includes(String($)))g.push(`${B}="${$}" is not valid. Allowed values: ${X.join(", ")}`)}return g}function R5(f,g=qf){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{Z5 as writeEnv,Q5 as validateEnv,D5 as trackedEnvFiles,Tf as tenantEnvPrefix,_5 as stripForeignTenantEnv,u2 as setEnv,o2 as runtimeInfo,af as runtime,y2 as rotateKeypair,of as resolvePrivateKey,m2 as resolveEnvFile,b2 as resetPrivateKeyCache,R5 as requireEnv,g5 as providerInfo,g2 as provider,j2 as process,ff as platform,X5 as plaintextSecrets,H2 as partitionTenantEnv,Q2 as parseEnvFromKey,M as parse,Z2 as migrateEncryptedValue,M2 as loadEnvFiles,Jf as loadEnv,p2 as isWindows,If as isNode,f2 as isMinimal,l2 as isMacOS,s2 as isLinux,O2 as isLegacyEncryptedValue,B2 as isEncryptedValue,e2 as isDebug,f5 as isColorSupported,ef as isCI,Rf as isBun,a2 as hasWindow,wf as hasTTY,o as getPrivateKey,t2 as getKeypair,r2 as getEnv,S as generateKeypair,A5 as foreignTenantKeys,pf as envPlugin,Uf as envEnum,qf as env,c as encryptValue,d2 as encryptEnv,b as decryptValue,Gf as decryptEnvValue,lf as decryptEnv,S2 as autoLoadEnv,J2 as aesEncrypt,Vf as aesDecrypt,l as activeEnvName,$2 as SECRET_NAME_PATTERN};
24
+ `,T=R2(g.output,D.publicKey,j,f),I=`${process.pid}-${Date.now()}`,R=L(Q2(X),`.${a(X)}.${I}.rotate`),Z=L(Q2($),`.${a($)}.${I}.rotate`);try{if(V(R,T,{encoding:"utf8",mode:384}),V(Z,w,{encoding:"utf8",mode:384}),f.stdout)return s(Z,$),{success:!0,output:T};s(R,X);try{s(Z,$)}catch(U){throw V(X,H),U}return{success:!0,output:`\u2714 rotated (${f.file||".env"}) to encrypted:v2`}}catch(U){if(!z($))V($,W,{mode:384});return{success:!1,error:`Failed to rotate without exposing plaintext: ${U instanceof Error?U.message:"Unknown error"}`}}finally{Z2(R,{force:!0}),Z2(Z,{force:!0})}}import O from"process";import{platform as s2}from"os";var I2=typeof Bun<"u",w2=typeof O<"u"&&O.versions?.node!==void 0,a2=I2?"bun":w2?"node":"unknown",pf={name:a2,version:I2?Bun.version:w2?O.version:void 0},g2=s2(),lf=g2==="win32",sf=g2==="darwin",af=g2==="linux",U2=Boolean(O.stdout?.isTTY),ef=typeof globalThis.window<"u",e2=Boolean(O.env.CI||O.env.CONTINUOUS_INTEGRATION||O.env.BUILD_NUMBER||O.env.RUN_ID),f5=Boolean(O.env.DEBUG||O.env.VERBOSE||O.argv.includes("--debug")||O.argv.includes("--verbose")),ff=e2||!U2,g5=Boolean(!ff&&(U2||O.env.COLORTERM||O.env.FORCE_COLOR||O.env.TERM&&O.env.TERM!=="dumb")),f2={github:{name:"GitHub Actions",detected:Boolean(O.env.GITHUB_ACTIONS)},gitlab:{name:"GitLab CI",detected:Boolean(O.env.GITLAB_CI)},circle:{name:"CircleCI",detected:Boolean(O.env.CIRCLECI)},travis:{name:"Travis CI",detected:Boolean(O.env.TRAVIS)},jenkins:{name:"Jenkins",detected:Boolean(O.env.JENKINS_URL)},vercel:{name:"Vercel",detected:Boolean(O.env.VERCEL)},netlify:{name:"Netlify",detected:Boolean(O.env.NETLIFY)},heroku:{name:"Heroku",detected:Boolean(O.env.DYNO)},aws:{name:"AWS",detected:Boolean(O.env.AWS_REGION||O.env.AWS_LAMBDA_FUNCTION_NAME)},azure:{name:"Azure",detected:Boolean(O.env.AZURE_HTTP_USER_AGENT)},cloudflare:{name:"Cloudflare",detected:Boolean(O.env.CF_PAGES)},railway:{name:"Railway",detected:Boolean(O.env.RAILWAY_ENVIRONMENT)},render:{name:"Render",detected:Boolean(O.env.RENDER)}},gf=Object.keys(f2).find((f)=>{let g=f2[f];return g!==void 0&&g.detected})||"unknown",B5=f2[gf]||{name:"Unknown",detected:!1};import _f from"process";import{projectPath as Af}from"@stacksjs/path";import M2 from"fs";var T2={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 Bf(f){return typeof f==="string"&&(f.startsWith("encrypted:")||f.startsWith("enc:"))}var $f=/(?:^|_)(?:PASSWORD|PASSWD|SECRET|TOKEN|APIKEY|API_KEY|CREDENTIAL|CREDENTIALS|PRIVATE|SALT|DSN|CERT|CIPHER|SIGNATURE|WEBHOOK)(?:_|$)|(?:^|_)(?:ACCESS|SECRET)_KEY(?:_|$)|_KEY$/,Xf=/^DOTENV_(?:PUBLIC|PRIVATE)_KEY/,Df=/^\$\{[^}]+\}$/;function D5(f,g={}){let{placeholders:B={},strict:X=!1}=g,$=[];for(let[D,H]of Object.entries(f)){if(!H||H.trim()==="")continue;if(Bf(H))continue;if(Xf.test(D))continue;if(Df.test(H.trim()))continue;let W=$f.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 H5(f){return f.split(`
25
+ `).map((g)=>g.trim()).filter(Boolean).filter((g)=>g!==".env.example").filter((g)=>/(?:^|\/)\.env(?:\.|$)/.test(g)).filter((g)=>!g.endsWith(".env.keys"))}function L2(f){return`${f.replace(/[^a-z0-9]/gi,"").toUpperCase()}_`}function Hf(f,g={}){let B=g.self?L2(g.self):void 0,X=(g.tenants??[]).filter((H)=>typeof H==="string"&&H.trim().length>0).map((H)=>({slug:H,prefix:L2(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 A5(f,g={}){return Hf(f,g).own}function W5(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 q2=new Set;function Wf(f){if(q2.has(f))return;q2.add(f);let g=l();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 Yf={get:(f,g)=>{let B=f[g];if(typeof B==="string"&&(B.startsWith("encrypted:")||B.startsWith("enc:"))){let $=J2(B);if($===void 0){Wf(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 jf(){return typeof Bun<"u"?Bun.env:_f.env}var z2=new Proxy(jf(),Yf);function Q5(f,g,B){let X=B?.path||Af(".env"),D=M2.readFileSync(X,"utf-8").split(`
26
+ `),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}`);M2.writeFileSync(X,D.join(`
27
+ `))}function R5(f=z2){let g=[];for(let[B,X]of Object.entries(T2)){let $=f[B];if($!==void 0&&$!==""&&!X.includes(String($)))g.push(`${B}="${$}" is not valid. Allowed values: ${X.join(", ")}`)}return g}function I5(f,g=z2){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{Q5 as writeEnv,R5 as validateEnv,H5 as trackedEnvFiles,L2 as tenantEnvPrefix,A5 as stripForeignTenantEnv,uf as setEnv,pf as runtimeInfo,a2 as runtime,yf as rotateKeypair,o2 as resolvePrivateKey,mf as resolveEnvFile,bf as resetPrivateKeyCache,I5 as requireEnv,B5 as providerInfo,gf as provider,jf as process,g2 as platform,D5 as plaintextSecrets,Hf as partitionTenantEnv,Qf as parseEnvFromKey,M as parse,Zf as migrateEncryptedValue,Mf as loadEnvFiles,O2 as loadEnv,lf as isWindows,w2 as isNode,ff as isMinimal,sf as isMacOS,af as isLinux,Of as isLegacyEncryptedValue,Bf as isEncryptedValue,f5 as isDebug,g5 as isColorSupported,e2 as isCI,I2 as isBun,ef as hasWindow,U2 as hasTTY,o as getPrivateKey,tf as getKeypair,rf as getEnv,S as generateKeypair,W5 as foreignTenantKeys,p2 as envPlugin,e as envKeyNames,T2 as envEnum,z2 as env,c as encryptValue,df as encryptEnv,b as decryptValue,J2 as decryptEnvValue,l2 as decryptEnv,Sf as autoLoadEnv,Jf as aesEncrypt,E2 as aesDecrypt,l as activeEnvName,$f as SECRET_NAME_PATTERN};
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/env",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.70.355",
5
+ "version": "0.70.357",
6
6
  "description": "Stacks env helper methods.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -56,7 +56,7 @@
56
56
  },
57
57
  "devDependencies": {
58
58
  "better-dx": "^0.2.17",
59
- "@stacksjs/path": "0.70.355",
60
- "@stacksjs/validation": "0.70.355"
59
+ "@stacksjs/path": "0.70.357",
60
+ "@stacksjs/validation": "0.70.357"
61
61
  }
62
62
  }