@stacksjs/env 0.70.183 → 0.70.185

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
@@ -1,3 +1,19 @@
1
+ /**
2
+ * Decide which env file a command should act on.
3
+ *
4
+ * `buddy` carries a global `--env <environment>` flag, and reaching for it is
5
+ * the obvious way to say "this secret belongs to production". The env commands
6
+ * only ever read `-f/--file` though, so `buddy env:set KEY value --env
7
+ * production` wrote to `.env` — accepted, no warning, wrong file. The value
8
+ * then sat in the development env encrypted against a production key nobody
9
+ * holds locally, which surfaces much later as a decryption warning rather than
10
+ * as the mis-targeted write it actually was.
11
+ *
12
+ * `--file` still wins when given, since it names a path outright. `development`
13
+ * (and an unset environment) keeps the plain `.env` convention, and returns the
14
+ * empty string so callers fall through to their own defaults unchanged.
15
+ */
16
+ export declare function resolveEnvFile(file?: string, environment?: string): string;
1
17
  /**
2
18
  * Encrypt .env file
3
19
  */
package/dist/cli.js CHANGED
@@ -1,24 +1,24 @@
1
1
  // @bun
2
- import{createCipheriv as u,createDecipheriv as K,createHash as C,createPrivateKey as e,createPublicKey as m,diffieHellman as k,generateKeyPairSync as n,hkdfSync as s,randomBytes as F}from"crypto";var b="x25519-public:",S="x25519-private:",V="encrypted:v2:",P="encrypted:",o=Buffer.from("stacks-env:v2","utf8"),i="Environment decryption failed: authentication or format error";function v(f){if(f.length===32)return f;return C("sha256").update(f).digest()}function _f(f,g){let r=F(16),c=u("aes-256-gcm",v(g),r);return{ciphertext:Buffer.concat([c.update(f,"utf8"),c.final()]).toString("hex"),iv:r.toString("hex"),authTag:c.getAuthTag().toString("hex")}}function ff(f,g,r,c){try{let E=K("aes-256-gcm",v(g),Buffer.from(r,"hex"));return E.setAuthTag(Buffer.from(c,"hex")),Buffer.concat([E.update(Buffer.from(f,"hex")),E.final()]).toString("utf8")}catch{throw Error("Decryption failed: authentication or format error")}}function U(f){return f.toString("base64url")}function z(f,g,r){if(typeof f!=="string"||f.length>0&&!/^[A-Za-z0-9_-]+$/.test(f))throw Error(`${g} is not canonical base64url`);let c=Buffer.from(f,"base64url");if(U(c)!==f)throw Error(`${g} is not canonical base64url`);if(r!==void 0&&c.length!==r)throw Error(`${g} has an invalid length`);return c}function gf(f){if(!f.startsWith(b))throw Error("Legacy or invalid public key. Run buddy env:rotate before creating new ciphertext.");let g=m({key:z(f.slice(b.length),"public key"),format:"der",type:"spki"});if(g.asymmetricKeyType!=="x25519")throw Error("Public key is not X25519");return g}function rf(f){if(!f.startsWith(S))throw Error("Private key is not a version 2 X25519 key");let g=e({key:z(f.slice(S.length),"private key"),format:"der",type:"pkcs8"});if(g.asymmetricKeyType!=="x25519")throw Error("Private key is not X25519");return g}function y(f){return Buffer.from(`stacks-env:v2;${f.epk};${f.salt};${f.nonce}`,"utf8")}function q(){let{publicKey:f,privateKey:g}=n("x25519"),r=f.export({format:"der",type:"spki"}),c=g.export({format:"der",type:"pkcs8"});return{publicKey:`${b}${U(r)}`,privateKey:`${S}${U(c)}`}}function L(f,g){let r=gf(g),c=n("x25519"),E=U(c.publicKey.export({format:"der",type:"spki"})),h=F(16),A=F(12),x=k({privateKey:c.privateKey,publicKey:r}),$=Buffer.from(s("sha256",x,h,o,32)),B={epk:E,salt:U(h),nonce:U(A)};try{let w=u("aes-256-gcm",$,A);w.setAAD(y(B));let _=Buffer.concat([w.update(f,"utf8"),w.final()]),D={v:2,...B,ciphertext:U(_),tag:U(w.getAuthTag())};return`${V}${U(Buffer.from(JSON.stringify(D),"utf8"))}`}finally{x.fill(0),$.fill(0)}}function Ef(f){let g=z(f.slice(V.length),"envelope").toString("utf8"),r=JSON.parse(g);if(!r||typeof r!=="object"||Array.isArray(r))throw Error("envelope is not an object");let c=Object.keys(r).sort(),E=["ciphertext","epk","nonce","salt","tag","v"];if(c.length!==E.length||c.some((h,A)=>h!==E[A]))throw Error("envelope fields are invalid");if(r.v!==2)throw Error("envelope version is unsupported");for(let h of["epk","salt","nonce","ciphertext","tag"])if(typeof r[h]!=="string")throw Error(`${h} is invalid`);return r}function cf(f,g){try{let r=Ef(f),c=rf(g),E=m({key:z(r.epk,"ephemeral public key"),format:"der",type:"spki"});if(E.asymmetricKeyType!=="x25519")throw Error("ephemeral key is not X25519");let h=z(r.salt,"salt",16),A=z(r.nonce,"nonce",12),x=z(r.ciphertext,"ciphertext"),$=z(r.tag,"tag",16),B=k({privateKey:c,publicKey:E}),w=Buffer.from(s("sha256",B,h,o,32));try{let _=K("aes-256-gcm",w,A);return _.setAAD(y(r)),_.setAuthTag($),Buffer.concat([_.update(x),_.final()]).toString("utf8")}finally{B.fill(0),w.fill(0)}}catch{throw Error(i)}}function Bf(f,g){try{if(!/^[0-9a-f]{64}$/.test(g))throw Error("invalid legacy private key");let r=Buffer.from(f.slice(P.length),"base64");if(r.length<32)throw Error("invalid legacy payload");let c=C("sha256").update(Buffer.from(g,"hex")).digest(),E=C("sha256").update(c).digest();return ff(r.subarray(32).toString("hex"),E,r.subarray(0,16).toString("hex"),r.subarray(16,32).toString("hex"))}catch{throw Error(i)}}function If(f){return f.startsWith(P)&&!f.startsWith(V)}function Q(f,g){if(!f.startsWith(P))return f;return f.startsWith(V)?cf(f,g):Bf(f,g)}function Tf(f,g,r){return L(Q(f,g),r)}function tf(f){let g=f.match(/^DOTENV_PRIVATE_KEY_(.+)$/);return g&&g[1]?g[1].toLowerCase():""}function Rf(f=""){return f?process.env[`DOTENV_PRIVATE_KEY_${f.toUpperCase()}`]:process.env.DOTENV_PRIVATE_KEY}function Z(f,g={}){let r={},c=[],E=[],h=f.split(`
3
- `);for(let A of h){let x=A.trim();if(!x||x.startsWith("#"))continue;if(x.startsWith("DOTENV_PUBLIC_KEY=")){let _=x.match(/^DOTENV_PUBLIC_KEY=["']?([^"'\n]+)["']?/);if(_&&_[1]!==void 0)r.DOTENV_PUBLIC_KEY=_[1];continue}let $=x.match(/^([^=]+)=(.*)$/);if(!$||$[1]===void 0||$[2]===void 0)continue;let B=$[1].trim(),w=$[2].trim();if(w.startsWith('"')&&w.endsWith('"')||w.startsWith("'")&&w.endsWith("'")){if(w=w.slice(1,-1),w.includes("\\n"))w=w.replace(/\\n/g,`
4
- `)}if(w.startsWith("encrypted:")||w.startsWith("enc:")){if(!g.privateKey){E.push(B);continue}try{let _=w.startsWith("enc:")?`encrypted:${w.slice(4)}`:w;w=Q(_,g.privateKey)}catch(_){c.push(`Failed to decrypt ${B}: ${_ instanceof Error?_.message:"Unknown error"}`)}}w=hf(w,{...g.processEnv||process.env,...r}),w=$f(w),r[B]=w}return{parsed:r,errors:c,skippedEncrypted:E}}function hf(f,g){return f.replace(/\$\{([^}]+)\}/g,(r,c)=>{let E=c.match(/^([^:\-+]+)(:-|-)(.+)$/);if(E){let[,A,x,$]=E,B=g[A];if(x===":-")return B||$;else return B!==void 0?B:$}let h=c.match(/^([^:\-+]+)(:?\+)(.+)$/);if(h){let[,A,x,$]=h,B=g[A];if(x===":+")return B?$:"";else return B!==void 0?$:""}return g[c]||""})}var wf=new Set(["date","hostname","whoami","uname","pwd","echo","printf","cat","basename","dirname"]);function $f(f){return f.replace(/\$\(([^)]+)\)/g,(g,r)=>{try{let c=r.trim().split(/\s+/),E=c[0];if(!E||!wf.has(E))return console.warn(`[env] Blocked command substitution for disallowed command: ${E}`),"";let h=Bun.spawnSync(c,{stdout:"pipe",stderr:"pipe"});if(h.exitCode===0)return new TextDecoder().decode(h.stdout).trim();console.warn(`[env] Command substitution failed (exit code ${h.exitCode}): ${E}`)}catch(c){console.warn(`[env] Command substitution error: ${c instanceof Error?c.message:String(c)}`)}return""})}async function Of(f,g={}){let r={},c=[],E=[];for(let h of f)try{let A=Bun.file(h);if(!A.size)continue;let x=await A.text(),{parsed:$,errors:B,skippedEncrypted:w}=Z(x,g);c.push(...B),E.push(...w);for(let[_,D]of Object.entries($)){if(!g.overload&&r[_]!==void 0)continue;r[_]=D}}catch(A){if(A?.code==="ENOENT")continue;c.push(`Failed to read ${h}: ${A instanceof Error?A.message:String(A)}`)}return{parsed:r,errors:c,skippedEncrypted:E}}import{existsSync as G,readFileSync as j,renameSync as d,rmSync as l,writeFileSync as J}from"fs";import{basename as N,dirname as p,resolve as W}from"path";function a(f,g,r,c){let E=["#/-------------------[DOTENV_PUBLIC_KEY]--------------------/","#/ versioned X25519 encryption for .env files /","#/ [how it works](https://stacksjs.com/encryption) /","#/----------------------------------------------------------/",`${r}="${g}"`,""];for(let h of f.split(`
5
- `)){let A=h.trim();if(!A||A.startsWith("#")){E.push(h);continue}if(A.startsWith("DOTENV_PUBLIC_KEY"))continue;let x=A.match(/^([^=]+)=(.*)$/);if(!x||x[1]===void 0||x[2]===void 0){E.push(h);continue}let $=x[1].trim(),B=x[2].trim();if(B.startsWith('"')&&B.endsWith('"')||B.startsWith("'")&&B.endsWith("'"))B=B.slice(1,-1);if((!c.key||$.includes(c.key))&&(!c.excludeKey||!$.includes(c.excludeKey))&&!B.startsWith("encrypted:"))B=L(B,g);E.push(`${$}="${B}"`)}return E.join(`
6
- `)}function Jf(f={}){let g=f.cwd||process.cwd(),r=W(g,f.file||".env"),c=W(g,f.keysFile||".env.keys");if(!G(r))return{success:!1,error:`File not found: ${r}`};try{let E,h;if(G(c)){let w=j(c,"utf-8"),{parsed:_}=Z(w),R=((f.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),X=R?`DOTENV_PUBLIC_KEY_${R}`:"DOTENV_PUBLIC_KEY",H=R?`DOTENV_PRIVATE_KEY_${R}`:"DOTENV_PRIVATE_KEY";if(E=_[X]||"",h=_[H]||"",!E||!h){let t=q();E=t.publicKey,h=t.privateKey;let O=`
7
- ${X}="${E}"
2
+ import{createCipheriv as u,createDecipheriv as K,createHash as C,createPrivateKey as e,createPublicKey as k,diffieHellman as m,generateKeyPairSync as n,hkdfSync as s,randomBytes as F}from"crypto";var b="x25519-public:",S="x25519-private:",V="encrypted:v2:",P="encrypted:",o=Buffer.from("stacks-env:v2","utf8"),i="Environment decryption failed: authentication or format error";function v(f){if(f.length===32)return f;return C("sha256").update(f).digest()}function _f(f,r){let g=F(16),c=u("aes-256-gcm",v(r),g);return{ciphertext:Buffer.concat([c.update(f,"utf8"),c.final()]).toString("hex"),iv:g.toString("hex"),authTag:c.getAuthTag().toString("hex")}}function ff(f,r,g,c){try{let E=K("aes-256-gcm",v(r),Buffer.from(g,"hex"));return E.setAuthTag(Buffer.from(c,"hex")),Buffer.concat([E.update(Buffer.from(f,"hex")),E.final()]).toString("utf8")}catch{throw Error("Decryption failed: authentication or format error")}}function U(f){return f.toString("base64url")}function z(f,r,g){if(typeof f!=="string"||f.length>0&&!/^[A-Za-z0-9_-]+$/.test(f))throw Error(`${r} is not canonical base64url`);let c=Buffer.from(f,"base64url");if(U(c)!==f)throw Error(`${r} is not canonical base64url`);if(g!==void 0&&c.length!==g)throw Error(`${r} has an invalid length`);return c}function gf(f){if(!f.startsWith(b))throw Error("Legacy or invalid public key. Run buddy env:rotate before creating new ciphertext.");let r=k({key:z(f.slice(b.length),"public key"),format:"der",type:"spki"});if(r.asymmetricKeyType!=="x25519")throw Error("Public key is not X25519");return r}function rf(f){if(!f.startsWith(S))throw Error("Private key is not a version 2 X25519 key");let r=e({key:z(f.slice(S.length),"private key"),format:"der",type:"pkcs8"});if(r.asymmetricKeyType!=="x25519")throw Error("Private key is not X25519");return r}function y(f){return Buffer.from(`stacks-env:v2;${f.epk};${f.salt};${f.nonce}`,"utf8")}function q(){let{publicKey:f,privateKey:r}=n("x25519"),g=f.export({format:"der",type:"spki"}),c=r.export({format:"der",type:"pkcs8"});return{publicKey:`${b}${U(g)}`,privateKey:`${S}${U(c)}`}}function L(f,r){let g=gf(r),c=n("x25519"),E=U(c.publicKey.export({format:"der",type:"spki"})),h=F(16),A=F(12),x=m({privateKey:c.privateKey,publicKey:g}),$=Buffer.from(s("sha256",x,h,o,32)),B={epk:E,salt:U(h),nonce:U(A)};try{let w=u("aes-256-gcm",$,A);w.setAAD(y(B));let _=Buffer.concat([w.update(f,"utf8"),w.final()]),X={v:2,...B,ciphertext:U(_),tag:U(w.getAuthTag())};return`${V}${U(Buffer.from(JSON.stringify(X),"utf8"))}`}finally{x.fill(0),$.fill(0)}}function Ef(f){let r=z(f.slice(V.length),"envelope").toString("utf8"),g=JSON.parse(r);if(!g||typeof g!=="object"||Array.isArray(g))throw Error("envelope is not an object");let c=Object.keys(g).sort(),E=["ciphertext","epk","nonce","salt","tag","v"];if(c.length!==E.length||c.some((h,A)=>h!==E[A]))throw Error("envelope fields are invalid");if(g.v!==2)throw Error("envelope version is unsupported");for(let h of["epk","salt","nonce","ciphertext","tag"])if(typeof g[h]!=="string")throw Error(`${h} is invalid`);return g}function cf(f,r){try{let g=Ef(f),c=rf(r),E=k({key:z(g.epk,"ephemeral public key"),format:"der",type:"spki"});if(E.asymmetricKeyType!=="x25519")throw Error("ephemeral key is not X25519");let h=z(g.salt,"salt",16),A=z(g.nonce,"nonce",12),x=z(g.ciphertext,"ciphertext"),$=z(g.tag,"tag",16),B=m({privateKey:c,publicKey:E}),w=Buffer.from(s("sha256",B,h,o,32));try{let _=K("aes-256-gcm",w,A);return _.setAAD(y(g)),_.setAuthTag($),Buffer.concat([_.update(x),_.final()]).toString("utf8")}finally{B.fill(0),w.fill(0)}}catch{throw Error(i)}}function Bf(f,r){try{if(!/^[0-9a-f]{64}$/.test(r))throw Error("invalid legacy private key");let g=Buffer.from(f.slice(P.length),"base64");if(g.length<32)throw Error("invalid legacy payload");let c=C("sha256").update(Buffer.from(r,"hex")).digest(),E=C("sha256").update(c).digest();return ff(g.subarray(32).toString("hex"),E,g.subarray(0,16).toString("hex"),g.subarray(16,32).toString("hex"))}catch{throw Error(i)}}function If(f){return f.startsWith(P)&&!f.startsWith(V)}function Q(f,r){if(!f.startsWith(P))return f;return f.startsWith(V)?cf(f,r):Bf(f,r)}function Tf(f,r,g){return L(Q(f,r),g)}function Rf(f){let r=f.match(/^DOTENV_PRIVATE_KEY_(.+)$/);return r&&r[1]?r[1].toLowerCase():""}function Df(f=""){return f?process.env[`DOTENV_PRIVATE_KEY_${f.toUpperCase()}`]:process.env.DOTENV_PRIVATE_KEY}function Z(f,r={}){let g={},c=[],E=[],h=f.split(`
3
+ `);for(let A of h){let x=A.trim();if(!x||x.startsWith("#"))continue;if(x.startsWith("DOTENV_PUBLIC_KEY=")){let _=x.match(/^DOTENV_PUBLIC_KEY=["']?([^"'\n]+)["']?/);if(_&&_[1]!==void 0)g.DOTENV_PUBLIC_KEY=_[1];continue}let $=x.match(/^([^=]+)=(.*)$/);if(!$||$[1]===void 0||$[2]===void 0)continue;let B=$[1].trim(),w=$[2].trim();if(w.startsWith('"')&&w.endsWith('"')||w.startsWith("'")&&w.endsWith("'")){if(w=w.slice(1,-1),w.includes("\\n"))w=w.replace(/\\n/g,`
4
+ `)}if(w.startsWith("encrypted:")||w.startsWith("enc:")){if(!r.privateKey){E.push(B);continue}try{let _=w.startsWith("enc:")?`encrypted:${w.slice(4)}`:w;w=Q(_,r.privateKey)}catch(_){c.push(`Failed to decrypt ${B}: ${_ instanceof Error?_.message:"Unknown error"}`)}}w=hf(w,{...r.processEnv||process.env,...g}),w=$f(w),g[B]=w}return{parsed:g,errors:c,skippedEncrypted:E}}function hf(f,r){return f.replace(/\$\{([^}]+)\}/g,(g,c)=>{let E=c.match(/^([^:\-+]+)(:-|-)(.+)$/);if(E){let[,A,x,$]=E,B=r[A];if(x===":-")return B||$;else return B!==void 0?B:$}let h=c.match(/^([^:\-+]+)(:?\+)(.+)$/);if(h){let[,A,x,$]=h,B=r[A];if(x===":+")return B?$:"";else return B!==void 0?$:""}return r[c]||""})}var wf=new Set(["date","hostname","whoami","uname","pwd","echo","printf","cat","basename","dirname"]);function $f(f){return f.replace(/\$\(([^)]+)\)/g,(r,g)=>{try{let c=g.trim().split(/\s+/),E=c[0];if(!E||!wf.has(E))return console.warn(`[env] Blocked command substitution for disallowed command: ${E}`),"";let h=Bun.spawnSync(c,{stdout:"pipe",stderr:"pipe"});if(h.exitCode===0)return new TextDecoder().decode(h.stdout).trim();console.warn(`[env] Command substitution failed (exit code ${h.exitCode}): ${E}`)}catch(c){console.warn(`[env] Command substitution error: ${c instanceof Error?c.message:String(c)}`)}return""})}async function Of(f,r={}){let g={},c=[],E=[];for(let h of f)try{let A=Bun.file(h);if(!A.size)continue;let x=await A.text(),{parsed:$,errors:B,skippedEncrypted:w}=Z(x,r);c.push(...B),E.push(...w);for(let[_,X]of Object.entries($)){if(!r.overload&&g[_]!==void 0)continue;g[_]=X}}catch(A){if(A?.code==="ENOENT")continue;c.push(`Failed to read ${h}: ${A instanceof Error?A.message:String(A)}`)}return{parsed:g,errors:c,skippedEncrypted:E}}import{existsSync as G,readFileSync as j,renameSync as d,rmSync as l,writeFileSync as J}from"fs";import{basename as N,dirname as p,resolve as W}from"path";function Jf(f,r){if(f)return f;let g=String(r??"").trim();if(!g||g==="development"||g==="dev"||g==="local")return"";if(!/^[\w.-]+$/.test(g))return"";return`.env.${g}`}function a(f,r,g,c){let E=["#/-------------------[DOTENV_PUBLIC_KEY]--------------------/","#/ versioned X25519 encryption for .env files /","#/ [how it works](https://stacksjs.com/encryption) /","#/----------------------------------------------------------/",`${g}="${r}"`,""];for(let h of f.split(`
5
+ `)){let A=h.trim();if(!A||A.startsWith("#")){E.push(h);continue}if(A.startsWith("DOTENV_PUBLIC_KEY"))continue;let x=A.match(/^([^=]+)=(.*)$/);if(!x||x[1]===void 0||x[2]===void 0){E.push(h);continue}let $=x[1].trim(),B=x[2].trim();if(B.startsWith('"')&&B.endsWith('"')||B.startsWith("'")&&B.endsWith("'"))B=B.slice(1,-1);if((!c.key||$.includes(c.key))&&(!c.excludeKey||!$.includes(c.excludeKey))&&!B.startsWith("encrypted:"))B=L(B,r);E.push(`${$}="${B}"`)}return E.join(`
6
+ `)}function Uf(f={}){let r=f.cwd||process.cwd(),g=W(r,f.file||".env"),c=W(r,f.keysFile||".env.keys");if(!G(g))return{success:!1,error:`File not found: ${g}`};try{let E,h;if(G(c)){let w=j(c,"utf-8"),{parsed:_}=Z(w),D=((f.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),t=D?`DOTENV_PUBLIC_KEY_${D}`:"DOTENV_PUBLIC_KEY",H=D?`DOTENV_PRIVATE_KEY_${D}`:"DOTENV_PRIVATE_KEY";if(E=_[t]||"",h=_[H]||"",!E||!h){let R=q();E=R.publicKey,h=R.privateKey;let O=`
7
+ ${t}="${E}"
8
8
  ${H}="${h}"
9
- `;J(c,w+O,"utf-8")}}else{let w=q();E=w.publicKey,h=w.privateKey;let I=((f.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),R=I?`DOTENV_PUBLIC_KEY_${I}`:"DOTENV_PUBLIC_KEY",X=I?`DOTENV_PRIVATE_KEY_${I}`:"DOTENV_PRIVATE_KEY",H=`# .env.keys - Keep this file secure and never commit to source control
10
- ${R}="${E}"
11
- ${X}="${h}"
12
- `;J(c,H,"utf-8")}let A=j(r,"utf-8"),x=f.file?f.file.replace(/^\.env\./,"").toUpperCase():"",$=x?`DOTENV_PUBLIC_KEY_${x}`:"DOTENV_PUBLIC_KEY",B=a(A,E,$,f);if(f.stdout)return{success:!0,output:B};return J(r,B,"utf-8"),{success:!0,output:`\u2714 encrypted (${f.file||".env"})
13
- \u2714 key added to ${f.keysFile||".env.keys"}`}}catch(E){return{success:!1,error:`Failed to encrypt: ${E instanceof Error?E.message:"Unknown error"}`}}}function xf(f={}){let g=f.cwd||process.cwd(),r=W(g,f.file||".env"),c=W(g,f.keysFile||".env.keys");if(!G(r))return{success:!1,error:`File not found: ${r}`};if(!G(c))return{success:!1,error:`Keys file not found: ${c}`};try{let E=j(c,"utf-8"),{parsed:h}=Z(E),$=((f.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),B=$?`DOTENV_PRIVATE_KEY_${$}`:"DOTENV_PRIVATE_KEY",w=h[B];if(!w)return{success:!1,error:`Private key not found: ${B}`};let D=j(r,"utf-8").split(`
14
- `),I=[];for(let X of D){let H=X.trim();if(!H||H.startsWith("#")){I.push(X);continue}if(H.startsWith("DOTENV_PUBLIC_KEY"))continue;let t=H.match(/^([^=]+)=(.*)$/);if(!t||t[1]===void 0||t[2]===void 0){I.push(X);continue}let O=t[1].trim(),T=t[2].trim();if(T.startsWith('"')&&T.endsWith('"')||T.startsWith("'")&&T.endsWith("'"))T=T.slice(1,-1);let Y=T.startsWith("encrypted:");if(f.key&&!O.includes(f.key))Y=!1;if(Y)T=Q(T,w);I.push(`${O}="${T}"`)}let R=I.join(`
15
- `);if(f.stdout)return{success:!0,output:R};return J(r,R,"utf-8"),{success:!0,output:`\u2714 decrypted (${f.file||".env"})`}}catch(E){return{success:!1,error:`Failed to decrypt: ${E instanceof Error?E.message:"Unknown error"}`}}}function Uf(f,g,r={}){let c=r.cwd||process.cwd(),E=W(c,r.file||".env"),h=W(c,r.keysFile||".env.keys");try{let A="";if(G(E))A=j(E,"utf-8");let x=A.split(`
16
- `),$=!1,B,D=((r.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),I=D?`DOTENV_PUBLIC_KEY_${D}`:"DOTENV_PUBLIC_KEY",R=D?`DOTENV_PRIVATE_KEY_${D}`:"DOTENV_PRIVATE_KEY";for(let t of x){let O=t.trim();if(O.startsWith(`${I}=`)){let T=O.match(/^[^=]+=["']?([^"'\n]+)["']?/);if(T)B=T[1];break}}if(B){let t=!1;if(G(h)){let O=j(h,"utf-8"),{parsed:T}=Z(O);t=Boolean(T[R])}if(!t)B=void 0}if(!r.plain&&!B){let t=q();B=t.publicKey;let O="";if(G(h))O=j(h,"utf-8");O+=`
17
- ${I}="${t.publicKey}"
18
- ${R}="${t.privateKey}"
19
- `,J(h,O,"utf-8");for(let T=x.length-1;T>=0;T--){let Y=x[T];if(Y!==void 0&&Y.trim().startsWith(`${I}=`))x.splice(T,1)}x.unshift(`${I}="${B}"`)}let X=g;if(!r.plain&&B)X=L(g,B);for(let t=0;t<x.length;t++){let O=x[t];if(O===void 0)continue;if(O.trim().startsWith(`${f}=`)){x[t]=`${f}="${X}"`,$=!0;break}}if(!$)x.push(`${f}="${X}"`);let H=x.join(`
20
- `);return J(E,H,"utf-8"),{success:!0,output:`set ${f}${r.plain?"":" with encryption"} (${r.file||".env"})`}}catch(A){return{success:!1,error:`Failed to set: ${A instanceof Error?A.message:"Unknown error"}`}}}function zf(f,g={}){let r=g.cwd||process.cwd(),c=W(r,g.file||".env");if(!G(c))return{success:!1,error:`File not found: ${c}`};try{let E,h=W(r,g.keysFile||".env.keys"),$=((g.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),B=$?`DOTENV_PRIVATE_KEY_${$}`:"DOTENV_PRIVATE_KEY";if(G(h)){let R=j(h,"utf-8"),{parsed:X}=Z(R);E=X[B]}if(!E)E=process.env[B];let w=j(c,"utf-8"),{parsed:_}=Z(w,{privateKey:E});if(f&&!g.all){let R=_[f];if(R===void 0)return{success:!1,error:`Key not found: ${f}`};return{success:!0,output:R}}let D=g.all?{...process.env,..._}:_,I;switch(g.format){case"shell":I=Object.entries(D).map(([R,X])=>`${R}=${X}`).join(" ");break;case"eval":I=Object.entries(D).map(([R,X])=>`${R}="${X}"`).join(`
21
- `);break;case"json":default:I=g.prettyPrint?JSON.stringify(D,null,2):JSON.stringify(D);break}return{success:!0,output:I}}catch(E){return{success:!1,error:`Failed to get: ${E instanceof Error?E.message:"Unknown error"}`}}}function Zf(f,g={}){let r=g.cwd||process.cwd(),c=W(r,g.keysFile||".env.keys");if(!G(c))return{success:!1,error:`Keys file not found: ${c}`};try{let E=j(c,"utf-8"),{parsed:h}=Z(E),$=((g.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),B=$?`DOTENV_PUBLIC_KEY_${$}`:"DOTENV_PUBLIC_KEY",w=$?`DOTENV_PRIVATE_KEY_${$}`:"DOTENV_PRIVATE_KEY";if(f){let I=h[f];if(!I)return{success:!1,error:`Key not found: ${f}`};return{success:!0,output:I}}let _={[B]:h[B],[w]:h[w]};return{success:!0,output:g.format==="shell"?`${B}=${_[B]} ${w}=${_[w]}`:JSON.stringify(_)}}catch(E){return{success:!1,error:`Failed to get keypair: ${E instanceof Error?E.message:"Unknown error"}`}}}function qf(f={}){let g=xf({file:f.file,keysFile:f.keysFile,stdout:!0,cwd:f.cwd});if(!g.success)return g;let r=f.cwd||process.cwd(),c=W(r,f.file||".env"),E=W(r,f.keysFile||".env.keys");if(g.output===void 0)return{success:!1,error:"Rotation produced no decrypted content"};let h=q(),A=j(c),x=j(E),$=x.toString("utf8").split(`
22
- `),B=f.file||".env",w=N(B).replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),_=w?`DOTENV_PUBLIC_KEY_${w}`:"DOTENV_PUBLIC_KEY",D=w?`DOTENV_PRIVATE_KEY_${w}`:"DOTENV_PRIVATE_KEY",I=!1,R=!1;for(let Y=0;Y<$.length;Y++){let M=$[Y];if(M===void 0)continue;if(M.startsWith(`${_}=`))$[Y]=`${_}="${h.publicKey}"`,I=!0;else if(M.startsWith(`${D}=`))$[Y]=`${D}="${h.privateKey}"`,R=!0}if(!I)$.push(`${_}="${h.publicKey}"`);if(!R)$.push(`${D}="${h.privateKey}"`);let X=`${$.join(`
9
+ `;J(c,w+O,"utf-8")}}else{let w=q();E=w.publicKey,h=w.privateKey;let I=((f.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),D=I?`DOTENV_PUBLIC_KEY_${I}`:"DOTENV_PUBLIC_KEY",t=I?`DOTENV_PRIVATE_KEY_${I}`:"DOTENV_PRIVATE_KEY",H=`# .env.keys - Keep this file secure and never commit to source control
10
+ ${D}="${E}"
11
+ ${t}="${h}"
12
+ `;J(c,H,"utf-8")}let A=j(g,"utf-8"),x=f.file?f.file.replace(/^\.env\./,"").toUpperCase():"",$=x?`DOTENV_PUBLIC_KEY_${x}`:"DOTENV_PUBLIC_KEY",B=a(A,E,$,f);if(f.stdout)return{success:!0,output:B};return J(g,B,"utf-8"),{success:!0,output:`\u2714 encrypted (${f.file||".env"})
13
+ \u2714 key added to ${f.keysFile||".env.keys"}`}}catch(E){return{success:!1,error:`Failed to encrypt: ${E instanceof Error?E.message:"Unknown error"}`}}}function xf(f={}){let r=f.cwd||process.cwd(),g=W(r,f.file||".env"),c=W(r,f.keysFile||".env.keys");if(!G(g))return{success:!1,error:`File not found: ${g}`};if(!G(c))return{success:!1,error:`Keys file not found: ${c}`};try{let E=j(c,"utf-8"),{parsed:h}=Z(E),$=((f.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),B=$?`DOTENV_PRIVATE_KEY_${$}`:"DOTENV_PRIVATE_KEY",w=h[B];if(!w)return{success:!1,error:`Private key not found: ${B}`};let X=j(g,"utf-8").split(`
14
+ `),I=[];for(let t of X){let H=t.trim();if(!H||H.startsWith("#")){I.push(t);continue}if(H.startsWith("DOTENV_PUBLIC_KEY"))continue;let R=H.match(/^([^=]+)=(.*)$/);if(!R||R[1]===void 0||R[2]===void 0){I.push(t);continue}let O=R[1].trim(),T=R[2].trim();if(T.startsWith('"')&&T.endsWith('"')||T.startsWith("'")&&T.endsWith("'"))T=T.slice(1,-1);let Y=T.startsWith("encrypted:");if(f.key&&!O.includes(f.key))Y=!1;if(Y)T=Q(T,w);I.push(`${O}="${T}"`)}let D=I.join(`
15
+ `);if(f.stdout)return{success:!0,output:D};return J(g,D,"utf-8"),{success:!0,output:`\u2714 decrypted (${f.file||".env"})`}}catch(E){return{success:!1,error:`Failed to decrypt: ${E instanceof Error?E.message:"Unknown error"}`}}}function zf(f,r,g={}){let c=g.cwd||process.cwd(),E=W(c,g.file||".env"),h=W(c,g.keysFile||".env.keys");try{let A="";if(G(E))A=j(E,"utf-8");let x=A.split(`
16
+ `),$=!1,B,X=((g.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),I=X?`DOTENV_PUBLIC_KEY_${X}`:"DOTENV_PUBLIC_KEY",D=X?`DOTENV_PRIVATE_KEY_${X}`:"DOTENV_PRIVATE_KEY";for(let R of x){let O=R.trim();if(O.startsWith(`${I}=`)){let T=O.match(/^[^=]+=["']?([^"'\n]+)["']?/);if(T)B=T[1];break}}if(B){let R=!1;if(G(h)){let O=j(h,"utf-8"),{parsed:T}=Z(O);R=Boolean(T[D])}if(!R)B=void 0}if(!g.plain&&!B){let R=q();B=R.publicKey;let O="";if(G(h))O=j(h,"utf-8");O+=`
17
+ ${I}="${R.publicKey}"
18
+ ${D}="${R.privateKey}"
19
+ `,J(h,O,"utf-8");for(let T=x.length-1;T>=0;T--){let Y=x[T];if(Y!==void 0&&Y.trim().startsWith(`${I}=`))x.splice(T,1)}x.unshift(`${I}="${B}"`)}let t=r;if(!g.plain&&B)t=L(r,B);for(let R=0;R<x.length;R++){let O=x[R];if(O===void 0)continue;if(O.trim().startsWith(`${f}=`)){x[R]=`${f}="${t}"`,$=!0;break}}if(!$)x.push(`${f}="${t}"`);let H=x.join(`
20
+ `);return J(E,H,"utf-8"),{success:!0,output:`set ${f}${g.plain?"":" with encryption"} (${g.file||".env"})`}}catch(A){return{success:!1,error:`Failed to set: ${A instanceof Error?A.message:"Unknown error"}`}}}function Zf(f,r={}){let g=r.cwd||process.cwd(),c=W(g,r.file||".env");if(!G(c))return{success:!1,error:`File not found: ${c}`};try{let E,h=W(g,r.keysFile||".env.keys"),$=((r.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),B=$?`DOTENV_PRIVATE_KEY_${$}`:"DOTENV_PRIVATE_KEY";if(G(h)){let D=j(h,"utf-8"),{parsed:t}=Z(D);E=t[B]}if(!E)E=process.env[B];let w=j(c,"utf-8"),{parsed:_}=Z(w,{privateKey:E});if(f&&!r.all){let D=_[f];if(D===void 0)return{success:!1,error:`Key not found: ${f}`};return{success:!0,output:D}}let X=r.all?{...process.env,..._}:_,I;switch(r.format){case"shell":I=Object.entries(X).map(([D,t])=>`${D}=${t}`).join(" ");break;case"eval":I=Object.entries(X).map(([D,t])=>`${D}="${t}"`).join(`
21
+ `);break;case"json":default:I=r.prettyPrint?JSON.stringify(X,null,2):JSON.stringify(X);break}return{success:!0,output:I}}catch(E){return{success:!1,error:`Failed to get: ${E instanceof Error?E.message:"Unknown error"}`}}}function qf(f,r={}){let g=r.cwd||process.cwd(),c=W(g,r.keysFile||".env.keys");if(!G(c))return{success:!1,error:`Keys file not found: ${c}`};try{let E=j(c,"utf-8"),{parsed:h}=Z(E),$=((r.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),B=$?`DOTENV_PUBLIC_KEY_${$}`:"DOTENV_PUBLIC_KEY",w=$?`DOTENV_PRIVATE_KEY_${$}`:"DOTENV_PRIVATE_KEY";if(f){let I=h[f];if(!I)return{success:!1,error:`Key not found: ${f}`};return{success:!0,output:I}}let _={[B]:h[B],[w]:h[w]};return{success:!0,output:r.format==="shell"?`${B}=${_[B]} ${w}=${_[w]}`:JSON.stringify(_)}}catch(E){return{success:!1,error:`Failed to get keypair: ${E instanceof Error?E.message:"Unknown error"}`}}}function Qf(f={}){let r=xf({file:f.file,keysFile:f.keysFile,stdout:!0,cwd:f.cwd});if(!r.success)return r;let g=f.cwd||process.cwd(),c=W(g,f.file||".env"),E=W(g,f.keysFile||".env.keys");if(r.output===void 0)return{success:!1,error:"Rotation produced no decrypted content"};let h=q(),A=j(c),x=j(E),$=x.toString("utf8").split(`
22
+ `),B=f.file||".env",w=N(B).replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),_=w?`DOTENV_PUBLIC_KEY_${w}`:"DOTENV_PUBLIC_KEY",X=w?`DOTENV_PRIVATE_KEY_${w}`:"DOTENV_PRIVATE_KEY",I=!1,D=!1;for(let Y=0;Y<$.length;Y++){let M=$[Y];if(M===void 0)continue;if(M.startsWith(`${_}=`))$[Y]=`${_}="${h.publicKey}"`,I=!0;else if(M.startsWith(`${X}=`))$[Y]=`${X}="${h.privateKey}"`,D=!0}if(!I)$.push(`${_}="${h.publicKey}"`);if(!D)$.push(`${X}="${h.privateKey}"`);let t=`${$.join(`
23
23
  `).replace(/\n+$/,"")}
24
- `,H=a(g.output,h.publicKey,_,f),t=`${process.pid}-${Date.now()}`,O=W(p(c),`.${N(c)}.${t}.rotate`),T=W(p(E),`.${N(E)}.${t}.rotate`);try{if(J(O,H,{encoding:"utf8",mode:384}),J(T,X,{encoding:"utf8",mode:384}),f.stdout)return d(T,E),{success:!0,output:H};d(O,c);try{d(T,E)}catch(Y){throw J(c,A),Y}return{success:!0,output:`\u2714 rotated (${f.file||".env"}) to encrypted:v2`}}catch(Y){if(!G(E))J(E,x,{mode:384});return{success:!1,error:`Failed to rotate without exposing plaintext: ${Y instanceof Error?Y.message:"Unknown error"}`}}finally{l(O,{force:!0}),l(T,{force:!0})}}export{Uf as setEnv,qf as rotateKeypair,Zf as getKeypair,zf as getEnv,Jf as encryptEnv,xf as decryptEnv};
24
+ `,H=a(r.output,h.publicKey,_,f),R=`${process.pid}-${Date.now()}`,O=W(p(c),`.${N(c)}.${R}.rotate`),T=W(p(E),`.${N(E)}.${R}.rotate`);try{if(J(O,H,{encoding:"utf8",mode:384}),J(T,t,{encoding:"utf8",mode:384}),f.stdout)return d(T,E),{success:!0,output:H};d(O,c);try{d(T,E)}catch(Y){throw J(c,A),Y}return{success:!0,output:`\u2714 rotated (${f.file||".env"}) to encrypted:v2`}}catch(Y){if(!G(E))J(E,x,{mode:384});return{success:!1,error:`Failed to rotate without exposing plaintext: ${Y instanceof Error?Y.message:"Unknown error"}`}}finally{l(O,{force:!0}),l(T,{force:!0})}}export{zf as setEnv,Qf as rotateKeypair,Jf as resolveEnvFile,qf as getKeypair,Zf as getEnv,Uf as encryptEnv,xf as decryptEnv};
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  // @bun
2
- import{createCipheriv as e,createDecipheriv as ff,createHash as d,createPrivateKey as Ef,createPublicKey as gf,diffieHellman as Bf,generateKeyPairSync as $f,hkdfSync as Af,randomBytes as u}from"crypto";var r="x25519-public:",t="x25519-private:",c="encrypted:v2:",y="encrypted:",_f=Buffer.from("stacks-env:v2","utf8"),Xf="Environment decryption failed: authentication or format error";function Df(f){if(f.length===32)return f;return d("sha256").update(f).digest()}function pf(f,g){let B=u(16),A=e("aes-256-gcm",Df(g),B);return{ciphertext:Buffer.concat([A.update(f,"utf8"),A.final()]).toString("hex"),iv:B.toString("hex"),authTag:A.getAuthTag().toString("hex")}}function qf(f,g,B,A){try{let $=ff("aes-256-gcm",Df(g),Buffer.from(B,"hex"));return $.setAuthTag(Buffer.from(A,"hex")),Buffer.concat([$.update(Buffer.from(f,"hex")),$.final()]).toString("utf8")}catch{throw Error("Decryption failed: authentication or format error")}}function z(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 A=Buffer.from(f,"base64url");if(z(A)!==f)throw Error(`${g} is not canonical base64url`);if(B!==void 0&&A.length!==B)throw Error(`${g} has an invalid length`);return A}function Lf(f){if(!f.startsWith(r))throw Error("Legacy or invalid public key. Run buddy env:rotate before creating new ciphertext.");let g=gf({key:C(f.slice(r.length),"public key"),format:"der",type:"spki"});if(g.asymmetricKeyType!=="x25519")throw Error("Public key is not X25519");return g}function xf(f){if(!f.startsWith(t))throw Error("Private key is not a version 2 X25519 key");let g=Ef({key:C(f.slice(t.length),"private key"),format:"der",type:"pkcs8"});if(g.asymmetricKeyType!=="x25519")throw Error("Private key is not X25519");return g}function Hf(f){return Buffer.from(`stacks-env:v2;${f.epk};${f.salt};${f.nonce}`,"utf8")}function F(){let{publicKey:f,privateKey:g}=$f("x25519"),B=f.export({format:"der",type:"spki"}),A=g.export({format:"der",type:"pkcs8"});return{publicKey:`${r}${z(B)}`,privateKey:`${t}${z(A)}`}}function P(f,g){let B=Lf(g),A=$f("x25519"),$=z(A.publicKey.export({format:"der",type:"spki"})),_=u(16),D=u(12),O=Bf({privateKey:A.privateKey,publicKey:B}),R=Buffer.from(Af("sha256",O,_,_f,32)),X={epk:$,salt:z(_),nonce:z(D)};try{let H=e("aes-256-gcm",R,D);H.setAAD(Hf(X));let W=Buffer.concat([H.update(f,"utf8"),H.final()]),G={v:2,...X,ciphertext:z(W),tag:z(H.getAuthTag())};return`${c}${z(Buffer.from(JSON.stringify(G),"utf8"))}`}finally{O.fill(0),R.fill(0)}}function Mf(f){let g=C(f.slice(c.length),"envelope").toString("utf8"),B=JSON.parse(g);if(!B||typeof B!=="object"||Array.isArray(B))throw Error("envelope is not an object");let A=Object.keys(B).sort(),$=["ciphertext","epk","nonce","salt","tag","v"];if(A.length!==$.length||A.some((_,D)=>_!==$[D]))throw Error("envelope fields are invalid");if(B.v!==2)throw Error("envelope version is unsupported");for(let _ of["epk","salt","nonce","ciphertext","tag"])if(typeof B[_]!=="string")throw Error(`${_} is invalid`);return B}function zf(f,g){try{let B=Mf(f),A=xf(g),$=gf({key:C(B.epk,"ephemeral public key"),format:"der",type:"spki"});if($.asymmetricKeyType!=="x25519")throw Error("ephemeral key is not X25519");let _=C(B.salt,"salt",16),D=C(B.nonce,"nonce",12),O=C(B.ciphertext,"ciphertext"),R=C(B.tag,"tag",16),X=Bf({privateKey:A,publicKey:$}),H=Buffer.from(Af("sha256",X,_,_f,32));try{let W=ff("aes-256-gcm",H,D);return W.setAAD(Hf(B)),W.setAuthTag(R),Buffer.concat([W.update(O),W.final()]).toString("utf8")}finally{X.fill(0),H.fill(0)}}catch{throw Error(Xf)}}function Vf(f,g){try{if(!/^[0-9a-f]{64}$/.test(g))throw Error("invalid legacy private key");let B=Buffer.from(f.slice(y.length),"base64");if(B.length<32)throw Error("invalid legacy payload");let A=d("sha256").update(Buffer.from(g,"hex")).digest(),$=d("sha256").update(A).digest();return qf(B.subarray(32).toString("hex"),$,B.subarray(0,16).toString("hex"),B.subarray(16,32).toString("hex"))}catch{throw Error(Xf)}}function lf(f){return f.startsWith(y)&&!f.startsWith(c)}function h(f,g){if(!f.startsWith(y))return f;return f.startsWith(c)?zf(f,g):Vf(f,g)}function sf(f,g,B){return P(h(f,g),B)}function af(f){let g=f.match(/^DOTENV_PRIVATE_KEY_(.+)$/);return g&&g[1]?g[1].toLowerCase():""}function n(f=""){return f?process.env[`DOTENV_PRIVATE_KEY_${f.toUpperCase()}`]:process.env.DOTENV_PRIVATE_KEY}function q(f,g={}){let B={},A=[],$=[],_=f.split(`
2
+ import{createCipheriv as e,createDecipheriv as ff,createHash as d,createPrivateKey as Ef,createPublicKey as gf,diffieHellman as Bf,generateKeyPairSync as $f,hkdfSync as Af,randomBytes as u}from"crypto";var r="x25519-public:",t="x25519-private:",c="encrypted:v2:",y="encrypted:",_f=Buffer.from("stacks-env:v2","utf8"),Xf="Environment decryption failed: authentication or format error";function Df(f){if(f.length===32)return f;return d("sha256").update(f).digest()}function pf(f,g){let B=u(16),A=e("aes-256-gcm",Df(g),B);return{ciphertext:Buffer.concat([A.update(f,"utf8"),A.final()]).toString("hex"),iv:B.toString("hex"),authTag:A.getAuthTag().toString("hex")}}function qf(f,g,B,A){try{let $=ff("aes-256-gcm",Df(g),Buffer.from(B,"hex"));return $.setAuthTag(Buffer.from(A,"hex")),Buffer.concat([$.update(Buffer.from(f,"hex")),$.final()]).toString("utf8")}catch{throw Error("Decryption failed: authentication or format error")}}function z(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 A=Buffer.from(f,"base64url");if(z(A)!==f)throw Error(`${g} is not canonical base64url`);if(B!==void 0&&A.length!==B)throw Error(`${g} has an invalid length`);return A}function Lf(f){if(!f.startsWith(r))throw Error("Legacy or invalid public key. Run buddy env:rotate before creating new ciphertext.");let g=gf({key:C(f.slice(r.length),"public key"),format:"der",type:"spki"});if(g.asymmetricKeyType!=="x25519")throw Error("Public key is not X25519");return g}function xf(f){if(!f.startsWith(t))throw Error("Private key is not a version 2 X25519 key");let g=Ef({key:C(f.slice(t.length),"private key"),format:"der",type:"pkcs8"});if(g.asymmetricKeyType!=="x25519")throw Error("Private key is not X25519");return g}function Hf(f){return Buffer.from(`stacks-env:v2;${f.epk};${f.salt};${f.nonce}`,"utf8")}function F(){let{publicKey:f,privateKey:g}=$f("x25519"),B=f.export({format:"der",type:"spki"}),A=g.export({format:"der",type:"pkcs8"});return{publicKey:`${r}${z(B)}`,privateKey:`${t}${z(A)}`}}function P(f,g){let B=Lf(g),A=$f("x25519"),$=z(A.publicKey.export({format:"der",type:"spki"})),_=u(16),D=u(12),O=Bf({privateKey:A.privateKey,publicKey:B}),R=Buffer.from(Af("sha256",O,_,_f,32)),X={epk:$,salt:z(_),nonce:z(D)};try{let H=e("aes-256-gcm",R,D);H.setAAD(Hf(X));let W=Buffer.concat([H.update(f,"utf8"),H.final()]),G={v:2,...X,ciphertext:z(W),tag:z(H.getAuthTag())};return`${c}${z(Buffer.from(JSON.stringify(G),"utf8"))}`}finally{O.fill(0),R.fill(0)}}function Mf(f){let g=C(f.slice(c.length),"envelope").toString("utf8"),B=JSON.parse(g);if(!B||typeof B!=="object"||Array.isArray(B))throw Error("envelope is not an object");let A=Object.keys(B).sort(),$=["ciphertext","epk","nonce","salt","tag","v"];if(A.length!==$.length||A.some((_,D)=>_!==$[D]))throw Error("envelope fields are invalid");if(B.v!==2)throw Error("envelope version is unsupported");for(let _ of["epk","salt","nonce","ciphertext","tag"])if(typeof B[_]!=="string")throw Error(`${_} is invalid`);return B}function zf(f,g){try{let B=Mf(f),A=xf(g),$=gf({key:C(B.epk,"ephemeral public key"),format:"der",type:"spki"});if($.asymmetricKeyType!=="x25519")throw Error("ephemeral key is not X25519");let _=C(B.salt,"salt",16),D=C(B.nonce,"nonce",12),O=C(B.ciphertext,"ciphertext"),R=C(B.tag,"tag",16),X=Bf({privateKey:A,publicKey:$}),H=Buffer.from(Af("sha256",X,_,_f,32));try{let W=ff("aes-256-gcm",H,D);return W.setAAD(Hf(B)),W.setAuthTag(R),Buffer.concat([W.update(O),W.final()]).toString("utf8")}finally{X.fill(0),H.fill(0)}}catch{throw Error(Xf)}}function Vf(f,g){try{if(!/^[0-9a-f]{64}$/.test(g))throw Error("invalid legacy private key");let B=Buffer.from(f.slice(y.length),"base64");if(B.length<32)throw Error("invalid legacy payload");let A=d("sha256").update(Buffer.from(g,"hex")).digest(),$=d("sha256").update(A).digest();return qf(B.subarray(32).toString("hex"),$,B.subarray(0,16).toString("hex"),B.subarray(16,32).toString("hex"))}catch{throw Error(Xf)}}function lf(f){return f.startsWith(y)&&!f.startsWith(c)}function h(f,g){if(!f.startsWith(y))return f;return f.startsWith(c)?zf(f,g):Vf(f,g)}function sf(f,g,B){return P(h(f,g),B)}function af(f){let g=f.match(/^DOTENV_PRIVATE_KEY_(.+)$/);return g&&g[1]?g[1].toLowerCase():""}function v(f=""){return f?process.env[`DOTENV_PRIVATE_KEY_${f.toUpperCase()}`]:process.env.DOTENV_PRIVATE_KEY}function q(f,g={}){let B={},A=[],$=[],_=f.split(`
3
3
  `);for(let D of _){let O=D.trim();if(!O||O.startsWith("#"))continue;if(O.startsWith("DOTENV_PUBLIC_KEY=")){let W=O.match(/^DOTENV_PUBLIC_KEY=["']?([^"'\n]+)["']?/);if(W&&W[1]!==void 0)B.DOTENV_PUBLIC_KEY=W[1];continue}let R=O.match(/^([^=]+)=(.*)$/);if(!R||R[1]===void 0||R[2]===void 0)continue;let X=R[1].trim(),H=R[2].trim();if(H.startsWith('"')&&H.endsWith('"')||H.startsWith("'")&&H.endsWith("'")){if(H=H.slice(1,-1),H.includes("\\n"))H=H.replace(/\\n/g,`
4
- `)}if(H.startsWith("encrypted:")||H.startsWith("enc:")){if(!g.privateKey){$.push(X);continue}try{let W=H.startsWith("enc:")?`encrypted:${H.slice(4)}`:H;H=h(W,g.privateKey)}catch(W){A.push(`Failed to decrypt ${X}: ${W instanceof Error?W.message:"Unknown error"}`)}}H=Cf(H,{...g.processEnv||process.env,...B}),H=hf(H),B[X]=H}return{parsed:B,errors:A,skippedEncrypted:$}}function Cf(f,g){return f.replace(/\$\{([^}]+)\}/g,(B,A)=>{let $=A.match(/^([^:\-+]+)(:-|-)(.+)$/);if($){let[,D,O,R]=$,X=g[D];if(O===":-")return X||R;else return X!==void 0?X:R}let _=A.match(/^([^:\-+]+)(:?\+)(.+)$/);if(_){let[,D,O,R]=_,X=g[D];if(O===":+")return X?R:"";else return X!==void 0?R:""}return g[A]||""})}var bf=new Set(["date","hostname","whoami","uname","pwd","echo","printf","cat","basename","dirname"]);function hf(f){return f.replace(/\$\(([^)]+)\)/g,(g,B)=>{try{let A=B.trim().split(/\s+/),$=A[0];if(!$||!bf.has($))return console.warn(`[env] Blocked command substitution for disallowed command: ${$}`),"";let _=Bun.spawnSync(A,{stdout:"pipe",stderr:"pipe"});if(_.exitCode===0)return new TextDecoder().decode(_.stdout).trim();console.warn(`[env] Command substitution failed (exit code ${_.exitCode}): ${$}`)}catch(A){console.warn(`[env] Command substitution error: ${A instanceof Error?A.message:String(A)}`)}return""})}async function gg(f,g={}){let B={},A=[],$=[];for(let _ of f)try{let D=Bun.file(_);if(!D.size)continue;let O=await D.text(),{parsed:R,errors:X,skippedEncrypted:H}=q(O,g);A.push(...X),$.push(...H);for(let[W,G]of Object.entries(R)){if(!g.overload&&B[W]!==void 0)continue;B[W]=G}}catch(D){if(D?.code==="ENOENT")continue;A.push(`Failed to read ${_}: ${D instanceof Error?D.message:String(D)}`)}return{parsed:B,errors:A,skippedEncrypted:$}}import{existsSync as m}from"fs";import{readFileSync as v}from"fs";import{resolve as k}from"path";function S(f){return typeof f==="string"&&(f.startsWith("encrypted:")||f.startsWith("enc:"))}function b(f){if(!f||S(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 i=null,o;function Ff(f,g,B=".env.keys"){let A=k(g,B);if(!m(A))return;try{let{parsed:$}=q(v(A,"utf-8"));if(f){let _=$[`DOTENV_PRIVATE_KEY_${f.toUpperCase()}`];if(_)return _}return $.DOTENV_PRIVATE_KEY}catch{return}}function Sf(f={}){let g=b(f.env)||b(process.env.APP_ENV)||b("development")||b(process.env.DOTENV_ENV)||"development",B=f.cwd||process.cwd();if(i===g)return o;let A=n(g);if(!A)A=process.env.DOTENV_PRIVATE_KEY;if(!A)A=Ff(g,B);return i=g,o=A,A}function Og(){i=null,o=void 0}function Of(f,g={}){if(!S(f))return f;let B=Sf(g);if(!B)return;try{let A=f.startsWith("enc:")?`encrypted:${f.slice(4)}`:f;return h(A,B)}catch{return}}function Rf(f={}){let{path:g=[".env"],overload:B=!1,env:A,privateKey:$,keysFile:_=".env.keys",quiet:D=!1,cwd:O=process.cwd()}=f,R=Array.isArray(g)?g:[g],X=[],H=0,W=new Set(Object.entries(process.env).filter(([,T])=>T!==void 0&&!S(T)).map(([T])=>T)),G=$;if(!G&&_){let T=k(O,_);if(m(T))try{let Y=v(T,"utf-8"),{parsed:Z}=q(Y);if(A){let U=`DOTENV_PRIVATE_KEY_${A.toUpperCase()}`;G=Z[U]}else G=Z.DOTENV_PRIVATE_KEY}catch(Y){X.push(`Failed to load keys file: ${Y instanceof Error?Y.message:"Unknown error"}`)}}if(!G)G=n(A||"");for(let T of R){let Y=k(O,T);if(!m(Y)){if(!D)X.push(`File not found: ${Y}`);continue}try{let Z=v(Y,"utf-8"),{parsed:U,errors:I,skippedEncrypted:Q}=q(Z,{privateKey:G,processEnv:process.env});if(X.push(...I),Q.length>0){for(let K of Q)if(S(process.env[K]))delete process.env[K];let w=A?`DOTENV_PRIVATE_KEY_${A.toUpperCase()}`:"DOTENV_PRIVATE_KEY",V=Q.slice(0,5).join(", "),N=Q.length>5?`, \u2026 +${Q.length-5} more`:"";console.warn(`[env] warning: skipped ${Q.length} encrypted value(s) in ${T} (${w} not set; defaults apply): ${V}${N}`)}let J=0;for(let[w,V]of Object.entries(U)){if(w==="DOTENV_PUBLIC_KEY")continue;let N=process.env[w],K=S(N)&&V!==N;if(B||!W.has(w)||K)process.env[w]=V,H++,J++}if(!D&&!process.env.__ENV_LOADED__)console.error(`[env] loaded ${J}/${Object.keys(U).length} variables from ${T}`),process.env.__ENV_LOADED__="1"}catch(Z){X.push(`Failed to load ${Y}: ${Z instanceof Error?Z.message:"Unknown error"}`)}}return{loaded:H,errors:X}}function Nf(f={}){return{name:"env-plugin",setup(g){Rf(f)}}}function Rg(f={}){let g=b(f.env)||b("development")||b(process.env.DOTENV_ENV)||b(process.env.APP_ENV)||"development",B=f.cwd||process.cwd(),A=[],$=(O)=>{if(!A.includes(O)&&m(k(B,O)))A.push(O)};$(".env"),$(".env.local");let _=`.env.${g}`,D=`.env.${g}.local`;return $(_),$(D),Rf({...f,path:A,env:g})}var Tg=Nf;import{existsSync as x,readFileSync as L,renameSync as p,rmSync as Wf,writeFileSync as M}from"fs";import{basename as l,dirname as Tf,resolve as E}from"path";function Yf(f,g,B,A){let $=["#/-------------------[DOTENV_PUBLIC_KEY]--------------------/","#/ versioned X25519 encryption for .env files /","#/ [how it works](https://stacksjs.com/encryption) /","#/----------------------------------------------------------/",`${B}="${g}"`,""];for(let _ of f.split(`
4
+ `)}if(H.startsWith("encrypted:")||H.startsWith("enc:")){if(!g.privateKey){$.push(X);continue}try{let W=H.startsWith("enc:")?`encrypted:${H.slice(4)}`:H;H=h(W,g.privateKey)}catch(W){A.push(`Failed to decrypt ${X}: ${W instanceof Error?W.message:"Unknown error"}`)}}H=Cf(H,{...g.processEnv||process.env,...B}),H=hf(H),B[X]=H}return{parsed:B,errors:A,skippedEncrypted:$}}function Cf(f,g){return f.replace(/\$\{([^}]+)\}/g,(B,A)=>{let $=A.match(/^([^:\-+]+)(:-|-)(.+)$/);if($){let[,D,O,R]=$,X=g[D];if(O===":-")return X||R;else return X!==void 0?X:R}let _=A.match(/^([^:\-+]+)(:?\+)(.+)$/);if(_){let[,D,O,R]=_,X=g[D];if(O===":+")return X?R:"";else return X!==void 0?R:""}return g[A]||""})}var bf=new Set(["date","hostname","whoami","uname","pwd","echo","printf","cat","basename","dirname"]);function hf(f){return f.replace(/\$\(([^)]+)\)/g,(g,B)=>{try{let A=B.trim().split(/\s+/),$=A[0];if(!$||!bf.has($))return console.warn(`[env] Blocked command substitution for disallowed command: ${$}`),"";let _=Bun.spawnSync(A,{stdout:"pipe",stderr:"pipe"});if(_.exitCode===0)return new TextDecoder().decode(_.stdout).trim();console.warn(`[env] Command substitution failed (exit code ${_.exitCode}): ${$}`)}catch(A){console.warn(`[env] Command substitution error: ${A instanceof Error?A.message:String(A)}`)}return""})}async function gg(f,g={}){let B={},A=[],$=[];for(let _ of f)try{let D=Bun.file(_);if(!D.size)continue;let O=await D.text(),{parsed:R,errors:X,skippedEncrypted:H}=q(O,g);A.push(...X),$.push(...H);for(let[W,G]of Object.entries(R)){if(!g.overload&&B[W]!==void 0)continue;B[W]=G}}catch(D){if(D?.code==="ENOENT")continue;A.push(`Failed to read ${_}: ${D instanceof Error?D.message:String(D)}`)}return{parsed:B,errors:A,skippedEncrypted:$}}import{existsSync as m}from"fs";import{readFileSync as n}from"fs";import{resolve as k}from"path";function S(f){return typeof f==="string"&&(f.startsWith("encrypted:")||f.startsWith("enc:"))}function b(f){if(!f||S(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 i=null,o;function Ff(f,g,B=".env.keys"){let A=k(g,B);if(!m(A))return;try{let{parsed:$}=q(n(A,"utf-8"));if(f){let _=$[`DOTENV_PRIVATE_KEY_${f.toUpperCase()}`];if(_)return _}return $.DOTENV_PRIVATE_KEY}catch{return}}function Sf(f={}){let g=b(f.env)||b(process.env.APP_ENV)||b("development")||b(process.env.DOTENV_ENV)||"development",B=f.cwd||process.cwd();if(i===g)return o;let A=v(g);if(!A)A=process.env.DOTENV_PRIVATE_KEY;if(!A)A=Ff(g,B);return i=g,o=A,A}function Og(){i=null,o=void 0}function Of(f,g={}){if(!S(f))return f;let B=Sf(g);if(!B)return;try{let A=f.startsWith("enc:")?`encrypted:${f.slice(4)}`:f;return h(A,B)}catch{return}}function Rf(f={}){let{path:g=[".env"],overload:B=!1,env:A,privateKey:$,keysFile:_=".env.keys",quiet:D=!1,cwd:O=process.cwd()}=f,R=Array.isArray(g)?g:[g],X=[],H=0,W=new Set(Object.entries(process.env).filter(([,T])=>T!==void 0&&!S(T)).map(([T])=>T)),G=$;if(!G&&_){let T=k(O,_);if(m(T))try{let Y=n(T,"utf-8"),{parsed:Z}=q(Y);if(A){let U=`DOTENV_PRIVATE_KEY_${A.toUpperCase()}`;G=Z[U]}else G=Z.DOTENV_PRIVATE_KEY}catch(Y){X.push(`Failed to load keys file: ${Y instanceof Error?Y.message:"Unknown error"}`)}}if(!G)G=v(A||"");for(let T of R){let Y=k(O,T);if(!m(Y)){if(!D)X.push(`File not found: ${Y}`);continue}try{let Z=n(Y,"utf-8"),{parsed:U,errors:I,skippedEncrypted:Q}=q(Z,{privateKey:G,processEnv:process.env});if(X.push(...I),Q.length>0){for(let K of Q)if(S(process.env[K]))delete process.env[K];let w=A?`DOTENV_PRIVATE_KEY_${A.toUpperCase()}`:"DOTENV_PRIVATE_KEY",V=Q.slice(0,5).join(", "),N=Q.length>5?`, \u2026 +${Q.length-5} more`:"";console.warn(`[env] warning: skipped ${Q.length} encrypted value(s) in ${T} (${w} not set; defaults apply): ${V}${N}`)}let J=0;for(let[w,V]of Object.entries(U)){if(w==="DOTENV_PUBLIC_KEY")continue;let N=process.env[w],K=S(N)&&V!==N;if(B||!W.has(w)||K)process.env[w]=V,H++,J++}if(!D&&!process.env.__ENV_LOADED__)console.error(`[env] loaded ${J}/${Object.keys(U).length} variables from ${T}`),process.env.__ENV_LOADED__="1"}catch(Z){X.push(`Failed to load ${Y}: ${Z instanceof Error?Z.message:"Unknown error"}`)}}return{loaded:H,errors:X}}function Nf(f={}){return{name:"env-plugin",setup(g){Rf(f)}}}function Rg(f={}){let g=b(f.env)||b("development")||b(process.env.DOTENV_ENV)||b(process.env.APP_ENV)||"development",B=f.cwd||process.cwd(),A=[],$=(O)=>{if(!A.includes(O)&&m(k(B,O)))A.push(O)};$(".env"),$(".env.local");let _=`.env.${g}`,D=`.env.${g}.local`;return $(_),$(D),Rf({...f,path:A,env:g})}var Tg=Nf;import{existsSync as x,readFileSync as L,renameSync as p,rmSync as Wf,writeFileSync as M}from"fs";import{basename as l,dirname as Tf,resolve as E}from"path";function Zg(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 Yf(f,g,B,A){let $=["#/-------------------[DOTENV_PUBLIC_KEY]--------------------/","#/ versioned X25519 encryption for .env files /","#/ [how it works](https://stacksjs.com/encryption) /","#/----------------------------------------------------------/",`${B}="${g}"`,""];for(let _ of f.split(`
5
5
  `)){let D=_.trim();if(!D||D.startsWith("#")){$.push(_);continue}if(D.startsWith("DOTENV_PUBLIC_KEY"))continue;let O=D.match(/^([^=]+)=(.*)$/);if(!O||O[1]===void 0||O[2]===void 0){$.push(_);continue}let R=O[1].trim(),X=O[2].trim();if(X.startsWith('"')&&X.endsWith('"')||X.startsWith("'")&&X.endsWith("'"))X=X.slice(1,-1);if((!A.key||R.includes(A.key))&&(!A.excludeKey||!R.includes(A.excludeKey))&&!X.startsWith("encrypted:"))X=P(X,g);$.push(`${R}="${X}"`)}return $.join(`
6
- `)}function Zg(f={}){let g=f.cwd||process.cwd(),B=E(g,f.file||".env"),A=E(g,f.keysFile||".env.keys");if(!x(B))return{success:!1,error:`File not found: ${B}`};try{let $,_;if(x(A)){let H=L(A,"utf-8"),{parsed:W}=q(H),Y=((f.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),Z=Y?`DOTENV_PUBLIC_KEY_${Y}`:"DOTENV_PUBLIC_KEY",U=Y?`DOTENV_PRIVATE_KEY_${Y}`:"DOTENV_PRIVATE_KEY";if($=W[Z]||"",_=W[U]||"",!$||!_){let I=F();$=I.publicKey,_=I.privateKey;let Q=`
6
+ `)}function Ig(f={}){let g=f.cwd||process.cwd(),B=E(g,f.file||".env"),A=E(g,f.keysFile||".env.keys");if(!x(B))return{success:!1,error:`File not found: ${B}`};try{let $,_;if(x(A)){let H=L(A,"utf-8"),{parsed:W}=q(H),Y=((f.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),Z=Y?`DOTENV_PUBLIC_KEY_${Y}`:"DOTENV_PUBLIC_KEY",U=Y?`DOTENV_PRIVATE_KEY_${Y}`:"DOTENV_PRIVATE_KEY";if($=W[Z]||"",_=W[U]||"",!$||!_){let I=F();$=I.publicKey,_=I.privateKey;let Q=`
7
7
  ${Z}="${$}"
8
8
  ${U}="${_}"
9
9
  `;M(A,H+Q,"utf-8")}}else{let H=F();$=H.publicKey,_=H.privateKey;let T=((f.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),Y=T?`DOTENV_PUBLIC_KEY_${T}`:"DOTENV_PUBLIC_KEY",Z=T?`DOTENV_PRIVATE_KEY_${T}`:"DOTENV_PRIVATE_KEY",U=`# .env.keys - Keep this file secure and never commit to source control
@@ -12,15 +12,15 @@ ${Z}="${_}"
12
12
  `;M(A,U,"utf-8")}let D=L(B,"utf-8"),O=f.file?f.file.replace(/^\.env\./,"").toUpperCase():"",R=O?`DOTENV_PUBLIC_KEY_${O}`:"DOTENV_PUBLIC_KEY",X=Yf(D,$,R,f);if(f.stdout)return{success:!0,output:X};return M(B,X,"utf-8"),{success:!0,output:`\u2714 encrypted (${f.file||".env"})
13
13
  \u2714 key added to ${f.keysFile||".env.keys"}`}}catch($){return{success:!1,error:`Failed to encrypt: ${$ instanceof Error?$.message:"Unknown error"}`}}}function Kf(f={}){let g=f.cwd||process.cwd(),B=E(g,f.file||".env"),A=E(g,f.keysFile||".env.keys");if(!x(B))return{success:!1,error:`File not found: ${B}`};if(!x(A))return{success:!1,error:`Keys file not found: ${A}`};try{let $=L(A,"utf-8"),{parsed:_}=q($),R=((f.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),X=R?`DOTENV_PRIVATE_KEY_${R}`:"DOTENV_PRIVATE_KEY",H=_[X];if(!H)return{success:!1,error:`Private key not found: ${X}`};let G=L(B,"utf-8").split(`
14
14
  `),T=[];for(let Z of G){let U=Z.trim();if(!U||U.startsWith("#")){T.push(Z);continue}if(U.startsWith("DOTENV_PUBLIC_KEY"))continue;let I=U.match(/^([^=]+)=(.*)$/);if(!I||I[1]===void 0||I[2]===void 0){T.push(Z);continue}let Q=I[1].trim(),J=I[2].trim();if(J.startsWith('"')&&J.endsWith('"')||J.startsWith("'")&&J.endsWith("'"))J=J.slice(1,-1);let w=J.startsWith("encrypted:");if(f.key&&!Q.includes(f.key))w=!1;if(w)J=h(J,H);T.push(`${Q}="${J}"`)}let Y=T.join(`
15
- `);if(f.stdout)return{success:!0,output:Y};return M(B,Y,"utf-8"),{success:!0,output:`\u2714 decrypted (${f.file||".env"})`}}catch($){return{success:!1,error:`Failed to decrypt: ${$ instanceof Error?$.message:"Unknown error"}`}}}function Ig(f,g,B={}){let A=B.cwd||process.cwd(),$=E(A,B.file||".env"),_=E(A,B.keysFile||".env.keys");try{let D="";if(x($))D=L($,"utf-8");let O=D.split(`
15
+ `);if(f.stdout)return{success:!0,output:Y};return M(B,Y,"utf-8"),{success:!0,output:`\u2714 decrypted (${f.file||".env"})`}}catch($){return{success:!1,error:`Failed to decrypt: ${$ instanceof Error?$.message:"Unknown error"}`}}}function Qg(f,g,B={}){let A=B.cwd||process.cwd(),$=E(A,B.file||".env"),_=E(A,B.keysFile||".env.keys");try{let D="";if(x($))D=L($,"utf-8");let O=D.split(`
16
16
  `),R=!1,X,G=((B.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),T=G?`DOTENV_PUBLIC_KEY_${G}`:"DOTENV_PUBLIC_KEY",Y=G?`DOTENV_PRIVATE_KEY_${G}`:"DOTENV_PRIVATE_KEY";for(let I of O){let Q=I.trim();if(Q.startsWith(`${T}=`)){let J=Q.match(/^[^=]+=["']?([^"'\n]+)["']?/);if(J)X=J[1];break}}if(X){let I=!1;if(x(_)){let Q=L(_,"utf-8"),{parsed:J}=q(Q);I=Boolean(J[Y])}if(!I)X=void 0}if(!B.plain&&!X){let I=F();X=I.publicKey;let Q="";if(x(_))Q=L(_,"utf-8");Q+=`
17
17
  ${T}="${I.publicKey}"
18
18
  ${Y}="${I.privateKey}"
19
19
  `,M(_,Q,"utf-8");for(let J=O.length-1;J>=0;J--){let w=O[J];if(w!==void 0&&w.trim().startsWith(`${T}=`))O.splice(J,1)}O.unshift(`${T}="${X}"`)}let Z=g;if(!B.plain&&X)Z=P(g,X);for(let I=0;I<O.length;I++){let Q=O[I];if(Q===void 0)continue;if(Q.trim().startsWith(`${f}=`)){O[I]=`${f}="${Z}"`,R=!0;break}}if(!R)O.push(`${f}="${Z}"`);let U=O.join(`
20
- `);return M($,U,"utf-8"),{success:!0,output:`set ${f}${B.plain?"":" with encryption"} (${B.file||".env"})`}}catch(D){return{success:!1,error:`Failed to set: ${D instanceof Error?D.message:"Unknown error"}`}}}function Qg(f,g={}){let B=g.cwd||process.cwd(),A=E(B,g.file||".env");if(!x(A))return{success:!1,error:`File not found: ${A}`};try{let $,_=E(B,g.keysFile||".env.keys"),R=((g.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),X=R?`DOTENV_PRIVATE_KEY_${R}`:"DOTENV_PRIVATE_KEY";if(x(_)){let Y=L(_,"utf-8"),{parsed:Z}=q(Y);$=Z[X]}if(!$)$=process.env[X];let H=L(A,"utf-8"),{parsed:W}=q(H,{privateKey:$});if(f&&!g.all){let Y=W[f];if(Y===void 0)return{success:!1,error:`Key not found: ${f}`};return{success:!0,output:Y}}let G=g.all?{...process.env,...W}:W,T;switch(g.format){case"shell":T=Object.entries(G).map(([Y,Z])=>`${Y}=${Z}`).join(" ");break;case"eval":T=Object.entries(G).map(([Y,Z])=>`${Y}="${Z}"`).join(`
21
- `);break;case"json":default:T=g.prettyPrint?JSON.stringify(G,null,2):JSON.stringify(G);break}return{success:!0,output:T}}catch($){return{success:!1,error:`Failed to get: ${$ instanceof Error?$.message:"Unknown error"}`}}}function wg(f,g={}){let B=g.cwd||process.cwd(),A=E(B,g.keysFile||".env.keys");if(!x(A))return{success:!1,error:`Keys file not found: ${A}`};try{let $=L(A,"utf-8"),{parsed:_}=q($),R=((g.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),X=R?`DOTENV_PUBLIC_KEY_${R}`:"DOTENV_PUBLIC_KEY",H=R?`DOTENV_PRIVATE_KEY_${R}`:"DOTENV_PRIVATE_KEY";if(f){let T=_[f];if(!T)return{success:!1,error:`Key not found: ${f}`};return{success:!0,output:T}}let W={[X]:_[X],[H]:_[H]};return{success:!0,output:g.format==="shell"?`${X}=${W[X]} ${H}=${W[H]}`:JSON.stringify(W)}}catch($){return{success:!1,error:`Failed to get keypair: ${$ instanceof Error?$.message:"Unknown error"}`}}}function Ug(f={}){let g=Kf({file:f.file,keysFile:f.keysFile,stdout:!0,cwd:f.cwd});if(!g.success)return g;let B=f.cwd||process.cwd(),A=E(B,f.file||".env"),$=E(B,f.keysFile||".env.keys");if(g.output===void 0)return{success:!1,error:"Rotation produced no decrypted content"};let _=F(),D=L(A),O=L($),R=O.toString("utf8").split(`
20
+ `);return M($,U,"utf-8"),{success:!0,output:`set ${f}${B.plain?"":" with encryption"} (${B.file||".env"})`}}catch(D){return{success:!1,error:`Failed to set: ${D instanceof Error?D.message:"Unknown error"}`}}}function wg(f,g={}){let B=g.cwd||process.cwd(),A=E(B,g.file||".env");if(!x(A))return{success:!1,error:`File not found: ${A}`};try{let $,_=E(B,g.keysFile||".env.keys"),R=((g.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),X=R?`DOTENV_PRIVATE_KEY_${R}`:"DOTENV_PRIVATE_KEY";if(x(_)){let Y=L(_,"utf-8"),{parsed:Z}=q(Y);$=Z[X]}if(!$)$=process.env[X];let H=L(A,"utf-8"),{parsed:W}=q(H,{privateKey:$});if(f&&!g.all){let Y=W[f];if(Y===void 0)return{success:!1,error:`Key not found: ${f}`};return{success:!0,output:Y}}let G=g.all?{...process.env,...W}:W,T;switch(g.format){case"shell":T=Object.entries(G).map(([Y,Z])=>`${Y}=${Z}`).join(" ");break;case"eval":T=Object.entries(G).map(([Y,Z])=>`${Y}="${Z}"`).join(`
21
+ `);break;case"json":default:T=g.prettyPrint?JSON.stringify(G,null,2):JSON.stringify(G);break}return{success:!0,output:T}}catch($){return{success:!1,error:`Failed to get: ${$ instanceof Error?$.message:"Unknown error"}`}}}function Ug(f,g={}){let B=g.cwd||process.cwd(),A=E(B,g.keysFile||".env.keys");if(!x(A))return{success:!1,error:`Keys file not found: ${A}`};try{let $=L(A,"utf-8"),{parsed:_}=q($),R=((g.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),X=R?`DOTENV_PUBLIC_KEY_${R}`:"DOTENV_PUBLIC_KEY",H=R?`DOTENV_PRIVATE_KEY_${R}`:"DOTENV_PRIVATE_KEY";if(f){let T=_[f];if(!T)return{success:!1,error:`Key not found: ${f}`};return{success:!0,output:T}}let W={[X]:_[X],[H]:_[H]};return{success:!0,output:g.format==="shell"?`${X}=${W[X]} ${H}=${W[H]}`:JSON.stringify(W)}}catch($){return{success:!1,error:`Failed to get keypair: ${$ instanceof Error?$.message:"Unknown error"}`}}}function Eg(f={}){let g=Kf({file:f.file,keysFile:f.keysFile,stdout:!0,cwd:f.cwd});if(!g.success)return g;let B=f.cwd||process.cwd(),A=E(B,f.file||".env"),$=E(B,f.keysFile||".env.keys");if(g.output===void 0)return{success:!1,error:"Rotation produced no decrypted content"};let _=F(),D=L(A),O=L($),R=O.toString("utf8").split(`
22
22
  `),X=f.file||".env",H=l(X).replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),W=H?`DOTENV_PUBLIC_KEY_${H}`:"DOTENV_PUBLIC_KEY",G=H?`DOTENV_PRIVATE_KEY_${H}`:"DOTENV_PRIVATE_KEY",T=!1,Y=!1;for(let w=0;w<R.length;w++){let V=R[w];if(V===void 0)continue;if(V.startsWith(`${W}=`))R[w]=`${W}="${_.publicKey}"`,T=!0;else if(V.startsWith(`${G}=`))R[w]=`${G}="${_.privateKey}"`,Y=!0}if(!T)R.push(`${W}="${_.publicKey}"`);if(!Y)R.push(`${G}="${_.privateKey}"`);let Z=`${R.join(`
23
23
  `).replace(/\n+$/,"")}
24
- `,U=Yf(g.output,_.publicKey,W,f),I=`${process.pid}-${Date.now()}`,Q=E(Tf(A),`.${l(A)}.${I}.rotate`),J=E(Tf($),`.${l($)}.${I}.rotate`);try{if(M(Q,U,{encoding:"utf8",mode:384}),M(J,Z,{encoding:"utf8",mode:384}),f.stdout)return p(J,$),{success:!0,output:U};p(Q,A);try{p(J,$)}catch(w){throw M(A,D),w}return{success:!0,output:`\u2714 rotated (${f.file||".env"}) to encrypted:v2`}}catch(w){if(!x($))M($,O,{mode:384});return{success:!1,error:`Failed to rotate without exposing plaintext: ${w instanceof Error?w.message:"Unknown error"}`}}finally{Wf(Q,{force:!0}),Wf(J,{force:!0})}}import j from"process";import{platform as cf}from"os";var jf=typeof Bun<"u",Gf=typeof j<"u"&&j.versions?.node!==void 0,Pf=jf?"bun":Gf?"node":"unknown",xg={name:Pf,version:jf?Bun.version:Gf?j.version:void 0},a=cf(),Mg=a==="win32",zg=a==="darwin",Vg=a==="linux",Jf=Boolean(j.stdout?.isTTY),Cg=typeof globalThis.window<"u",mf=Boolean(j.env.CI||j.env.CONTINUOUS_INTEGRATION||j.env.BUILD_NUMBER||j.env.RUN_ID),bg=Boolean(j.env.DEBUG||j.env.VERBOSE||j.argv.includes("--debug")||j.argv.includes("--verbose")),kf=mf||!Jf,hg=Boolean(!kf&&(Jf||j.env.COLORTERM||j.env.FORCE_COLOR||j.env.TERM&&j.env.TERM!=="dumb")),s={github:{name:"GitHub Actions",detected:Boolean(j.env.GITHUB_ACTIONS)},gitlab:{name:"GitLab CI",detected:Boolean(j.env.GITLAB_CI)},circle:{name:"CircleCI",detected:Boolean(j.env.CIRCLECI)},travis:{name:"Travis CI",detected:Boolean(j.env.TRAVIS)},jenkins:{name:"Jenkins",detected:Boolean(j.env.JENKINS_URL)},vercel:{name:"Vercel",detected:Boolean(j.env.VERCEL)},netlify:{name:"Netlify",detected:Boolean(j.env.NETLIFY)},heroku:{name:"Heroku",detected:Boolean(j.env.DYNO)},aws:{name:"AWS",detected:Boolean(j.env.AWS_REGION||j.env.AWS_LAMBDA_FUNCTION_NAME)},azure:{name:"Azure",detected:Boolean(j.env.AZURE_HTTP_USER_AGENT)},cloudflare:{name:"Cloudflare",detected:Boolean(j.env.CF_PAGES)},railway:{name:"Railway",detected:Boolean(j.env.RAILWAY_ENVIRONMENT)},render:{name:"Render",detected:Boolean(j.env.RENDER)}},df=Object.keys(s).find((f)=>{let g=s[f];return g!==void 0&&g.detected})||"unknown",Fg=s[df]||{name:"Unknown",detected:!1};import rf from"process";import{projectPath as tf}from"@stacksjs/path";import Qf from"fs";var Zf={APP_ENV:["local","dev","development","staging","prod","production"],DB_CONNECTION:["mysql","sqlite","postgres","dynamodb"],DB_MIGRATE_FRESH:["allow","confirm","disabled"],MAIL_MAILER:["smtp","mailgun","ses","log","sendgrid","mailtrap"],SEARCH_ENGINE_DRIVER:["opensearch","meilisearch","algolia","typesense"],FRONTEND_APP_ENV:["development","staging","production"]};function If(f){return`${f.replace(/[^a-z0-9]/gi,"").toUpperCase()}_`}function uf(f,g={}){let B=g.self?If(g.self):void 0,A=(g.tenants??[]).filter((D)=>typeof D==="string"&&D.trim().length>0).map((D)=>({slug:D,prefix:If(D)})).filter(({prefix:D})=>D!==B).sort((D,O)=>O.prefix.length-D.prefix.length),$={},_={};for(let[D,O]of Object.entries(f)){let R=A.find(({prefix:X})=>D.startsWith(X));if(R){(_[R.slug]??={})[D]=O;continue}$[D]=O}return{own:$,foreign:_}}function Kg(f,g={}){return uf(f,g).own}function cg(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 wf=new Set;function yf(f){if(wf.has(f))return;wf.add(f),console.warn(`[env] warning: "${f}" is encrypted but no usable private key was found to decrypt it; falling back to its default. Set DOTENV_PRIVATE_KEY_<ENV> or ship .env.keys.`)}var nf={get:(f,g)=>{let B=f[g];if(typeof B==="string"&&(B.startsWith("encrypted:")||B.startsWith("enc:"))){let $=Of(B);if($===void 0){yf(g),delete f[g];return}f[g]=$,B=$}let A=["_PORT","_TIMEOUT","_TTL","_SIZE","_LIMIT","_MAX","_MIN","_INTERVAL","_RETRIES","_CONCURRENCY","_WORKERS","_CONNECTIONS"];if(typeof B==="string"&&/^\d+$/.test(B)&&!B.startsWith("0")&&A.some(($)=>g.endsWith($)))return Number(B);if(typeof B==="string"){let $=B.toLowerCase();if($==="true")return!0;if($==="false")return!1}return B}};function vf(){return typeof Bun<"u"?Bun.env:rf.env}var Uf=new Proxy(vf(),nf);function tg(f,g,B){let A=B?.path||tf(".env"),_=Qf.readFileSync(A,"utf-8").split(`
24
+ `,U=Yf(g.output,_.publicKey,W,f),I=`${process.pid}-${Date.now()}`,Q=E(Tf(A),`.${l(A)}.${I}.rotate`),J=E(Tf($),`.${l($)}.${I}.rotate`);try{if(M(Q,U,{encoding:"utf8",mode:384}),M(J,Z,{encoding:"utf8",mode:384}),f.stdout)return p(J,$),{success:!0,output:U};p(Q,A);try{p(J,$)}catch(w){throw M(A,D),w}return{success:!0,output:`\u2714 rotated (${f.file||".env"}) to encrypted:v2`}}catch(w){if(!x($))M($,O,{mode:384});return{success:!1,error:`Failed to rotate without exposing plaintext: ${w instanceof Error?w.message:"Unknown error"}`}}finally{Wf(Q,{force:!0}),Wf(J,{force:!0})}}import j from"process";import{platform as cf}from"os";var jf=typeof Bun<"u",Gf=typeof j<"u"&&j.versions?.node!==void 0,Pf=jf?"bun":Gf?"node":"unknown",Mg={name:Pf,version:jf?Bun.version:Gf?j.version:void 0},a=cf(),zg=a==="win32",Vg=a==="darwin",Cg=a==="linux",Jf=Boolean(j.stdout?.isTTY),bg=typeof globalThis.window<"u",mf=Boolean(j.env.CI||j.env.CONTINUOUS_INTEGRATION||j.env.BUILD_NUMBER||j.env.RUN_ID),hg=Boolean(j.env.DEBUG||j.env.VERBOSE||j.argv.includes("--debug")||j.argv.includes("--verbose")),kf=mf||!Jf,Fg=Boolean(!kf&&(Jf||j.env.COLORTERM||j.env.FORCE_COLOR||j.env.TERM&&j.env.TERM!=="dumb")),s={github:{name:"GitHub Actions",detected:Boolean(j.env.GITHUB_ACTIONS)},gitlab:{name:"GitLab CI",detected:Boolean(j.env.GITLAB_CI)},circle:{name:"CircleCI",detected:Boolean(j.env.CIRCLECI)},travis:{name:"Travis CI",detected:Boolean(j.env.TRAVIS)},jenkins:{name:"Jenkins",detected:Boolean(j.env.JENKINS_URL)},vercel:{name:"Vercel",detected:Boolean(j.env.VERCEL)},netlify:{name:"Netlify",detected:Boolean(j.env.NETLIFY)},heroku:{name:"Heroku",detected:Boolean(j.env.DYNO)},aws:{name:"AWS",detected:Boolean(j.env.AWS_REGION||j.env.AWS_LAMBDA_FUNCTION_NAME)},azure:{name:"Azure",detected:Boolean(j.env.AZURE_HTTP_USER_AGENT)},cloudflare:{name:"Cloudflare",detected:Boolean(j.env.CF_PAGES)},railway:{name:"Railway",detected:Boolean(j.env.RAILWAY_ENVIRONMENT)},render:{name:"Render",detected:Boolean(j.env.RENDER)}},df=Object.keys(s).find((f)=>{let g=s[f];return g!==void 0&&g.detected})||"unknown",Sg=s[df]||{name:"Unknown",detected:!1};import rf from"process";import{projectPath as tf}from"@stacksjs/path";import Qf from"fs";var Zf={APP_ENV:["local","dev","development","staging","prod","production"],DB_CONNECTION:["mysql","sqlite","postgres","dynamodb"],DB_MIGRATE_FRESH:["allow","confirm","disabled"],MAIL_MAILER:["smtp","mailgun","ses","log","sendgrid","mailtrap"],SEARCH_ENGINE_DRIVER:["opensearch","meilisearch","algolia","typesense"],FRONTEND_APP_ENV:["development","staging","production"]};function If(f){return`${f.replace(/[^a-z0-9]/gi,"").toUpperCase()}_`}function uf(f,g={}){let B=g.self?If(g.self):void 0,A=(g.tenants??[]).filter((D)=>typeof D==="string"&&D.trim().length>0).map((D)=>({slug:D,prefix:If(D)})).filter(({prefix:D})=>D!==B).sort((D,O)=>O.prefix.length-D.prefix.length),$={},_={};for(let[D,O]of Object.entries(f)){let R=A.find(({prefix:X})=>D.startsWith(X));if(R){(_[R.slug]??={})[D]=O;continue}$[D]=O}return{own:$,foreign:_}}function cg(f,g={}){return uf(f,g).own}function Pg(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 wf=new Set;function yf(f){if(wf.has(f))return;wf.add(f),console.warn(`[env] warning: "${f}" is encrypted but no usable private key was found to decrypt it; falling back to its default. Set DOTENV_PRIVATE_KEY_<ENV> or ship .env.keys.`)}var vf={get:(f,g)=>{let B=f[g];if(typeof B==="string"&&(B.startsWith("encrypted:")||B.startsWith("enc:"))){let $=Of(B);if($===void 0){yf(g),delete f[g];return}f[g]=$,B=$}let A=["_PORT","_TIMEOUT","_TTL","_SIZE","_LIMIT","_MAX","_MIN","_INTERVAL","_RETRIES","_CONCURRENCY","_WORKERS","_CONNECTIONS"];if(typeof B==="string"&&/^\d+$/.test(B)&&!B.startsWith("0")&&A.some(($)=>g.endsWith($)))return Number(B);if(typeof B==="string"){let $=B.toLowerCase();if($==="true")return!0;if($==="false")return!1}return B}};function nf(){return typeof Bun<"u"?Bun.env:rf.env}var Uf=new Proxy(nf(),vf);function yg(f,g,B){let A=B?.path||tf(".env"),_=Qf.readFileSync(A,"utf-8").split(`
25
25
  `),D=_.findIndex((X)=>X.startsWith(`${f}=`)),R=/[\s"'#$\\]/.test(g)?`"${g.replace(/"/g,"\\\"")}"`:g;if(D!==-1)_[D]=`${f}=${R}`;else _.push(`${f}=${R}`);Qf.writeFileSync(A,_.join(`
26
- `))}function yg(f=Uf){let g=[];for(let[B,A]of Object.entries(Zf)){let $=f[B];if($!==void 0&&$!==""&&!A.includes(String($)))g.push(`${B}="${$}" is not valid. Allowed values: ${A.join(", ")}`)}return g}function ng(f,g=Uf){let B=[];for(let A of f){let $=g[A];if($===void 0||$===""||$===null)B.push(A)}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{tg as writeEnv,yg as validateEnv,If as tenantEnvPrefix,Kg as stripForeignTenantEnv,Ig as setEnv,xg as runtimeInfo,Pf as runtime,Ug as rotateKeypair,Sf as resolvePrivateKey,Og as resetPrivateKeyCache,ng as requireEnv,Fg as providerInfo,df as provider,vf as process,a as platform,uf as partitionTenantEnv,af as parseEnvFromKey,q as parse,sf as migrateEncryptedValue,gg as loadEnvFiles,Rf as loadEnv,Mg as isWindows,Gf as isNode,kf as isMinimal,zg as isMacOS,Vg as isLinux,lf as isLegacyEncryptedValue,bg as isDebug,hg as isColorSupported,mf as isCI,jf as isBun,Cg as hasWindow,Jf as hasTTY,n as getPrivateKey,wg as getKeypair,Qg as getEnv,F as generateKeypair,cg as foreignTenantKeys,Nf as envPlugin,Zf as envEnum,Uf as env,P as encryptValue,Zg as encryptEnv,h as decryptValue,Of as decryptEnvValue,Kf as decryptEnv,Rg as autoLoadEnv,pf as aesEncrypt,qf as aesDecrypt};
26
+ `))}function vg(f=Uf){let g=[];for(let[B,A]of Object.entries(Zf)){let $=f[B];if($!==void 0&&$!==""&&!A.includes(String($)))g.push(`${B}="${$}" is not valid. Allowed values: ${A.join(", ")}`)}return g}function ng(f,g=Uf){let B=[];for(let A of f){let $=g[A];if($===void 0||$===""||$===null)B.push(A)}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{yg as writeEnv,vg as validateEnv,If as tenantEnvPrefix,cg as stripForeignTenantEnv,Qg as setEnv,Mg as runtimeInfo,Pf as runtime,Eg as rotateKeypair,Sf as resolvePrivateKey,Zg as resolveEnvFile,Og as resetPrivateKeyCache,ng as requireEnv,Sg as providerInfo,df as provider,nf as process,a as platform,uf as partitionTenantEnv,af as parseEnvFromKey,q as parse,sf as migrateEncryptedValue,gg as loadEnvFiles,Rf as loadEnv,zg as isWindows,Gf as isNode,kf as isMinimal,Vg as isMacOS,Cg as isLinux,lf as isLegacyEncryptedValue,hg as isDebug,Fg as isColorSupported,mf as isCI,jf as isBun,bg as hasWindow,Jf as hasTTY,v as getPrivateKey,Ug as getKeypair,wg as getEnv,F as generateKeypair,Pg as foreignTenantKeys,Nf as envPlugin,Zf as envEnum,Uf as env,P as encryptValue,Ig as encryptEnv,h as decryptValue,Of as decryptEnvValue,Kf as decryptEnv,Rg as autoLoadEnv,pf as aesEncrypt,qf as aesDecrypt};
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.183",
5
+ "version": "0.70.185",
6
6
  "description": "Stacks env helper methods.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -51,7 +51,7 @@
51
51
  },
52
52
  "devDependencies": {
53
53
  "better-dx": "^0.2.17",
54
- "@stacksjs/path": "0.70.183",
55
- "@stacksjs/validation": "0.70.183"
54
+ "@stacksjs/path": "0.70.185",
55
+ "@stacksjs/validation": "0.70.185"
56
56
  }
57
57
  }