@super-repo/envx 0.2.3-b.2 → 0.2.3-b.3

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"src-CwrtyfZE.js","names":[],"sources":["../../../libs/src/crypto.ts","../../../libs/src/parser.ts","../../../common/src/logger.ts","../../../libs/src/expand.ts","../../../libs/src/env.ts","../../../libs/src/keys.ts","../../../libs/src/match.ts","../../../libs/src/encrypt.ts","../../../libs/src/decrypt.ts","../../../libs/src/rotate.ts","../../../libs/src/config.ts","../../../libs/src/index.ts"],"sourcesContent":["import { PrivateKey, encrypt as eciesEncrypt, decrypt as eciesDecrypt } from \"eciesjs\";\n\n// #region -- Format Constants ------------------------------\n\n/**\n * Visual prefix for encrypted values. Matches upstream `dotenvx` so\n * encrypted entries are recognizable in diffs and across tools.\n *\n * \"encrypted:\" + base64(<eciesjs blob>)\n *\n * The blob layout is delegated to `eciesjs` (secp256k1 + AES-256-GCM)\n * so files encrypted by either envx or dotenvx round-trip under the\n * other given the matching private key.\n */\nexport const ENCRYPTED_PREFIX = \"encrypted:\";\n\n// #endregion -----------------------------------------------\n\n// #region -- Predicate -------------------------------------\n\n/** Returns true if `value` looks like one of our ciphertext blobs. */\nexport function isEncrypted(value: string): boolean {\n return value.startsWith(ENCRYPTED_PREFIX);\n}\n\n// #endregion -----------------------------------------------\n\n// #region -- Asymmetric (ECIES) ----------------------------\n\n/**\n * Generate a fresh secp256k1 keypair, returned as hex strings:\n * - publicKey: 33-byte compressed public key (66 hex chars)\n * - privateKey: 32-byte scalar (64 hex chars)\n *\n * Wire-format compatible with the upstream `dotenvx` convention so files\n * encrypted by either tool can be decrypted by the other.\n */\nexport function generateKeyPair(): { publicKey: string; privateKey: string } {\n const sk = new PrivateKey();\n // eciesjs ≥0.4 returns Uint8Array; wrap with Buffer to use the\n // hex-encoding overload of toString().\n return {\n privateKey: Buffer.from(sk.secret).toString(\"hex\"),\n publicKey: Buffer.from(sk.publicKey.compressed).toString(\"hex\"),\n };\n}\n\n/**\n * ECIES-encrypt `plaintext` to a recipient's compressed secp256k1 public\n * key (hex). Anyone with `publicKeyHex` can encrypt; only the matching\n * private-key holder can decrypt.\n */\nexport function encryptValueAsymmetric(plaintext: string, publicKeyHex: string): string {\n try {\n const blob = eciesEncrypt(publicKeyHex, Buffer.from(plaintext, \"utf8\"));\n return ENCRYPTED_PREFIX + Buffer.from(blob).toString(\"base64\");\n } catch (e) {\n throw makeError(\"ENCRYPTION_FAILED\", `ECIES encrypt failed: ${(e as Error).message}`);\n }\n}\n\n/** ECIES-decrypt a `encrypted:<base64>` blob using the recipient's secp256k1 private key (hex). */\nexport function decryptValueAsymmetric(blob: string, privateKeyHex: string): string {\n if (!isEncrypted(blob)) {\n throw makeError(\"INVALID_CIPHERTEXT\", `value does not start with \"${ENCRYPTED_PREFIX}\"`);\n }\n const data = Buffer.from(blob.slice(ENCRYPTED_PREFIX.length), \"base64\");\n try {\n const out = eciesDecrypt(privateKeyHex, data);\n return Buffer.from(out).toString(\"utf8\");\n } catch (e) {\n throw makeError(\"DECRYPTION_FAILED\", `ECIES decrypt failed: ${(e as Error).message}`);\n }\n}\n\n// #endregion -----------------------------------------------\n\n// #region -- Errors ----------------------------------------\n\ninterface CryptoError extends Error {\n code: string;\n}\n\nfunction makeError(code: string, message: string): CryptoError {\n const e = new Error(message) as CryptoError;\n e.code = code;\n return e;\n}\n\n// #endregion -----------------------------------------------\n","// #region -- Env File Parser -------------------------------\n\n/**\n * Line-preserving env-file parser. We parse to a typed line array\n * rather than a flat key→value map so that round-tripping (encrypt\n * one value, write file back) preserves comments, blank lines, and\n * declaration order — the signal-to-noise ratio of an env file's diff\n * is critical when these files live in version control.\n */\nexport interface KvLine {\n readonly type: \"kv\";\n readonly key: string;\n /** Raw value as it appeared in the file (still quoted if it was). */\n readonly raw: string;\n /** Logical value — quotes stripped, escape sequences resolved. */\n value: string;\n /** Quote style for serialization. Mutable so transforms (e.g. encrypt)\n * can drop quotes when the new value is self-quote-safe (URL-safe base64). */\n quote: '\"' | \"'\" | \"\";\n /** Trailing comment, including the leading '#' and whitespace. */\n readonly trailing: string;\n}\n\nexport interface RawLine {\n readonly type: \"raw\";\n /** Comment lines, blank lines, malformed lines — preserved verbatim. */\n readonly raw: string;\n}\n\nexport type EnvLine = KvLine | RawLine;\n\nconst KV_REGEX = /^\\s*([A-Za-z_][A-Za-z0-9_]*)\\s*=\\s*(.*?)\\s*$/;\n\n/** Parse a raw env-file string into a line array. */\nexport function parseEnv(content: string): EnvLine[] {\n const lines = content.split(\"\\n\");\n // If the file has a trailing newline, split() leaves a trailing \"\".\n // Drop it so we don't emit an extra blank raw line on round-trip.\n const trailingEmpty = lines.length > 0 && lines[lines.length - 1] === \"\";\n const body = trailingEmpty ? lines.slice(0, -1) : lines;\n\n const out: EnvLine[] = body.map((line) => {\n if (line.trim() === \"\" || line.trim().startsWith(\"#\")) {\n return { type: \"raw\", raw: line };\n }\n const match = KV_REGEX.exec(line);\n if (!match) return { type: \"raw\", raw: line };\n\n const key = match[1]!;\n const rawValueAndComment = match[2]!;\n const { rawValue, trailing } = splitTrailingComment(rawValueAndComment);\n const { value, quote } = unquote(rawValue);\n\n return { type: \"kv\", key, raw: rawValue, value, quote, trailing };\n });\n\n if (trailingEmpty) out.push({ type: \"raw\", raw: \"\" });\n return out;\n}\n\n/** Re-serialize a parsed line array. Round-trip safe for unmodified inputs. */\nexport function serializeEnv(lines: readonly EnvLine[]): string {\n return lines\n .map((line) => {\n if (line.type === \"raw\") return line.raw;\n const valueText = renderValue(line.value, line.quote);\n return `${line.key}=${valueText}${line.trailing}`;\n })\n .join(\"\\n\");\n}\n\n// #endregion -----------------------------------------------\n\n// #region -- Internal helpers ------------------------------\n\nfunction splitTrailingComment(s: string): { rawValue: string; trailing: string } {\n // Quoted values: anything past the closing quote is trailing\n // whitespace + comment, returned verbatim so round-tripping is exact.\n if (s.startsWith('\"') || s.startsWith(\"'\")) {\n const quote = s[0]!;\n let i = 1;\n while (i < s.length) {\n if (s[i] === \"\\\\\") {\n i += 2;\n continue;\n }\n if (s[i] === quote) {\n i += 1;\n break;\n }\n i += 1;\n }\n return { rawValue: s.slice(0, i), trailing: s.slice(i) };\n }\n // Unquoted: # starts a comment when preceded by whitespace.\n const m = /\\s+#.*$/.exec(s);\n if (!m) return { rawValue: s, trailing: \"\" };\n return {\n rawValue: s.slice(0, m.index),\n trailing: s.slice(m.index),\n };\n}\n\nfunction unquote(raw: string): { value: string; quote: '\"' | \"'\" | \"\" } {\n if (raw.length >= 2 && raw.startsWith('\"') && raw.endsWith('\"')) {\n return { value: unescapeDoubleQuoted(raw.slice(1, -1)), quote: '\"' };\n }\n if (raw.length >= 2 && raw.startsWith(\"'\") && raw.endsWith(\"'\")) {\n return { value: raw.slice(1, -1), quote: \"'\" };\n }\n return { value: raw, quote: \"\" };\n}\n\nfunction unescapeDoubleQuoted(s: string): string {\n return s\n .replace(/\\\\\\\\/g, \"\u0000\")\n .replace(/\\\\n/g, \"\\n\")\n .replace(/\\\\r/g, \"\\r\")\n .replace(/\\\\t/g, \"\\t\")\n .replace(/\\\\\"/g, '\"')\n .replace(/\u0000/g, \"\\\\\");\n}\n\nfunction escapeDoubleQuoted(s: string): string {\n return s\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, \"\\\\n\")\n .replace(/\\r/g, \"\\\\r\")\n .replace(/\\t/g, \"\\\\t\");\n}\n\nfunction renderValue(value: string, originalQuote: '\"' | \"'\" | \"\"): string {\n // Decide a safe quote style. If the value contains characters that\n // would break unquoted form (whitespace, $, #, ', \"), force quoting.\n const needsQuotes = /[\\s#$\"'`\\\\]/.test(value) || value === \"\";\n if (originalQuote === \"'\" && !value.includes(\"'\")) {\n return `'${value}'`;\n }\n if (originalQuote === \"'\" && value.includes(\"'\")) {\n // Single quotes can't escape — fall back to double.\n return `\"${escapeDoubleQuoted(value)}\"`;\n }\n if (originalQuote === '\"') {\n return `\"${escapeDoubleQuoted(value)}\"`;\n }\n // No original quotes — keep unquoted unless we have to.\n if (!needsQuotes) return value;\n return `\"${escapeDoubleQuoted(value)}\"`;\n}\n\n// #endregion -----------------------------------------------\n\n// #region -- Convenience -----------------------------------\n\n/** Build a flat key→value map from a parsed line array. Last write wins. */\nexport function toRecord(lines: readonly EnvLine[]): Record<string, string> {\n const out: Record<string, string> = {};\n for (const line of lines) {\n if (line.type === \"kv\") out[line.key] = line.value;\n }\n return out;\n}\n\n// #endregion -----------------------------------------------\n","// #region -- ANSI Color Codes -----------------------------\n\nconst RESET = '\\x1b[0m'\nconst RED = '\\x1b[31m'\nconst GREEN = '\\x1b[32m'\nconst YELLOW = '\\x1b[33m'\nconst CYAN = '\\x1b[36m'\nconst DIM = '\\x1b[2m'\n\n// #endregion -- ANSI Color Codes --------------------------\n\n// #region -- Logger Interface -----------------------------\n\nexport interface Logger {\n readonly success: (msg: string) => void\n readonly error: (msg: string) => void\n readonly warn: (msg: string) => void\n readonly info: (msg: string) => void\n readonly dim: (msg: string) => void\n}\n\n// #endregion -- Logger Interface --------------------------\n\n// #region -- Logger Implementation ------------------------\n\nexport const log: Logger = {\n success: (msg: string): void => {\n console.log(`${GREEN}${msg}${RESET}`)\n },\n error: (msg: string): void => {\n console.error(`${RED}${msg}${RESET}`)\n },\n warn: (msg: string): void => {\n console.log(`${YELLOW}${msg}${RESET}`)\n },\n info: (msg: string): void => {\n console.log(`${CYAN}${msg}${RESET}`)\n },\n dim: (msg: string): void => {\n console.log(`${DIM}${msg}${RESET}`)\n },\n}\n\n// #endregion -- Logger Implementation ---------------------\n","import { parseEnv, serializeEnv } from \"./parser.js\";\n\n// #region -- Variable Expansion ----------------------------\n\n/**\n * Expand `${VAR}`, `$VAR`, `${VAR:-default}`, and `${VAR:?error}` references\n * in env-file values. Iterative + cycle-safe: builds a dependency graph,\n * topologically substitutes, and bails with a clear error on cycles.\n *\n * More robust than the bash version in `.github/actions/decrypt-vault`:\n * - Handles `$VAR` (bare) and `${VAR}` syntactically.\n * - Supports `${VAR:-default}` (use default if VAR is unset/empty)\n * and `${VAR:?msg}` (error if unset/empty).\n * - Supports `\\${VAR}` and `\\$VAR` escapes for literal `${VAR}` / `$VAR`.\n * - Detects cycles instead of silently truncating after N passes.\n * - Reports each unresolved variable, doesn't silently leave them.\n */\n\nexport interface ExpandOptions {\n /**\n * Variables to layer in beneath the file's own values. The file's\n * values take precedence; this is the fallback (typically process.env).\n */\n readonly fallback?: Readonly<Record<string, string | undefined>>;\n /**\n * What to do when a `${UNSET_VAR}` reference can't be resolved.\n * - \"leave\" (default): keep the literal `${UNSET_VAR}` in place,\n * and add a warning to the result.\n * - \"empty\": substitute an empty string, add a warning.\n * - \"throw\": throw an Error listing all unresolved refs.\n */\n readonly onMissing?: \"leave\" | \"empty\" | \"throw\";\n}\n\nexport interface ExpandResult {\n /** Expanded key→value map (file-only — fallback is not included). */\n readonly values: Record<string, string>;\n /** Re-serialized env-file content with all values expanded. */\n readonly envSrc: string;\n /** Variables that were referenced but couldn't be resolved. */\n readonly unresolved: string[];\n /** Variables that participated in an unresolvable cycle. */\n readonly cycles: string[][];\n}\n\n/** Expand a key→value record. Returns the expanded record + diagnostics. */\nexport function expandRecord(\n values: Readonly<Record<string, string>>,\n opts: ExpandOptions = {},\n): { values: Record<string, string>; unresolved: string[]; cycles: string[][] } {\n const onMissing = opts.onMissing ?? \"leave\";\n const fallback = opts.fallback ?? {};\n\n // Iterative DFS-based resolution. Cache resolved values; track in-flight\n // keys to detect cycles.\n const resolved: Record<string, string> = {};\n const inFlight = new Set<string>();\n const unresolved = new Set<string>();\n const cycles: string[][] = [];\n\n function isDefined(name: string): boolean {\n if (Object.prototype.hasOwnProperty.call(values, name)) return true;\n if (Object.prototype.hasOwnProperty.call(fallback, name) && fallback[name] !== undefined) {\n return true;\n }\n return false;\n }\n\n function lookup(name: string, stack: string[]): string {\n if (Object.prototype.hasOwnProperty.call(resolved, name)) return resolved[name]!;\n if (inFlight.has(name)) {\n const cycleStart = stack.indexOf(name);\n cycles.push(stack.slice(cycleStart === -1 ? 0 : cycleStart).concat(name));\n return \"\";\n }\n if (Object.prototype.hasOwnProperty.call(values, name)) {\n inFlight.add(name);\n const result = expandString(values[name]!, [...stack, name]);\n inFlight.delete(name);\n resolved[name] = result;\n return result;\n }\n if (Object.prototype.hasOwnProperty.call(fallback, name)) {\n const v = fallback[name];\n if (v !== undefined) {\n resolved[name] = v;\n return v;\n }\n }\n return \"\";\n }\n\n function expandString(input: string, stack: string[]): string {\n return substitute(input, (varName, fallbackForm) => {\n const defined = isDefined(varName);\n\n // Fully unresolved: short-circuit on default/error before touching unresolved set.\n if (!defined) {\n if (fallbackForm?.kind === \"default\") {\n return expandString(fallbackForm.value, stack);\n }\n if (fallbackForm?.kind === \"error\") {\n throw new Error(\n `[${varName}:?] ${fallbackForm.value || \"variable is unset or empty\"}`,\n );\n }\n unresolved.add(varName);\n if (onMissing === \"empty\") return \"\";\n return \"${\" + varName + \"}\";\n }\n\n const v = lookup(varName, stack);\n // Defined but empty → POSIX :- and :? still treat empty as \"unset\".\n if (v === \"\" && fallbackForm?.kind === \"default\") {\n return expandString(fallbackForm.value, stack);\n }\n if (v === \"\" && fallbackForm?.kind === \"error\") {\n throw new Error(\n `[${varName}:?] ${fallbackForm.value || \"variable is unset or empty\"}`,\n );\n }\n return v;\n });\n }\n\n for (const key of Object.keys(values)) {\n if (resolved[key] === undefined && !inFlight.has(key)) {\n inFlight.add(key);\n try {\n resolved[key] = expandString(values[key]!, [key]);\n } finally {\n inFlight.delete(key);\n }\n }\n }\n\n if (onMissing === \"throw\" && (unresolved.size > 0 || cycles.length > 0)) {\n const parts: string[] = [];\n if (unresolved.size > 0) parts.push(`unresolved: ${[...unresolved].join(\", \")}`);\n if (cycles.length > 0) {\n parts.push(`cycles: ${cycles.map((c) => c.join(\" → \")).join(\"; \")}`);\n }\n throw new Error(`expansion failed — ${parts.join(\"; \")}`);\n }\n\n return { values: resolved, unresolved: [...unresolved], cycles };\n}\n\n/**\n * Expand variables in a parsed env file (string in, string out). The\n * file's own values take precedence; `opts.fallback` (default:\n * `process.env`) fills in the rest.\n */\nexport function expandEnvSrc(envSrc: string, opts: ExpandOptions = {}): ExpandResult {\n const lines = parseEnv(envSrc);\n const values: Record<string, string> = {};\n for (const line of lines) {\n if (line.type === \"kv\") values[line.key] = line.value;\n }\n const fallback = opts.fallback ?? process.env;\n const result = expandRecord(values, { ...opts, fallback });\n for (const line of lines) {\n if (line.type === \"kv\") line.value = result.values[line.key] ?? line.value;\n }\n return {\n values: result.values,\n envSrc: serializeEnv(lines),\n unresolved: result.unresolved,\n cycles: result.cycles,\n };\n}\n\n// #endregion -----------------------------------------------\n\n// #region -- Tokenization ----------------------------------\n\ninterface DefaultRef {\n readonly kind: \"default\";\n readonly value: string;\n}\n\ninterface ErrorRef {\n readonly kind: \"error\";\n readonly value: string;\n}\n\ntype FallbackForm = DefaultRef | ErrorRef | undefined;\n\ntype Substituter = (name: string, fallback: FallbackForm) => string;\n\n/**\n * Walk the input character-by-character. Recognizes:\n * - `\\$` — escape, keep literal `$`\n * - `\\${` — escape, keep literal `${`\n * - `${NAME}`, `${NAME:-default}`, `${NAME:?msg}`\n * - `$NAME`\n */\nfunction substitute(input: string, substituter: Substituter): string {\n let out = \"\";\n let i = 0;\n while (i < input.length) {\n const c = input[i]!;\n if (c === \"\\\\\" && (input[i + 1] === \"$\" || input[i + 1] === \"\\\\\")) {\n out += input[i + 1];\n i += 2;\n continue;\n }\n if (c !== \"$\") {\n out += c;\n i += 1;\n continue;\n }\n // Possible $... reference\n const next = input[i + 1];\n if (next === \"{\") {\n const close = findMatchingBrace(input, i + 1);\n if (close === -1) {\n // No closing brace; treat as literal\n out += c;\n i += 1;\n continue;\n }\n const inner = input.slice(i + 2, close);\n const { name, fallback } = parseRef(inner);\n if (!isValidName(name)) {\n out += input.slice(i, close + 1);\n } else {\n out += substituter(name, fallback);\n }\n i = close + 1;\n continue;\n }\n if (next !== undefined && /[A-Za-z_]/.test(next)) {\n let j = i + 1;\n while (j < input.length && /[A-Za-z0-9_]/.test(input[j]!)) j++;\n const name = input.slice(i + 1, j);\n out += substituter(name, undefined);\n i = j;\n continue;\n }\n // Lone $\n out += c;\n i += 1;\n }\n return out;\n}\n\nfunction findMatchingBrace(s: string, openIdx: number): number {\n let depth = 0;\n for (let i = openIdx; i < s.length; i++) {\n if (s[i] === \"{\") depth++;\n else if (s[i] === \"}\") {\n depth--;\n if (depth === 0) return i;\n }\n }\n return -1;\n}\n\nfunction parseRef(inner: string): { name: string; fallback: FallbackForm } {\n // ${NAME:-default}\n let idx = inner.indexOf(\":-\");\n if (idx !== -1) {\n return {\n name: inner.slice(0, idx),\n fallback: { kind: \"default\", value: inner.slice(idx + 2) },\n };\n }\n // ${NAME:?msg}\n idx = inner.indexOf(\":?\");\n if (idx !== -1) {\n return {\n name: inner.slice(0, idx),\n fallback: { kind: \"error\", value: inner.slice(idx + 2) },\n };\n }\n return { name: inner, fallback: undefined };\n}\n\nfunction isValidName(name: string): boolean {\n return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name);\n}\n\n// #endregion -----------------------------------------------\n","import * as fs from \"fs\";\nimport * as path from \"path\";\nimport * as dotenv from \"@dotenvx/dotenvx\";\n\nimport { log } from \"@honeycluster/common\";\n\nimport { expandRecord } from \"./expand.js\";\n\n// #region -- Environment Detection -------------------------\n\n/**\n * Built-in `NODE_ENV → suffix` mapping. Users can extend or override\n * via `nodeEnvMap` in `envx.config.{ts,js,json}`. Anything NOT in the\n * resolved map passes through as the lowercased `NODE_ENV` value —\n * so `NODE_ENV=staging` becomes the `staging` suffix → `.env.staging`.\n */\nexport const DEFAULT_NODE_ENV_MAP: Readonly<Record<string, string>> = {\n production: \"prod\",\n development: \"dev\",\n local: \"local\",\n};\n\nexport interface DetectEnvironmentOptions {\n /** Override the NODE_ENV → suffix mapping. Merged on top of the defaults. */\n readonly nodeEnvMap?: Readonly<Record<string, string>>;\n}\n\n/**\n * Detect the deployment environment from well-known platform variables.\n * Order of precedence: Vercel → Netlify → `NODE_ENV` → 'root'.\n *\n * `NODE_ENV` resolution:\n * 1. If the lowercased value is in `opts.nodeEnvMap` (or the built-in\n * `DEFAULT_NODE_ENV_MAP`), use the mapped suffix. Empty string\n * means \"no suffix\" (just `.env`).\n * 2. Otherwise, use the lowercased value directly so unmapped values\n * like `staging`, `qa`, `preview` resolve to `.env.<value>`\n * automatically.\n */\nexport function detectEnvironment(opts: DetectEnvironmentOptions = {}): string {\n if (process.env[\"VERCEL\"]) {\n if (process.env[\"VERCEL_ENV\"] === \"production\") return \"prod\";\n return \"dev\";\n }\n\n if (process.env[\"NETLIFY\"]) {\n if (process.env[\"CONTEXT\"] === \"production\") return \"prod\";\n if (\n process.env[\"CONTEXT\"] === \"deploy-preview\" ||\n process.env[\"CONTEXT\"] === \"branch-deploy\"\n ) {\n return \"dev\";\n }\n return \"dev\";\n }\n\n const nodeEnv = process.env[\"NODE_ENV\"];\n if (nodeEnv) {\n const lowered = nodeEnv.toLowerCase();\n const map = { ...DEFAULT_NODE_ENV_MAP, ...(opts.nodeEnvMap ?? {}) };\n if (Object.prototype.hasOwnProperty.call(map, lowered)) {\n // Mapped value — empty string means \"no suffix\" (just `.env`).\n return map[lowered] === \"\" ? \"root\" : map[lowered]!;\n }\n // Unmapped — pass through as the suffix (so NODE_ENV=staging → \"staging\").\n return lowered;\n }\n\n return \"root\";\n}\n\n// #endregion -- Environment Detection ----------------------\n\n// #region -- CLI Variable Parsing --------------------------\n\n/**\n * Parse a `KEY=value` string from `-v KEY=value` into a `[key, value]` tuple.\n * Calls `process.exit(1)` on malformed input.\n */\nexport function validateCmdVariable(param: string): [string, string] {\n const match = param.match(/^(\\w+)=([\\s\\S]+)$/m);\n if (!match) {\n log.error(\n `Invalid variable. Expected '-v variable=value', got: \\`-v ${param}\\`.`,\n );\n process.exit(1);\n }\n const [, key, val] = match;\n if (!key || !val) {\n log.error(\n `Invalid variable. Expected '-v variable=value', got: \\`-v ${param}\\`.`,\n );\n process.exit(1);\n }\n return [key, val];\n}\n\n// #endregion -- CLI Variable Parsing -----------------------\n\n// #region -- Workspace Resolution --------------------------\n\nconst WORKSPACE_INDICATORS = [\n \"pnpm-workspace.yaml\",\n \"lerna.json\",\n \"nx.json\",\n \"rush.json\",\n \"yarn.lock\",\n \"pnpm-lock.yaml\",\n] as const;\n\n/**\n * Walk up from `startDir` looking for a `package.json` that declares\n * `workspaces` (npm/yarn) or `pnpm.workspaces`. Falls back to other\n * monorepo indicators (pnpm-workspace.yaml, nx.json, …). Returns\n * `startDir` if nothing is found.\n */\nexport function findWorkspaceRoot(startDir: string = process.cwd()): string {\n const root = path.parse(startDir).root;\n\n let currentDir = startDir;\n while (currentDir !== root) {\n const packageJsonPath = path.join(currentDir, \"package.json\");\n try {\n const packageJsonContent = fs.readFileSync(packageJsonPath, \"utf8\");\n const packageJson = JSON.parse(packageJsonContent);\n if (packageJson.workspaces || packageJson.pnpm?.workspaces) {\n return currentDir;\n }\n } catch {\n // package.json absent or unreadable — keep walking\n }\n currentDir = path.dirname(currentDir);\n }\n\n currentDir = startDir;\n while (currentDir !== root) {\n for (const indicator of WORKSPACE_INDICATORS) {\n if (fs.existsSync(path.join(currentDir, indicator))) {\n return currentDir;\n }\n }\n currentDir = path.dirname(currentDir);\n }\n\n return startDir;\n}\n\n/**\n * Resolve a relative path against `cwd` first; if nothing exists at the\n * cwd-rooted location, walk up to the workspace root and try there. If\n * neither exists, return the cwd-rooted path so callers downstream (who\n * may want to *create* the file/dir, e.g. `envx encrypt` writing a fresh\n * `.env.keys`) get the user's expected location.\n *\n * Absolute paths are returned verbatim.\n *\n * resolveCwdOrWorkspace(\".env.keys\", \"/repo/packages/web\")\n * → \"/repo/packages/web/.env.keys\" // exists in cwd\n * → \"/repo/.env.keys\" // exists at workspace root\n * → \"/repo/packages/web/.env.keys\" // neither — caller decides\n */\nexport function resolveCwdOrWorkspace(\n relPath: string,\n cwd: string = process.cwd(),\n): string {\n if (path.isAbsolute(relPath)) return relPath;\n const cwdResolved = path.resolve(cwd, relPath);\n if (fs.existsSync(cwdResolved)) return cwdResolved;\n const wsRoot = findWorkspaceRoot(cwd);\n if (wsRoot !== cwd) {\n const wsResolved = path.resolve(wsRoot, relPath);\n if (fs.existsSync(wsResolved)) return wsResolved;\n }\n return cwdResolved;\n}\n\n// #endregion -- Workspace Resolution -----------------------\n\n// #region -- Env File Discovery ----------------------------\n\n/**\n * List every `.env*` file in `dir`, sorted by name. Skips dotfile\n * subdirectories. Returns the basenames (not absolute paths) so callers\n * can hand them straight to `loadEnv` / `encryptFiles` / `decryptFiles`.\n *\n * Used by the CLI when `envPath`/`--vault` is set and `--env` is\n * omitted: the user's intent is \"all of the vault\", not just `.env`.\n */\nexport function listEnvFiles(dir: string): string[] {\n if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) return [];\n return fs\n .readdirSync(dir, { withFileTypes: true })\n .filter((d) => d.isFile())\n .map((d) => d.name)\n .filter((n) => n === \".env\" || n.startsWith(\".env.\"))\n .filter((n) => n !== \".env.keys\") // never operate on the keys file itself\n .sort();\n}\n\n// #endregion -----------------------------------------------\n\n// #region -- Cascade Path Expansion ------------------------\n\n/**\n * Expand env file paths according to the cascade strategy. For each\n * base path `p` and a `cascadeName` like `\"prod\"`, produces:\n *\n * [`${p}.prod.local`, `${p}.local`, `${p}.prod`, p]\n *\n * Order is most-specific-first so callers (which load them in order\n * with later entries overriding earlier ones) end up with the most\n * specific values winning at the lowest index.\n */\nexport function expandCascadePaths(\n paths: string[],\n cascadeName: string,\n): string[] {\n return paths.reduce<string[]>(\n (acc, p) =>\n acc.concat([`${p}.${cascadeName}.local`, `${p}.local`, `${p}.${cascadeName}`, p]),\n [],\n );\n}\n\n// #endregion -- Cascade Path Expansion ---------------------\n\n// #region -- Env Loading -----------------------------------\n\nexport interface ResolveEnvOptions {\n readonly envFiles?: string[] | string;\n /**\n * - `string` (e.g. `\"prod\"`) — cascade with this explicit name.\n * - `true` — cascade with the auto-detected environment name (if\n * detection produces `\"root\"`, no cascade is applied since there's\n * nothing to layer).\n * - `false` / undefined — no cascade.\n */\n readonly cascade?: string | boolean;\n /** Default `true`. Set to `false` to disable platform auto-detection. */\n readonly autoDetect?: boolean;\n /** Override the built-in `NODE_ENV → suffix` mapping. */\n readonly nodeEnvMap?: Readonly<Record<string, string>>;\n}\n\n/**\n * Resolve which `.env*` paths to load given the user's options. Handles\n * the auto-detect default (when only `.env` was passed), prefixing\n * bare names with `.env.`, and cascade expansion.\n */\nexport function resolveEnvPaths(opts: ResolveEnvOptions): string[] {\n const { cascade, autoDetect = true } = opts;\n const rawFiles = opts.envFiles ?? [\".env\"];\n const envFiles = Array.isArray(rawFiles) ? [...rawFiles] : [rawFiles];\n\n // Run detection once — both the auto-detect rewrite and a boolean\n // cascade name may need it.\n const detectOpts: DetectEnvironmentOptions = opts.nodeEnvMap !== undefined\n ? { nodeEnvMap: opts.nodeEnvMap }\n : {};\n const detected = autoDetect ? detectEnvironment(detectOpts) : \"root\";\n\n if (autoDetect && envFiles.length === 1 && envFiles[0] === \".env\" && detected !== \"root\") {\n envFiles[0] = `.env.${detected}`;\n log.dim(\n `Auto-detected environment: ${detected} ` +\n `(NODE_ENV=${process.env[\"NODE_ENV\"] ?? \"undefined\"}, ` +\n `VERCEL=${process.env[\"VERCEL\"] ?? \"undefined\"}, ` +\n `VERCEL_ENV=${process.env[\"VERCEL_ENV\"] ?? \"undefined\"}, ` +\n `NETLIFY=${process.env[\"NETLIFY\"] ?? \"undefined\"}, ` +\n `CONTEXT=${process.env[\"CONTEXT\"] ?? \"undefined\"})`,\n );\n }\n\n // Resolve `cascade: true` to the detected env name. If detection\n // produced `root` (no signal), drop the cascade — there's no\n // suffix to layer.\n const cascadeName: string | undefined =\n typeof cascade === \"string\"\n ? cascade\n : cascade === true && detected !== \"root\"\n ? detected\n : undefined;\n\n let paths = envFiles.map((e) =>\n path.isAbsolute(e) || e.startsWith(\".env\") ? e : `.env.${e}`,\n );\n if (cascadeName !== undefined) paths = expandCascadePaths(paths, cascadeName);\n return paths;\n}\n\nexport interface LoadEnvOptions {\n readonly envFiles?: string[] | string;\n readonly variables?: string[] | string;\n /**\n * - `string` — cascade with this explicit name.\n * - `true` — cascade with the auto-detected env name.\n * - `false` / undefined — no cascade.\n */\n readonly cascade?: string | boolean;\n /**\n * `true` is shorthand for `envPath: \"vault\"`. A string sets the\n * subdirectory explicitly. Either way, relative env files resolve to\n * `<workspaceRoot>/<envPath>/<file>`.\n */\n readonly vault?: boolean;\n /** Subdirectory of the workspace root where env files live. */\n readonly envPath?: string;\n readonly override?: boolean;\n readonly quiet?: boolean;\n /** Default `true`. Set to `false` to disable platform auto-detection. */\n readonly autoDetect?: boolean;\n /** Override the built-in `NODE_ENV → suffix` mapping. */\n readonly nodeEnvMap?: Readonly<Record<string, string>>;\n /**\n * Keys that MUST be set in `process.env` after loading completes.\n * Any missing values cause envx to log + `process.exit(1)`.\n */\n readonly required?: readonly string[];\n /**\n * Auto-resolve `${VAR}` / `$VAR` / `${VAR:-default}` / `${VAR:?msg}`\n * references in the loaded values after files load.\n */\n readonly expand?: boolean;\n /**\n * Fallback values for keys that are still unset after files +\n * `variables`. Different from `variables`, which always overrides.\n */\n readonly defaults?: Readonly<Record<string, string>>;\n /**\n * Explicit workspace root. If unset, envx walks up from cwd via\n * `findWorkspaceRoot()`.\n */\n readonly workspaceRoot?: string;\n}\n\n/**\n * Load env files (resolving relative paths against both the workspace\n * root and CWD) and apply `KEY=value` overrides from `--variables`.\n * Mutates `process.env`.\n */\nexport function loadEnv(opts: LoadEnvOptions): { paths: string[] } {\n const {\n cascade,\n vault = false,\n envPath,\n override = false,\n quiet = true,\n variables,\n required,\n expand = false,\n defaults,\n } = opts;\n\n if (cascade && override) {\n log.error(\"Invalid arguments. --cascade conflicts with --override.\");\n process.exit(1);\n }\n\n // envPath wins; --vault is a shortcut for \"vault\".\n const subdir = envPath ?? (vault ? \"vault\" : \"\");\n\n const paths = resolveEnvPaths({\n ...(opts.envFiles !== undefined ? { envFiles: opts.envFiles } : {}),\n ...(cascade !== undefined ? { cascade } : {}),\n ...(opts.autoDetect !== undefined ? { autoDetect: opts.autoDetect } : {}),\n ...(opts.nodeEnvMap !== undefined ? { nodeEnvMap: opts.nodeEnvMap } : {}),\n });\n // Use the explicit workspaceRoot if provided; otherwise walk up.\n // Relative paths in opts.workspaceRoot resolve against cwd.\n const workspaceRoot = opts.workspaceRoot\n ? path.resolve(process.cwd(), opts.workspaceRoot)\n : findWorkspaceRoot();\n\n paths.forEach((env) => {\n if (path.isAbsolute(env)) {\n log.dim(`Loading environment from: ${env}`);\n dotenv.config({ path: env, override, quiet });\n return;\n }\n\n const workspacePath = subdir\n ? path.resolve(workspaceRoot, subdir, env)\n : path.resolve(workspaceRoot, env);\n const workspaceKeysPath = path.resolve(workspaceRoot, \".env.keys\");\n const currentDirPath = subdir\n ? path.resolve(process.cwd(), subdir, env)\n : path.resolve(process.cwd(), env);\n const currentDirKeysPath = path.resolve(process.cwd(), \".env.keys\");\n\n const workspaceExists = fs.existsSync(workspacePath);\n const currentDirExists = fs.existsSync(currentDirPath);\n\n if (workspaceExists) {\n log.dim(`Loading environment from: ${workspacePath}`);\n dotenv.config({\n path: workspacePath,\n envKeysFile: workspaceKeysPath,\n override,\n quiet,\n });\n }\n\n if (currentDirExists) {\n log.dim(`Loading environment from: ${currentDirPath}`);\n dotenv.config({\n path: currentDirPath,\n envKeysFile: currentDirKeysPath,\n override,\n quiet,\n });\n }\n\n if (!workspaceExists && !currentDirExists) {\n log.dim(`Loading environment from: ${workspacePath}`);\n dotenv.config({\n path: workspacePath,\n envKeysFile: workspaceKeysPath,\n override,\n quiet,\n });\n }\n });\n\n if (variables !== undefined) {\n const list = Array.isArray(variables) ? variables : [variables];\n const parsed = Object.fromEntries(list.map(validateCmdVariable));\n Object.assign(process.env, parsed);\n }\n\n // Apply `defaults` AFTER files + variables, only for keys still unset.\n // Different from `variables`, which always overrides.\n if (defaults !== undefined) {\n for (const [key, value] of Object.entries(defaults)) {\n if (process.env[key] === undefined) {\n process.env[key] = value;\n }\n }\n }\n\n // Auto-expand `${VAR}` / `$VAR` references against process.env if\n // requested. Uses the same expansion engine as `envx expand`. Leaves\n // unresolved references as literal `${VAR}` so misconfigurations\n // surface visibly rather than silently emptying values.\n if (expand) {\n const snapshot: Record<string, string> = {};\n for (const [k, v] of Object.entries(process.env)) {\n if (typeof v === \"string\") snapshot[k] = v;\n }\n const { values: expanded, unresolved } = expandRecord(snapshot, {\n onMissing: \"leave\",\n });\n for (const [k, v] of Object.entries(expanded)) {\n process.env[k] = v;\n }\n if (unresolved.length > 0 && !quiet) {\n log.warn(`Unresolved variable references: ${unresolved.join(\", \")}`);\n }\n }\n\n // `required` gate. Run last so it sees the final shape of process.env\n // (after files, variables, defaults, and expansion).\n if (required && required.length > 0) {\n const missing = required.filter(\n (key) => process.env[key] === undefined || process.env[key] === \"\",\n );\n if (missing.length > 0) {\n log.error(\n `Missing required env variables: ${missing.join(\", \")}`,\n );\n log.dim(\n \"(set them in your env files, pass via -v KEY=VALUE, or add a `defaults` entry in envx.config)\",\n );\n process.exit(1);\n }\n }\n\n return { paths };\n}\n\n// #endregion -- Env Loading --------------------------------\n","import * as fs from \"fs\";\nimport * as path from \"path\";\n\nimport { resolveCwdOrWorkspace } from \"./env.js\";\nimport { parseEnv, serializeEnv, toRecord } from \"./parser.js\";\n\n// #region -- Per-file Key Naming ---------------------------\n\n/**\n * Canonical (default) prefix for envx private-key variables. Used when\n * encrypt writes a fresh entry to `.env.keys`.\n */\nexport const ENVX_PRIVATE_KEY_PREFIX = \"ENVX_PRIVATE_KEY\";\n\n/**\n * Canonical prefix for envx public-key variables. Stored in plaintext at\n * the top of the encrypted `.env*` file so anyone with read access can\n * encrypt new values; only the holder of the matching private key (in\n * `.env.keys`) can decrypt.\n */\nexport const ENVX_PUBLIC_KEY_PREFIX = \"ENVX_PUBLIC_KEY\";\n\n/**\n * Legacy prefix kept for backwards compatibility with the upstream\n * `dotenvx` convention. envx will read keys stored under this prefix\n * but never *writes* new ones with it.\n */\nexport const LEGACY_PRIVATE_KEY_PREFIX = \"DOTENV_PRIVATE_KEY\";\n\n/** Legacy public-key prefix (upstream dotenvx). Read-only fallback. */\nexport const LEGACY_PUBLIC_KEY_PREFIX = \"DOTENV_PUBLIC_KEY\";\n\n/**\n * Canonical private-key variable name for an env file:\n * `ENVX_PRIVATE_KEY` for `.env`, `ENVX_PRIVATE_KEY_PROD` for `.env.prod`,\n * etc. This is the name encrypt writes to `.env.keys`.\n *\n * Decrypt accepts both this and the legacy `DOTENV_PRIVATE_KEY*` form\n * — see {@link privateKeyCandidateNamesFor}.\n */\nexport function privateKeyNameFor(envFilePath: string): string {\n return privateKeyNameForWithPrefix(envFilePath, ENVX_PRIVATE_KEY_PREFIX);\n}\n\n/**\n * Both names a `.env.keys` file may use for the given env file's private\n * key, in resolution order:\n *\n * 1. `ENVX_PRIVATE_KEY*` (canonical — written by current envx)\n * 2. `DOTENV_PRIVATE_KEY*` (legacy — written by upstream dotenvx\n * and earlier envx versions)\n *\n * Callers should check each name against the keys map and use the first\n * match. New keys are always written under the canonical name.\n */\nexport function privateKeyCandidateNamesFor(\n envFilePath: string,\n): readonly string[] {\n return [\n privateKeyNameForWithPrefix(envFilePath, ENVX_PRIVATE_KEY_PREFIX),\n privateKeyNameForWithPrefix(envFilePath, LEGACY_PRIVATE_KEY_PREFIX),\n ];\n}\n\n/** Canonical `ENVX_PUBLIC_KEY*` variable name for an env file. */\nexport function publicKeyNameFor(envFilePath: string): string {\n return privateKeyNameForWithPrefix(envFilePath, ENVX_PUBLIC_KEY_PREFIX);\n}\n\n/**\n * Public-key candidate names, canonical first then legacy\n * `DOTENV_PUBLIC_KEY*`. Decrypt callers don't need the public key\n * (decryption uses the private key), but encrypt may read an existing\n * public-key header from a `.env*` file to skip re-generating one.\n */\nexport function publicKeyCandidateNamesFor(\n envFilePath: string,\n): readonly string[] {\n return [\n privateKeyNameForWithPrefix(envFilePath, ENVX_PUBLIC_KEY_PREFIX),\n privateKeyNameForWithPrefix(envFilePath, LEGACY_PUBLIC_KEY_PREFIX),\n ];\n}\n\nfunction privateKeyNameForWithPrefix(\n envFilePath: string,\n prefix: string,\n): string {\n const basename = path.basename(envFilePath);\n if (basename === \".env\") return prefix;\n const suffix = basename.replace(/^\\.env\\./, \"\");\n return `${prefix}_${normalize(suffix)}`;\n}\n\nfunction normalize(s: string): string {\n return s.toUpperCase().replace(/[^A-Z0-9]/g, \"_\");\n}\n\n// #endregion -----------------------------------------------\n\n// #region -- .env.keys File I/O ----------------------------\n\n/** Read a .env.keys file as a key→value map. Returns an empty map when the file doesn't exist. */\nexport function readKeysFile(keysPath: string): Map<string, string> {\n if (!fs.existsSync(keysPath)) return new Map();\n const lines = parseEnv(fs.readFileSync(keysPath, \"utf8\"));\n return new Map(Object.entries(toRecord(lines)));\n}\n\n/**\n * Banner for `.env.keys` files. Mirrors the upstream `dotenvx`\n * formatting so the file is self-describing and visually distinct.\n */\nexport const KEYS_FILE_BANNER =\n \"#/------------------!ENVX_PRIVATE_KEYS!---------------------/\\n\" +\n \"#/ private decryption keys. DO NOT commit to source control /\\n\" +\n \"#/ [how it works](https://dotenvx.com/encryption) /\\n\" +\n \"#/----------------------------------------------------------/\\n\";\n\n/**\n * Banner placed above an `ENVX_PUBLIC_KEY*` entry inside a `.env*` file.\n * Public keys are safe to commit — anyone with them can encrypt; only\n * the private-key holder can decrypt.\n */\nexport const PUBLIC_KEY_BANNER =\n \"#/-------------------[ENVX_PUBLIC_KEY]----------------------/\\n\" +\n \"#/ public-key encryption for .env files /\\n\" +\n \"#/ [how it works](https://dotenvx.com/encryption) /\\n\" +\n \"#/----------------------------------------------------------/\\n\";\n\n/**\n * Persist a key→value map to `.env.keys`. Overwrites the file. Preserves\n * order from the input map (so consumers get stable diffs) and emits the\n * private-keys banner when writing a non-empty map.\n *\n * `sectionFor` (optional): a function that, given a key name, returns a\n * section header to write above it (e.g. `# .env.dev`). Used by encrypt\n * to group keys by their associated env file.\n */\nexport function writeKeysFile(\n keysPath: string,\n keys: Map<string, string>,\n opts: {\n readonly sectionFor?: (keyName: string) => string | undefined;\n } = {},\n): void {\n if (keys.size === 0) {\n fs.writeFileSync(keysPath, KEYS_FILE_BANNER);\n return;\n }\n const out: string[] = [KEYS_FILE_BANNER];\n let lastSection: string | undefined;\n for (const [key, value] of keys) {\n const section = opts.sectionFor?.(key);\n if (section !== undefined && section !== lastSection) {\n out.push(`\\n# ${section}\\n`);\n lastSection = section;\n } else if (lastSection === undefined) {\n // No sections at all — separate the banner from the first kv with\n // a blank line for readability.\n out.push(\"\\n\");\n lastSection = \"\";\n }\n // Re-serialize one kv at a time so quoting follows the same parser\n // conventions used elsewhere.\n const line: import(\"./parser.js\").EnvLine = {\n type: \"kv\",\n key,\n value,\n raw: value,\n quote: '\"',\n trailing: \"\",\n };\n out.push(serializeEnv([line]) + \"\\n\");\n }\n fs.writeFileSync(keysPath, out.join(\"\"));\n}\n\n/**\n * Default location for `.env.keys`: cwd first, with a walk-up to the\n * workspace root if no `.env.keys` exists at cwd. This lets users run\n * envx from a subpackage and still find the workspace-level keys file\n * without passing `-fk`.\n *\n * If neither location has a file, falls back to `<cwd>/.env.keys` so a\n * fresh `envx encrypt` creates the file where the user expects it.\n */\nexport function defaultKeysPath(): string {\n return resolveCwdOrWorkspace(\".env.keys\");\n}\n\n/**\n * @deprecated Use `defaultKeysPath()` instead. Kept as a thin re-export so\n * external callers don't break — the `envFilepath` argument is now ignored\n * and the returned path is always `<cwd>/.env.keys`.\n */\nexport function defaultKeysPathFor(_envFilepath: string): string {\n return defaultKeysPath();\n}\n\n// #endregion -----------------------------------------------\n","// #region -- Glob matching ---------------------------------\n\n/**\n * Tiny glob matcher: `*` matches any run of characters within a key\n * name, `?` matches a single character, anything else is literal.\n * Sufficient for the include/exclude key filters; for full picomatch\n * semantics, swap this out — the predicates only consume a string.\n */\nfunction compile(pattern: string): RegExp {\n const escaped = pattern.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const body = escaped.replace(/\\*/g, \".*\").replace(/\\?/g, \".\");\n return new RegExp(`^${body}$`);\n}\n\nexport function matchesAny(key: string, patterns: readonly string[]): boolean {\n for (const pattern of patterns) {\n if (compile(pattern).test(key)) return true;\n }\n return false;\n}\n\n/**\n * Decide whether to operate on a key given include + exclude filters.\n * Empty includes means \"all keys\". Excludes always win.\n */\nexport function isSelected(\n key: string,\n includes: readonly string[],\n excludes: readonly string[],\n): boolean {\n if (excludes.length > 0 && matchesAny(key, excludes)) return false;\n if (includes.length === 0) return true;\n return matchesAny(key, includes);\n}\n\n// #endregion -----------------------------------------------\n","import * as fs from \"fs\";\nimport * as path from \"path\";\n\nimport {\n ENCRYPTED_PREFIX,\n encryptValueAsymmetric,\n generateKeyPair,\n isEncrypted,\n} from \"./crypto.js\";\nimport {\n PUBLIC_KEY_BANNER,\n defaultKeysPath,\n privateKeyCandidateNamesFor,\n publicKeyCandidateNamesFor,\n readKeysFile,\n writeKeysFile,\n} from \"./keys.js\";\nimport { resolveCwdOrWorkspace } from \"./env.js\";\nimport { isSelected } from \"./match.js\";\nimport { parseEnv, serializeEnv, toRecord } from \"./parser.js\";\nimport type { EnvLine } from \"./parser.js\";\nimport type { ProcessedEnv, RunOptions, RunResult } from \"./types.js\";\n\n// #region -- encryptFiles ----------------------------------\n\n/**\n * Encrypt selected values across one or more env files using\n * asymmetric ECIES (secp256k1 + AES-256-GCM via eciesjs). Pure: returns\n * new file contents but does not write to disk. Use {@link writeProcessed}\n * to persist.\n *\n * On first encrypt of a fresh file: generates a secp256k1 keypair,\n * prepends the public-key banner + `ENVX_PUBLIC_KEY*` header to the\n * env file, and writes the matching private key to `.env.keys` under\n * `ENVX_PRIVATE_KEY*`. Subsequent calls reuse the existing keypair.\n *\n * Files that already have a `*PUBLIC_KEY*` header are encrypted with\n * that public key; the matching private key in `.env.keys` is only\n * needed for decryption. Anyone with read access to the env file can\n * encrypt new values — only the private-key holder can decrypt.\n */\nexport function encryptFiles(opts: RunOptions): RunResult {\n const includes = opts.keys ?? [];\n const excludes = opts.excludeKeys ?? [];\n const processedEnvs: ProcessedEnv[] = [];\n const changedFilepaths: string[] = [];\n const unchangedFilepaths: string[] = [];\n\n for (const envFilePath of opts.envFiles) {\n const filepath = path.resolve(envFilePath);\n // `-fk` (and the default `.env.keys` lookup) resolves cwd-first\n // with a walk-up to the workspace root if nothing exists at the\n // cwd-rooted path. Absolute paths bypass the walk and are taken\n // verbatim.\n const keysPath = opts.envKeysFile\n ? resolveCwdOrWorkspace(opts.envKeysFile)\n : defaultKeysPath();\n\n if (!fs.existsSync(filepath)) {\n processedEnvs.push({\n envFilepath: envFilePath,\n filepath,\n envSrc: \"\",\n changed: false,\n keys: [],\n error: {\n code: \"MISSING_ENV_FILE\",\n message: `env file not found: ${envFilePath}`,\n help: `add one with [echo \"HELLO=World\" > ${envFilePath}] and re-run`,\n },\n });\n continue;\n }\n\n let lines: EnvLine[] = parseEnv(fs.readFileSync(filepath, \"utf8\"));\n const linesAsRecord = toRecord(lines);\n\n // Locate the existing public-key header. ENVX_PUBLIC_KEY* is the\n // canonical name; DOTENV_PUBLIC_KEY* is accepted as a dotenvx-compat\n // fallback at read time.\n const pubCandidates = publicKeyCandidateNamesFor(envFilePath);\n const canonicalPubName = pubCandidates[0]!;\n let publicKeyHex: string | undefined;\n let activePubName = canonicalPubName;\n for (const name of pubCandidates) {\n const v = linesAsRecord[name];\n if (v && v.length > 0) {\n publicKeyHex = v;\n activePubName = name;\n break;\n }\n }\n\n const keysMap = readKeysFile(keysPath);\n const privCandidates = privateKeyCandidateNamesFor(envFilePath);\n const canonicalPrivName = privCandidates[0]!;\n let privateKeyHex: string | undefined;\n let privateKeyName = canonicalPrivName;\n for (const name of privCandidates) {\n const v = keysMap.get(name);\n if (v) {\n privateKeyHex = v;\n privateKeyName = name;\n break;\n }\n }\n\n let privateKeyAdded = false;\n\n // No header yet → fresh file. Generate a keypair, prepend the\n // public-key banner + header, persist the private key to .env.keys.\n if (publicKeyHex === undefined) {\n const pair = generateKeyPair();\n publicKeyHex = pair.publicKey;\n privateKeyHex = pair.privateKey;\n keysMap.set(canonicalPrivName, privateKeyHex);\n privateKeyName = canonicalPrivName;\n lines = injectPublicKeyHeader(lines, canonicalPubName, publicKeyHex);\n writeKeysFile(keysPath, keysMap, {\n sectionFor: name => sectionForKey(name),\n });\n privateKeyAdded = true;\n }\n\n const fileKeys: string[] = [];\n let changed = false;\n\n for (const line of lines) {\n if (line.type !== \"kv\") continue;\n fileKeys.push(line.key);\n if (isEncrypted(line.value)) continue;\n if (!isSelected(line.key, includes, excludes)) continue;\n // Don't encrypt the public-key header (it's plaintext by design).\n if (line.key === activePubName || line.key === canonicalPubName) continue;\n if (line.key === pubCandidates[1]) continue;\n\n line.value = encryptValueAsymmetric(line.value, publicKeyHex);\n // `encrypted:<base64>` is URL-safe — drop any inherited quote\n // style so diffs stay minimal.\n line.quote = \"\";\n changed = true;\n }\n\n const envSrc = serializeEnv(lines);\n const processed: ProcessedEnv = {\n envFilepath: envFilePath,\n filepath,\n envSrc,\n changed,\n keys: fileKeys,\n ...(privateKeyAdded\n ? { privateKeyAdded: true, privateKeyName, privateKey: privateKeyHex }\n : {}),\n };\n processedEnvs.push(processed);\n if (changed) changedFilepaths.push(filepath);\n else unchangedFilepaths.push(filepath);\n }\n\n return { processedEnvs, changedFilepaths, unchangedFilepaths };\n}\n\n// #region -- helpers ----------------------------------------\n\n/**\n * Insert the public-key banner + `ENVX_PUBLIC_KEY*=...` line at the top\n * of the parsed env. Anything already at line 0 (typically a leading\n * comment block) is preserved beneath the new header.\n */\nfunction injectPublicKeyHeader(\n lines: ReadonlyArray<EnvLine>,\n publicKeyName: string,\n publicKeyHex: string,\n): EnvLine[] {\n const out: EnvLine[] = [];\n for (const raw of PUBLIC_KEY_BANNER.split(\"\\n\")) {\n if (raw.length === 0) continue;\n out.push({ type: \"raw\", raw });\n }\n out.push({\n type: \"kv\",\n key: publicKeyName,\n value: publicKeyHex,\n raw: publicKeyHex,\n quote: '\"',\n trailing: \"\",\n });\n out.push({ type: \"raw\", raw: \"\" });\n for (const line of lines) out.push(line);\n return out;\n}\n\n/**\n * Section header to emit above a `.env.keys` entry, derived from the\n * private-key variable name. e.g. `ENVX_PRIVATE_KEY` → `.env`,\n * `ENVX_PRIVATE_KEY_DEV` → `.env.dev`.\n */\nfunction sectionForKey(keyName: string): string | undefined {\n const m = keyName.match(/^(?:ENVX|DOTENV)_PRIVATE_KEY(?:_(.+))?$/);\n if (!m) return undefined;\n const suffix = m[1];\n return suffix ? `.env.${suffix.toLowerCase()}` : \".env\";\n}\n\n// #endregion -----------------------------------------------\n\n// Re-export the prefix so callers can detect ciphertext at the consumer level.\nexport { ENCRYPTED_PREFIX };\n\n// #endregion -----------------------------------------------\n","import * as fs from \"fs\";\nimport * as path from \"path\";\n\nimport { decryptValueAsymmetric, isEncrypted } from \"./crypto.js\";\nimport { resolveCwdOrWorkspace } from \"./env.js\";\nimport {\n defaultKeysPath,\n privateKeyCandidateNamesFor,\n publicKeyCandidateNamesFor,\n readKeysFile,\n} from \"./keys.js\";\nimport { isSelected } from \"./match.js\";\nimport { parseEnv, serializeEnv, toRecord } from \"./parser.js\";\n\nfunction needsQuotes(value: string): boolean {\n return /[\\s#$\"'`\\\\]/.test(value) || value === \"\";\n}\nimport type { ProcessedEnv, ProcessingError, RunOptions, RunResult } from \"./types.js\";\n\n// #region -- decryptFiles ----------------------------------\n\n/**\n * Decrypt selected values across one or more env files. Pure: returns\n * new file contents but does not write to disk. Errors per-file (missing\n * env file, missing private key, bad ciphertext) surface in the\n * `processed.error` field so a single broken file doesn't kill the\n * whole batch.\n */\nexport function decryptFiles(opts: RunOptions): RunResult {\n const includes = opts.keys ?? [];\n const excludes = opts.excludeKeys ?? [];\n const processedEnvs: ProcessedEnv[] = [];\n const changedFilepaths: string[] = [];\n const unchangedFilepaths: string[] = [];\n\n for (const envFilePath of opts.envFiles) {\n const filepath = path.resolve(envFilePath);\n // Same resolution as encrypt — see encrypt.ts for rationale.\n const keysPath = opts.envKeysFile\n ? resolveCwdOrWorkspace(opts.envKeysFile)\n : defaultKeysPath();\n\n if (!fs.existsSync(filepath)) {\n processedEnvs.push({\n envFilepath: envFilePath,\n filepath,\n envSrc: \"\",\n changed: false,\n keys: [],\n error: {\n code: \"MISSING_ENV_FILE\",\n message: `env file not found: ${envFilePath}`,\n },\n });\n continue;\n }\n\n const keysMap = readKeysFile(keysPath);\n // Try the canonical ENVX_PRIVATE_KEY* name first, then fall back to\n // the legacy DOTENV_PRIVATE_KEY* name (upstream dotenvx convention).\n // Error messages reference the canonical name since that's what new\n // setups should produce.\n const candidateNames = privateKeyCandidateNamesFor(envFilePath);\n const canonicalName = candidateNames[0]!;\n const keyName = canonicalName;\n let keyHex: string | undefined;\n for (const name of candidateNames) {\n const v = keysMap.get(name);\n if (v) {\n keyHex = v;\n break;\n }\n }\n\n const lines = parseEnv(fs.readFileSync(filepath, \"utf8\"));\n const fileKeys: string[] = [];\n let changed = false;\n let decryptError: ProcessingError | undefined;\n\n // The file MUST have a public-key header — that's how we locate\n // which keypair the values were encrypted under. Files lacking a\n // header can't be decrypted (and likely aren't envx-encrypted).\n const linesAsRecord = toRecord(lines);\n const pubCandidates = publicKeyCandidateNamesFor(envFilePath);\n let hasPublicKeyHeader = false;\n for (const name of pubCandidates) {\n if (linesAsRecord[name] && linesAsRecord[name]!.length > 0) {\n hasPublicKeyHeader = true;\n break;\n }\n }\n\n for (const line of lines) {\n if (line.type !== \"kv\") continue;\n fileKeys.push(line.key);\n if (!isEncrypted(line.value)) continue;\n if (!isSelected(line.key, includes, excludes)) continue;\n if (!hasPublicKeyHeader) {\n decryptError = {\n code: \"INVALID_CIPHERTEXT\",\n message: `${envFilePath} has encrypted values but no ${pubCandidates[0]} header`,\n help: `add the public-key header (run \\`envx encrypt\\` against a fresh file to see the layout) or remove the encrypted values`,\n };\n break;\n }\n if (!keyHex) {\n decryptError = {\n code: \"MISSING_PRIVATE_KEY\",\n message: `no ${keyName} found in ${keysPath}`,\n help: `set ${keyName} in your .env.keys file or pass --env-keys-file`,\n };\n break;\n }\n try {\n line.value = decryptValueAsymmetric(line.value, keyHex);\n // Re-derive a sensible quote style from the decrypted content. We\n // can't recover the user's original quote choice (it was lost when\n // we encrypted), but we can quote whenever the value would be\n // ambiguous unquoted, and use double quotes by default so escape\n // sequences round-trip cleanly.\n line.quote = needsQuotes(line.value) ? '\"' : \"\";\n changed = true;\n } catch (e) {\n decryptError = {\n code:\n (e as { code?: string }).code === \"DECRYPTION_FAILED\"\n ? \"DECRYPTION_FAILED\"\n : \"INVALID_CIPHERTEXT\",\n message: `${envFilePath}: ${(e as Error).message} (key=${line.key})`,\n };\n break;\n }\n }\n\n const envSrc = serializeEnv(lines);\n const processed: ProcessedEnv = {\n envFilepath: envFilePath,\n filepath,\n envSrc: decryptError ? fs.readFileSync(filepath, \"utf8\") : envSrc,\n changed: !decryptError && changed,\n keys: fileKeys,\n ...(decryptError ? { error: decryptError } : {}),\n };\n processedEnvs.push(processed);\n if (processed.changed) changedFilepaths.push(filepath);\n else if (!decryptError) unchangedFilepaths.push(filepath);\n }\n\n return { processedEnvs, changedFilepaths, unchangedFilepaths };\n}\n\n// #endregion -----------------------------------------------\n","import * as fs from \"fs\";\nimport * as path from \"path\";\n\nimport {\n decryptValueAsymmetric,\n encryptValueAsymmetric,\n generateKeyPair,\n isEncrypted,\n} from \"./crypto.js\";\nimport { resolveCwdOrWorkspace } from \"./env.js\";\nimport {\n defaultKeysPath,\n privateKeyCandidateNamesFor,\n publicKeyCandidateNamesFor,\n readKeysFile,\n writeKeysFile,\n} from \"./keys.js\";\nimport { isSelected } from \"./match.js\";\nimport { parseEnv, serializeEnv, toRecord } from \"./parser.js\";\nimport type { EnvLine } from \"./parser.js\";\nimport type { ProcessedEnv, ProcessingError, RunOptions, RunResult } from \"./types.js\";\n\n// #region -- rotateFiles -----------------------------------\n\n/**\n * Rotate the keypair on one or more `.env*` files. For each file:\n *\n * 1. Locate the existing public-key header (`ENVX_PUBLIC_KEY*` —\n * or `DOTENV_PUBLIC_KEY*` for dotenvx-compat reads). Files without\n * a header surface an `INVALID_CIPHERTEXT` error: rotation needs\n * the existing keypair to round-trip the values.\n * 2. Look up the matching private key in `.env.keys` (canonical\n * `ENVX_PRIVATE_KEY*` first, then `DOTENV_PRIVATE_KEY*`).\n * 3. Generate a fresh secp256k1 keypair.\n * 4. Decrypt every encrypted value with the *old* private key,\n * re-encrypt with the *new* public key.\n * 5. Replace the public-key header line in the env file with the\n * new public key, and write the new private key under the\n * canonical name in `.env.keys`. The keys file is written eagerly\n * so the returned envSrc is decryptable on disk if persisted.\n *\n * Pure (per the encrypt/decrypt convention): returns new file contents\n * but does not write the env files themselves to disk. Use\n * {@link writeProcessed} on the returned `processedEnvs` to persist.\n */\nexport function rotateFiles(opts: RunOptions): RunResult {\n const includes = opts.keys ?? [];\n const excludes = opts.excludeKeys ?? [];\n const processedEnvs: ProcessedEnv[] = [];\n const changedFilepaths: string[] = [];\n const unchangedFilepaths: string[] = [];\n\n for (const envFilePath of opts.envFiles) {\n const filepath = path.resolve(envFilePath);\n // Same -fk resolution as encrypt/decrypt: cwd-first, walk up to\n // workspace root if nothing exists there. Absolute paths bypass.\n const keysPath = opts.envKeysFile\n ? resolveCwdOrWorkspace(opts.envKeysFile)\n : defaultKeysPath();\n\n if (!fs.existsSync(filepath)) {\n processedEnvs.push({\n envFilepath: envFilePath,\n filepath,\n envSrc: \"\",\n changed: false,\n keys: [],\n error: {\n code: \"MISSING_ENV_FILE\",\n message: `env file not found: ${envFilePath}`,\n },\n });\n continue;\n }\n\n const lines: EnvLine[] = parseEnv(fs.readFileSync(filepath, \"utf8\"));\n const linesAsRecord = toRecord(lines);\n const fileKeys: string[] = lines.flatMap(l => (l.type === \"kv\" ? [l.key] : []));\n\n // Locate the public-key header. No header → legacy AES; skip.\n const pubCandidates = publicKeyCandidateNamesFor(envFilePath);\n let publicKeyName: string | undefined;\n let oldPublicKey: string | undefined;\n for (const name of pubCandidates) {\n const v = linesAsRecord[name];\n if (v && v.length > 0) {\n publicKeyName = name;\n oldPublicKey = v;\n break;\n }\n }\n if (publicKeyName === undefined) {\n processedEnvs.push({\n envFilepath: envFilePath,\n filepath,\n envSrc: serializeEnv(lines),\n changed: false,\n keys: fileKeys,\n error: {\n code: \"INVALID_CIPHERTEXT\",\n message: `${envFilePath} has no public-key header — nothing to rotate`,\n help: `run \\`envx encrypt -e ${envFilePath}\\` first to set up an envx-encrypted file`,\n },\n });\n unchangedFilepaths.push(filepath);\n continue;\n }\n\n // Locate the matching private key. Without it we can't decrypt the\n // existing values, so rotation is impossible.\n const keysMap = readKeysFile(keysPath);\n const privCandidates = privateKeyCandidateNamesFor(envFilePath);\n const canonicalPrivName = privCandidates[0]!;\n let oldPrivateKeyName: string | undefined;\n let oldPrivateKey: string | undefined;\n for (const name of privCandidates) {\n const v = keysMap.get(name);\n if (v) {\n oldPrivateKeyName = name;\n oldPrivateKey = v;\n break;\n }\n }\n if (oldPrivateKey === undefined) {\n processedEnvs.push({\n envFilepath: envFilePath,\n filepath,\n envSrc: serializeEnv(lines),\n changed: false,\n keys: fileKeys,\n error: {\n code: \"MISSING_PRIVATE_KEY\",\n message: `no ${canonicalPrivName} found in ${keysPath}`,\n help: `set ${canonicalPrivName} in your .env.keys file or pass --env-keys-file`,\n },\n });\n unchangedFilepaths.push(filepath);\n continue;\n }\n\n // Generate the replacement keypair up front so a per-line decrypt\n // failure doesn't leave the file half-rotated.\n const newPair = generateKeyPair();\n const newPublicKey = newPair.publicKey;\n const newPrivateKey = newPair.privateKey;\n\n let rotateError: ProcessingError | undefined;\n let changed = false;\n\n // Re-encrypt every selected encrypted value with the new public key.\n // We do this on a temp copy of `lines` so a mid-loop failure leaves\n // the source state intact.\n const rotated: EnvLine[] = lines.map(l => ({ ...l }));\n for (const line of rotated) {\n if (line.type !== \"kv\") continue;\n // Don't rotate the public-key header itself — handled separately\n // below — and skip excluded keys.\n if (line.key === publicKeyName) continue;\n if (!isEncrypted(line.value)) continue;\n if (!isSelected(line.key, includes, excludes)) continue;\n try {\n const plaintext = decryptValueAsymmetric(line.value, oldPrivateKey);\n line.value = encryptValueAsymmetric(plaintext, newPublicKey);\n line.quote = \"\";\n changed = true;\n } catch (e) {\n rotateError = {\n code:\n (e as { code?: string }).code === \"DECRYPTION_FAILED\"\n ? \"DECRYPTION_FAILED\"\n : \"INVALID_CIPHERTEXT\",\n message: `${envFilePath}: ${(e as Error).message} (key=${line.key})`,\n };\n break;\n }\n }\n\n if (rotateError) {\n processedEnvs.push({\n envFilepath: envFilePath,\n filepath,\n envSrc: serializeEnv(lines),\n changed: false,\n keys: fileKeys,\n error: rotateError,\n });\n continue;\n }\n\n // Replace the public-key header value. Whichever name was in use\n // (canonical or legacy), keep that name so the file's diff shows\n // only the value change. Migrating the header name itself is left\n // to a separate command if/when added.\n for (const line of rotated) {\n if (line.type === \"kv\" && line.key === publicKeyName) {\n line.value = newPublicKey;\n line.quote = '\"';\n changed = true;\n break;\n }\n }\n\n // Persist the new private key. If the file used the legacy\n // DOTENV_PRIVATE_KEY name, write the new key under that *same* name\n // so consumers reading either prefix continue to work; otherwise\n // write under the canonical ENVX_PRIVATE_KEY name.\n const newPrivKeyName = oldPrivateKeyName ?? canonicalPrivName;\n keysMap.set(newPrivKeyName, newPrivateKey);\n writeKeysFile(keysPath, keysMap, {\n sectionFor: name => sectionForKey(name),\n });\n\n const envSrc = serializeEnv(rotated);\n processedEnvs.push({\n envFilepath: envFilePath,\n filepath,\n envSrc,\n changed,\n keys: fileKeys,\n privateKeyAdded: true,\n privateKeyName: newPrivKeyName,\n privateKey: newPrivateKey,\n });\n if (changed) changedFilepaths.push(filepath);\n else unchangedFilepaths.push(filepath);\n\n // Suppress unused-binding warning for oldPublicKey — kept readable\n // for any future callers that want to surface the \"from → to\" pair.\n void oldPublicKey;\n }\n\n return { processedEnvs, changedFilepaths, unchangedFilepaths };\n}\n\n// Section header to emit above a `.env.keys` entry — same convention\n// as encrypt.ts. Inlined to avoid a cross-file dependency.\nfunction sectionForKey(keyName: string): string | undefined {\n const m = keyName.match(/^(?:ENVX|DOTENV)_PRIVATE_KEY(?:_(.+))?$/);\n if (!m) return undefined;\n const suffix = m[1];\n return suffix ? `.env.${suffix.toLowerCase()}` : \".env\";\n}\n\n// #endregion -----------------------------------------------\n","import * as fs from \"fs\";\nimport * as path from \"path\";\nimport { createRequire } from \"module\";\nimport { pathToFileURL } from \"url\";\n\n// #region -- Schema ----------------------------------------\n\n/**\n * Mapping shape: per-file allowlist of keys (or globs). Used to decide\n * which env file an inline key belongs in, and to constrain\n * encrypt/decrypt to the right scope.\n *\n * {\n * '.env.shared': ['DATABASE_URL', 'REDIS_URL'],\n * '.env.app': ['APP_*'],\n * }\n */\nexport type EnvFileMapping = Readonly<Record<string, readonly string[]>>;\n\nexport interface DotenvxConfig {\n /** Default env files to load when --env is not supplied. */\n readonly envFiles?: readonly string[];\n /**\n * Subdirectory where env files live (e.g. \"vault\"). Cwd-first; falls\n * back to `<workspaceRoot>/<envPath>` if the cwd path doesn't exist.\n * Equivalent to passing `--vault` (which is a shortcut for `envPath: \"vault\"`).\n */\n readonly envPath?: string;\n /**\n * When `true`, envx expands each base env file into four layered\n * paths (most-specific-first): `.env.<env>.local`, `.env.local`,\n * `.env.<env>`, `.env` — where `<env>` is the auto-detected\n * environment name (see `detectEnvironment()` / `nodeEnvMap`).\n *\n * `false` (or omitted) disables cascading. If you need to force a\n * specific cascade name regardless of detection, pass `--cascade\n * <name>` on the CLI; the explicit string overrides this flag.\n */\n readonly cascade?: boolean;\n /** Path to the keys file. Cwd-first with workspace-root fallback. */\n readonly envKeysFile?: string;\n /** Per-file allowlist of keys (literal names or globs). */\n readonly mapping?: EnvFileMapping;\n /** Override existing process.env when loading. */\n readonly override?: boolean;\n /** Suppress dotenv-style logging. */\n readonly quiet?: boolean;\n /**\n * Toggle auto-detection of the deployment environment from\n * `VERCEL_ENV` / Netlify `CONTEXT` / `NODE_ENV`. When `false`, envx\n * never rewrites the `.env` suffix from platform signals — explicit\n * `--env` / `envFiles` are the only inputs. Default: `true`.\n */\n readonly autoDetect?: boolean;\n /**\n * Override the `NODE_ENV → suffix` mapping used during auto-detection.\n * Keys are lowercased `NODE_ENV` values; values are the suffix to\n * append after `.env.` (e.g. `staging` → `.env.staging`).\n *\n * Built-in defaults:\n * { production: \"prod\", development: \"dev\", local: \"local\" }\n *\n * Anything not in the map (e.g. `NODE_ENV=qa`) passes through\n * lowercased verbatim — `qa` becomes `.env.qa`. Set an entry to\n * empty string `\"\"` to force \"no suffix\" (just `.env`) for that\n * NODE_ENV value.\n */\n readonly nodeEnvMap?: Readonly<Record<string, string>>;\n /**\n * Keys that MUST be set in `process.env` after envx finishes loading.\n * If any are still unset, envx logs the missing names and exits with\n * a non-zero status. Use this to fail fast in CI / production rather\n * than discovering an unset `DATABASE_URL` deep in app startup.\n */\n readonly required?: readonly string[];\n /**\n * Auto-resolve `${VAR}` / `$VAR` / `${VAR:-default}` / `${VAR:?msg}`\n * references in loaded values. When `true`, envx runs the same\n * expansion logic as `envx expand` against `process.env` after files\n * load. Default: `false` (values are loaded verbatim, like dotenv).\n */\n readonly expand?: boolean;\n /**\n * Fallback values applied AFTER env files (and `variables`) finish\n * loading, only for keys that are still unset. Different from\n * `variables`, which always overrides. Useful for non-secret\n * defaults that belong in code rather than `.env*` files.\n */\n readonly defaults?: Readonly<Record<string, string>>;\n /**\n * Explicit workspace root, skipping `findWorkspaceRoot()` walk-up.\n * Useful when the auto-detection picks the wrong directory (Bazel,\n * custom CI sandboxes, weird symlink layouts). The path is taken\n * verbatim — relative paths resolve against cwd at load time.\n */\n readonly workspaceRoot?: string;\n}\n\n// #endregion -----------------------------------------------\n\n// #region -- Loader ----------------------------------------\n\nexport interface LoadConfigOptions {\n /** Explicit config path (highest precedence). */\n readonly configPath?: string;\n /** Where to start the search. Defaults to process.cwd(). */\n readonly cwd?: string;\n}\n\nexport interface LoadedConfig {\n /** Resolved config values. Always present (defaults merged in). */\n readonly config: DotenvxConfig;\n /** Absolute path the config was loaded from, or null if defaults. */\n readonly source: string | null;\n /** Source kind: how the config was discovered. */\n readonly origin: \"explicit\" | \"package.json\" | \"auto\" | \"defaults\";\n}\n\nconst AUTO_NAMES = [\n \"envx.config.ts\",\n \"envx.config.mts\",\n \"envx.config.js\",\n \"envx.config.mjs\",\n \"envx.config.cjs\",\n \"envx.config.json\",\n] as const;\n\n/**\n * Resolve the dotenvx config in this order:\n *\n * 1. `opts.configPath` (typically from `--config <path>`).\n * 2. `package.json` → `dotenvx.config` (string path) — walks upward\n * from `cwd` to find the nearest package.json.\n * 3. The first `dotenvx.config.{ts,js,json,…}` discovered at `cwd`.\n * 4. Built-in defaults (returns `source: null`, `origin: \"defaults\"`).\n */\nexport function loadDotenvxConfig(\n opts: LoadConfigOptions = {},\n): LoadedConfig {\n const cwd = opts.cwd ?? process.cwd();\n\n if (opts.configPath) {\n const resolved = path.resolve(cwd, opts.configPath);\n return {\n config: loadConfigFile(resolved),\n source: resolved,\n origin: \"explicit\",\n };\n }\n\n const fromPackageJson = findInPackageJson(cwd);\n if (fromPackageJson) {\n return {\n config: loadConfigFile(fromPackageJson.path),\n source: fromPackageJson.path,\n origin: \"package.json\",\n };\n }\n\n const fromAuto = findAutoConfig(cwd);\n if (fromAuto) {\n return {\n config: loadConfigFile(fromAuto),\n source: fromAuto,\n origin: \"auto\",\n };\n }\n\n return { config: {}, source: null, origin: \"defaults\" };\n}\n\n// #endregion -----------------------------------------------\n\n// #region -- Discovery ------------------------------------\n\nfunction findInPackageJson(\n startDir: string,\n): { path: string; pkgPath: string } | null {\n let dir = startDir;\n const root = path.parse(dir).root;\n while (true) {\n const pkgPath = path.join(dir, \"package.json\");\n if (fs.existsSync(pkgPath)) {\n try {\n const raw = JSON.parse(fs.readFileSync(pkgPath, \"utf8\")) as {\n envx?: { config?: string };\n // legacy spelling — read but don't write; we'll drop in a major bump.\n dotenvx?: { config?: string };\n };\n const ref = raw.envx?.config ?? raw.dotenvx?.config;\n if (typeof ref === \"string\" && ref.length > 0) {\n return { path: path.resolve(dir, ref), pkgPath };\n }\n } catch {\n // Ignore unreadable/malformed package.json — keep walking up.\n }\n }\n if (dir === root) return null;\n const parent = path.dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\nfunction findAutoConfig(startDir: string): string | null {\n for (const name of AUTO_NAMES) {\n const p = path.join(startDir, name);\n if (fs.existsSync(p)) return p;\n }\n return null;\n}\n\n// #endregion -----------------------------------------------\n\n// #region -- File loading ----------------------------------\n\nconst require_ = createRequire(import.meta.url);\n\nfunction loadConfigFile(filePath: string): DotenvxConfig {\n if (!fs.existsSync(filePath)) {\n throw new Error(`[CONFIG_NOT_FOUND] no config at ${filePath}`);\n }\n const ext = path.extname(filePath).toLowerCase();\n switch (ext) {\n case \".json\":\n return normalize(JSON.parse(fs.readFileSync(filePath, \"utf8\")));\n case \".cjs\":\n return normalize(require_(filePath));\n case \".js\":\n case \".mjs\":\n // .js files require sync loading. CJS works via require; ESM under\n // a \"type\": \"module\" parent doesn't. Try require first; if it\n // throws ERR_REQUIRE_ESM, fall back to dynamic import (caller must\n // be in an async context — we surface the limitation in the docs).\n return normalize(loadJsSync(filePath));\n case \".ts\":\n case \".mts\":\n // TypeScript configs require a runtime transpiler (tsx, ts-node).\n // We try to require via tsx if it's been installed, else throw a\n // clear error.\n return normalize(loadTsSync(filePath));\n default:\n throw new Error(`[CONFIG_BAD_EXT] unsupported config extension: ${ext}`);\n }\n}\n\nfunction loadJsSync(filePath: string): unknown {\n try {\n const mod = require_(filePath);\n return (mod as { default?: unknown }).default ?? mod;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === \"ERR_REQUIRE_ESM\") {\n throw new Error(\n `[CONFIG_ESM_REQUIRES_ASYNC] ${filePath} is ESM. Either rename it to .cjs, ` +\n `add \"type\": \"commonjs\" to its package.json, or use loadDotenvxConfigAsync.`,\n );\n }\n throw e;\n }\n}\n\nfunction loadTsSync(filePath: string): unknown {\n // tsx registers a require hook when it's loaded. If the consumer has\n // it as a dev dep we'll find it; otherwise tell them how to enable it.\n try {\n require_(\"tsx/cjs\");\n } catch {\n throw new Error(\n `[CONFIG_TS_NEEDS_TSX] loading ${filePath} requires \"tsx\" to be installed. ` +\n `Add it as a devDependency, or use a .js / .json config instead.`,\n );\n }\n const mod = require_(filePath);\n return (mod as { default?: unknown }).default ?? mod;\n}\n\nfunction normalize(raw: unknown): DotenvxConfig {\n if (raw === null || typeof raw !== \"object\") {\n throw new Error(\"[CONFIG_BAD_SHAPE] config must export an object\");\n }\n // Trust but verify — coerce only the bits we touch.\n const r = raw as Record<string, unknown>;\n const out: { -readonly [K in keyof DotenvxConfig]: DotenvxConfig[K] } = {};\n if (Array.isArray(r[\"envFiles\"])) {\n out.envFiles = r[\"envFiles\"].filter((x): x is string => typeof x === \"string\");\n }\n if (typeof r[\"envPath\"] === \"string\") out.envPath = r[\"envPath\"];\n if (typeof r[\"cascade\"] === \"boolean\") out.cascade = r[\"cascade\"];\n if (typeof r[\"envKeysFile\"] === \"string\") out.envKeysFile = r[\"envKeysFile\"];\n if (typeof r[\"override\"] === \"boolean\") out.override = r[\"override\"];\n if (typeof r[\"quiet\"] === \"boolean\") out.quiet = r[\"quiet\"];\n if (typeof r[\"autoDetect\"] === \"boolean\") out.autoDetect = r[\"autoDetect\"];\n if (r[\"nodeEnvMap\"] && typeof r[\"nodeEnvMap\"] === \"object\") {\n const map: Record<string, string> = {};\n for (const [k, v] of Object.entries(r[\"nodeEnvMap\"] as Record<string, unknown>)) {\n if (typeof v === \"string\") map[k.toLowerCase()] = v;\n }\n out.nodeEnvMap = map;\n }\n if (Array.isArray(r[\"required\"])) {\n out.required = r[\"required\"].filter((x): x is string => typeof x === \"string\");\n }\n if (typeof r[\"expand\"] === \"boolean\") out.expand = r[\"expand\"];\n if (r[\"defaults\"] && typeof r[\"defaults\"] === \"object\" && !Array.isArray(r[\"defaults\"])) {\n const map: Record<string, string> = {};\n for (const [k, v] of Object.entries(r[\"defaults\"] as Record<string, unknown>)) {\n if (typeof v === \"string\") map[k] = v;\n }\n out.defaults = map;\n }\n if (typeof r[\"workspaceRoot\"] === \"string\") out.workspaceRoot = r[\"workspaceRoot\"];\n if (r[\"mapping\"] && typeof r[\"mapping\"] === \"object\") {\n const mapping: Record<string, string[]> = {};\n for (const [file, keys] of Object.entries(r[\"mapping\"] as Record<string, unknown>)) {\n if (Array.isArray(keys)) {\n mapping[file] = keys.filter((x): x is string => typeof x === \"string\");\n }\n }\n out.mapping = mapping;\n }\n return out;\n}\n\n// #endregion -----------------------------------------------\n","// #region -- Public API ------------------------------------\n\nexport type {\n ProcessedEnv,\n ProcessingError,\n ErrorCode,\n RunOptions,\n RunResult,\n} from \"./types.js\";\n\n// Crypto primitives + format constants\nexport {\n ENCRYPTED_PREFIX,\n encryptValueAsymmetric,\n decryptValueAsymmetric,\n generateKeyPair,\n isEncrypted,\n} from \"./crypto.js\";\n\n// Env-file parsing\nexport {\n parseEnv,\n serializeEnv,\n toRecord,\n type EnvLine,\n type KvLine,\n type RawLine,\n} from \"./parser.js\";\n\n// Key file management\nexport {\n ENVX_PRIVATE_KEY_PREFIX,\n ENVX_PUBLIC_KEY_PREFIX,\n KEYS_FILE_BANNER,\n LEGACY_PRIVATE_KEY_PREFIX,\n LEGACY_PUBLIC_KEY_PREFIX,\n PUBLIC_KEY_BANNER,\n defaultKeysPath,\n defaultKeysPathFor,\n privateKeyCandidateNamesFor,\n privateKeyNameFor,\n publicKeyCandidateNamesFor,\n publicKeyNameFor,\n readKeysFile,\n writeKeysFile,\n} from \"./keys.js\";\n\n// Glob filter helpers\nexport { isSelected, matchesAny } from \"./match.js\";\n\n// High-level operations\nexport { encryptFiles } from \"./encrypt.js\";\nexport { decryptFiles } from \"./decrypt.js\";\nexport { rotateFiles } from \"./rotate.js\";\n\n// Variable expansion\nexport {\n expandRecord,\n expandEnvSrc,\n type ExpandOptions,\n type ExpandResult,\n} from \"./expand.js\";\n\n// Config loading\nexport {\n loadDotenvxConfig,\n type DotenvxConfig,\n type LoadConfigOptions,\n type LoadedConfig,\n} from \"./config.js\";\n\n// Env loading + resolution helpers (originally in core/src/lib)\nexport {\n DEFAULT_NODE_ENV_MAP,\n detectEnvironment,\n validateCmdVariable,\n findWorkspaceRoot,\n resolveCwdOrWorkspace,\n expandCascadePaths,\n listEnvFiles,\n resolveEnvPaths,\n loadEnv,\n type DetectEnvironmentOptions,\n type ResolveEnvOptions,\n type LoadEnvOptions,\n} from \"./env.js\";\n\n// #endregion -----------------------------------------------\n\n// #region -- Persistence Helper ----------------------------\n\nimport * as fs from \"fs\";\n\nimport type { ProcessedEnv } from \"./types.js\";\n\n/**\n * Persist any changed processedEnvs to disk. Returns the absolute paths\n * that were actually written. Skips entries with errors and entries\n * marked unchanged.\n */\nexport function writeProcessed(\n processed: readonly ProcessedEnv[],\n): { written: string[] } {\n const written: string[] = [];\n for (const p of processed) {\n if (p.error || !p.changed) continue;\n fs.writeFileSync(p.filepath, p.envSrc);\n written.push(p.filepath);\n }\n return { written };\n}\n\n// #endregion -----------------------------------------------\n"],"mappings":"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"}
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as createCli } from "./chunks/commands-B8vc6UKO.js";
2
+ import { t as createCli } from "./chunks/commands-DNf_gJRx.js";
3
3
  import { hideBin } from "yargs/helpers";
