@stacksjs/env 0.70.163 → 0.70.165

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/README.md CHANGED
@@ -462,7 +462,7 @@ For help, discussion about best practices, or any other conversation that would
462
462
 
463
463
  For casual chit-chat with others using this package:
464
464
 
465
- [Join the Stacks Discord Server](https://discord.gg/stacksjs)
465
+ [Join the Stacks Discord Server](https://stacksjs.com/discord)
466
466
 
467
467
  ## 📄 License
468
468
 
package/dist/index.d.ts CHANGED
@@ -24,4 +24,5 @@ export * from './crypto';
24
24
  export * from './parser';
25
25
  export * from './plugin';
26
26
  export * from './cli';
27
+ export * from './tenants';
27
28
  export * from './utils';
package/dist/index.js CHANGED
@@ -1,26 +1,26 @@
1
1
  // @bun
2
- import{createCipheriv as e,createDecipheriv as ff,createHash as k,createPrivateKey as xf,createPublicKey as gf,diffieHellman as Bf,generateKeyPairSync as $f,hkdfSync as Af,randomBytes as d}from"crypto";var t="x25519-public:",r="x25519-private:",c="encrypted:v2:",n="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 k("sha256").update(f).digest()}function vf(f,g){let B=d(16),$=e("aes-256-gcm",Df(g),B);return{ciphertext:Buffer.concat([$.update(f,"utf8"),$.final()]).toString("hex"),iv:B.toString("hex"),authTag:$.getAuthTag().toString("hex")}}function Ef(f,g,B,$){try{let A=ff("aes-256-gcm",Df(g),Buffer.from(B,"hex"));return A.setAuthTag(Buffer.from($,"hex")),Buffer.concat([A.update(Buffer.from(f,"hex")),A.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 $=Buffer.from(f,"base64url");if(z($)!==f)throw Error(`${g} is not canonical base64url`);if(B!==void 0&&$.length!==B)throw Error(`${g} has an invalid length`);return $}function Uf(f){if(!f.startsWith(t))throw Error("Legacy or invalid public key. Run buddy env:rotate before creating new ciphertext.");let g=gf({key:C(f.slice(t.length),"public key"),format:"der",type:"spki"});if(g.asymmetricKeyType!=="x25519")throw Error("Public key is not X25519");return g}function qf(f){if(!f.startsWith(r))throw Error("Private key is not a version 2 X25519 key");let g=xf({key:C(f.slice(r.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"}),$=g.export({format:"der",type:"pkcs8"});return{publicKey:`${t}${z(B)}`,privateKey:`${r}${z($)}`}}function P(f,g){let B=Uf(g),$=$f("x25519"),A=z($.publicKey.export({format:"der",type:"spki"})),_=d(16),R=d(12),O=Bf({privateKey:$.privateKey,publicKey:B}),H=Buffer.from(Af("sha256",O,_,_f,32)),X={epk:A,salt:z(_),nonce:z(R)};try{let D=e("aes-256-gcm",H,R);D.setAAD(Hf(X));let T=Buffer.concat([D.update(f,"utf8"),D.final()]),G={v:2,...X,ciphertext:z(T),tag:z(D.getAuthTag())};return`${c}${z(Buffer.from(JSON.stringify(G),"utf8"))}`}finally{O.fill(0),H.fill(0)}}function Lf(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 $=Object.keys(B).sort(),A=["ciphertext","epk","nonce","salt","tag","v"];if($.length!==A.length||$.some((_,R)=>_!==A[R]))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 Mf(f,g){try{let B=Lf(f),$=qf(g),A=gf({key:C(B.epk,"ephemeral public key"),format:"der",type:"spki"});if(A.asymmetricKeyType!=="x25519")throw Error("ephemeral key is not X25519");let _=C(B.salt,"salt",16),R=C(B.nonce,"nonce",12),O=C(B.ciphertext,"ciphertext"),H=C(B.tag,"tag",16),X=Bf({privateKey:$,publicKey:A}),D=Buffer.from(Af("sha256",X,_,_f,32));try{let T=ff("aes-256-gcm",D,R);return T.setAAD(Hf(B)),T.setAuthTag(H),Buffer.concat([T.update(O),T.final()]).toString("utf8")}finally{X.fill(0),D.fill(0)}}catch{throw Error(Xf)}}function zf(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 $=k("sha256").update(Buffer.from(g,"hex")).digest(),A=k("sha256").update($).digest();return Ef(B.subarray(32).toString("hex"),A,B.subarray(0,16).toString("hex"),B.subarray(16,32).toString("hex"))}catch{throw Error(Xf)}}function of(f){return f.startsWith(n)&&!f.startsWith(c)}function h(f,g){if(!f.startsWith(n))return f;return f.startsWith(c)?Mf(f,g):zf(f,g)}function pf(f,g,B){return P(h(f,g),B)}function lf(f){let g=f.match(/^DOTENV_PRIVATE_KEY_(.+)$/);return g&&g[1]?g[1].toLowerCase():""}function i(f=""){return f?process.env[`DOTENV_PRIVATE_KEY_${f.toUpperCase()}`]:process.env.DOTENV_PRIVATE_KEY}function U(f,g={}){let B={},$=[],A=[],_=f.split(`
3
- `);for(let R of _){let O=R.trim();if(!O||O.startsWith("#"))continue;if(O.startsWith("DOTENV_PUBLIC_KEY=")){let T=O.match(/^DOTENV_PUBLIC_KEY=["']?([^"'\n]+)["']?/);if(T&&T[1]!==void 0)B.DOTENV_PUBLIC_KEY=T[1];continue}let H=O.match(/^([^=]+)=(.*)$/);if(!H||H[1]===void 0||H[2]===void 0)continue;let X=H[1].trim(),D=H[2].trim();if(D.startsWith('"')&&D.endsWith('"')||D.startsWith("'")&&D.endsWith("'")){if(D=D.slice(1,-1),D.includes("\\n"))D=D.replace(/\\n/g,`
4
- `)}if(D.startsWith("encrypted:")||D.startsWith("enc:")){if(!g.privateKey){A.push(X);continue}try{let T=D.startsWith("enc:")?`encrypted:${D.slice(4)}`:D;D=h(T,g.privateKey)}catch(T){$.push(`Failed to decrypt ${X}: ${T instanceof Error?T.message:"Unknown error"}`)}}D=Vf(D,{...g.processEnv||process.env,...B}),D=bf(D),B[X]=D}return{parsed:B,errors:$,skippedEncrypted:A}}function Vf(f,g){return f.replace(/\$\{([^}]+)\}/g,(B,$)=>{let A=$.match(/^([^:\-+]+)(:-|-)(.+)$/);if(A){let[,R,O,H]=A,X=g[R];if(O===":-")return X||H;else return X!==void 0?X:H}let _=$.match(/^([^:\-+]+)(:?\+)(.+)$/);if(_){let[,R,O,H]=_,X=g[R];if(O===":+")return X?H:"";else return X!==void 0?H:""}return g[$]||""})}var Cf=new Set(["date","hostname","whoami","uname","pwd","echo","printf","cat","basename","dirname"]);function bf(f){return f.replace(/\$\(([^)]+)\)/g,(g,B)=>{try{let $=B.trim().split(/\s+/),A=$[0];if(!A||!Cf.has(A))return console.warn(`[env] Blocked command substitution for disallowed command: ${A}`),"";let _=Bun.spawnSync($,{stdout:"pipe",stderr:"pipe"});if(_.exitCode===0)return new TextDecoder().decode(_.stdout).trim();console.warn(`[env] Command substitution failed (exit code ${_.exitCode}): ${A}`)}catch($){console.warn(`[env] Command substitution error: ${$ instanceof Error?$.message:String($)}`)}return""})}async function ef(f,g={}){let B={},$=[],A=[];for(let _ of f)try{let R=Bun.file(_);if(!R.size)continue;let O=await R.text(),{parsed:H,errors:X,skippedEncrypted:D}=U(O,g);$.push(...X),A.push(...D);for(let[T,G]of Object.entries(H)){if(!g.overload&&B[T]!==void 0)continue;B[T]=G}}catch(R){if(R?.code==="ENOENT")continue;$.push(`Failed to read ${_}: ${R instanceof Error?R.message:String(R)}`)}return{parsed:B,errors:$,skippedEncrypted:A}}import{existsSync as m}from"fs";import{readFileSync as y}from"fs";import{resolve as u}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 v=null,o;function hf(f,g,B=".env.keys"){let $=u(g,B);if(!m($))return;try{let{parsed:A}=U(y($,"utf-8"));if(f){let _=A[`DOTENV_PRIVATE_KEY_${f.toUpperCase()}`];if(_)return _}return A.DOTENV_PRIVATE_KEY}catch{return}}function Ff(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(v===g)return o;let $=i(g);if(!$)$=process.env.DOTENV_PRIVATE_KEY;if(!$)$=hf(g,B);return v=g,o=$,$}function Dg(){v=null,o=void 0}function Of(f,g={}){if(!S(f))return f;let B=Ff(g);if(!B)return;try{let $=f.startsWith("enc:")?`encrypted:${f.slice(4)}`:f;return h($,B)}catch{return}}function Rf(f={}){let{path:g=[".env"],overload:B=!1,env:$,privateKey:A,keysFile:_=".env.keys",quiet:R=!1,cwd:O=process.cwd()}=f,H=Array.isArray(g)?g:[g],X=[],D=0,T=new Set(Object.entries(process.env).filter(([,W])=>W!==void 0&&!S(W)).map(([W])=>W)),G=A;if(!G&&_){let W=u(O,_);if(m(W))try{let Y=y(W,"utf-8"),{parsed:w}=U(Y);if($){let x=`DOTENV_PRIVATE_KEY_${$.toUpperCase()}`;G=w[x]}else G=w.DOTENV_PRIVATE_KEY}catch(Y){X.push(`Failed to load keys file: ${Y instanceof Error?Y.message:"Unknown error"}`)}}if(!G)G=i($||"");for(let W of H){let Y=u(O,W);if(!m(Y)){if(!R)X.push(`File not found: ${Y}`);continue}try{let w=y(Y,"utf-8"),{parsed:x,errors:I,skippedEncrypted:Z}=U(w,{privateKey:G,processEnv:process.env});if(X.push(...I),Z.length>0){for(let K of Z)if(S(process.env[K]))delete process.env[K];let Q=$?`DOTENV_PRIVATE_KEY_${$.toUpperCase()}`:"DOTENV_PRIVATE_KEY",V=Z.slice(0,5).join(", "),N=Z.length>5?`, \u2026 +${Z.length-5} more`:"";console.warn(`[env] warning: skipped ${Z.length} encrypted value(s) in ${W} (${Q} not set; defaults apply): ${V}${N}`)}let J=0;for(let[Q,V]of Object.entries(x)){if(Q==="DOTENV_PUBLIC_KEY")continue;let N=process.env[Q],K=S(N)&&V!==N;if(B||!T.has(Q)||K)process.env[Q]=V,D++,J++}if(!R&&!process.env.__ENV_LOADED__)console.error(`[env] loaded ${J}/${Object.keys(x).length} variables from ${W}`),process.env.__ENV_LOADED__="1"}catch(w){X.push(`Failed to load ${Y}: ${w instanceof Error?w.message:"Unknown error"}`)}}return{loaded:D,errors:X}}function Sf(f={}){return{name:"env-plugin",setup(g){Rf(f)}}}function Hg(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(!$.includes(O)&&m(u(B,O)))$.push(O)};A(".env"),A(".env.local");let _=`.env.${g}`,R=`.env.${g}.local`;return A(_),A(R),Rf({...f,path:$,env:g})}var Rg=Sf;import{existsSync as L,readFileSync as q,renameSync as p,rmSync as Tf,writeFileSync as M}from"fs";import{basename as l,dirname as Wf,resolve as E}from"path";function Yf(f,g,B,$){let A=["#/-------------------[DOTENV_PUBLIC_KEY]--------------------/","#/ versioned X25519 encryption for .env files /","#/ [how it works](https://stacksjs.com/encryption) /","#/----------------------------------------------------------/",`${B}="${g}"`,""];for(let _ of f.split(`
5
- `)){let R=_.trim();if(!R||R.startsWith("#")){A.push(_);continue}if(R.startsWith("DOTENV_PUBLIC_KEY"))continue;let O=R.match(/^([^=]+)=(.*)$/);if(!O||O[1]===void 0||O[2]===void 0){A.push(_);continue}let H=O[1].trim(),X=O[2].trim();if(X.startsWith('"')&&X.endsWith('"')||X.startsWith("'")&&X.endsWith("'"))X=X.slice(1,-1);if((!$.key||H.includes($.key))&&(!$.excludeKey||!H.includes($.excludeKey))&&!X.startsWith("encrypted:"))X=P(X,g);A.push(`${H}="${X}"`)}return A.join(`
6
- `)}function Gg(f={}){let g=f.cwd||process.cwd(),B=E(g,f.file||".env"),$=E(g,f.keysFile||".env.keys");if(!L(B))return{success:!1,error:`File not found: ${B}`};try{let A,_;if(L($)){let D=q($,"utf-8"),{parsed:T}=U(D),Y=((f.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),w=Y?`DOTENV_PUBLIC_KEY_${Y}`:"DOTENV_PUBLIC_KEY",x=Y?`DOTENV_PRIVATE_KEY_${Y}`:"DOTENV_PRIVATE_KEY";if(A=T[w]||"",_=T[x]||"",!A||!_){let I=F();A=I.publicKey,_=I.privateKey;let Z=`
7
- ${w}="${A}"
8
- ${x}="${_}"
9
- `;M($,D+Z,"utf-8")}}else{let D=F();A=D.publicKey,_=D.privateKey;let W=((f.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),Y=W?`DOTENV_PUBLIC_KEY_${W}`:"DOTENV_PUBLIC_KEY",w=W?`DOTENV_PRIVATE_KEY_${W}`:"DOTENV_PRIVATE_KEY",x=`# .env.keys - Keep this file secure and never commit to source control
10
- ${Y}="${A}"
11
- ${w}="${_}"
12
- `;M($,x,"utf-8")}let R=q(B,"utf-8"),O=f.file?f.file.replace(/^\.env\./,"").toUpperCase():"",H=O?`DOTENV_PUBLIC_KEY_${O}`:"DOTENV_PUBLIC_KEY",X=Yf(R,A,H,f);if(f.stdout)return{success:!0,output:X};return M(B,X,"utf-8"),{success:!0,output:`\u2714 encrypted (${f.file||".env"})
13
- \u2714 key added to ${f.keysFile||".env.keys"}`}}catch(A){return{success:!1,error:`Failed to encrypt: ${A instanceof Error?A.message:"Unknown error"}`}}}function Nf(f={}){let g=f.cwd||process.cwd(),B=E(g,f.file||".env"),$=E(g,f.keysFile||".env.keys");if(!L(B))return{success:!1,error:`File not found: ${B}`};if(!L($))return{success:!1,error:`Keys file not found: ${$}`};try{let A=q($,"utf-8"),{parsed:_}=U(A),H=((f.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),X=H?`DOTENV_PRIVATE_KEY_${H}`:"DOTENV_PRIVATE_KEY",D=_[X];if(!D)return{success:!1,error:`Private key not found: ${X}`};let G=q(B,"utf-8").split(`
14
- `),W=[];for(let w of G){let x=w.trim();if(!x||x.startsWith("#")){W.push(w);continue}if(x.startsWith("DOTENV_PUBLIC_KEY"))continue;let I=x.match(/^([^=]+)=(.*)$/);if(!I||I[1]===void 0||I[2]===void 0){W.push(w);continue}let Z=I[1].trim(),J=I[2].trim();if(J.startsWith('"')&&J.endsWith('"')||J.startsWith("'")&&J.endsWith("'"))J=J.slice(1,-1);let Q=J.startsWith("encrypted:");if(f.key&&!Z.includes(f.key))Q=!1;if(Q)J=h(J,D);W.push(`${Z}="${J}"`)}let Y=W.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(A){return{success:!1,error:`Failed to decrypt: ${A instanceof Error?A.message:"Unknown error"}`}}}function Jg(f,g,B={}){let $=B.cwd||process.cwd(),A=E($,B.file||".env"),_=E($,B.keysFile||".env.keys");try{let R="";if(L(A))R=q(A,"utf-8");let O=R.split(`
16
- `),H=!1,X,G=((B.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),W=G?`DOTENV_PUBLIC_KEY_${G}`:"DOTENV_PUBLIC_KEY",Y=G?`DOTENV_PRIVATE_KEY_${G}`:"DOTENV_PRIVATE_KEY";for(let I of O){let Z=I.trim();if(Z.startsWith(`${W}=`)){let J=Z.match(/^[^=]+=["']?([^"'\n]+)["']?/);if(J)X=J[1];break}}if(X){let I=!1;if(L(_)){let Z=q(_,"utf-8"),{parsed:J}=U(Z);I=Boolean(J[Y])}if(!I)X=void 0}if(!B.plain&&!X){let I=F();X=I.publicKey;let Z="";if(L(_))Z=q(_,"utf-8");Z+=`
17
- ${W}="${I.publicKey}"
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(`
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(`
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=`
7
+ ${Z}="${$}"
8
+ ${U}="${_}"
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
10
+ ${Y}="${$}"
11
+ ${Z}="${_}"
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
+ \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
+ `),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(`
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
+ ${T}="${I.publicKey}"
18
18
  ${Y}="${I.privateKey}"
19
- `,M(_,Z,"utf-8");for(let J=O.length-1;J>=0;J--){let Q=O[J];if(Q!==void 0&&Q.trim().startsWith(`${W}=`))O.splice(J,1)}O.unshift(`${W}="${X}"`)}let w=g;if(!B.plain&&X)w=P(g,X);for(let I=0;I<O.length;I++){let Z=O[I];if(Z===void 0)continue;if(Z.trim().startsWith(`${f}=`)){O[I]=`${f}="${w}"`,H=!0;break}}if(!H)O.push(`${f}="${w}"`);let x=O.join(`
20
- `);return M(A,x,"utf-8"),{success:!0,output:`set ${f}${B.plain?"":" with encryption"} (${B.file||".env"})`}}catch(R){return{success:!1,error:`Failed to set: ${R instanceof Error?R.message:"Unknown error"}`}}}function wg(f,g={}){let B=g.cwd||process.cwd(),$=E(B,g.file||".env");if(!L($))return{success:!1,error:`File not found: ${$}`};try{let A,_=E(B,g.keysFile||".env.keys"),H=((g.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),X=H?`DOTENV_PRIVATE_KEY_${H}`:"DOTENV_PRIVATE_KEY";if(L(_)){let Y=q(_,"utf-8"),{parsed:w}=U(Y);A=w[X]}if(!A)A=process.env[X];let D=q($,"utf-8"),{parsed:T}=U(D,{privateKey:A});if(f&&!g.all){let Y=T[f];if(Y===void 0)return{success:!1,error:`Key not found: ${f}`};return{success:!0,output:Y}}let G=g.all?{...process.env,...T}:T,W;switch(g.format){case"shell":W=Object.entries(G).map(([Y,w])=>`${Y}=${w}`).join(" ");break;case"eval":W=Object.entries(G).map(([Y,w])=>`${Y}="${w}"`).join(`
21
- `);break;case"json":default:W=g.prettyPrint?JSON.stringify(G,null,2):JSON.stringify(G);break}return{success:!0,output:W}}catch(A){return{success:!1,error:`Failed to get: ${A instanceof Error?A.message:"Unknown error"}`}}}function Ig(f,g={}){let B=g.cwd||process.cwd(),$=E(B,g.keysFile||".env.keys");if(!L($))return{success:!1,error:`Keys file not found: ${$}`};try{let A=q($,"utf-8"),{parsed:_}=U(A),H=((g.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),X=H?`DOTENV_PUBLIC_KEY_${H}`:"DOTENV_PUBLIC_KEY",D=H?`DOTENV_PRIVATE_KEY_${H}`:"DOTENV_PRIVATE_KEY";if(f){let W=_[f];if(!W)return{success:!1,error:`Key not found: ${f}`};return{success:!0,output:W}}let T={[X]:_[X],[D]:_[D]};return{success:!0,output:g.format==="shell"?`${X}=${T[X]} ${D}=${T[D]}`:JSON.stringify(T)}}catch(A){return{success:!1,error:`Failed to get keypair: ${A instanceof Error?A.message:"Unknown error"}`}}}function Zg(f={}){let g=Nf({file:f.file,keysFile:f.keysFile,stdout:!0,cwd:f.cwd});if(!g.success)return g;let B=f.cwd||process.cwd(),$=E(B,f.file||".env"),A=E(B,f.keysFile||".env.keys");if(g.output===void 0)return{success:!1,error:"Rotation produced no decrypted content"};let _=F(),R=q($),O=q(A),H=O.toString("utf8").split(`
22
- `),X=f.file||".env",D=l(X).replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),T=D?`DOTENV_PUBLIC_KEY_${D}`:"DOTENV_PUBLIC_KEY",G=D?`DOTENV_PRIVATE_KEY_${D}`:"DOTENV_PRIVATE_KEY",W=!1,Y=!1;for(let Q=0;Q<H.length;Q++){let V=H[Q];if(V===void 0)continue;if(V.startsWith(`${T}=`))H[Q]=`${T}="${_.publicKey}"`,W=!0;else if(V.startsWith(`${G}=`))H[Q]=`${G}="${_.privateKey}"`,Y=!0}if(!W)H.push(`${T}="${_.publicKey}"`);if(!Y)H.push(`${G}="${_.privateKey}"`);let w=`${H.join(`
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(`
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
- `,x=Yf(g.output,_.publicKey,T,f),I=`${process.pid}-${Date.now()}`,Z=E(Wf($),`.${l($)}.${I}.rotate`),J=E(Wf(A),`.${l(A)}.${I}.rotate`);try{if(M(Z,x,{encoding:"utf8",mode:384}),M(J,w,{encoding:"utf8",mode:384}),f.stdout)return p(J,A),{success:!0,output:x};p(Z,$);try{p(J,A)}catch(Q){throw M($,R),Q}return{success:!0,output:`\u2714 rotated (${f.file||".env"}) to encrypted:v2`}}catch(Q){if(!L(A))M(A,O,{mode:384});return{success:!1,error:`Failed to rotate without exposing plaintext: ${Q instanceof Error?Q.message:"Unknown error"}`}}finally{Tf(Z,{force:!0}),Tf(J,{force:!0})}}import j from"process";import{platform as Kf}from"os";var jf=typeof Bun<"u",Gf=typeof j<"u"&&j.versions?.node!==void 0,cf=jf?"bun":Gf?"node":"unknown",Ug={name:cf,version:jf?Bun.version:Gf?j.version:void 0},a=Kf(),qg=a==="win32",Lg=a==="darwin",Mg=a==="linux",Jf=Boolean(j.stdout?.isTTY),zg=typeof globalThis.window<"u",Pf=Boolean(j.env.CI||j.env.CONTINUOUS_INTEGRATION||j.env.BUILD_NUMBER||j.env.RUN_ID),Vg=Boolean(j.env.DEBUG||j.env.VERBOSE||j.argv.includes("--debug")||j.argv.includes("--verbose")),mf=Pf||!Jf,Cg=Boolean(!mf&&(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)}},uf=Object.keys(s).find((f)=>{let g=s[f];return g!==void 0&&g.detected})||"unknown",bg=s[uf]||{name:"Unknown",detected:!1};import kf from"process";import{projectPath as df}from"@stacksjs/path";import If from"fs";var wf={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"]};var Zf=new Set;function tf(f){if(Zf.has(f))return;Zf.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 rf={get:(f,g)=>{let B=f[g];if(typeof B==="string"&&(B.startsWith("encrypted:")||B.startsWith("enc:"))){let A=Of(B);if(A===void 0){tf(g),delete f[g];return}f[g]=A,B=A}let $=["_PORT","_TIMEOUT","_TTL","_SIZE","_LIMIT","_MAX","_MIN","_INTERVAL","_RETRIES","_CONCURRENCY","_WORKERS","_CONNECTIONS"];if(typeof B==="string"&&/^\d+$/.test(B)&&!B.startsWith("0")&&$.some((A)=>g.endsWith(A)))return Number(B);if(typeof B==="string"){let A=B.toLowerCase();if(A==="true")return!0;if(A==="false")return!1}return B}};function nf(){return typeof Bun<"u"?Bun.env:kf.env}var Qf=new Proxy(nf(),rf);function mg(f,g,B){let $=B?.path||df(".env"),_=If.readFileSync($,"utf-8").split(`
25
- `),R=_.findIndex((X)=>X.startsWith(`${f}=`)),H=/[\s"'#$\\]/.test(g)?`"${g.replace(/"/g,"\\\"")}"`:g;if(R!==-1)_[R]=`${f}=${H}`;else _.push(`${f}=${H}`);If.writeFileSync($,_.join(`
26
- `))}function ug(f=Qf){let g=[];for(let[B,$]of Object.entries(wf)){let A=f[B];if(A!==void 0&&A!==""&&!$.includes(String(A)))g.push(`${B}="${A}" is not valid. Allowed values: ${$.join(", ")}`)}return g}function kg(f,g=Qf){let B=[];for(let $ of f){let A=g[$];if(A===void 0||A===""||A===null)B.push($)}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{mg as writeEnv,ug as validateEnv,Jg as setEnv,Ug as runtimeInfo,cf as runtime,Zg as rotateKeypair,Ff as resolvePrivateKey,Dg as resetPrivateKeyCache,kg as requireEnv,bg as providerInfo,uf as provider,nf as process,a as platform,lf as parseEnvFromKey,U as parse,pf as migrateEncryptedValue,ef as loadEnvFiles,Rf as loadEnv,qg as isWindows,Gf as isNode,mf as isMinimal,Lg as isMacOS,Mg as isLinux,of as isLegacyEncryptedValue,Vg as isDebug,Cg as isColorSupported,Pf as isCI,jf as isBun,zg as hasWindow,Jf as hasTTY,i as getPrivateKey,Ig as getKeypair,wg as getEnv,F as generateKeypair,Sf as envPlugin,wf as envEnum,Qf as env,P as encryptValue,Gg as encryptEnv,h as decryptValue,Of as decryptEnvValue,Nf as decryptEnv,Hg as autoLoadEnv,vf as aesEncrypt,Ef as aesDecrypt};
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(`
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};
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Converts a project slug into the env prefix it namespaces keys with:
3
+ * `analytics-hq` and `analytics_hq` both become `ANALYTICSHQ_`.
4
+ */
5
+ export declare function tenantEnvPrefix(slug: string): string;
6
+ /**
7
+ * Splits a flat env map into the keys this project owns and the keys namespaced
8
+ * to another tenant on the same box.
9
+ *
10
+ * A key is foreign only when its prefix matches a **declared** tenant slug. The
11
+ * check is never heuristic: `STRIPE_SECRET_KEY` and `AWS_REGION` are vendor
12
+ * prefixes that happen to look the same, and guessing would strip real config.
13
+ * Declare no tenants and nothing is ever classified as foreign.
14
+ *
15
+ * `self` wins over `tenants`, so a project that mistakenly lists itself keeps
16
+ * its own `SELF_`-prefixed values.
17
+ */
18
+ export declare function partitionTenantEnv(values: Record<string, string>, options?: TenantEnvOptions): TenantEnvPartition;
19
+ /**
20
+ * The env map with every declared tenant's keys removed. Shorthand for the
21
+ * `own` half of {@link partitionTenantEnv}, which is what a deploy ships.
22
+ */
23
+ export declare function stripForeignTenantEnv(values: Record<string, string>, options?: TenantEnvOptions): Record<string, string>;
24
+ /**
25
+ * Flattens a partition's foreign half into a sorted `slug` / `keys` report,
26
+ * for a CLI to print.
27
+ */
28
+ export declare function foreignTenantKeys(partition: TenantEnvPartition): Array<{ tenant: string, keys: string[] }>;
29
+ export declare interface TenantEnvPartition {
30
+ own: Record<string, string>
31
+ foreign: Record<string, Record<string, string>>
32
+ }
33
+ export declare interface TenantEnvOptions {
34
+ self?: string
35
+ tenants?: readonly string[]
36
+ }
37
+ /**
38
+ * Tenant namespacing for environment values.
39
+ *
40
+ * A shared box hosts several projects: one owns it, the rest attach to it with
41
+ * `cloud.attachTo`. Each project deploys from its own repository and brings its
42
+ * own `.env.<environment>`, so no project ever needs another's secrets.
43
+ *
44
+ * In practice they leak anyway. A tenant's values get pasted into the owner's
45
+ * env file under a `TENANT_` prefix - usually while debugging a deploy - and
46
+ * then stay. That is worse than untidy: the deploy pipeline ships the owner's
47
+ * whole env file to every site it deploys, so those foreign secrets end up in
48
+ * the `.env` of an unrelated site on disk.
49
+ *
50
+ * Declaring the tenants attached to your box (`cloud.tenants` in
51
+ * `config/cloud.ts`) lets the framework tell the two apart, keep foreign values
52
+ * out of what it ships, and point at them so they can be deleted.
53
+ */
54
+ /** Where a key sits relative to the project reading the env file. */
55
+ export type EnvKeyOwnership = 'own' | 'foreign';
package/dist/types.d.ts CHANGED
@@ -139,7 +139,5 @@ export declare interface FrontendEnv {
139
139
  }
140
140
  declare type EnvValueConfig = StringEnvConfig | NumberEnvConfig | BooleanEnvConfig | EnumEnvConfig;
141
141
  export type EnvConfig = Partial<Record<EnvKey, EnvValueConfig>>;
142
- /** @deprecated Use `StacksEnv` instead */
143
- export type Env = StacksEnv;
144
142
  export type EnvSchema = any;
145
143
  export type FrontendEnvKeys = keyof FrontendEnv;
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.163",
5
+ "version": "0.70.165",
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.163",
55
- "@stacksjs/validation": "0.70.163"
54
+ "@stacksjs/path": "0.70.165",
55
+ "@stacksjs/validation": "0.70.165"
56
56
  }
57
57
  }