@stacksjs/env 0.73.2 → 0.74.0

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
@@ -76,9 +76,19 @@ export declare function getEnv(key?: string, options?: GetOptions): { success: b
76
76
  */
77
77
  export declare function getKeypair(keyName?: string, options?: { file?: string, keysFile?: string, format?: 'json' | 'shell', cwd?: string }): { success: boolean, output?: string, error?: string };
78
78
  /**
79
- * Rotate keypair and re-encrypt all values
79
+ * Rotate an env file's keypair and re-encrypt every value under the new one.
80
+ *
81
+ * A rotation is only meaningful as a PAIR: the re-encrypted file and the
82
+ * keypair that opens it have to land together, or one of them is useless.
83
+ * `--stdout` used to break that pair - it replaced the keypair in `.env.keys`
84
+ * and printed the new env content instead of writing it, so the file left on
85
+ * disk was still encrypted under a private key that had just been overwritten,
86
+ * and every value in it was unrecoverable (stacksjs/stacks#2398). It now writes
87
+ * nothing and hands back both halves. `--dry-run` writes nothing either, and
88
+ * does not even generate a keypair: a preview that produced real key material
89
+ * and threw it away would be a rotation, not a preview.
80
90
  */
81
- export declare function rotateKeypair(options?: { file?: string, keysFile?: string, key?: string, excludeKey?: string, stdout?: boolean, cwd?: string }): { success: boolean, output?: string, error?: string };
91
+ export declare function rotateKeypair(options?: RotateOptions): RotateResult;
82
92
  export declare interface EncryptOptions {
83
93
  file?: string
84
94
  keysFile?: string
@@ -108,3 +118,18 @@ export declare interface GetOptions {
108
118
  prettyPrint?: boolean
109
119
  cwd?: string
110
120
  }
121
+ export declare interface RotateOptions {
122
+ file?: string
123
+ keysFile?: string
124
+ key?: string
125
+ excludeKey?: string
126
+ stdout?: boolean
127
+ dryRun?: boolean
128
+ cwd?: string
129
+ }
130
+ export declare interface RotateResult {
131
+ success: boolean
132
+ output?: string
133
+ error?: string
134
+ notice?: string
135
+ }
package/dist/cli.js CHANGED
@@ -1,25 +1,28 @@
1
1
  // @bun
2
- import{createCipheriv as j,createDecipheriv as X,createHash as R,createPrivateKey as ce,createPublicKey as H,diffieHellman as J,generateKeyPairSync as M,hkdfSync as z,randomBytes as W}from"crypto";var S="x25519-public:",Y="x25519-private:",D="encrypted:v2:",A="encrypted:",G=Buffer.from("stacks-env:v2","utf8"),q="Environment decryption failed: authentication or format error";function Z(t){if(t.length===32)return t;return R("sha256").update(t).digest()}function Te(t,n){let e=W(16),s=j("aes-256-gcm",Z(n),e);return{ciphertext:Buffer.concat([s.update(t,"utf8"),s.final()]).toString("hex"),iv:e.toString("hex"),authTag:s.getAuthTag().toString("hex")}}function ae(t,n,e,s){try{let r=X("aes-256-gcm",Z(n),Buffer.from(e,"hex"));return r.setAuthTag(Buffer.from(s,"hex")),Buffer.concat([r.update(Buffer.from(t,"hex")),r.final()]).toString("utf8")}catch{throw Error("Decryption failed: authentication or format error")}}function _(t){return t.toString("base64url")}function P(t,n,e){if(typeof t!=="string"||t.length>0&&!/^[A-Za-z0-9_-]+$/.test(t))throw Error(`${n} is not canonical base64url`);let s=Buffer.from(t,"base64url");if(_(s)!==t)throw Error(`${n} is not canonical base64url`);if(e!==void 0&&s.length!==e)throw Error(`${n} has an invalid length`);return s}function ue(t){if(!t.startsWith(S))throw Error("Legacy or invalid public key. Run buddy env:rotate before creating new ciphertext.");let n=H({key:P(t.slice(S.length),"public key"),format:"der",type:"spki"});if(n.asymmetricKeyType!=="x25519")throw Error("Public key is not X25519");return n}function fe(t){if(!t.startsWith(Y))throw Error("Private key is not a version 2 X25519 key");let n=ce({key:P(t.slice(Y.length),"private key"),format:"der",type:"pkcs8"});if(n.asymmetricKeyType!=="x25519")throw Error("Private key is not X25519");return n}function Q(t){return Buffer.from(`stacks-env:v2;${t.epk};${t.salt};${t.nonce}`,"utf8")}function T(){let{publicKey:t,privateKey:n}=M("x25519"),e=t.export({format:"der",type:"spki"}),s=n.export({format:"der",type:"pkcs8"});return{publicKey:`${S}${_(e)}`,privateKey:`${Y}${_(s)}`}}function F(t,n){let e=ue(n),s=M("x25519"),r=_(s.publicKey.export({format:"der",type:"spki"})),i=W(16),u=W(12),f=J({privateKey:s.privateKey,publicKey:e}),c=Buffer.from(z("sha256",f,i,G,32)),a={epk:r,salt:_(i),nonce:_(u)};try{let o=j("aes-256-gcm",c,u);o.setAAD(Q(a));let l=Buffer.concat([o.update(t,"utf8"),o.final()]),p={v:2,...a,ciphertext:_(l),tag:_(o.getAuthTag())};return`${D}${_(Buffer.from(JSON.stringify(p),"utf8"))}`}finally{f.fill(0),c.fill(0)}}function le(t){let n=P(t.slice(D.length),"envelope").toString("utf8"),e=JSON.parse(n);if(!e||typeof e!=="object"||Array.isArray(e))throw Error("envelope is not an object");let s=Object.keys(e).sort(),r=["ciphertext","epk","nonce","salt","tag","v"];if(s.length!==r.length||s.some((i,u)=>i!==r[u]))throw Error("envelope fields are invalid");if(e.v!==2)throw Error("envelope version is unsupported");for(let i of["epk","salt","nonce","ciphertext","tag"])if(typeof e[i]!=="string")throw Error(`${i} is invalid`);return e}function pe(t,n){try{let e=le(t),s=fe(n),r=H({key:P(e.epk,"ephemeral public key"),format:"der",type:"spki"});if(r.asymmetricKeyType!=="x25519")throw Error("ephemeral key is not X25519");let i=P(e.salt,"salt",16),u=P(e.nonce,"nonce",12),f=P(e.ciphertext,"ciphertext"),c=P(e.tag,"tag",16),a=J({privateKey:s,publicKey:r}),o=Buffer.from(z("sha256",a,i,G,32));try{let l=X("aes-256-gcm",o,u);return l.setAAD(Q(e)),l.setAuthTag(c),Buffer.concat([l.update(f),l.final()]).toString("utf8")}finally{a.fill(0),o.fill(0)}}catch{throw Error(q)}}function de(t,n){try{if(!/^[0-9a-f]{64}$/.test(n))throw Error("invalid legacy private key");let e=Buffer.from(t.slice(A.length),"base64");if(e.length<32)throw Error("invalid legacy payload");let s=R("sha256").update(Buffer.from(n,"hex")).digest(),r=R("sha256").update(s).digest();return ae(e.subarray(32).toString("hex"),r,e.subarray(0,16).toString("hex"),e.subarray(16,32).toString("hex"))}catch{throw Error(q)}}function Oe(t){return t.startsWith(A)&&!t.startsWith(D)}function O(t,n){if(!t.startsWith(A))return t;return t.startsWith(D)?pe(t,n):de(t,n)}function De(t,n,e){return F(O(t,n),e)}function Fe(t){let n=t.match(/^DOTENV_PRIVATE_KEY_(.+)$/);return n&&n[1]?n[1].toLowerCase():""}function Ce(t=""){return t?process.env[`DOTENV_PRIVATE_KEY_${t.toUpperCase()}`]:process.env.DOTENV_PRIVATE_KEY}import{spawnSync as ye}from"child_process";import{readFileSync as ge}from"fs";import{arch as he,hostname as me,platform as ee,release as ve,userInfo as Ee}from"os";import{basename as $e,dirname as ke,resolve as Ke}from"path";function x(t,n={}){let e={},s=[],r=[],i=t.split(`
3
- `);for(let u of i){let f=u.trim();if(!f||f.startsWith("#"))continue;if(f.startsWith("DOTENV_PUBLIC_KEY=")){let l=f.match(/^DOTENV_PUBLIC_KEY=["']?([^"'\n]+)["']?/);if(l&&l[1]!==void 0)e.DOTENV_PUBLIC_KEY=l[1];continue}let c=f.match(/^([^=]+)=(.*)$/);if(!c||c[1]===void 0||c[2]===void 0)continue;let a=c[1].trim(),o=c[2].trim();if(o.startsWith('"')&&o.endsWith('"')||o.startsWith("'")&&o.endsWith("'")){if(o=o.slice(1,-1),o.includes("\\n"))o=o.replace(/\\n/g,`
4
- `)}if(o.startsWith("encrypted:")||o.startsWith("enc:")){if(!n.privateKey){r.push(a);continue}try{let l=o.startsWith("enc:")?`encrypted:${o.slice(4)}`:o;o=O(l,n.privateKey)}catch(l){s.push(`Failed to decrypt ${a}: ${l instanceof Error?l.message:"Unknown error"}`)}}o=we(o,{...n.processEnv||process.env,...e}),o=Pe(o),e[a]=o}return{parsed:e,errors:s,skippedEncrypted:r}}function we(t,n){return t.replace(/\$\{([^}]+)\}/g,(e,s)=>{let r=s.match(/^([^:\-+]+)(:-|-)(.+)$/);if(r){let[,u,f,c]=r,a=n[u];if(f===":-")return a||c;else return a!==void 0?a:c}let i=s.match(/^([^:\-+]+)(:?\+)(.+)$/);if(i){let[,u,f,c]=i,a=n[u];if(f===":+")return a?c:"";else return a!==void 0?c:""}return n[s]||""})}var be=new Set(["date","hostname","whoami","uname","pwd","echo","printf","cat","basename","dirname"]);function _e(t){let n=[],e="",s=null,r=!1;for(let i of t.trim()){if(r){e+=i,r=!1;continue}if(i==="\\"&&s!=="'"){r=!0;continue}if(s){if(i===s)s=null;else e+=i;continue}if(i==='"'||i==="'"){s=i;continue}if(/\s/.test(i)){if(e)n.push(e),e="";continue}e+=i}if(r||s)throw Error("Command substitution contains an unterminated escape or quote");if(e)n.push(e);return n}function Ne(t){let[n,...e]=t,s=e.some((r)=>r.startsWith("-"));if(n==="echo"&&!s)return e.join(" ");if(n==="printf"&&e.length===1&&!e[0]?.includes("%"))return e[0];if(n==="hostname"&&e.length===0)return me();if(n==="whoami"&&e.length===0)return Ee().username;if(n==="pwd"&&e.length===0)return process.cwd();if(n==="basename"&&e.length===1)return $e(e[0]??"");if(n==="dirname"&&e.length===1)return ke(e[0]??"");if(n==="cat"&&e.length>0&&!s)return e.map((r)=>ge(Ke(process.cwd(),r),"utf8")).join("");if(n==="uname"){let r=e[0]??"-s";if(e.length<=1&&r==="-m")return he();if(e.length<=1&&r==="-r")return ve();if(e.length<=1&&r==="-s")return{aix:"AIX",android:"Android",cygwin:"CYGWIN",darwin:"Darwin",freebsd:"FreeBSD",haiku:"Haiku",linux:"Linux",netbsd:"NetBSD",openbsd:"OpenBSD",sunos:"SunOS",win32:"Windows_NT"}[ee()]??ee()}return}function Pe(t){return t.replace(/\$\(([^)]+)\)/g,(n,e)=>{try{let s=_e(e),r=s[0];if(!r||!be.has(r))return console.warn(`[env] Blocked command substitution for disallowed command: ${r}`),"";let i=Ne(s);if(i!==void 0)return i.trim();let u=ye(r,s.slice(1),{encoding:"utf8",env:process.env});if(u.status===0)return u.stdout.trim();let f=u.stderr.trim();console.warn(`[env] Command substitution failed (exit code ${u.status??"unknown"}): ${r}${f?`: ${f}`:""}`)}catch(s){console.warn(`[env] Command substitution error: ${s instanceof Error?s.message:String(s)}`)}return""})}async function Ae(t,n={}){let e={},s=[],r=[];for(let i of t)try{let u=Bun.file(i);if(!u.size)continue;let f=await u.text(),{parsed:c,errors:a,skippedEncrypted:o}=x(f,n);s.push(...a),r.push(...o);for(let[l,p]of Object.entries(c)){if(!n.overload&&e[l]!==void 0)continue;e[l]=p}}catch(u){if(u?.code==="ENOENT")continue;s.push(`Failed to read ${i}: ${u instanceof Error?u.message:String(u)}`)}return{parsed:e,errors:s,skippedEncrypted:r}}import{existsSync as w,readFileSync as K,renameSync as U,rmSync as te,writeFileSync as b}from"fs";import{basename as L,dirname as ne,resolve as k}from"path";function Je(t,n){if(t)return t;let e=String(n??"").trim();if(!e||e==="development"||e==="dev"||e==="local")return"";if(!/^[\w.-]+$/.test(e))return"";return`.env.${e}`}function re(t,n,e,s){let r=["#/-------------------[DOTENV_PUBLIC_KEY]--------------------/","#/ versioned X25519 encryption for .env files /","#/ [how it works](https://stacksjs.com/encryption) /","#/----------------------------------------------------------/",`${e}="${n}"`,""],i=!1;for(let u of t.split(`
5
- `)){let f=u.trim();if(f.startsWith("#/"))continue;if(f.startsWith("DOTENV_PUBLIC_KEY"))continue;if(!f&&!i)continue;if(!f||f.startsWith("#")){i=!0,r.push(u);continue}i=!0;let c=f.match(/^([^=]+)=(.*)$/);if(!c||c[1]===void 0||c[2]===void 0){r.push(u);continue}let a=c[1].trim(),o=c[2].trim();if(o.startsWith('"')&&o.endsWith('"')||o.startsWith("'")&&o.endsWith("'"))o=o.slice(1,-1);if((!s.key||a.includes(s.key))&&(!s.excludeKey||!a.includes(s.excludeKey))&&!o.startsWith("encrypted:"))o=F(o,n);r.push(`${a}="${o}"`)}return r.join(`
6
- `)}function C(t){let e=((t||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase();return{publicKeyName:e?`DOTENV_PUBLIC_KEY_${e}`:"DOTENV_PUBLIC_KEY",privateKeyName:e?`DOTENV_PRIVATE_KEY_${e}`:"DOTENV_PRIVATE_KEY"}}function se(t,n){let e,s=!1;for(let r of t.split(`
7
- `)){let i=r.trim();if(!i||i.startsWith("#"))continue;let u=i.match(/^([^=]+)=(.*)$/);if(!u?.[1]||u[2]===void 0)continue;let f=u[1].trim(),c=u[2].trim().replace(/^["']|["']$/g,"");if(f===n)e=c;else if(c.startsWith("encrypted:")||c.startsWith("enc:"))s=!0}return e&&s?e:void 0}function Me(t={}){let n=t.cwd||process.cwd(),e=k(n,t.file||".env"),s=k(n,t.keysFile||".env.keys");if(!w(e))return{success:!1,error:`File not found: ${e}`};try{let r,i,u=se(K(e,"utf-8"),C(t.file).publicKeyName);if(u)r=u;else if(w(s)){let a=K(s,"utf-8"),{parsed:o}=x(a),{publicKeyName:l,privateKeyName:p}=C(t.file);if(r=o[l]||"",i=o[p]||"",!r||!i){let g=T();r=g.publicKey,i=g.privateKey;let y=`
2
+ import{createCipheriv as U,createDecipheriv as L,createHash as W,createPrivateKey as ae,createPublicKey as j,diffieHellman as X,generateKeyPairSync as H,hkdfSync as J,randomBytes as B}from"crypto";var I="x25519-public:",S="x25519-private:",D="encrypted:v2:",Y="encrypted:",M=Buffer.from("stacks-env:v2","utf8"),z="Environment decryption failed: authentication or format error";function G(t){if(t.length===32)return t;return W("sha256").update(t).digest()}function Fe(t,n){let e=B(16),s=U("aes-256-gcm",G(n),e);return{ciphertext:Buffer.concat([s.update(t,"utf8"),s.final()]).toString("hex"),iv:e.toString("hex"),authTag:s.getAuthTag().toString("hex")}}function ue(t,n,e,s){try{let r=L("aes-256-gcm",G(n),Buffer.from(e,"hex"));return r.setAuthTag(Buffer.from(s,"hex")),Buffer.concat([r.update(Buffer.from(t,"hex")),r.final()]).toString("utf8")}catch{throw Error("Decryption failed: authentication or format error")}}function _(t){return t.toString("base64url")}function x(t,n,e){if(typeof t!=="string"||t.length>0&&!/^[A-Za-z0-9_-]+$/.test(t))throw Error(`${n} is not canonical base64url`);let s=Buffer.from(t,"base64url");if(_(s)!==t)throw Error(`${n} is not canonical base64url`);if(e!==void 0&&s.length!==e)throw Error(`${n} has an invalid length`);return s}function fe(t){if(!t.startsWith(I))throw Error("Legacy or invalid public key. Run buddy env:rotate before creating new ciphertext.");let n=j({key:x(t.slice(I.length),"public key"),format:"der",type:"spki"});if(n.asymmetricKeyType!=="x25519")throw Error("Public key is not X25519");return n}function le(t){if(!t.startsWith(S))throw Error("Private key is not a version 2 X25519 key");let n=ae({key:x(t.slice(S.length),"private key"),format:"der",type:"pkcs8"});if(n.asymmetricKeyType!=="x25519")throw Error("Private key is not X25519");return n}function q(t){return Buffer.from(`stacks-env:v2;${t.epk};${t.salt};${t.nonce}`,"utf8")}function V(){let{publicKey:t,privateKey:n}=H("x25519"),e=t.export({format:"der",type:"spki"}),s=n.export({format:"der",type:"pkcs8"});return{publicKey:`${I}${_(e)}`,privateKey:`${S}${_(s)}`}}function F(t,n){let e=fe(n),s=H("x25519"),r=_(s.publicKey.export({format:"der",type:"spki"})),i=B(16),c=B(12),a=X({privateKey:s.privateKey,publicKey:e}),u=Buffer.from(J("sha256",a,i,M,32)),f={epk:r,salt:_(i),nonce:_(c)};try{let o=U("aes-256-gcm",u,c);o.setAAD(q(f));let l=Buffer.concat([o.update(t,"utf8"),o.final()]),d={v:2,...f,ciphertext:_(l),tag:_(o.getAuthTag())};return`${D}${_(Buffer.from(JSON.stringify(d),"utf8"))}`}finally{a.fill(0),u.fill(0)}}function pe(t){let n=x(t.slice(D.length),"envelope").toString("utf8"),e=JSON.parse(n);if(!e||typeof e!=="object"||Array.isArray(e))throw Error("envelope is not an object");let s=Object.keys(e).sort(),r=["ciphertext","epk","nonce","salt","tag","v"];if(s.length!==r.length||s.some((i,c)=>i!==r[c]))throw Error("envelope fields are invalid");if(e.v!==2)throw Error("envelope version is unsupported");for(let i of["epk","salt","nonce","ciphertext","tag"])if(typeof e[i]!=="string")throw Error(`${i} is invalid`);return e}function de(t,n){try{let e=pe(t),s=le(n),r=j({key:x(e.epk,"ephemeral public key"),format:"der",type:"spki"});if(r.asymmetricKeyType!=="x25519")throw Error("ephemeral key is not X25519");let i=x(e.salt,"salt",16),c=x(e.nonce,"nonce",12),a=x(e.ciphertext,"ciphertext"),u=x(e.tag,"tag",16),f=X({privateKey:s,publicKey:r}),o=Buffer.from(J("sha256",f,i,M,32));try{let l=L("aes-256-gcm",o,c);return l.setAAD(q(e)),l.setAuthTag(u),Buffer.concat([l.update(a),l.final()]).toString("utf8")}finally{f.fill(0),o.fill(0)}}catch{throw Error(z)}}function ye(t,n){try{if(!/^[0-9a-f]{64}$/.test(n))throw Error("invalid legacy private key");let e=Buffer.from(t.slice(Y.length),"base64");if(e.length<32)throw Error("invalid legacy payload");let s=W("sha256").update(Buffer.from(n,"hex")).digest(),r=W("sha256").update(s).digest();return ue(e.subarray(32).toString("hex"),r,e.subarray(0,16).toString("hex"),e.subarray(16,32).toString("hex"))}catch{throw Error(z)}}function Re(t){return t.startsWith(Y)&&!t.startsWith(D)}function O(t,n){if(!t.startsWith(Y))return t;return t.startsWith(D)?de(t,n):ye(t,n)}function Ce(t,n,e){return F(O(t,n),e)}function We(t){let n=t.match(/^DOTENV_PRIVATE_KEY_(.+)$/);return n&&n[1]?n[1].toLowerCase():""}function Be(t=""){return t?process.env[`DOTENV_PRIVATE_KEY_${t.toUpperCase()}`]:process.env.DOTENV_PRIVATE_KEY}import{spawnSync as ge}from"child_process";import{readFileSync as he}from"fs";import{arch as me,hostname as ve,platform as Z,release as Ee,userInfo as $e}from"os";import{basename as ke,dirname as Ke,resolve as we}from"path";function P(t,n={}){let e={},s=[],r=[],i=t.split(`
3
+ `);for(let c of i){let a=c.trim();if(!a||a.startsWith("#"))continue;if(a.startsWith("DOTENV_PUBLIC_KEY=")){let l=a.match(/^DOTENV_PUBLIC_KEY=["']?([^"'\n]+)["']?/);if(l&&l[1]!==void 0)e.DOTENV_PUBLIC_KEY=l[1];continue}let u=a.match(/^([^=]+)=(.*)$/);if(!u||u[1]===void 0||u[2]===void 0)continue;let f=u[1].trim(),o=u[2].trim();if(o.startsWith('"')&&o.endsWith('"')||o.startsWith("'")&&o.endsWith("'")){if(o=o.slice(1,-1),o.includes("\\n"))o=o.replace(/\\n/g,`
4
+ `)}if(o.startsWith("encrypted:")||o.startsWith("enc:")){if(!n.privateKey){r.push(f);continue}try{let l=o.startsWith("enc:")?`encrypted:${o.slice(4)}`:o;o=O(l,n.privateKey)}catch(l){s.push(`Failed to decrypt ${f}: ${l instanceof Error?l.message:"Unknown error"}`)}}o=be(o,{...n.processEnv||process.env,...e}),o=Pe(o),e[f]=o}return{parsed:e,errors:s,skippedEncrypted:r}}function be(t,n){return t.replace(/\$\{([^}]+)\}/g,(e,s)=>{let r=s.match(/^([^:\-+]+)(:-|-)(.+)$/);if(r){let[,c,a,u]=r,f=n[c];if(a===":-")return f||u;else return f!==void 0?f:u}let i=s.match(/^([^:\-+]+)(:?\+)(.+)$/);if(i){let[,c,a,u]=i,f=n[c];if(a===":+")return f?u:"";else return f!==void 0?u:""}return n[s]||""})}var Ne=new Set(["date","hostname","whoami","uname","pwd","echo","printf","cat","basename","dirname"]);function _e(t){let n=[],e="",s=null,r=!1;for(let i of t.trim()){if(r){e+=i,r=!1;continue}if(i==="\\"&&s!=="'"){r=!0;continue}if(s){if(i===s)s=null;else e+=i;continue}if(i==='"'||i==="'"){s=i;continue}if(/\s/.test(i)){if(e)n.push(e),e="";continue}e+=i}if(r||s)throw Error("Command substitution contains an unterminated escape or quote");if(e)n.push(e);return n}function xe(t){let[n,...e]=t,s=e.some((r)=>r.startsWith("-"));if(n==="echo"&&!s)return e.join(" ");if(n==="printf"&&e.length===1&&!e[0]?.includes("%"))return e[0];if(n==="hostname"&&e.length===0)return ve();if(n==="whoami"&&e.length===0)return $e().username;if(n==="pwd"&&e.length===0)return process.cwd();if(n==="basename"&&e.length===1)return ke(e[0]??"");if(n==="dirname"&&e.length===1)return Ke(e[0]??"");if(n==="cat"&&e.length>0&&!s)return e.map((r)=>he(we(process.cwd(),r),"utf8")).join("");if(n==="uname"){let r=e[0]??"-s";if(e.length<=1&&r==="-m")return me();if(e.length<=1&&r==="-r")return Ee();if(e.length<=1&&r==="-s")return{aix:"AIX",android:"Android",cygwin:"CYGWIN",darwin:"Darwin",freebsd:"FreeBSD",haiku:"Haiku",linux:"Linux",netbsd:"NetBSD",openbsd:"OpenBSD",sunos:"SunOS",win32:"Windows_NT"}[Z()]??Z()}return}function Pe(t){return t.replace(/\$\(([^)]+)\)/g,(n,e)=>{try{let s=_e(e),r=s[0];if(!r||!Ne.has(r))return console.warn(`[env] Blocked command substitution for disallowed command: ${r}`),"";let i=xe(s);if(i!==void 0)return i.trim();let c=ge(r,s.slice(1),{encoding:"utf8",env:process.env});if(c.status===0)return c.stdout.trim();let a=c.stderr.trim();console.warn(`[env] Command substitution failed (exit code ${c.status??"unknown"}): ${r}${a?`: ${a}`:""}`)}catch(s){console.warn(`[env] Command substitution error: ${s instanceof Error?s.message:String(s)}`)}return""})}async function je(t,n={}){let e={},s=[],r=[];for(let i of t)try{let c=Bun.file(i);if(!c.size)continue;let a=await c.text(),{parsed:u,errors:f,skippedEncrypted:o}=P(a,n);s.push(...f),r.push(...o);for(let[l,d]of Object.entries(u)){if(!n.overload&&e[l]!==void 0)continue;e[l]=d}}catch(c){if(c?.code==="ENOENT")continue;s.push(`Failed to read ${i}: ${c instanceof Error?c.message:String(c)}`)}return{parsed:e,errors:s,skippedEncrypted:r}}import{existsSync as w,readFileSync as K,renameSync as Q,rmSync as ee,writeFileSync as b}from"fs";import{basename as te,dirname as ne,resolve as k}from"path";function Ge(t,n){if(t)return t;let e=String(n??"").trim();if(!e||e==="development"||e==="dev"||e==="local")return"";if(!/^[\w.-]+$/.test(e))return"";return`.env.${e}`}function re(t,n){return(!n.key||t.includes(n.key))&&(!n.excludeKey||!t.includes(n.excludeKey))}function Ve(t,n){let e=[];for(let s of t.split(`
5
+ `)){let r=s.trim();if(!r||r.startsWith("#"))continue;if(r.startsWith("DOTENV_PUBLIC_KEY"))continue;let i=r.match(/^([^=]+)=(.*)$/);if(!i||i[1]===void 0)continue;let c=i[1].trim();if(re(c,n))e.push(c)}return e}function se(t,n,e,s){let r=["#/-------------------[DOTENV_PUBLIC_KEY]--------------------/","#/ versioned X25519 encryption for .env files /","#/ [how it works](https://stacksjs.com/encryption) /","#/----------------------------------------------------------/",`${e}="${n}"`,""],i=!1;for(let c of t.split(`
6
+ `)){let a=c.trim();if(a.startsWith("#/"))continue;if(a.startsWith("DOTENV_PUBLIC_KEY"))continue;if(!a&&!i)continue;if(!a||a.startsWith("#")){i=!0,r.push(c);continue}i=!0;let u=a.match(/^([^=]+)=(.*)$/);if(!u||u[1]===void 0||u[2]===void 0){r.push(c);continue}let f=u[1].trim(),o=u[2].trim();if(o.startsWith('"')&&o.endsWith('"')||o.startsWith("'")&&o.endsWith("'"))o=o.slice(1,-1);if(re(f,s)&&!o.startsWith("encrypted:"))o=F(o,n);r.push(`${f}="${o}"`)}return r.join(`
7
+ `)}function T(t){let e=((t||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase();return{publicKeyName:e?`DOTENV_PUBLIC_KEY_${e}`:"DOTENV_PUBLIC_KEY",privateKeyName:e?`DOTENV_PRIVATE_KEY_${e}`:"DOTENV_PRIVATE_KEY"}}function ie(t,n){let e,s=!1;for(let r of t.split(`
8
+ `)){let i=r.trim();if(!i||i.startsWith("#"))continue;let c=i.match(/^([^=]+)=(.*)$/);if(!c?.[1]||c[2]===void 0)continue;let a=c[1].trim(),u=c[2].trim().replace(/^["']|["']$/g,"");if(a===n)e=u;else if(u.startsWith("encrypted:")||u.startsWith("enc:"))s=!0}return e&&s?e:void 0}function qe(t={}){let n=t.cwd||process.cwd(),e=k(n,t.file||".env"),s=k(n,t.keysFile||".env.keys");if(!w(e))return{success:!1,error:`File not found: ${e}`};try{let r,i,c=ie(K(e,"utf-8"),T(t.file).publicKeyName);if(c)r=c;else if(w(s)){let f=K(s,"utf-8"),{parsed:o}=P(f),{publicKeyName:l,privateKeyName:d}=T(t.file);if(r=o[l]||"",i=o[d]||"",!r||!i){let g=V();r=g.publicKey,i=g.privateKey;let y=`
8
9
  ${l}="${r}"
9
- ${p}="${i}"
10
- `;b(s,a+y,"utf-8")}}else{let a=T();r=a.publicKey,i=a.privateKey;let{publicKeyName:o,privateKeyName:l}=C(t.file),p=`# .env.keys - Keep this file secure and never commit to source control
10
+ ${d}="${i}"
11
+ `;b(s,f+y,"utf-8")}}else{let f=V();r=f.publicKey,i=f.privateKey;let{publicKeyName:o,privateKeyName:l}=T(t.file),d=`# .env.keys - Keep this file secure and never commit to source control
11
12
  ${o}="${r}"
12
13
  ${l}="${i}"
13
- `;b(s,p,"utf-8")}let f=K(e,"utf-8"),c=re(f,r,C(t.file).publicKeyName,t);if(t.stdout)return{success:!0,output:c};return b(e,c,"utf-8"),{success:!0,output:`\u2714 encrypted (${t.file||".env"})
14
- \u2714 key added to ${t.keysFile||".env.keys"}`}}catch(r){return{success:!1,error:`Failed to encrypt: ${r instanceof Error?r.message:"Unknown error"}`}}}function xe(t={}){let n=t.cwd||process.cwd(),e=k(n,t.file||".env"),s=k(n,t.keysFile||".env.keys");if(!w(e))return{success:!1,error:`File not found: ${e}`};if(!w(s))return{success:!1,error:`Keys file not found: ${s}`};try{let r=K(s,"utf-8"),{parsed:i}=x(r),c=((t.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),a=c?`DOTENV_PRIVATE_KEY_${c}`:"DOTENV_PRIVATE_KEY",o=i[a];if(!o)return{success:!1,error:`Private key not found: ${a}`};let l=[o];if(a!=="DOTENV_PRIVATE_KEY"&&i.DOTENV_PRIVATE_KEY&&i.DOTENV_PRIVATE_KEY!==o)l.push(i.DOTENV_PRIVATE_KEY);let g=K(e,"utf-8").split(`
15
- `),y=[];for(let N of g){let h=N.trim();if(!h||h.startsWith("#")){y.push(N);continue}if(h.startsWith("DOTENV_PUBLIC_KEY"))continue;let m=h.match(/^([^=]+)=(.*)$/);if(!m||m[1]===void 0||m[2]===void 0){y.push(N);continue}let v=m[1].trim(),d=m[2].trim();if(d.startsWith('"')&&d.endsWith('"')||d.startsWith("'")&&d.endsWith("'"))d=d.slice(1,-1);let V=d.startsWith("encrypted:");if(t.key&&!v.includes(t.key))V=!1;if(V){let B,I;for(let ie of l)try{B=O(d,ie);break}catch(oe){I=oe}if(B===void 0)throw I instanceof Error?I:Error(`Unable to decrypt ${v}`);d=B}y.push(`${v}="${d}"`)}let E=y.join(`
16
- `);if(t.stdout)return{success:!0,output:E};return b(e,E,"utf-8"),{success:!0,output:`\u2714 decrypted (${t.file||".env"})`}}catch(r){return{success:!1,error:`Failed to decrypt: ${r instanceof Error?r.message:"Unknown error"}`}}}function ze(t,n,e={}){let s=e.cwd||process.cwd(),r=k(s,e.file||".env"),i=k(s,e.keysFile||".env.keys");try{let u="";if(w(r))u=K(r,"utf-8");let f=u.split(`
17
- `),c=!1,a,p=((e.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),g=p?`DOTENV_PUBLIC_KEY_${p}`:"DOTENV_PUBLIC_KEY",y=p?`DOTENV_PRIVATE_KEY_${p}`:"DOTENV_PRIVATE_KEY";for(let h of f){let m=h.trim();if(m.startsWith(`${g}=`)){let v=m.match(/^[^=]+=["']?([^"'\n]+)["']?/);if(v)a=v[1];break}}if(a){let h=!1;if(w(i)){let m=K(i,"utf-8"),{parsed:v}=x(m);h=Boolean(v[y])}if(!h&&!process.env[y]&&!se(u,g))a=void 0}if(!e.plain&&!a){let h=T();a=h.publicKey;let m="";if(w(i))m=K(i,"utf-8");m+=`
18
- ${g}="${h.publicKey}"
19
- ${y}="${h.privateKey}"
20
- `,b(i,m,"utf-8");for(let v=f.length-1;v>=0;v--){let d=f[v];if(d!==void 0&&d.trim().startsWith(`${g}=`))f.splice(v,1)}f.unshift(`${g}="${a}"`)}let E=n;if(!e.plain&&a)E=F(n,a);for(let h=0;h<f.length;h++){let m=f[h];if(m===void 0)continue;if(m.trim().startsWith(`${t}=`)){f[h]=`${t}="${E}"`,c=!0;break}}if(!c)f.push(`${t}="${E}"`);let N=f.join(`
21
- `);return b(r,N,"utf-8"),{success:!0,output:`set ${t}${e.plain?"":" with encryption"} (${e.file||".env"})`}}catch(u){return{success:!1,error:`Failed to set: ${u instanceof Error?u.message:"Unknown error"}`}}}function Ge(t,n={}){let e=n.cwd||process.cwd(),s=k(e,n.file||".env");if(!w(s))return{success:!1,error:`File not found: ${s}`};try{let r,i=k(e,n.keysFile||".env.keys"),c=((n.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),a=c?`DOTENV_PRIVATE_KEY_${c}`:"DOTENV_PRIVATE_KEY";if(w(i)){let y=K(i,"utf-8"),{parsed:E}=x(y);r=E[a]}if(!r)r=process.env[a];let o=K(s,"utf-8"),{parsed:l}=x(o,{privateKey:r});if(t&&!n.all){let y=l[t];if(y===void 0)return{success:!1,error:`Key not found: ${t}`};return{success:!0,output:y}}let p=n.all?{...process.env,...l}:l,g;switch(n.format){case"shell":g=Object.entries(p).map(([y,E])=>`${y}=${E}`).join(" ");break;case"eval":g=Object.entries(p).map(([y,E])=>`${y}="${E}"`).join(`
22
- `);break;case"json":default:g=n.prettyPrint?JSON.stringify(p,null,2):JSON.stringify(p);break}return{success:!0,output:g}}catch(r){return{success:!1,error:`Failed to get: ${r instanceof Error?r.message:"Unknown error"}`}}}function qe(t,n={}){let e=n.cwd||process.cwd(),s=k(e,n.keysFile||".env.keys");if(!w(s))return{success:!1,error:`Keys file not found: ${s}`};try{let r=K(s,"utf-8"),{parsed:i}=x(r),c=((n.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),a=c?`DOTENV_PUBLIC_KEY_${c}`:"DOTENV_PUBLIC_KEY",o=c?`DOTENV_PRIVATE_KEY_${c}`:"DOTENV_PRIVATE_KEY";if(t){let g=i[t];if(!g)return{success:!1,error:`Key not found: ${t}`};return{success:!0,output:g}}let l={[a]:i[a],[o]:i[o]};return{success:!0,output:n.format==="shell"?`${a}=${l[a]} ${o}=${l[o]}`:JSON.stringify(l)}}catch(r){return{success:!1,error:`Failed to get keypair: ${r instanceof Error?r.message:"Unknown error"}`}}}function Ze(t={}){let n=xe({file:t.file,keysFile:t.keysFile,stdout:!0,cwd:t.cwd});if(!n.success)return n;let e=t.cwd||process.cwd(),s=k(e,t.file||".env"),r=k(e,t.keysFile||".env.keys");if(n.output===void 0)return{success:!1,error:"Rotation produced no decrypted content"};let i=T(),u=K(s),f=K(r),c=f.toString("utf8").split(`
23
- `),a=t.file||".env",o=L(a).replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),l=o?`DOTENV_PUBLIC_KEY_${o}`:"DOTENV_PUBLIC_KEY",p=o?`DOTENV_PRIVATE_KEY_${o}`:"DOTENV_PRIVATE_KEY",g=!1,y=!1;for(let d=0;d<c.length;d++){let V=c[d];if(V===void 0)continue;if(V.startsWith(`${l}=`))c[d]=`${l}="${i.publicKey}"`,g=!0;else if(V.startsWith(`${p}=`))c[d]=`${p}="${i.privateKey}"`,y=!0}if(!g)c.push(`${l}="${i.publicKey}"`);if(!y)c.push(`${p}="${i.privateKey}"`);let E=`${c.join(`
14
+ `;b(s,d,"utf-8")}let a=K(e,"utf-8"),u=se(a,r,T(t.file).publicKeyName,t);if(t.stdout)return{success:!0,output:u};return b(e,u,"utf-8"),{success:!0,output:`\u2714 encrypted (${t.file||".env"})
15
+ \u2714 key added to ${t.keysFile||".env.keys"}`}}catch(r){return{success:!1,error:`Failed to encrypt: ${r instanceof Error?r.message:"Unknown error"}`}}}function Oe(t={}){let n=t.cwd||process.cwd(),e=k(n,t.file||".env"),s=k(n,t.keysFile||".env.keys");if(!w(e))return{success:!1,error:`File not found: ${e}`};if(!w(s))return{success:!1,error:`Keys file not found: ${s}`};try{let r=K(s,"utf-8"),{parsed:i}=P(r),u=((t.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),f=u?`DOTENV_PRIVATE_KEY_${u}`:"DOTENV_PRIVATE_KEY",o=i[f];if(!o)return{success:!1,error:`Private key not found: ${f}`};let l=[o];if(f!=="DOTENV_PRIVATE_KEY"&&i.DOTENV_PRIVATE_KEY&&i.DOTENV_PRIVATE_KEY!==o)l.push(i.DOTENV_PRIVATE_KEY);let g=K(e,"utf-8").split(`
16
+ `),y=[];for(let N of g){let p=N.trim();if(!p||p.startsWith("#")){y.push(N);continue}if(p.startsWith("DOTENV_PUBLIC_KEY"))continue;let h=p.match(/^([^=]+)=(.*)$/);if(!h||h[1]===void 0||h[2]===void 0){y.push(N);continue}let v=h[1].trim(),E=h[2].trim();if(E.startsWith('"')&&E.endsWith('"')||E.startsWith("'")&&E.endsWith("'"))E=E.slice(1,-1);let A=E.startsWith("encrypted:");if(t.key&&!v.includes(t.key))A=!1;if(A){let R,C;for(let oe of l)try{R=O(E,oe);break}catch(ce){C=ce}if(R===void 0)throw C instanceof Error?C:Error(`Unable to decrypt ${v}`);E=R}y.push(`${v}="${E}"`)}let m=y.join(`
17
+ `);if(t.stdout)return{success:!0,output:m};return b(e,m,"utf-8"),{success:!0,output:`\u2714 decrypted (${t.file||".env"})`}}catch(r){return{success:!1,error:`Failed to decrypt: ${r instanceof Error?r.message:"Unknown error"}`}}}function Ze(t,n,e={}){let s=e.cwd||process.cwd(),r=k(s,e.file||".env"),i=k(s,e.keysFile||".env.keys");try{let c="";if(w(r))c=K(r,"utf-8");let a=c.split(`
18
+ `),u=!1,f,d=((e.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),g=d?`DOTENV_PUBLIC_KEY_${d}`:"DOTENV_PUBLIC_KEY",y=d?`DOTENV_PRIVATE_KEY_${d}`:"DOTENV_PRIVATE_KEY";for(let p of a){let h=p.trim();if(h.startsWith(`${g}=`)){let v=h.match(/^[^=]+=["']?([^"'\n]+)["']?/);if(v)f=v[1];break}}if(f){let p=!1;if(w(i)){let h=K(i,"utf-8"),{parsed:v}=P(h);p=Boolean(v[y])}if(!p&&!process.env[y]&&!ie(c,g))f=void 0}if(!e.plain&&!f){let p=V();f=p.publicKey;let h="";if(w(i))h=K(i,"utf-8");h+=`
19
+ ${g}="${p.publicKey}"
20
+ ${y}="${p.privateKey}"
21
+ `,b(i,h,"utf-8");for(let v=a.length-1;v>=0;v--){let E=a[v];if(E!==void 0&&E.trim().startsWith(`${g}=`))a.splice(v,1)}a.unshift(`${g}="${f}"`)}let m=n;if(!e.plain&&f)m=F(n,f);for(let p=0;p<a.length;p++){let h=a[p];if(h===void 0)continue;if(h.trim().startsWith(`${t}=`)){a[p]=`${t}="${m}"`,u=!0;break}}if(!u)a.push(`${t}="${m}"`);let N=a.join(`
22
+ `);return b(r,N,"utf-8"),{success:!0,output:`set ${t}${e.plain?"":" with encryption"} (${e.file||".env"})`}}catch(c){return{success:!1,error:`Failed to set: ${c instanceof Error?c.message:"Unknown error"}`}}}function Qe(t,n={}){let e=n.cwd||process.cwd(),s=k(e,n.file||".env");if(!w(s))return{success:!1,error:`File not found: ${s}`};try{let r,i=k(e,n.keysFile||".env.keys"),u=((n.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),f=u?`DOTENV_PRIVATE_KEY_${u}`:"DOTENV_PRIVATE_KEY";if(w(i)){let y=K(i,"utf-8"),{parsed:m}=P(y);r=m[f]}if(!r)r=process.env[f];let o=K(s,"utf-8"),{parsed:l}=P(o,{privateKey:r});if(t&&!n.all){let y=l[t];if(y===void 0)return{success:!1,error:`Key not found: ${t}`};return{success:!0,output:y}}let d=n.all?{...process.env,...l}:l,g;switch(n.format){case"shell":g=Object.entries(d).map(([y,m])=>`${y}=${m}`).join(" ");break;case"eval":g=Object.entries(d).map(([y,m])=>`${y}="${m}"`).join(`
23
+ `);break;case"json":default:g=n.prettyPrint?JSON.stringify(d,null,2):JSON.stringify(d);break}return{success:!0,output:g}}catch(r){return{success:!1,error:`Failed to get: ${r instanceof Error?r.message:"Unknown error"}`}}}function et(t,n={}){let e=n.cwd||process.cwd(),s=k(e,n.keysFile||".env.keys");if(!w(s))return{success:!1,error:`Keys file not found: ${s}`};try{let r=K(s,"utf-8"),{parsed:i}=P(r),u=((n.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),f=u?`DOTENV_PUBLIC_KEY_${u}`:"DOTENV_PUBLIC_KEY",o=u?`DOTENV_PRIVATE_KEY_${u}`:"DOTENV_PRIVATE_KEY";if(t){let g=i[t];if(!g)return{success:!1,error:`Key not found: ${t}`};return{success:!0,output:g}}let l={[f]:i[f],[o]:i[o]};return{success:!0,output:n.format==="shell"?`${f}=${l[f]} ${o}=${l[o]}`:JSON.stringify(l)}}catch(r){return{success:!1,error:`Failed to get keypair: ${r instanceof Error?r.message:"Unknown error"}`}}}function Te(t,n,e,s){let r=t.split(`
24
+ `),i=!1,c=!1;for(let a=0;a<r.length;a++){let u=r[a];if(u===void 0)continue;if(u.startsWith(`${e}=`))r[a]=`${e}="${n.publicKey}"`,i=!0;else if(u.startsWith(`${s}=`))r[a]=`${s}="${n.privateKey}"`,c=!0}if(!i)r.push(`${e}="${n.publicKey}"`);if(!c)r.push(`${s}="${n.privateKey}"`);return`${r.join(`
24
25
  `).replace(/\n+$/,"")}
25
- `,N=re(n.output,i.publicKey,l,t),h=`${process.pid}-${Date.now()}`,m=k(ne(s),`.${L(s)}.${h}.rotate`),v=k(ne(r),`.${L(r)}.${h}.rotate`);try{if(b(m,N,{encoding:"utf8",mode:384}),b(v,E,{encoding:"utf8",mode:384}),t.stdout)return U(v,r),{success:!0,output:N};U(m,s);try{U(v,r)}catch(d){throw b(s,u),d}return{success:!0,output:`\u2714 rotated (${t.file||".env"}) to encrypted:v2`}}catch(d){if(!w(r))b(r,f,{mode:384});return{success:!1,error:`Failed to rotate without exposing plaintext: ${d instanceof Error?d.message:"Unknown error"}`}}finally{te(m,{force:!0}),te(v,{force:!0})}}export{xe as decryptEnv,Me as encryptEnv,C as envKeyNames,Ge as getEnv,qe as getKeypair,Je as resolveEnvFile,se as reusableEnvPublicKey,Ze as rotateKeypair,ze as setEnv};
26
+ `}function tt(t={}){let n=Oe({file:t.file,keysFile:t.keysFile,stdout:!0,cwd:t.cwd});if(!n.success)return n;if(n.output===void 0)return{success:!1,error:"Rotation produced no decrypted content"};let e=t.cwd||process.cwd(),s=t.file||".env",r=t.keysFile||".env.keys",i=k(e,s),c=k(e,r),{publicKeyName:a,privateKeyName:u}=T(t.file);if(t.dryRun){let p=Ve(n.output,t);return{success:!0,output:["Dry run: nothing was written.",`${s}: ${p.length} value${p.length===1?"":"s"} would be re-encrypted under a new ${a}.`,`${r}: ${a} and ${u} would be replaced.`,`The current ${u} decrypts nothing once that happens, so keep a copy until the rotation is verified.`].join(`
27
+ `)}}let f=V(),o=se(n.output,f.publicKey,a,t);if(t.stdout)return{success:!0,output:o,notice:[`Nothing was written. To adopt the output above, put this keypair in ${r}:`,"",`${a}="${f.publicKey}"`,`${u}="${f.privateKey}"`,"","Without it that output cannot be decrypted. Run without --stdout to have both applied together."].join(`
28
+ `)};let l=K(i),d=K(c),g=Te(d.toString("utf8"),f,a,u),y=`${process.pid}-${Date.now()}`,m=k(ne(i),`.${te(i)}.${y}.rotate`),N=k(ne(c),`.${te(c)}.${y}.rotate`);try{b(m,o,{encoding:"utf8",mode:384}),b(N,g,{encoding:"utf8",mode:384}),Q(m,i);try{Q(N,c)}catch(p){throw b(i,l),p}return{success:!0,output:`\u2714 rotated (${s}) to encrypted:v2`}}catch(p){if(!w(c))b(c,d,{mode:384});return{success:!1,error:`Failed to rotate without exposing plaintext: ${p instanceof Error?p.message:"Unknown error"}`}}finally{ee(m,{force:!0}),ee(N,{force:!0})}}export{Oe as decryptEnv,qe as encryptEnv,T as envKeyNames,Qe as getEnv,et as getKeypair,Ge as resolveEnvFile,ie as reusableEnvPublicKey,tt as rotateKeypair,Ze as setEnv};
package/dist/index.js CHANGED
@@ -1,28 +1,31 @@
1
1
  // @bun
2
- import{createCipheriv as re,createDecipheriv as ie,createHash as G,createPrivateKey as Ce,createPublicKey as se,diffieHellman as oe,generateKeyPairSync as de,hkdfSync as ae,randomBytes as H}from"crypto";var X="x25519-public:",j="x25519-private:",x="encrypted:v2:",Q="encrypted:",ce=Buffer.from("stacks-env:v2","utf8"),ue="Environment decryption failed: authentication or format error";function fe(e){if(e.length===32)return e;return G("sha256").update(e).digest()}function Rn(e,n){let t=H(16),r=re("aes-256-gcm",fe(n),t);return{ciphertext:Buffer.concat([r.update(e,"utf8"),r.final()]).toString("hex"),iv:t.toString("hex"),authTag:r.getAuthTag().toString("hex")}}function be(e,n,t,r){try{let i=ie("aes-256-gcm",fe(n),Buffer.from(t,"hex"));return i.setAuthTag(Buffer.from(r,"hex")),Buffer.concat([i.update(Buffer.from(e,"hex")),i.final()]).toString("utf8")}catch{throw Error("Decryption failed: authentication or format error")}}function P(e){return e.toString("base64url")}function b(e,n,t){if(typeof e!=="string"||e.length>0&&!/^[A-Za-z0-9_-]+$/.test(e))throw Error(`${n} is not canonical base64url`);let r=Buffer.from(e,"base64url");if(P(r)!==e)throw Error(`${n} is not canonical base64url`);if(t!==void 0&&r.length!==t)throw Error(`${n} has an invalid length`);return r}function Ke(e){if(!e.startsWith(X))throw Error("Legacy or invalid public key. Run buddy env:rotate before creating new ciphertext.");let n=se({key:b(e.slice(X.length),"public key"),format:"der",type:"spki"});if(n.asymmetricKeyType!=="x25519")throw Error("Public key is not X25519");return n}function Le(e){if(!e.startsWith(j))throw Error("Private key is not a version 2 X25519 key");let n=Ce({key:b(e.slice(j.length),"private key"),format:"der",type:"pkcs8"});if(n.asymmetricKeyType!=="x25519")throw Error("Private key is not X25519");return n}function Ee(e){return Buffer.from(`stacks-env:v2;${e.epk};${e.salt};${e.nonce}`,"utf8")}function k(){let{publicKey:e,privateKey:n}=de("x25519"),t=e.export({format:"der",type:"spki"}),r=n.export({format:"der",type:"pkcs8"});return{publicKey:`${X}${P(t)}`,privateKey:`${j}${P(r)}`}}function B(e,n){let t=Ke(n),r=de("x25519"),i=P(r.publicKey.export({format:"der",type:"spki"})),s=H(16),o=H(12),c=oe({privateKey:r.privateKey,publicKey:t}),a=Buffer.from(ae("sha256",c,s,ce,32)),u={epk:i,salt:P(s),nonce:P(o)};try{let d=re("aes-256-gcm",a,o);d.setAAD(Ee(u));let f=Buffer.concat([d.update(e,"utf8"),d.final()]),p={v:2,...u,ciphertext:P(f),tag:P(d.getAuthTag())};return`${x}${P(Buffer.from(JSON.stringify(p),"utf8"))}`}finally{c.fill(0),a.fill(0)}}function ke(e){let n=b(e.slice(x.length),"envelope").toString("utf8"),t=JSON.parse(n);if(!t||typeof t!=="object"||Array.isArray(t))throw Error("envelope is not an object");let r=Object.keys(t).sort(),i=["ciphertext","epk","nonce","salt","tag","v"];if(r.length!==i.length||r.some((s,o)=>s!==i[o]))throw Error("envelope fields are invalid");if(t.v!==2)throw Error("envelope version is unsupported");for(let s of["epk","salt","nonce","ciphertext","tag"])if(typeof t[s]!=="string")throw Error(`${s} is invalid`);return t}function Ue(e,n){try{let t=ke(e),r=Le(n),i=se({key:b(t.epk,"ephemeral public key"),format:"der",type:"spki"});if(i.asymmetricKeyType!=="x25519")throw Error("ephemeral key is not X25519");let s=b(t.salt,"salt",16),o=b(t.nonce,"nonce",12),c=b(t.ciphertext,"ciphertext"),a=b(t.tag,"tag",16),u=oe({privateKey:r,publicKey:i}),d=Buffer.from(ae("sha256",u,s,ce,32));try{let f=ie("aes-256-gcm",d,o);return f.setAAD(Ee(t)),f.setAuthTag(a),Buffer.concat([f.update(c),f.final()]).toString("utf8")}finally{u.fill(0),d.fill(0)}}catch{throw Error(ue)}}function xe(e,n){try{if(!/^[0-9a-f]{64}$/.test(n))throw Error("invalid legacy private key");let t=Buffer.from(e.slice(Q.length),"base64");if(t.length<32)throw Error("invalid legacy payload");let r=G("sha256").update(Buffer.from(n,"hex")).digest(),i=G("sha256").update(r).digest();return be(t.subarray(32).toString("hex"),i,t.subarray(0,16).toString("hex"),t.subarray(16,32).toString("hex"))}catch{throw Error(ue)}}function mn(e){return e.startsWith(Q)&&!e.startsWith(x)}function L(e,n){if(!e.startsWith(Q))return e;return e.startsWith(x)?Ue(e,n):xe(e,n)}function yn(e,n,t){return B(L(e,n),t)}function vn(e){let n=e.match(/^DOTENV_PRIVATE_KEY_(.+)$/);return n&&n[1]?n[1].toLowerCase():""}function q(e=""){return e?process.env[`DOTENV_PRIVATE_KEY_${e.toUpperCase()}`]:process.env.DOTENV_PRIVATE_KEY}import{spawnSync as Be}from"child_process";import{readFileSync as $e}from"fs";import{arch as we,hostname as Ve,platform as le,release as Me,userInfo as Fe}from"os";import{basename as Ye,dirname as We,resolve as Ge}from"path";function I(e,n={}){let t={},r=[],i=[],s=e.split(`
3
- `);for(let o of s){let c=o.trim();if(!c||c.startsWith("#"))continue;if(c.startsWith("DOTENV_PUBLIC_KEY=")){let f=c.match(/^DOTENV_PUBLIC_KEY=["']?([^"'\n]+)["']?/);if(f&&f[1]!==void 0)t.DOTENV_PUBLIC_KEY=f[1];continue}let a=c.match(/^([^=]+)=(.*)$/);if(!a||a[1]===void 0||a[2]===void 0)continue;let u=a[1].trim(),d=a[2].trim();if(d.startsWith('"')&&d.endsWith('"')||d.startsWith("'")&&d.endsWith("'")){if(d=d.slice(1,-1),d.includes("\\n"))d=d.replace(/\\n/g,`
4
- `)}if(d.startsWith("encrypted:")||d.startsWith("enc:")){if(!n.privateKey){i.push(u);continue}try{let f=d.startsWith("enc:")?`encrypted:${d.slice(4)}`:d;d=L(f,n.privateKey)}catch(f){r.push(`Failed to decrypt ${u}: ${f instanceof Error?f.message:"Unknown error"}`)}}d=He(d,{...n.processEnv||process.env,...t}),d=qe(d),t[u]=d}return{parsed:t,errors:r,skippedEncrypted:i}}function He(e,n){return e.replace(/\$\{([^}]+)\}/g,(t,r)=>{let i=r.match(/^([^:\-+]+)(:-|-)(.+)$/);if(i){let[,o,c,a]=i,u=n[o];if(c===":-")return u||a;else return u!==void 0?u:a}let s=r.match(/^([^:\-+]+)(:?\+)(.+)$/);if(s){let[,o,c,a]=s,u=n[o];if(c===":+")return u?a:"";else return u!==void 0?a:""}return n[r]||""})}var Xe=new Set(["date","hostname","whoami","uname","pwd","echo","printf","cat","basename","dirname"]);function je(e){let n=[],t="",r=null,i=!1;for(let s of e.trim()){if(i){t+=s,i=!1;continue}if(s==="\\"&&r!=="'"){i=!0;continue}if(r){if(s===r)r=null;else t+=s;continue}if(s==='"'||s==="'"){r=s;continue}if(/\s/.test(s)){if(t)n.push(t),t="";continue}t+=s}if(i||r)throw Error("Command substitution contains an unterminated escape or quote");if(t)n.push(t);return n}function Qe(e){let[n,...t]=e,r=t.some((i)=>i.startsWith("-"));if(n==="echo"&&!r)return t.join(" ");if(n==="printf"&&t.length===1&&!t[0]?.includes("%"))return t[0];if(n==="hostname"&&t.length===0)return Ve();if(n==="whoami"&&t.length===0)return Fe().username;if(n==="pwd"&&t.length===0)return process.cwd();if(n==="basename"&&t.length===1)return Ye(t[0]??"");if(n==="dirname"&&t.length===1)return We(t[0]??"");if(n==="cat"&&t.length>0&&!r)return t.map((i)=>$e(Ge(process.cwd(),i),"utf8")).join("");if(n==="uname"){let i=t[0]??"-s";if(t.length<=1&&i==="-m")return we();if(t.length<=1&&i==="-r")return Me();if(t.length<=1&&i==="-s")return{aix:"AIX",android:"Android",cygwin:"CYGWIN",darwin:"Darwin",freebsd:"FreeBSD",haiku:"Haiku",linux:"Linux",netbsd:"NetBSD",openbsd:"OpenBSD",sunos:"SunOS",win32:"Windows_NT"}[le()]??le()}return}function qe(e){return e.replace(/\$\(([^)]+)\)/g,(n,t)=>{try{let r=je(t),i=r[0];if(!i||!Xe.has(i))return console.warn(`[env] Blocked command substitution for disallowed command: ${i}`),"";let s=Qe(r);if(s!==void 0)return s.trim();let o=Be(i,r.slice(1),{encoding:"utf8",env:process.env});if(o.status===0)return o.stdout.trim();let c=o.stderr.trim();console.warn(`[env] Command substitution failed (exit code ${o.status??"unknown"}): ${i}${c?`: ${c}`:""}`)}catch(r){console.warn(`[env] Command substitution error: ${r instanceof Error?r.message:String(r)}`)}return""})}async function Pn(e,n={}){let t={},r=[],i=[];for(let s of e)try{let o=Bun.file(s);if(!o.size)continue;let c=await o.text(),{parsed:a,errors:u,skippedEncrypted:d}=I(c,n);r.push(...u),i.push(...d);for(let[f,p]of Object.entries(a)){if(!n.overload&&t[f]!==void 0)continue;t[f]=p}}catch(o){if(o?.code==="ENOENT")continue;r.push(`Failed to read ${s}: ${o instanceof Error?o.message:String(o)}`)}return{parsed:t,errors:r,skippedEncrypted:i}}import{existsSync as w}from"fs";import{readFileSync as z}from"fs";import{resolve as V}from"path";function U(e){return typeof e==="string"&&(e.startsWith("encrypted:")||e.startsWith("enc:"))}function K(e){if(!e||U(e))return;let n=e.trim().toLowerCase();if(!/^[a-z0-9_-]+$/.test(n))return;if(n==="prod")return"production";if(n==="stage")return"staging";if(n==="dev")return"development";return n}var M=null,F,Y;function ze(e,n,t=".env.keys"){let r=V(n,t);if(!w(r))return;try{let{parsed:i}=I(z(r,"utf-8"));if(e){let s=i[`DOTENV_PRIVATE_KEY_${e.toUpperCase()}`];if(s)return s}return i.DOTENV_PRIVATE_KEY}catch{return}}function J(e={}){return K(e.env)||Y||K("development")||K(process.env.DOTENV_ENV)||K(process.env.APP_ENV)||"development"}function Je(e={}){let n=J(e),t=e.cwd||process.cwd();if(M===n)return F;let r=q(n);if(!r)r=process.env.DOTENV_PRIVATE_KEY;if(!r)r=ze(n,t);return M=n,F=r,r}function Un(){M=null,F=void 0,Y=void 0}function pe(e,n={}){if(!U(e))return e;let t=Je(n);if(!t)return;try{let r=e.startsWith("enc:")?`encrypted:${e.slice(4)}`:e;return L(r,t)}catch{return}}function ge(e={}){let{path:n=[".env"],overload:t=!1,env:r,privateKey:i,keysFile:s=".env.keys",quiet:o=!1,cwd:c=process.cwd()}=e,a=Array.isArray(n)?n:[n],u=[],d=0,f=new Set(Object.entries(process.env).filter(([,E])=>E!==void 0&&!U(E)).map(([E])=>E)),p=i;if(!p&&s){let E=V(c,s);if(w(E))try{let l=z(E,"utf-8"),{parsed:m}=I(l);if(r){let v=`DOTENV_PRIVATE_KEY_${r.toUpperCase()}`;p=m[v]}else p=m.DOTENV_PRIVATE_KEY}catch(l){u.push(`Failed to load keys file: ${l instanceof Error?l.message:"Unknown error"}`)}}if(!p)p=q(r||"");for(let E of a){let l=V(c,E);if(!w(l)){if(!o)u.push(`File not found: ${l}`);continue}try{let m=z(l,"utf-8"),{parsed:v,errors:R,skippedEncrypted:T}=I(m,{privateKey:p,processEnv:process.env});if(u.push(...R),T.length>0){for(let C of T)if(U(process.env[C]))delete process.env[C];let g=r?`DOTENV_PRIVATE_KEY_${r.toUpperCase()}`:"DOTENV_PRIVATE_KEY",N=T.slice(0,5).join(", "),D=T.length>5?`, \u2026 +${T.length-5} more`:"";console.warn(`[env] warning: skipped ${T.length} encrypted value(s) in ${E} (${g} not set; defaults apply): ${N}${D}`)}let y=0;for(let[g,N]of Object.entries(v)){if(g==="DOTENV_PUBLIC_KEY")continue;let D=process.env[g],C=U(D)&&N!==D;if(t||!f.has(g)||C)process.env[g]=N,d++,y++}if(!o&&!process.env.__ENV_LOADED__)console.error(`[env] loaded ${y}/${Object.keys(v).length} variables from ${E}`),process.env.__ENV_LOADED__="1"}catch(m){u.push(`Failed to load ${l}: ${m instanceof Error?m.message:"Unknown error"}`)}}return{loaded:d,errors:u}}function Ze(e={}){return{name:"env-plugin",setup(n){ge(e)}}}function xn(e={}){let n=K(e.env)||K("development")||K(process.env.DOTENV_ENV)||K(process.env.APP_ENV)||"development",t=e.cwd||process.cwd();if(Y!==n)Y=n,M=null,F=void 0;let r=[],i=(c)=>{if(!r.includes(c)&&w(V(t,c)))r.push(c)};i(".env"),i(".env.local");let s=`.env.${n}`,o=`.env.${n}.local`;return i(s),i(o),ge({...e,path:r,env:n})}var $n=Ze;import{existsSync as S,readFileSync as O,renameSync as Z,rmSync as _e,writeFileSync as h}from"fs";import{basename as ee,dirname as Te,resolve as A}from"path";function Yn(e,n){if(e)return e;let t=String(n??"").trim();if(!t||t==="development"||t==="dev"||t==="local")return"";if(!/^[\w.-]+$/.test(t))return"";return`.env.${t}`}function Re(e,n,t,r){let i=["#/-------------------[DOTENV_PUBLIC_KEY]--------------------/","#/ versioned X25519 encryption for .env files /","#/ [how it works](https://stacksjs.com/encryption) /","#/----------------------------------------------------------/",`${t}="${n}"`,""],s=!1;for(let o of e.split(`
5
- `)){let c=o.trim();if(c.startsWith("#/"))continue;if(c.startsWith("DOTENV_PUBLIC_KEY"))continue;if(!c&&!s)continue;if(!c||c.startsWith("#")){s=!0,i.push(o);continue}s=!0;let a=c.match(/^([^=]+)=(.*)$/);if(!a||a[1]===void 0||a[2]===void 0){i.push(o);continue}let u=a[1].trim(),d=a[2].trim();if(d.startsWith('"')&&d.endsWith('"')||d.startsWith("'")&&d.endsWith("'"))d=d.slice(1,-1);if((!r.key||u.includes(r.key))&&(!r.excludeKey||!u.includes(r.excludeKey))&&!d.startsWith("encrypted:"))d=B(d,n);i.push(`${u}="${d}"`)}return i.join(`
6
- `)}function W(e){let t=((e||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase();return{publicKeyName:t?`DOTENV_PUBLIC_KEY_${t}`:"DOTENV_PUBLIC_KEY",privateKeyName:t?`DOTENV_PRIVATE_KEY_${t}`:"DOTENV_PRIVATE_KEY"}}function me(e,n){let t,r=!1;for(let i of e.split(`
7
- `)){let s=i.trim();if(!s||s.startsWith("#"))continue;let o=s.match(/^([^=]+)=(.*)$/);if(!o?.[1]||o[2]===void 0)continue;let c=o[1].trim(),a=o[2].trim().replace(/^["']|["']$/g,"");if(c===n)t=a;else if(a.startsWith("encrypted:")||a.startsWith("enc:"))r=!0}return t&&r?t:void 0}function Wn(e={}){let n=e.cwd||process.cwd(),t=A(n,e.file||".env"),r=A(n,e.keysFile||".env.keys");if(!S(t))return{success:!1,error:`File not found: ${t}`};try{let i,s,o=me(O(t,"utf-8"),W(e.file).publicKeyName);if(o)i=o;else if(S(r)){let u=O(r,"utf-8"),{parsed:d}=I(u),{publicKeyName:f,privateKeyName:p}=W(e.file);if(i=d[f]||"",s=d[p]||"",!i||!s){let E=k();i=E.publicKey,s=E.privateKey;let l=`
2
+ import{createCipheriv as ne,createDecipheriv as te,createHash as G,createPrivateKey as be,createPublicKey as re,diffieHellman as ie,generateKeyPairSync as se,hkdfSync as oe,randomBytes as H}from"crypto";var j="x25519-public:",X="x25519-private:",U="encrypted:v2:",Q="encrypted:",de=Buffer.from("stacks-env:v2","utf8"),ce="Environment decryption failed: authentication or format error";function ae(e){if(e.length===32)return e;return G("sha256").update(e).digest()}function vn(e,n){let t=H(16),r=ne("aes-256-gcm",ae(n),t);return{ciphertext:Buffer.concat([r.update(e,"utf8"),r.final()]).toString("hex"),iv:t.toString("hex"),authTag:r.getAuthTag().toString("hex")}}function Ke(e,n,t,r){try{let i=te("aes-256-gcm",ae(n),Buffer.from(t,"hex"));return i.setAuthTag(Buffer.from(r,"hex")),Buffer.concat([i.update(Buffer.from(e,"hex")),i.final()]).toString("utf8")}catch{throw Error("Decryption failed: authentication or format error")}}function S(e){return e.toString("base64url")}function C(e,n,t){if(typeof e!=="string"||e.length>0&&!/^[A-Za-z0-9_-]+$/.test(e))throw Error(`${n} is not canonical base64url`);let r=Buffer.from(e,"base64url");if(S(r)!==e)throw Error(`${n} is not canonical base64url`);if(t!==void 0&&r.length!==t)throw Error(`${n} has an invalid length`);return r}function Le(e){if(!e.startsWith(j))throw Error("Legacy or invalid public key. Run buddy env:rotate before creating new ciphertext.");let n=re({key:C(e.slice(j.length),"public key"),format:"der",type:"spki"});if(n.asymmetricKeyType!=="x25519")throw Error("Public key is not X25519");return n}function ke(e){if(!e.startsWith(X))throw Error("Private key is not a version 2 X25519 key");let n=be({key:C(e.slice(X.length),"private key"),format:"der",type:"pkcs8"});if(n.asymmetricKeyType!=="x25519")throw Error("Private key is not X25519");return n}function ue(e){return Buffer.from(`stacks-env:v2;${e.epk};${e.salt};${e.nonce}`,"utf8")}function k(){let{publicKey:e,privateKey:n}=se("x25519"),t=e.export({format:"der",type:"spki"}),r=n.export({format:"der",type:"pkcs8"});return{publicKey:`${j}${S(t)}`,privateKey:`${X}${S(r)}`}}function B(e,n){let t=Le(n),r=se("x25519"),i=S(r.publicKey.export({format:"der",type:"spki"})),s=H(16),o=H(12),d=ie({privateKey:r.privateKey,publicKey:t}),c=Buffer.from(oe("sha256",d,s,de,32)),u={epk:i,salt:S(s),nonce:S(o)};try{let a=ne("aes-256-gcm",c,o);a.setAAD(ue(u));let f=Buffer.concat([a.update(e,"utf8"),a.final()]),p={v:2,...u,ciphertext:S(f),tag:S(a.getAuthTag())};return`${U}${S(Buffer.from(JSON.stringify(p),"utf8"))}`}finally{d.fill(0),c.fill(0)}}function $e(e){let n=C(e.slice(U.length),"envelope").toString("utf8"),t=JSON.parse(n);if(!t||typeof t!=="object"||Array.isArray(t))throw Error("envelope is not an object");let r=Object.keys(t).sort(),i=["ciphertext","epk","nonce","salt","tag","v"];if(r.length!==i.length||r.some((s,o)=>s!==i[o]))throw Error("envelope fields are invalid");if(t.v!==2)throw Error("envelope version is unsupported");for(let s of["epk","salt","nonce","ciphertext","tag"])if(typeof t[s]!=="string")throw Error(`${s} is invalid`);return t}function we(e,n){try{let t=$e(e),r=ke(n),i=re({key:C(t.epk,"ephemeral public key"),format:"der",type:"spki"});if(i.asymmetricKeyType!=="x25519")throw Error("ephemeral key is not X25519");let s=C(t.salt,"salt",16),o=C(t.nonce,"nonce",12),d=C(t.ciphertext,"ciphertext"),c=C(t.tag,"tag",16),u=ie({privateKey:r,publicKey:i}),a=Buffer.from(oe("sha256",u,s,de,32));try{let f=te("aes-256-gcm",a,o);return f.setAAD(ue(t)),f.setAuthTag(c),Buffer.concat([f.update(d),f.final()]).toString("utf8")}finally{u.fill(0),a.fill(0)}}catch{throw Error(ce)}}function xe(e,n){try{if(!/^[0-9a-f]{64}$/.test(n))throw Error("invalid legacy private key");let t=Buffer.from(e.slice(Q.length),"base64");if(t.length<32)throw Error("invalid legacy payload");let r=G("sha256").update(Buffer.from(n,"hex")).digest(),i=G("sha256").update(r).digest();return Ke(t.subarray(32).toString("hex"),i,t.subarray(0,16).toString("hex"),t.subarray(16,32).toString("hex"))}catch{throw Error(ce)}}function An(e){return e.startsWith(Q)&&!e.startsWith(U)}function L(e,n){if(!e.startsWith(Q))return e;return e.startsWith(U)?we(e,n):xe(e,n)}function On(e,n,t){return B(L(e,n),t)}function In(e){let n=e.match(/^DOTENV_PRIVATE_KEY_(.+)$/);return n&&n[1]?n[1].toLowerCase():""}function q(e=""){return e?process.env[`DOTENV_PRIVATE_KEY_${e.toUpperCase()}`]:process.env.DOTENV_PRIVATE_KEY}import{spawnSync as Ue}from"child_process";import{readFileSync as Be}from"fs";import{arch as Ve,hostname as Me,platform as fe,release as Fe,userInfo as We}from"os";import{basename as Ye,dirname as Ge,resolve as He}from"path";function I(e,n={}){let t={},r=[],i=[],s=e.split(`
3
+ `);for(let o of s){let d=o.trim();if(!d||d.startsWith("#"))continue;if(d.startsWith("DOTENV_PUBLIC_KEY=")){let f=d.match(/^DOTENV_PUBLIC_KEY=["']?([^"'\n]+)["']?/);if(f&&f[1]!==void 0)t.DOTENV_PUBLIC_KEY=f[1];continue}let c=d.match(/^([^=]+)=(.*)$/);if(!c||c[1]===void 0||c[2]===void 0)continue;let u=c[1].trim(),a=c[2].trim();if(a.startsWith('"')&&a.endsWith('"')||a.startsWith("'")&&a.endsWith("'")){if(a=a.slice(1,-1),a.includes("\\n"))a=a.replace(/\\n/g,`
4
+ `)}if(a.startsWith("encrypted:")||a.startsWith("enc:")){if(!n.privateKey){i.push(u);continue}try{let f=a.startsWith("enc:")?`encrypted:${a.slice(4)}`:a;a=L(f,n.privateKey)}catch(f){r.push(`Failed to decrypt ${u}: ${f instanceof Error?f.message:"Unknown error"}`)}}a=je(a,{...n.processEnv||process.env,...t}),a=ze(a),t[u]=a}return{parsed:t,errors:r,skippedEncrypted:i}}function je(e,n){return e.replace(/\$\{([^}]+)\}/g,(t,r)=>{let i=r.match(/^([^:\-+]+)(:-|-)(.+)$/);if(i){let[,o,d,c]=i,u=n[o];if(d===":-")return u||c;else return u!==void 0?u:c}let s=r.match(/^([^:\-+]+)(:?\+)(.+)$/);if(s){let[,o,d,c]=s,u=n[o];if(d===":+")return u?c:"";else return u!==void 0?c:""}return n[r]||""})}var Xe=new Set(["date","hostname","whoami","uname","pwd","echo","printf","cat","basename","dirname"]);function Qe(e){let n=[],t="",r=null,i=!1;for(let s of e.trim()){if(i){t+=s,i=!1;continue}if(s==="\\"&&r!=="'"){i=!0;continue}if(r){if(s===r)r=null;else t+=s;continue}if(s==='"'||s==="'"){r=s;continue}if(/\s/.test(s)){if(t)n.push(t),t="";continue}t+=s}if(i||r)throw Error("Command substitution contains an unterminated escape or quote");if(t)n.push(t);return n}function qe(e){let[n,...t]=e,r=t.some((i)=>i.startsWith("-"));if(n==="echo"&&!r)return t.join(" ");if(n==="printf"&&t.length===1&&!t[0]?.includes("%"))return t[0];if(n==="hostname"&&t.length===0)return Me();if(n==="whoami"&&t.length===0)return We().username;if(n==="pwd"&&t.length===0)return process.cwd();if(n==="basename"&&t.length===1)return Ye(t[0]??"");if(n==="dirname"&&t.length===1)return Ge(t[0]??"");if(n==="cat"&&t.length>0&&!r)return t.map((i)=>Be(He(process.cwd(),i),"utf8")).join("");if(n==="uname"){let i=t[0]??"-s";if(t.length<=1&&i==="-m")return Ve();if(t.length<=1&&i==="-r")return Fe();if(t.length<=1&&i==="-s")return{aix:"AIX",android:"Android",cygwin:"CYGWIN",darwin:"Darwin",freebsd:"FreeBSD",haiku:"Haiku",linux:"Linux",netbsd:"NetBSD",openbsd:"OpenBSD",sunos:"SunOS",win32:"Windows_NT"}[fe()]??fe()}return}function ze(e){return e.replace(/\$\(([^)]+)\)/g,(n,t)=>{try{let r=Qe(t),i=r[0];if(!i||!Xe.has(i))return console.warn(`[env] Blocked command substitution for disallowed command: ${i}`),"";let s=qe(r);if(s!==void 0)return s.trim();let o=Ue(i,r.slice(1),{encoding:"utf8",env:process.env});if(o.status===0)return o.stdout.trim();let d=o.stderr.trim();console.warn(`[env] Command substitution failed (exit code ${o.status??"unknown"}): ${i}${d?`: ${d}`:""}`)}catch(r){console.warn(`[env] Command substitution error: ${r instanceof Error?r.message:String(r)}`)}return""})}async function bn(e,n={}){let t={},r=[],i=[];for(let s of e)try{let o=Bun.file(s);if(!o.size)continue;let d=await o.text(),{parsed:c,errors:u,skippedEncrypted:a}=I(d,n);r.push(...u),i.push(...a);for(let[f,p]of Object.entries(c)){if(!n.overload&&t[f]!==void 0)continue;t[f]=p}}catch(o){if(o?.code==="ENOENT")continue;r.push(`Failed to read ${s}: ${o instanceof Error?o.message:String(o)}`)}return{parsed:t,errors:r,skippedEncrypted:i}}import{existsSync as V}from"fs";import{readFileSync as z}from"fs";import{resolve as M}from"path";function w(e){return typeof e==="string"&&(e.startsWith("encrypted:")||e.startsWith("enc:"))}function b(e){if(!e||w(e))return;let n=e.trim().toLowerCase();if(!/^[a-z0-9_-]+$/.test(n))return;if(n==="prod")return"production";if(n==="stage")return"staging";if(n==="dev")return"development";return n}var F=null,W,Y;function Je(e,n,t=".env.keys"){let r=M(n,t);if(!V(r))return;try{let{parsed:i}=I(z(r,"utf-8"));if(e){let s=i[`DOTENV_PRIVATE_KEY_${e.toUpperCase()}`];if(s)return s}return i.DOTENV_PRIVATE_KEY}catch{return}}function J(e={}){return b(e.env)||Y||b("development")||b(process.env.DOTENV_ENV)||b(process.env.APP_ENV)||"development"}function Ze(e={}){let n=J(e),t=e.cwd||process.cwd();if(F===n)return W;let r=q(n);if(!r)r=process.env.DOTENV_PRIVATE_KEY;if(!r)r=Je(n,t);return F=n,W=r,r}function Un(){F=null,W=void 0,Y=void 0}function Ee(e,n={}){if(!w(e))return e;let t=Ze(n);if(!t)return;try{let r=e.startsWith("enc:")?`encrypted:${e.slice(4)}`:e;return L(r,t)}catch{return}}function le(e={}){let{path:n=[".env"],overload:t=!1,env:r,privateKey:i,keysFile:s=".env.keys",quiet:o=!1,cwd:d=process.cwd()}=e,c=Array.isArray(n)?n:[n],u=[],a=0,f=new Set(Object.entries(process.env).filter(([,E])=>E!==void 0&&!w(E)).map(([E])=>E)),p=i;if(!p&&s){let E=M(d,s);if(V(E))try{let l=z(E,"utf-8"),{parsed:T}=I(l);if(r){let v=`DOTENV_PRIVATE_KEY_${r.toUpperCase()}`;p=T[v]}else p=T.DOTENV_PRIVATE_KEY}catch(l){u.push(`Failed to load keys file: ${l instanceof Error?l.message:"Unknown error"}`)}}if(!p)p=q(r||"");for(let E of c){let l=M(d,E);if(!V(l)){if(!o)u.push(`File not found: ${l}`);continue}try{let T=z(l,"utf-8"),{parsed:v,errors:_,skippedEncrypted:y}=I(T,{privateKey:p,processEnv:process.env});if(u.push(..._),y.length>0){for(let D of y)if(w(process.env[D]))delete process.env[D];let R=r?`DOTENV_PRIVATE_KEY_${r.toUpperCase()}`:"DOTENV_PRIVATE_KEY",K=y.slice(0,5).join(", "),P=y.length>5?`, \u2026 +${y.length-5} more`:"";console.warn(`[env] warning: skipped ${y.length} encrypted value(s) in ${E} (${R} not set; defaults apply): ${K}${P}`)}let m=0;for(let[R,K]of Object.entries(v)){if(R==="DOTENV_PUBLIC_KEY")continue;let P=process.env[R],D=w(P)&&K!==P;if(t||!f.has(R)||D)process.env[R]=K,a++,m++}if(!o&&!process.env.__ENV_LOADED__)console.error(`[env] loaded ${m}/${Object.keys(v).length} variables from ${E}`),process.env.__ENV_LOADED__="1"}catch(T){u.push(`Failed to load ${l}: ${T instanceof Error?T.message:"Unknown error"}`)}}return{loaded:a,errors:u}}function en(e={}){return{name:"env-plugin",setup(n){le(e)}}}function Bn(e={}){let n=b(e.env)||b("development")||b(process.env.DOTENV_ENV)||b(process.env.APP_ENV)||"development",t=e.cwd||process.cwd();if(Y!==n)Y=n,F=null,W=void 0;let r=[],i=(d)=>{if(!r.includes(d)&&V(M(t,d)))r.push(d)};i(".env"),i(".env.local");let s=`.env.${n}`,o=`.env.${n}.local`;return i(s),i(o),le({...e,path:r,env:n})}var Mn=en;import{existsSync as N,readFileSync as O,renameSync as pe,rmSync as ge,writeFileSync as h}from"fs";import{basename as _e,dirname as Te,resolve as A}from"path";function Hn(e,n){if(e)return e;let t=String(n??"").trim();if(!t||t==="development"||t==="dev"||t==="local")return"";if(!/^[\w.-]+$/.test(t))return"";return`.env.${t}`}function ye(e,n){return(!n.key||e.includes(n.key))&&(!n.excludeKey||!e.includes(n.excludeKey))}function nn(e,n){let t=[];for(let r of e.split(`
5
+ `)){let i=r.trim();if(!i||i.startsWith("#"))continue;if(i.startsWith("DOTENV_PUBLIC_KEY"))continue;let s=i.match(/^([^=]+)=(.*)$/);if(!s||s[1]===void 0)continue;let o=s[1].trim();if(ye(o,n))t.push(o)}return t}function Re(e,n,t,r){let i=["#/-------------------[DOTENV_PUBLIC_KEY]--------------------/","#/ versioned X25519 encryption for .env files /","#/ [how it works](https://stacksjs.com/encryption) /","#/----------------------------------------------------------/",`${t}="${n}"`,""],s=!1;for(let o of e.split(`
6
+ `)){let d=o.trim();if(d.startsWith("#/"))continue;if(d.startsWith("DOTENV_PUBLIC_KEY"))continue;if(!d&&!s)continue;if(!d||d.startsWith("#")){s=!0,i.push(o);continue}s=!0;let c=d.match(/^([^=]+)=(.*)$/);if(!c||c[1]===void 0||c[2]===void 0){i.push(o);continue}let u=c[1].trim(),a=c[2].trim();if(a.startsWith('"')&&a.endsWith('"')||a.startsWith("'")&&a.endsWith("'"))a=a.slice(1,-1);if(ye(u,r)&&!a.startsWith("encrypted:"))a=B(a,n);i.push(`${u}="${a}"`)}return i.join(`
7
+ `)}function x(e){let t=((e||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase();return{publicKeyName:t?`DOTENV_PUBLIC_KEY_${t}`:"DOTENV_PUBLIC_KEY",privateKeyName:t?`DOTENV_PRIVATE_KEY_${t}`:"DOTENV_PRIVATE_KEY"}}function me(e,n){let t,r=!1;for(let i of e.split(`
8
+ `)){let s=i.trim();if(!s||s.startsWith("#"))continue;let o=s.match(/^([^=]+)=(.*)$/);if(!o?.[1]||o[2]===void 0)continue;let d=o[1].trim(),c=o[2].trim().replace(/^["']|["']$/g,"");if(d===n)t=c;else if(c.startsWith("encrypted:")||c.startsWith("enc:"))r=!0}return t&&r?t:void 0}function jn(e={}){let n=e.cwd||process.cwd(),t=A(n,e.file||".env"),r=A(n,e.keysFile||".env.keys");if(!N(t))return{success:!1,error:`File not found: ${t}`};try{let i,s,o=me(O(t,"utf-8"),x(e.file).publicKeyName);if(o)i=o;else if(N(r)){let u=O(r,"utf-8"),{parsed:a}=I(u),{publicKeyName:f,privateKeyName:p}=x(e.file);if(i=a[f]||"",s=a[p]||"",!i||!s){let E=k();i=E.publicKey,s=E.privateKey;let l=`
8
9
  ${f}="${i}"
9
10
  ${p}="${s}"
10
- `;h(r,u+l,"utf-8")}}else{let u=k();i=u.publicKey,s=u.privateKey;let{publicKeyName:d,privateKeyName:f}=W(e.file),p=`# .env.keys - Keep this file secure and never commit to source control
11
- ${d}="${i}"
11
+ `;h(r,u+l,"utf-8")}}else{let u=k();i=u.publicKey,s=u.privateKey;let{publicKeyName:a,privateKeyName:f}=x(e.file),p=`# .env.keys - Keep this file secure and never commit to source control
12
+ ${a}="${i}"
12
13
  ${f}="${s}"
13
- `;h(r,p,"utf-8")}let c=O(t,"utf-8"),a=Re(c,i,W(e.file).publicKeyName,e);if(e.stdout)return{success:!0,output:a};return h(t,a,"utf-8"),{success:!0,output:`\u2714 encrypted (${e.file||".env"})
14
- \u2714 key added to ${e.keysFile||".env.keys"}`}}catch(i){return{success:!1,error:`Failed to encrypt: ${i instanceof Error?i.message:"Unknown error"}`}}}function en(e={}){let n=e.cwd||process.cwd(),t=A(n,e.file||".env"),r=A(n,e.keysFile||".env.keys");if(!S(t))return{success:!1,error:`File not found: ${t}`};if(!S(r))return{success:!1,error:`Keys file not found: ${r}`};try{let i=O(r,"utf-8"),{parsed:s}=I(i),a=((e.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),u=a?`DOTENV_PRIVATE_KEY_${a}`:"DOTENV_PRIVATE_KEY",d=s[u];if(!d)return{success:!1,error:`Private key not found: ${u}`};let f=[d];if(u!=="DOTENV_PRIVATE_KEY"&&s.DOTENV_PRIVATE_KEY&&s.DOTENV_PRIVATE_KEY!==d)f.push(s.DOTENV_PRIVATE_KEY);let E=O(t,"utf-8").split(`
15
- `),l=[];for(let v of E){let R=v.trim();if(!R||R.startsWith("#")){l.push(v);continue}if(R.startsWith("DOTENV_PUBLIC_KEY"))continue;let T=R.match(/^([^=]+)=(.*)$/);if(!T||T[1]===void 0||T[2]===void 0){l.push(v);continue}let y=T[1].trim(),g=T[2].trim();if(g.startsWith('"')&&g.endsWith('"')||g.startsWith("'")&&g.endsWith("'"))g=g.slice(1,-1);let N=g.startsWith("encrypted:");if(e.key&&!y.includes(e.key))N=!1;if(N){let D,C;for(let Pe of f)try{D=L(g,Pe);break}catch(De){C=De}if(D===void 0)throw C instanceof Error?C:Error(`Unable to decrypt ${y}`);g=D}l.push(`${y}="${g}"`)}let m=l.join(`
16
- `);if(e.stdout)return{success:!0,output:m};return h(t,m,"utf-8"),{success:!0,output:`\u2714 decrypted (${e.file||".env"})`}}catch(i){return{success:!1,error:`Failed to decrypt: ${i instanceof Error?i.message:"Unknown error"}`}}}function Gn(e,n,t={}){let r=t.cwd||process.cwd(),i=A(r,t.file||".env"),s=A(r,t.keysFile||".env.keys");try{let o="";if(S(i))o=O(i,"utf-8");let c=o.split(`
17
- `),a=!1,u,p=((t.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),E=p?`DOTENV_PUBLIC_KEY_${p}`:"DOTENV_PUBLIC_KEY",l=p?`DOTENV_PRIVATE_KEY_${p}`:"DOTENV_PRIVATE_KEY";for(let R of c){let T=R.trim();if(T.startsWith(`${E}=`)){let y=T.match(/^[^=]+=["']?([^"'\n]+)["']?/);if(y)u=y[1];break}}if(u){let R=!1;if(S(s)){let T=O(s,"utf-8"),{parsed:y}=I(T);R=Boolean(y[l])}if(!R&&!process.env[l]&&!me(o,E))u=void 0}if(!t.plain&&!u){let R=k();u=R.publicKey;let T="";if(S(s))T=O(s,"utf-8");T+=`
18
- ${E}="${R.publicKey}"
19
- ${l}="${R.privateKey}"
20
- `,h(s,T,"utf-8");for(let y=c.length-1;y>=0;y--){let g=c[y];if(g!==void 0&&g.trim().startsWith(`${E}=`))c.splice(y,1)}c.unshift(`${E}="${u}"`)}let m=n;if(!t.plain&&u)m=B(n,u);for(let R=0;R<c.length;R++){let T=c[R];if(T===void 0)continue;if(T.trim().startsWith(`${e}=`)){c[R]=`${e}="${m}"`,a=!0;break}}if(!a)c.push(`${e}="${m}"`);let v=c.join(`
21
- `);return h(i,v,"utf-8"),{success:!0,output:`set ${e}${t.plain?"":" with encryption"} (${t.file||".env"})`}}catch(o){return{success:!1,error:`Failed to set: ${o instanceof Error?o.message:"Unknown error"}`}}}function Hn(e,n={}){let t=n.cwd||process.cwd(),r=A(t,n.file||".env");if(!S(r))return{success:!1,error:`File not found: ${r}`};try{let i,s=A(t,n.keysFile||".env.keys"),a=((n.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),u=a?`DOTENV_PRIVATE_KEY_${a}`:"DOTENV_PRIVATE_KEY";if(S(s)){let l=O(s,"utf-8"),{parsed:m}=I(l);i=m[u]}if(!i)i=process.env[u];let d=O(r,"utf-8"),{parsed:f}=I(d,{privateKey:i});if(e&&!n.all){let l=f[e];if(l===void 0)return{success:!1,error:`Key not found: ${e}`};return{success:!0,output:l}}let p=n.all?{...process.env,...f}:f,E;switch(n.format){case"shell":E=Object.entries(p).map(([l,m])=>`${l}=${m}`).join(" ");break;case"eval":E=Object.entries(p).map(([l,m])=>`${l}="${m}"`).join(`
22
- `);break;case"json":default:E=n.prettyPrint?JSON.stringify(p,null,2):JSON.stringify(p);break}return{success:!0,output:E}}catch(i){return{success:!1,error:`Failed to get: ${i instanceof Error?i.message:"Unknown error"}`}}}function Xn(e,n={}){let t=n.cwd||process.cwd(),r=A(t,n.keysFile||".env.keys");if(!S(r))return{success:!1,error:`Keys file not found: ${r}`};try{let i=O(r,"utf-8"),{parsed:s}=I(i),a=((n.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),u=a?`DOTENV_PUBLIC_KEY_${a}`:"DOTENV_PUBLIC_KEY",d=a?`DOTENV_PRIVATE_KEY_${a}`:"DOTENV_PRIVATE_KEY";if(e){let E=s[e];if(!E)return{success:!1,error:`Key not found: ${e}`};return{success:!0,output:E}}let f={[u]:s[u],[d]:s[d]};return{success:!0,output:n.format==="shell"?`${u}=${f[u]} ${d}=${f[d]}`:JSON.stringify(f)}}catch(i){return{success:!1,error:`Failed to get keypair: ${i instanceof Error?i.message:"Unknown error"}`}}}function jn(e={}){let n=en({file:e.file,keysFile:e.keysFile,stdout:!0,cwd:e.cwd});if(!n.success)return n;let t=e.cwd||process.cwd(),r=A(t,e.file||".env"),i=A(t,e.keysFile||".env.keys");if(n.output===void 0)return{success:!1,error:"Rotation produced no decrypted content"};let s=k(),o=O(r),c=O(i),a=c.toString("utf8").split(`
23
- `),u=e.file||".env",d=ee(u).replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),f=d?`DOTENV_PUBLIC_KEY_${d}`:"DOTENV_PUBLIC_KEY",p=d?`DOTENV_PRIVATE_KEY_${d}`:"DOTENV_PRIVATE_KEY",E=!1,l=!1;for(let g=0;g<a.length;g++){let N=a[g];if(N===void 0)continue;if(N.startsWith(`${f}=`))a[g]=`${f}="${s.publicKey}"`,E=!0;else if(N.startsWith(`${p}=`))a[g]=`${p}="${s.privateKey}"`,l=!0}if(!E)a.push(`${f}="${s.publicKey}"`);if(!l)a.push(`${p}="${s.privateKey}"`);let m=`${a.join(`
14
+ `;h(r,p,"utf-8")}let d=O(t,"utf-8"),c=Re(d,i,x(e.file).publicKeyName,e);if(e.stdout)return{success:!0,output:c};return h(t,c,"utf-8"),{success:!0,output:`\u2714 encrypted (${e.file||".env"})
15
+ \u2714 key added to ${e.keysFile||".env.keys"}`}}catch(i){return{success:!1,error:`Failed to encrypt: ${i instanceof Error?i.message:"Unknown error"}`}}}function tn(e={}){let n=e.cwd||process.cwd(),t=A(n,e.file||".env"),r=A(n,e.keysFile||".env.keys");if(!N(t))return{success:!1,error:`File not found: ${t}`};if(!N(r))return{success:!1,error:`Keys file not found: ${r}`};try{let i=O(r,"utf-8"),{parsed:s}=I(i),c=((e.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),u=c?`DOTENV_PRIVATE_KEY_${c}`:"DOTENV_PRIVATE_KEY",a=s[u];if(!a)return{success:!1,error:`Private key not found: ${u}`};let f=[a];if(u!=="DOTENV_PRIVATE_KEY"&&s.DOTENV_PRIVATE_KEY&&s.DOTENV_PRIVATE_KEY!==a)f.push(s.DOTENV_PRIVATE_KEY);let E=O(t,"utf-8").split(`
16
+ `),l=[];for(let v of E){let _=v.trim();if(!_||_.startsWith("#")){l.push(v);continue}if(_.startsWith("DOTENV_PUBLIC_KEY"))continue;let y=_.match(/^([^=]+)=(.*)$/);if(!y||y[1]===void 0||y[2]===void 0){l.push(v);continue}let m=y[1].trim(),R=y[2].trim();if(R.startsWith('"')&&R.endsWith('"')||R.startsWith("'")&&R.endsWith("'"))R=R.slice(1,-1);let K=R.startsWith("encrypted:");if(e.key&&!m.includes(e.key))K=!1;if(K){let P,D;for(let De of f)try{P=L(R,De);break}catch(Ce){D=Ce}if(P===void 0)throw D instanceof Error?D:Error(`Unable to decrypt ${m}`);R=P}l.push(`${m}="${R}"`)}let T=l.join(`
17
+ `);if(e.stdout)return{success:!0,output:T};return h(t,T,"utf-8"),{success:!0,output:`\u2714 decrypted (${e.file||".env"})`}}catch(i){return{success:!1,error:`Failed to decrypt: ${i instanceof Error?i.message:"Unknown error"}`}}}function Xn(e,n,t={}){let r=t.cwd||process.cwd(),i=A(r,t.file||".env"),s=A(r,t.keysFile||".env.keys");try{let o="";if(N(i))o=O(i,"utf-8");let d=o.split(`
18
+ `),c=!1,u,p=((t.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),E=p?`DOTENV_PUBLIC_KEY_${p}`:"DOTENV_PUBLIC_KEY",l=p?`DOTENV_PRIVATE_KEY_${p}`:"DOTENV_PRIVATE_KEY";for(let _ of d){let y=_.trim();if(y.startsWith(`${E}=`)){let m=y.match(/^[^=]+=["']?([^"'\n]+)["']?/);if(m)u=m[1];break}}if(u){let _=!1;if(N(s)){let y=O(s,"utf-8"),{parsed:m}=I(y);_=Boolean(m[l])}if(!_&&!process.env[l]&&!me(o,E))u=void 0}if(!t.plain&&!u){let _=k();u=_.publicKey;let y="";if(N(s))y=O(s,"utf-8");y+=`
19
+ ${E}="${_.publicKey}"
20
+ ${l}="${_.privateKey}"
21
+ `,h(s,y,"utf-8");for(let m=d.length-1;m>=0;m--){let R=d[m];if(R!==void 0&&R.trim().startsWith(`${E}=`))d.splice(m,1)}d.unshift(`${E}="${u}"`)}let T=n;if(!t.plain&&u)T=B(n,u);for(let _=0;_<d.length;_++){let y=d[_];if(y===void 0)continue;if(y.trim().startsWith(`${e}=`)){d[_]=`${e}="${T}"`,c=!0;break}}if(!c)d.push(`${e}="${T}"`);let v=d.join(`
22
+ `);return h(i,v,"utf-8"),{success:!0,output:`set ${e}${t.plain?"":" with encryption"} (${t.file||".env"})`}}catch(o){return{success:!1,error:`Failed to set: ${o instanceof Error?o.message:"Unknown error"}`}}}function Qn(e,n={}){let t=n.cwd||process.cwd(),r=A(t,n.file||".env");if(!N(r))return{success:!1,error:`File not found: ${r}`};try{let i,s=A(t,n.keysFile||".env.keys"),c=((n.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),u=c?`DOTENV_PRIVATE_KEY_${c}`:"DOTENV_PRIVATE_KEY";if(N(s)){let l=O(s,"utf-8"),{parsed:T}=I(l);i=T[u]}if(!i)i=process.env[u];let a=O(r,"utf-8"),{parsed:f}=I(a,{privateKey:i});if(e&&!n.all){let l=f[e];if(l===void 0)return{success:!1,error:`Key not found: ${e}`};return{success:!0,output:l}}let p=n.all?{...process.env,...f}:f,E;switch(n.format){case"shell":E=Object.entries(p).map(([l,T])=>`${l}=${T}`).join(" ");break;case"eval":E=Object.entries(p).map(([l,T])=>`${l}="${T}"`).join(`
23
+ `);break;case"json":default:E=n.prettyPrint?JSON.stringify(p,null,2):JSON.stringify(p);break}return{success:!0,output:E}}catch(i){return{success:!1,error:`Failed to get: ${i instanceof Error?i.message:"Unknown error"}`}}}function qn(e,n={}){let t=n.cwd||process.cwd(),r=A(t,n.keysFile||".env.keys");if(!N(r))return{success:!1,error:`Keys file not found: ${r}`};try{let i=O(r,"utf-8"),{parsed:s}=I(i),c=((n.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),u=c?`DOTENV_PUBLIC_KEY_${c}`:"DOTENV_PUBLIC_KEY",a=c?`DOTENV_PRIVATE_KEY_${c}`:"DOTENV_PRIVATE_KEY";if(e){let E=s[e];if(!E)return{success:!1,error:`Key not found: ${e}`};return{success:!0,output:E}}let f={[u]:s[u],[a]:s[a]};return{success:!0,output:n.format==="shell"?`${u}=${f[u]} ${a}=${f[a]}`:JSON.stringify(f)}}catch(i){return{success:!1,error:`Failed to get keypair: ${i instanceof Error?i.message:"Unknown error"}`}}}function rn(e,n,t,r){let i=e.split(`
24
+ `),s=!1,o=!1;for(let d=0;d<i.length;d++){let c=i[d];if(c===void 0)continue;if(c.startsWith(`${t}=`))i[d]=`${t}="${n.publicKey}"`,s=!0;else if(c.startsWith(`${r}=`))i[d]=`${r}="${n.privateKey}"`,o=!0}if(!s)i.push(`${t}="${n.publicKey}"`);if(!o)i.push(`${r}="${n.privateKey}"`);return`${i.join(`
24
25
  `).replace(/\n+$/,"")}
25
- `,v=Re(n.output,s.publicKey,f,e),R=`${process.pid}-${Date.now()}`,T=A(Te(r),`.${ee(r)}.${R}.rotate`),y=A(Te(i),`.${ee(i)}.${R}.rotate`);try{if(h(T,v,{encoding:"utf8",mode:384}),h(y,m,{encoding:"utf8",mode:384}),e.stdout)return Z(y,i),{success:!0,output:v};Z(T,r);try{Z(y,i)}catch(g){throw h(r,o),g}return{success:!0,output:`\u2714 rotated (${e.file||".env"}) to encrypted:v2`}}catch(g){if(!S(i))h(i,c,{mode:384});return{success:!1,error:`Failed to rotate without exposing plaintext: ${g instanceof Error?g.message:"Unknown error"}`}}finally{_e(T,{force:!0}),_e(y,{force:!0})}}import _ from"process";import{platform as nn}from"os";var ye=typeof Bun<"u",ve=typeof _<"u"&&_.versions?.node!==void 0,tn=ye?"bun":ve?"node":"unknown",Jn={name:tn,version:ye?Bun.version:ve?_.version:void 0},te=nn(),Zn=te==="win32",et=te==="darwin",nt=te==="linux",Ae=Boolean(_.stdout?.isTTY),tt=typeof globalThis.window<"u",rn=Boolean(_.env.CI||_.env.CONTINUOUS_INTEGRATION||_.env.BUILD_NUMBER||_.env.RUN_ID),rt=Boolean(_.env.DEBUG||_.env.VERBOSE||_.argv.includes("--debug")||_.argv.includes("--verbose")),sn=rn||!Ae,it=Boolean(!sn&&(Ae||_.env.COLORTERM||_.env.FORCE_COLOR||_.env.TERM&&_.env.TERM!=="dumb")),ne={github:{name:"GitHub Actions",detected:Boolean(_.env.GITHUB_ACTIONS)},gitlab:{name:"GitLab CI",detected:Boolean(_.env.GITLAB_CI)},circle:{name:"CircleCI",detected:Boolean(_.env.CIRCLECI)},travis:{name:"Travis CI",detected:Boolean(_.env.TRAVIS)},jenkins:{name:"Jenkins",detected:Boolean(_.env.JENKINS_URL)},vercel:{name:"Vercel",detected:Boolean(_.env.VERCEL)},netlify:{name:"Netlify",detected:Boolean(_.env.NETLIFY)},heroku:{name:"Heroku",detected:Boolean(_.env.DYNO)},aws:{name:"AWS",detected:Boolean(_.env.AWS_REGION||_.env.AWS_LAMBDA_FUNCTION_NAME)},azure:{name:"Azure",detected:Boolean(_.env.AZURE_HTTP_USER_AGENT)},cloudflare:{name:"Cloudflare",detected:Boolean(_.env.CF_PAGES)},railway:{name:"Railway",detected:Boolean(_.env.RAILWAY_ENVIRONMENT)},render:{name:"Render",detected:Boolean(_.env.RENDER)}},on=Object.keys(ne).find((e)=>{let n=ne[e];return n!==void 0&&n.detected})||"unknown",st=ne[on]||{name:"Unknown",detected:!1};import En from"process";import{projectPath as ln}from"@stacksjs/path";import Ne from"fs";var Oe={APP_ENV:["local","dev","development","staging","prod","production"],DB_CONNECTION:["mysql","sqlite","postgres","singlestore","vitess","dynamodb"],DB_MIGRATE_FRESH:["allow","confirm","disabled"],MAIL_MAILER:["smtp","mailgun","ses","log","sendgrid","mailtrap","capture"],SEARCH_ENGINE_DRIVER:["opensearch","meilisearch","algolia","typesense"],FRONTEND_APP_ENV:["development","staging","production"]};function dt(e){return e}function dn(e){return typeof e==="string"&&(e.startsWith("encrypted:")||e.startsWith("enc:"))}var an=/(?:^|_)(?:PASSWORD|PASSWD|SECRET|TOKEN|APIKEY|API_KEY|CREDENTIAL|CREDENTIALS|PRIVATE|SALT|DSN|CERT|CIPHER|SIGNATURE|WEBHOOK)(?:_|$)|(?:^|_)(?:ACCESS|SECRET)_KEY(?:_|$)|_KEY$/,cn=/^DOTENV_(?:PUBLIC|PRIVATE)_KEY/,un=/^\$\{[^}]+\}$/;function ct(e,n={}){let{placeholders:t={},strict:r=!1}=n,i=[];for(let[s,o]of Object.entries(e)){if(!o||o.trim()==="")continue;if(dn(o))continue;if(cn.test(s))continue;if(un.test(o.trim()))continue;let c=an.test(s),a=Object.prototype.hasOwnProperty.call(t,s)&&t[s]===o;if(c&&!a){i.push({key:s,reason:"plaintext-secret"});continue}if(r)i.push({key:s,reason:"plaintext-value"})}return i}function ut(e){return e.split(`
26
- `).map((n)=>n.trim()).filter(Boolean).filter((n)=>n!==".env.example").filter((n)=>/(?:^|\/)\.env(?:\.|$)/.test(n)).filter((n)=>!n.endsWith(".env.keys"))}function Ie(e){return`${e.replace(/[^a-z0-9]/gi,"").toUpperCase()}_`}function fn(e,n={}){let t=n.self?Ie(n.self):void 0,r=(n.tenants??[]).filter((o)=>typeof o==="string"&&o.trim().length>0).map((o)=>({slug:o,prefix:Ie(o)})).filter(({prefix:o})=>o!==t).sort((o,c)=>c.prefix.length-o.prefix.length),i={},s={};for(let[o,c]of Object.entries(e)){let a=r.find(({prefix:u})=>o.startsWith(u));if(a){(s[a.slug]??={})[o]=c;continue}i[o]=c}return{own:i,foreign:s}}function Et(e,n={}){return fn(e,n).own}function lt(e){return Object.entries(e.foreign).map(([n,t])=>({tenant:n,keys:Object.keys(t).sort()})).sort((n,t)=>n.tenant.localeCompare(t.tenant))}var Se=new Set;function pn(e){if(Se.has(e))return;Se.add(e);let n=J();console.warn(`[env] warning: "${e}" is encrypted but no usable private key was found for the "${n}" environment; falling back to its default. Set DOTENV_PRIVATE_KEY_${n.toUpperCase()} or ship .env.keys.`)}var gn={get:(e,n)=>{let t=e,r=t[n];if(typeof r==="string"&&(r.startsWith("encrypted:")||r.startsWith("enc:"))){let s=pe(r);if(s===void 0){pn(n),delete t[n];return}t[n]=s,r=s}let i=["_PORT","_TIMEOUT","_TTL","_SIZE","_LIMIT","_MAX","_MIN","_INTERVAL","_RETRIES","_CONCURRENCY","_WORKERS","_CONNECTIONS"];if(typeof r==="string"&&/^\d+$/.test(r)&&!r.startsWith("0")&&i.some((s)=>n.endsWith(s)))return Number(r);if(typeof r==="string"){let s=r.toLowerCase();if(s==="true")return!0;if(s==="false")return!1}return r}};function _n(){return typeof Bun<"u"?Bun.env:En.env}var he=new Proxy(_n(),gn);function yt(e,n,t){let r=t?.path||ln(".env"),s=Ne.readFileSync(r,"utf-8").split(`
27
- `),o=s.findIndex((u)=>u.startsWith(`${e}=`)),a=/[\s"'#$\\]/.test(n)?`"${n.replace(/"/g,"\\\"")}"`:n;if(o!==-1)s[o]=`${e}=${a}`;else s.push(`${e}=${a}`);Ne.writeFileSync(r,s.join(`
28
- `))}function vt(e=he){let n=[],t=e;for(let[r,i]of Object.entries(Oe)){let s=t[r];if(s!==void 0&&s!==""&&!i.includes(String(s)))n.push(`${r}="${s}" is not valid. Allowed values: ${i.join(", ")}`)}return n}function At(e,n=he){let t=[],r=n;for(let i of e){let s=r[i];if(s===void 0||s===""||s===null)t.push(i)}if(t.length>0)throw Error(`[env] Missing required environment variable(s): ${t.join(", ")}. Set them in .env or your process environment before booting.`);return n}export{an as SECRET_NAME_PATTERN,J as activeEnvName,be as aesDecrypt,Rn as aesEncrypt,xn as autoLoadEnv,en as decryptEnv,pe as decryptEnvValue,L as decryptValue,dt as defineEnv,Wn as encryptEnv,B as encryptValue,he as env,Oe as envEnum,W as envKeyNames,Ze as envPlugin,lt as foreignTenantKeys,k as generateKeypair,Hn as getEnv,Xn as getKeypair,q as getPrivateKey,Ae as hasTTY,tt as hasWindow,ye as isBun,rn as isCI,it as isColorSupported,rt as isDebug,dn as isEncryptedValue,mn as isLegacyEncryptedValue,nt as isLinux,et as isMacOS,sn as isMinimal,ve as isNode,Zn as isWindows,ge as loadEnv,Pn as loadEnvFiles,yn as migrateEncryptedValue,I as parse,vn as parseEnvFromKey,fn as partitionTenantEnv,ct as plaintextSecrets,te as platform,_n as process,on as provider,st as providerInfo,At as requireEnv,Un as resetPrivateKeyCache,Yn as resolveEnvFile,Je as resolvePrivateKey,me as reusableEnvPublicKey,jn as rotateKeypair,tn as runtime,Jn as runtimeInfo,Gn as setEnv,Et as stripForeignTenantEnv,Ie as tenantEnvPrefix,ut as trackedEnvFiles,vt as validateEnv,yt as writeEnv};
26
+ `}function zn(e={}){let n=tn({file:e.file,keysFile:e.keysFile,stdout:!0,cwd:e.cwd});if(!n.success)return n;if(n.output===void 0)return{success:!1,error:"Rotation produced no decrypted content"};let t=e.cwd||process.cwd(),r=e.file||".env",i=e.keysFile||".env.keys",s=A(t,r),o=A(t,i),{publicKeyName:d,privateKeyName:c}=x(e.file);if(e.dryRun){let _=nn(n.output,e);return{success:!0,output:["Dry run: nothing was written.",`${r}: ${_.length} value${_.length===1?"":"s"} would be re-encrypted under a new ${d}.`,`${i}: ${d} and ${c} would be replaced.`,`The current ${c} decrypts nothing once that happens, so keep a copy until the rotation is verified.`].join(`
27
+ `)}}let u=k(),a=Re(n.output,u.publicKey,d,e);if(e.stdout)return{success:!0,output:a,notice:[`Nothing was written. To adopt the output above, put this keypair in ${i}:`,"",`${d}="${u.publicKey}"`,`${c}="${u.privateKey}"`,"","Without it that output cannot be decrypted. Run without --stdout to have both applied together."].join(`
28
+ `)};let f=O(s),p=O(o),E=rn(p.toString("utf8"),u,d,c),l=`${process.pid}-${Date.now()}`,T=A(Te(s),`.${_e(s)}.${l}.rotate`),v=A(Te(o),`.${_e(o)}.${l}.rotate`);try{h(T,a,{encoding:"utf8",mode:384}),h(v,E,{encoding:"utf8",mode:384}),pe(T,s);try{pe(v,o)}catch(_){throw h(s,f),_}return{success:!0,output:`\u2714 rotated (${r}) to encrypted:v2`}}catch(_){if(!N(o))h(o,p,{mode:384});return{success:!1,error:`Failed to rotate without exposing plaintext: ${_ instanceof Error?_.message:"Unknown error"}`}}finally{ge(T,{force:!0}),ge(v,{force:!0})}}import g from"process";import{platform as sn}from"os";var ve=typeof Bun<"u",Ae=typeof g<"u"&&g.versions?.node!==void 0,on=ve?"bun":Ae?"node":"unknown",nt={name:on,version:ve?Bun.version:Ae?g.version:void 0},ee=sn(),tt=ee==="win32",rt=ee==="darwin",it=ee==="linux",Oe=Boolean(g.stdout?.isTTY),st=typeof globalThis.window<"u",dn=Boolean(g.env.CI||g.env.CONTINUOUS_INTEGRATION||g.env.BUILD_NUMBER||g.env.RUN_ID),ot=Boolean(g.env.DEBUG||g.env.VERBOSE||g.argv.includes("--debug")||g.argv.includes("--verbose")),cn=dn||!Oe,dt=Boolean(!cn&&(Oe||g.env.COLORTERM||g.env.FORCE_COLOR||g.env.TERM&&g.env.TERM!=="dumb")),Z={github:{name:"GitHub Actions",detected:Boolean(g.env.GITHUB_ACTIONS)},gitlab:{name:"GitLab CI",detected:Boolean(g.env.GITLAB_CI)},circle:{name:"CircleCI",detected:Boolean(g.env.CIRCLECI)},travis:{name:"Travis CI",detected:Boolean(g.env.TRAVIS)},jenkins:{name:"Jenkins",detected:Boolean(g.env.JENKINS_URL)},vercel:{name:"Vercel",detected:Boolean(g.env.VERCEL)},netlify:{name:"Netlify",detected:Boolean(g.env.NETLIFY)},heroku:{name:"Heroku",detected:Boolean(g.env.DYNO)},aws:{name:"AWS",detected:Boolean(g.env.AWS_REGION||g.env.AWS_LAMBDA_FUNCTION_NAME)},azure:{name:"Azure",detected:Boolean(g.env.AZURE_HTTP_USER_AGENT)},cloudflare:{name:"Cloudflare",detected:Boolean(g.env.CF_PAGES)},railway:{name:"Railway",detected:Boolean(g.env.RAILWAY_ENVIRONMENT)},render:{name:"Render",detected:Boolean(g.env.RENDER)}},an=Object.keys(Z).find((e)=>{let n=Z[e];return n!==void 0&&n.detected})||"unknown",ct=Z[an]||{name:"Unknown",detected:!1};import gn from"process";import{projectPath as _n}from"@stacksjs/path";import he from"fs";var Ie={APP_ENV:["local","dev","development","staging","prod","production"],DB_CONNECTION:["mysql","sqlite","postgres","singlestore","vitess","dynamodb"],DB_MIGRATE_FRESH:["allow","confirm","disabled"],MAIL_MAILER:["smtp","mailgun","ses","log","sendgrid","mailtrap","capture"],SEARCH_ENGINE_DRIVER:["opensearch","meilisearch","algolia","typesense"],FRONTEND_APP_ENV:["development","staging","production"]};function ut(e){return e}function un(e){return typeof e==="string"&&(e.startsWith("encrypted:")||e.startsWith("enc:"))}var fn=/(?:^|_)(?:PASSWORD|PASSWD|SECRET|TOKEN|APIKEY|API_KEY|CREDENTIAL|CREDENTIALS|PRIVATE|SALT|DSN|CERT|CIPHER|SIGNATURE|WEBHOOK)(?:_|$)|(?:^|_)(?:ACCESS|SECRET)_KEY(?:_|$)|_KEY$/,En=/^DOTENV_(?:PUBLIC|PRIVATE)_KEY/,ln=/^\$\{[^}]+\}$/;function Et(e,n={}){let{placeholders:t={},strict:r=!1}=n,i=[];for(let[s,o]of Object.entries(e)){if(!o||o.trim()==="")continue;if(un(o))continue;if(En.test(s))continue;if(ln.test(o.trim()))continue;let d=fn.test(s),c=Object.prototype.hasOwnProperty.call(t,s)&&t[s]===o;if(d&&!c){i.push({key:s,reason:"plaintext-secret"});continue}if(r)i.push({key:s,reason:"plaintext-value"})}return i}function lt(e){return e.split(`
29
+ `).map((n)=>n.trim()).filter(Boolean).filter((n)=>n!==".env.example").filter((n)=>/(?:^|\/)\.env(?:\.|$)/.test(n)).filter((n)=>!n.endsWith(".env.keys"))}function Ne(e){return`${e.replace(/[^a-z0-9]/gi,"").toUpperCase()}_`}function pn(e,n={}){let t=n.self?Ne(n.self):void 0,r=(n.tenants??[]).filter((o)=>typeof o==="string"&&o.trim().length>0).map((o)=>({slug:o,prefix:Ne(o)})).filter(({prefix:o})=>o!==t).sort((o,d)=>d.prefix.length-o.prefix.length),i={},s={};for(let[o,d]of Object.entries(e)){let c=r.find(({prefix:u})=>o.startsWith(u));if(c){(s[c.slug]??={})[o]=d;continue}i[o]=d}return{own:i,foreign:s}}function gt(e,n={}){return pn(e,n).own}function _t(e){return Object.entries(e.foreign).map(([n,t])=>({tenant:n,keys:Object.keys(t).sort()})).sort((n,t)=>n.tenant.localeCompare(t.tenant))}var Se=new Set;function Tn(e){if(Se.has(e))return;Se.add(e);let n=J();console.warn(`[env] warning: "${e}" is encrypted but no usable private key was found for the "${n}" environment; falling back to its default. Set DOTENV_PRIVATE_KEY_${n.toUpperCase()} or ship .env.keys.`)}var yn={get:(e,n)=>{let t=e,r=t[n];if(typeof r==="string"&&(r.startsWith("encrypted:")||r.startsWith("enc:"))){let s=Ee(r);if(s===void 0){Tn(n),delete t[n];return}t[n]=s,r=s}let i=["_PORT","_TIMEOUT","_TTL","_SIZE","_LIMIT","_MAX","_MIN","_INTERVAL","_RETRIES","_CONCURRENCY","_WORKERS","_CONNECTIONS"];if(typeof r==="string"&&/^\d+$/.test(r)&&!r.startsWith("0")&&i.some((s)=>n.endsWith(s)))return Number(r);if(typeof r==="string"){let s=r.toLowerCase();if(s==="true")return!0;if(s==="false")return!1}return r}};function Rn(){return typeof Bun<"u"?Bun.env:gn.env}var Pe=new Proxy(Rn(),yn);function Ot(e,n,t){let r=t?.path||_n(".env"),s=he.readFileSync(r,"utf-8").split(`
30
+ `),o=s.findIndex((u)=>u.startsWith(`${e}=`)),c=/[\s"'#$\\]/.test(n)?`"${n.replace(/"/g,"\\\"")}"`:n;if(o!==-1)s[o]=`${e}=${c}`;else s.push(`${e}=${c}`);he.writeFileSync(r,s.join(`
31
+ `))}function It(e=Pe){let n=[],t=e;for(let[r,i]of Object.entries(Ie)){let s=t[r];if(s!==void 0&&s!==""&&!i.includes(String(s)))n.push(`${r}="${s}" is not valid. Allowed values: ${i.join(", ")}`)}return n}function Nt(e,n=Pe){let t=[],r=n;for(let i of e){let s=r[i];if(s===void 0||s===""||s===null)t.push(i)}if(t.length>0)throw Error(`[env] Missing required environment variable(s): ${t.join(", ")}. Set them in .env or your process environment before booting.`);return n}export{fn as SECRET_NAME_PATTERN,J as activeEnvName,Ke as aesDecrypt,vn as aesEncrypt,Bn as autoLoadEnv,tn as decryptEnv,Ee as decryptEnvValue,L as decryptValue,ut as defineEnv,jn as encryptEnv,B as encryptValue,Pe as env,Ie as envEnum,x as envKeyNames,en as envPlugin,_t as foreignTenantKeys,k as generateKeypair,Qn as getEnv,qn as getKeypair,q as getPrivateKey,Oe as hasTTY,st as hasWindow,ve as isBun,dn as isCI,dt as isColorSupported,ot as isDebug,un as isEncryptedValue,An as isLegacyEncryptedValue,it as isLinux,rt as isMacOS,cn as isMinimal,Ae as isNode,tt as isWindows,le as loadEnv,bn as loadEnvFiles,On as migrateEncryptedValue,I as parse,In as parseEnvFromKey,pn as partitionTenantEnv,Et as plaintextSecrets,ee as platform,Rn as process,an as provider,ct as providerInfo,Nt as requireEnv,Un as resetPrivateKeyCache,Hn as resolveEnvFile,Ze as resolvePrivateKey,me as reusableEnvPublicKey,zn as rotateKeypair,on as runtime,nt as runtimeInfo,Xn as setEnv,gt as stripForeignTenantEnv,Ne as tenantEnvPrefix,lt as trackedEnvFiles,It as validateEnv,Ot as writeEnv};
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/env",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.73.2",
5
+ "version": "0.74.0",
6
6
  "description": "Stacks env helper methods.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -56,10 +56,10 @@
56
56
  "prepublishOnly": "bun run build"
57
57
  },
58
58
  "dependencies": {
59
- "@stacksjs/path": "0.73.2"
59
+ "@stacksjs/path": "0.74.0"
60
60
  },
61
61
  "devDependencies": {
62
62
  "better-dx": "^0.2.24",
63
- "@stacksjs/validation": "0.73.2"
63
+ "@stacksjs/validation": "0.74.0"
64
64
  }
65
65
  }