4
4
  //#region src/cli.ts
5
5
  createCli(hideBin(process.argv)).parseSync();
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC;AAoBlC;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAmJnD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC;AAsBlC;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CA4JnD"}
@@ -1,2 +1,2 @@
1
- import { t as createCli } from "../chunks/commands-B8vc6UKO.js";
1
+ import { t as createCli } from "../chunks/commands-DNf_gJRx.js";
2
2
  export { createCli };
@@ -0,0 +1,10 @@
1
+ import { CommandModule } from 'yargs';
2
+ /**
3
+ * Print everything envx knows about the current invocation: which
4
+ * config file it found, the resolved settings, which platform signals
5
+ * are present, what auto-detection produced, the NODE_ENV → suffix
6
+ * map (built-in + user overrides), and the env file paths it would
7
+ * load. Read-only — does not mutate process.env or write any files.
8
+ */
9
+ export declare const infoCommand: CommandModule;
10
+ //# sourceMappingURL=info.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"info.d.ts","sourceRoot":"","sources":["../../src/commands/info.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAc3C;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,EAAE,aAqIzB,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { CommandModule } from 'yargs';
2
+ /**
3
+ * Rotate the asymmetric keypair for one or more env files. Generates a
4
+ * fresh secp256k1 keypair, decrypts existing values with the old
5
+ * private key, re-encrypts with the new public key, and updates both
6
+ * the `ENVX_PUBLIC_KEY*` header in the env file and the
7
+ * `ENVX_PRIVATE_KEY*` entry in `.env.keys`.
8
+ *
9
+ * Files without a public-key header surface an error — run
10
+ * `envx encrypt` against them first to set up the keypair.
11
+ */
12
+ export declare const rotateCommand: CommandModule;
13
+ //# sourceMappingURL=rotate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rotate.d.ts","sourceRoot":"","sources":["../../src/commands/rotate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAO3C;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,EAAE,aAyD3B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAO3C,eAAO,MAAM,UAAU,EAAE,aAoCxB,CAAC"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAO3C,eAAO,MAAM,UAAU,EAAE,aA4CxB,CAAC"}
package/dist/index.d.ts CHANGED
@@ -15,8 +15,9 @@ import { LoadEnvOptions } from '../../libs/src/index.ts';
15
15
  *
16
16
  * In all cases `process.env` is mutated and returned for ergonomic
17
17
  * destructuring. Config file discovery (`envx.config.{ts,js,json}` /
18
- * `package.json` `envx.config`) runs automatically; CLI-level options
19
- * still win when both are passed.
18
+ * `package.json` `envx.config`) runs automatically; programmatic
19
+ * options layer on top of the discovered config, with caller-supplied
20
+ * fields winning per-field.
20
21
  */
21
22
  declare function envx(): NodeJS.ProcessEnv;
22
23
  declare function envx(scope: string): NodeJS.ProcessEnv;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;;;;;;;;;;;;GAkBG;AACH,iBAAS,IAAI,IAAI,MAAM,CAAC,UAAU,CAAC;AACnC,iBAAS,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC;AAChD,iBAAS,IAAI,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC;AA0DvD,eAAe,IAAI,CAAC;AACpB,OAAO,EAAE,IAAI,EAAE,CAAC;AAGhB,YAAY,EAAE,cAAc,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,iBAAS,IAAI,IAAI,MAAM,CAAC,UAAU,CAAC;AACnC,iBAAS,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC;AAChD,iBAAS,IAAI,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC;AAsEvD,eAAe,IAAI,CAAC;AACpB,OAAO,EAAE,IAAI,EAAE,CAAC;AAGhB,YAAY,EAAE,cAAc,EAAE,CAAC"}
package/dist/index.js CHANGED
@@ -1,34 +1,44 @@
1
- import { c as loadDotenvxConfig, i as loadEnv } from "./chunks/src-CDuEfaCY.js";
1
+ import { n as loadDotenvxConfig, u as loadEnv } from "./chunks/src-CwrtyfZE.js";
2
2
  //#region src/index.ts
3
3
  function envx(arg) {
4
4
  const { config } = loadDotenvxConfig();
5
- let opts;
6
- if (arg === void 0) opts = {
7
- envFiles: config.envFiles ? [...config.envFiles] : [".env"],
8
- ...config.cascade !== void 0 ? { cascade: config.cascade } : {},
9
- ...config.envPath !== void 0 ? { envPath: config.envPath } : {},
10
- ...config.override !== void 0 ? { override: config.override } : {},
11
- ...config.quiet !== void 0 ? { quiet: config.quiet } : {}
12
- };
13
- else if (typeof arg === "string") opts = {
14
- envFiles: config.envFiles ? [...config.envFiles] : [".env"],
15
- cascade: arg,
16
- ...config.envPath !== void 0 ? { envPath: config.envPath } : {},
17
- ...config.override !== void 0 ? { override: config.override } : {},
18
- ...config.quiet !== void 0 ? { quiet: config.quiet } : {}
19
- };
20
- else opts = {
21
- envFiles: arg.envFiles ?? (config.envFiles ? [...config.envFiles] : [".env"]),
22
- ...arg.cascade !== void 0 ? { cascade: arg.cascade } : config.cascade !== void 0 ? { cascade: config.cascade } : {},
23
- ...arg.envPath !== void 0 ? { envPath: arg.envPath } : config.envPath !== void 0 ? { envPath: config.envPath } : {},
24
- ...arg.vault !== void 0 ? { vault: arg.vault } : {},
25
- ...arg.variables !== void 0 ? { variables: arg.variables } : {},
26
- ...arg.override !== void 0 ? { override: arg.override } : config.override !== void 0 ? { override: config.override } : {},
27
- ...arg.quiet !== void 0 ? { quiet: arg.quiet } : config.quiet !== void 0 ? { quiet: config.quiet } : {}
28
- };
29
- loadEnv(opts);
5
+ loadEnv(mergeOpts(config, arg === void 0 ? {} : typeof arg === "string" ? { cascade: arg } : arg));
30
6
  return process.env;
31
7
  }
8
+ /**
9
+ * Per-field merge: caller-supplied option wins, falling through to the
10
+ * matching config field, falling through to whatever default `loadEnv`
11
+ * has built in. Fields the caller doesn't pass and the config doesn't
12
+ * set are simply omitted from the merged options object.
13
+ */
14
+ function mergeOpts(config, user) {
15
+ const out = {};
16
+ if (user.envFiles !== void 0) out.envFiles = user.envFiles;
17
+ else if (config.envFiles !== void 0) out.envFiles = [...config.envFiles];
18
+ if (user.envPath !== void 0) out.envPath = user.envPath;
19
+ else if (config.envPath !== void 0) out.envPath = config.envPath;
20
+ if (user.cascade !== void 0) out.cascade = user.cascade;
21
+ else if (config.cascade !== void 0) out.cascade = config.cascade;
22
+ if (user.vault !== void 0) out.vault = user.vault;
23
+ if (user.variables !== void 0) out.variables = user.variables;
24
+ if (user.override !== void 0) out.override = user.override;
25
+ else if (config.override !== void 0) out.override = config.override;
26
+ if (user.quiet !== void 0) out.quiet = user.quiet;
27
+ else if (config.quiet !== void 0) out.quiet = config.quiet;
28
+ if (user.autoDetect !== void 0) out.autoDetect = user.autoDetect;
29
+ else if (config.autoDetect !== void 0) out.autoDetect = config.autoDetect;
30
+ if (user.nodeEnvMap !== void 0) out.nodeEnvMap = user.nodeEnvMap;
31
+ else if (config.nodeEnvMap !== void 0) out.nodeEnvMap = config.nodeEnvMap;
32
+ if (user.required !== void 0) out.required = user.required;
33
+ else if (config.required !== void 0) out.required = config.required;
34
+ if (user.expand !== void 0) out.expand = user.expand;
35
+ else if (config.expand !== void 0) out.expand = config.expand;
36
+ if (user.defaults !== void 0) out.defaults = user.defaults;
37
+ else if (config.defaults !== void 0) out.defaults = config.defaults;
38
+ if (user.workspaceRoot !== void 0) out.workspaceRoot = user.workspaceRoot;
39
+ else if (config.workspaceRoot !== void 0) out.workspaceRoot = config.workspaceRoot;
40
+ return out;
41
+ }
32
42
  //#endregion
33
43
  export { envx as default, envx };
34
44
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["// #region -- Programmatic Entry Point ----------------------\n\nimport {\n loadEnv,\n loadDotenvxConfig,\n type LoadEnvOptions,\n} from \"@honeycluster/libs\";\n\n/**\n * Load env files into `process.env` and return it.\n *\n * Modeled after the `dotenv` API but with explicit scoping. Three call\n * shapes:\n *\n * ```ts\n * import envx from \"@honeycluster/envx\";\n *\n * envx(); // load `.env` (auto-detect from NODE_ENV / VERCEL_ENV / NETLIFY)\n * envx(\"prod\"); // load `.env` with cascade=prod (.env, .env.prod, .env.local, .env.prod.local)\n * envx({ envFiles: [\"vault/.env.prod\"], envPath: \"vault\", quiet: false }); // full options\n * ```\n *\n * In all cases `process.env` is mutated and returned for ergonomic\n * destructuring. Config file discovery (`envx.config.{ts,js,json}` /\n * `package.json` `envx.config`) runs automatically; CLI-level options\n * still win when both are passed.\n */\nfunction envx(): NodeJS.ProcessEnv;\nfunction envx(scope: string): NodeJS.ProcessEnv;\nfunction envx(opts: LoadEnvOptions): NodeJS.ProcessEnv;\nfunction envx(arg?: string | LoadEnvOptions): NodeJS.ProcessEnv {\n // Resolve config-file defaults first; CLI/programmatic args override.\n const { config } = loadDotenvxConfig();\n\n let opts: LoadEnvOptions;\n if (arg === undefined) {\n opts = {\n envFiles: config.envFiles ? [...config.envFiles] : [\".env\"],\n ...(config.cascade !== undefined ? { cascade: config.cascade } : {}),\n ...(config.envPath !== undefined ? { envPath: config.envPath } : {}),\n ...(config.override !== undefined ? { override: config.override } : {}),\n ...(config.quiet !== undefined ? { quiet: config.quiet } : {}),\n };\n } else if (typeof arg === \"string\") {\n // String form is a cascade scope shortcut: envx(\"prod\") loads\n // .env, .env.prod, .env.local, .env.prod.local in that order.\n opts = {\n envFiles: config.envFiles ? [...config.envFiles] : [\".env\"],\n cascade: arg,\n ...(config.envPath !== undefined ? { envPath: config.envPath } : {}),\n ...(config.override !== undefined ? { override: config.override } : {}),\n ...(config.quiet !== undefined ? { quiet: config.quiet } : {}),\n };\n } else {\n // Object form: caller's options layered over config defaults.\n opts = {\n envFiles:\n arg.envFiles ?? (config.envFiles ? [...config.envFiles] : [\".env\"]),\n ...(arg.cascade !== undefined\n ? { cascade: arg.cascade }\n : config.cascade !== undefined\n ? { cascade: config.cascade }\n : {}),\n ...(arg.envPath !== undefined\n ? { envPath: arg.envPath }\n : config.envPath !== undefined\n ? { envPath: config.envPath }\n : {}),\n ...(arg.vault !== undefined ? { vault: arg.vault } : {}),\n ...(arg.variables !== undefined ? { variables: arg.variables } : {}),\n ...(arg.override !== undefined\n ? { override: arg.override }\n : config.override !== undefined\n ? { override: config.override }\n : {}),\n ...(arg.quiet !== undefined\n ? { quiet: arg.quiet }\n : config.quiet !== undefined\n ? { quiet: config.quiet }\n : {}),\n };\n }\n\n loadEnv(opts);\n return process.env;\n}\n\nexport default envx;\nexport { envx };\n\n// Re-export the types callers will need to type their own wrappers.\nexport type { LoadEnvOptions };\n\n// #endregion -----------------------------------------------\n"],"mappings":";;AA8BA,SAAS,KAAK,KAAkD;CAE9D,MAAM,EAAE,WAAW,mBAAmB;CAEtC,IAAI;CACJ,IAAI,QAAQ,KAAA,GACV,OAAO;EACL,UAAU,OAAO,WAAW,CAAC,GAAG,OAAO,SAAS,GAAG,CAAC,OAAO;EAC3D,GAAI,OAAO,YAAY,KAAA,IAAY,EAAE,SAAS,OAAO,SAAS,GAAG,EAAE;EACnE,GAAI,OAAO,YAAY,KAAA,IAAY,EAAE,SAAS,OAAO,SAAS,GAAG,EAAE;EACnE,GAAI,OAAO,aAAa,KAAA,IAAY,EAAE,UAAU,OAAO,UAAU,GAAG,EAAE;EACtE,GAAI,OAAO,UAAU,KAAA,IAAY,EAAE,OAAO,OAAO,OAAO,GAAG,EAAE;EAC9D;MACI,IAAI,OAAO,QAAQ,UAGxB,OAAO;EACL,UAAU,OAAO,WAAW,CAAC,GAAG,OAAO,SAAS,GAAG,CAAC,OAAO;EAC3D,SAAS;EACT,GAAI,OAAO,YAAY,KAAA,IAAY,EAAE,SAAS,OAAO,SAAS,GAAG,EAAE;EACnE,GAAI,OAAO,aAAa,KAAA,IAAY,EAAE,UAAU,OAAO,UAAU,GAAG,EAAE;EACtE,GAAI,OAAO,UAAU,KAAA,IAAY,EAAE,OAAO,OAAO,OAAO,GAAG,EAAE;EAC9D;MAGD,OAAO;EACL,UACE,IAAI,aAAa,OAAO,WAAW,CAAC,GAAG,OAAO,SAAS,GAAG,CAAC,OAAO;EACpE,GAAI,IAAI,YAAY,KAAA,IAChB,EAAE,SAAS,IAAI,SAAS,GACxB,OAAO,YAAY,KAAA,IACjB,EAAE,SAAS,OAAO,SAAS,GAC3B,EAAE;EACR,GAAI,IAAI,YAAY,KAAA,IAChB,EAAE,SAAS,IAAI,SAAS,GACxB,OAAO,YAAY,KAAA,IACjB,EAAE,SAAS,OAAO,SAAS,GAC3B,EAAE;EACR,GAAI,IAAI,UAAU,KAAA,IAAY,EAAE,OAAO,IAAI,OAAO,GAAG,EAAE;EACvD,GAAI,IAAI,cAAc,KAAA,IAAY,EAAE,WAAW,IAAI,WAAW,GAAG,EAAE;EACnE,GAAI,IAAI,aAAa,KAAA,IACjB,EAAE,UAAU,IAAI,UAAU,GAC1B,OAAO,aAAa,KAAA,IAClB,EAAE,UAAU,OAAO,UAAU,GAC7B,EAAE;EACR,GAAI,IAAI,UAAU,KAAA,IACd,EAAE,OAAO,IAAI,OAAO,GACpB,OAAO,UAAU,KAAA,IACf,EAAE,OAAO,OAAO,OAAO,GACvB,EAAE;EACT;CAGH,QAAQ,KAAK;CACb,OAAO,QAAQ"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["// #region -- Programmatic Entry Point ----------------------\n\nimport {\n loadEnv,\n loadDotenvxConfig,\n type DotenvxConfig,\n type LoadEnvOptions,\n} from \"@honeycluster/libs\";\n\n/**\n * Load env files into `process.env` and return it.\n *\n * Modeled after the `dotenv` API but with explicit scoping. Three call\n * shapes:\n *\n * ```ts\n * import envx from \"@honeycluster/envx\";\n *\n * envx(); // load `.env` (auto-detect from NODE_ENV / VERCEL_ENV / NETLIFY)\n * envx(\"prod\"); // load `.env` with cascade=prod (.env, .env.prod, .env.local, .env.prod.local)\n * envx({ envFiles: [\"vault/.env.prod\"], envPath: \"vault\", quiet: false }); // full options\n * ```\n *\n * In all cases `process.env` is mutated and returned for ergonomic\n * destructuring. Config file discovery (`envx.config.{ts,js,json}` /\n * `package.json` `envx.config`) runs automatically; programmatic\n * options layer on top of the discovered config, with caller-supplied\n * fields winning per-field.\n */\nfunction envx(): NodeJS.ProcessEnv;\nfunction envx(scope: string): NodeJS.ProcessEnv;\nfunction envx(opts: LoadEnvOptions): NodeJS.ProcessEnv;\nfunction envx(arg?: string | LoadEnvOptions): NodeJS.ProcessEnv {\n const { config } = loadDotenvxConfig();\n const userOpts: LoadEnvOptions =\n arg === undefined\n ? {}\n : typeof arg === \"string\"\n ? { cascade: arg }\n : arg;\n loadEnv(mergeOpts(config, userOpts));\n return process.env;\n}\n\n/**\n * Per-field merge: caller-supplied option wins, falling through to the\n * matching config field, falling through to whatever default `loadEnv`\n * has built in. Fields the caller doesn't pass and the config doesn't\n * set are simply omitted from the merged options object.\n */\nfunction mergeOpts(\n config: DotenvxConfig,\n user: LoadEnvOptions,\n): LoadEnvOptions {\n const out: { -readonly [K in keyof LoadEnvOptions]: LoadEnvOptions[K] } = {};\n\n // env-file selection\n if (user.envFiles !== undefined) out.envFiles = user.envFiles;\n else if (config.envFiles !== undefined) out.envFiles = [...config.envFiles];\n\n if (user.envPath !== undefined) out.envPath = user.envPath;\n else if (config.envPath !== undefined) out.envPath = config.envPath;\n\n if (user.cascade !== undefined) out.cascade = user.cascade;\n else if (config.cascade !== undefined) out.cascade = config.cascade;\n\n if (user.vault !== undefined) out.vault = user.vault;\n\n if (user.variables !== undefined) out.variables = user.variables;\n\n // load behavior\n if (user.override !== undefined) out.override = user.override;\n else if (config.override !== undefined) out.override = config.override;\n\n if (user.quiet !== undefined) out.quiet = user.quiet;\n else if (config.quiet !== undefined) out.quiet = config.quiet;\n\n // auto-detection\n if (user.autoDetect !== undefined) out.autoDetect = user.autoDetect;\n else if (config.autoDetect !== undefined) out.autoDetect = config.autoDetect;\n\n if (user.nodeEnvMap !== undefined) out.nodeEnvMap = user.nodeEnvMap;\n else if (config.nodeEnvMap !== undefined) out.nodeEnvMap = config.nodeEnvMap;\n\n // post-load behavior\n if (user.required !== undefined) out.required = user.required;\n else if (config.required !== undefined) out.required = config.required;\n\n if (user.expand !== undefined) out.expand = user.expand;\n else if (config.expand !== undefined) out.expand = config.expand;\n\n if (user.defaults !== undefined) out.defaults = user.defaults;\n else if (config.defaults !== undefined) out.defaults = config.defaults;\n\n // advanced\n if (user.workspaceRoot !== undefined) out.workspaceRoot = user.workspaceRoot;\n else if (config.workspaceRoot !== undefined) out.workspaceRoot = config.workspaceRoot;\n\n return out;\n}\n\nexport default envx;\nexport { envx };\n\n// Re-export the types callers will need to type their own wrappers.\nexport type { LoadEnvOptions };\n\n// #endregion -----------------------------------------------\n"],"mappings":";;AAgCA,SAAS,KAAK,KAAkD;CAC9D,MAAM,EAAE,WAAW,mBAAmB;CAOtC,QAAQ,UAAU,QALhB,QAAQ,KAAA,IACJ,EAAE,GACF,OAAO,QAAQ,WACb,EAAE,SAAS,KAAK,GAChB,IAC2B,CAAC;CACpC,OAAO,QAAQ;;;;;;;;AASjB,SAAS,UACP,QACA,MACgB;CAChB,MAAM,MAAoE,EAAE;CAG5E,IAAI,KAAK,aAAa,KAAA,GAAW,IAAI,WAAW,KAAK;MAChD,IAAI,OAAO,aAAa,KAAA,GAAW,IAAI,WAAW,CAAC,GAAG,OAAO,SAAS;CAE3E,IAAI,KAAK,YAAY,KAAA,GAAW,IAAI,UAAU,KAAK;MAC9C,IAAI,OAAO,YAAY,KAAA,GAAW,IAAI,UAAU,OAAO;CAE5D,IAAI,KAAK,YAAY,KAAA,GAAW,IAAI,UAAU,KAAK;MAC9C,IAAI,OAAO,YAAY,KAAA,GAAW,IAAI,UAAU,OAAO;CAE5D,IAAI,KAAK,UAAU,KAAA,GAAW,IAAI,QAAQ,KAAK;CAE/C,IAAI,KAAK,cAAc,KAAA,GAAW,IAAI,YAAY,KAAK;CAGvD,IAAI,KAAK,aAAa,KAAA,GAAW,IAAI,WAAW,KAAK;MAChD,IAAI,OAAO,aAAa,KAAA,GAAW,IAAI,WAAW,OAAO;CAE9D,IAAI,KAAK,UAAU,KAAA,GAAW,IAAI,QAAQ,KAAK;MAC1C,IAAI,OAAO,UAAU,KAAA,GAAW,IAAI,QAAQ,OAAO;CAGxD,IAAI,KAAK,eAAe,KAAA,GAAW,IAAI,aAAa,KAAK;MACpD,IAAI,OAAO,eAAe,KAAA,GAAW,IAAI,aAAa,OAAO;CAElE,IAAI,KAAK,eAAe,KAAA,GAAW,IAAI,aAAa,KAAK;MACpD,IAAI,OAAO,eAAe,KAAA,GAAW,IAAI,aAAa,OAAO;CAGlE,IAAI,KAAK,aAAa,KAAA,GAAW,IAAI,WAAW,KAAK;MAChD,IAAI,OAAO,aAAa,KAAA,GAAW,IAAI,WAAW,OAAO;CAE9D,IAAI,KAAK,WAAW,KAAA,GAAW,IAAI,SAAS,KAAK;MAC5C,IAAI,OAAO,WAAW,KAAA,GAAW,IAAI,SAAS,OAAO;CAE1D,IAAI,KAAK,aAAa,KAAA,GAAW,IAAI,WAAW,KAAK;MAChD,IAAI,OAAO,aAAa,KAAA,GAAW,IAAI,WAAW,OAAO;CAG9D,IAAI,KAAK,kBAAkB,KAAA,GAAW,IAAI,gBAAgB,KAAK;MAC1D,IAAI,OAAO,kBAAkB,KAAA,GAAW,IAAI,gBAAgB,OAAO;CAExE,OAAO"}