@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.
- package/README.md +479 -104
- package/dist/chunks/{commands-B8vc6UKO.js → commands-DNf_gJRx.js} +192 -8
- package/dist/chunks/commands-DNf_gJRx.js.map +1 -0
- package/dist/chunks/{src-CDuEfaCY.js → src-CwrtyfZE.js} +0 -0
- package/dist/chunks/src-CwrtyfZE.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +1 -1
- package/dist/commands/info.d.ts +10 -0
- package/dist/commands/info.d.ts.map +1 -0
- package/dist/commands/rotate.d.ts +13 -0
- package/dist/commands/rotate.d.ts.map +1 -0
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +36 -26
- package/dist/index.js.map +1 -1
- package/docs/auto-detection.md +217 -0
- package/docs/configuration.md +224 -0
- package/docs/recipes.md +234 -0
- package/package.json +6 -4
- package/dist/chunks/commands-B8vc6UKO.js.map +0 -1
- package/dist/chunks/src-CDuEfaCY.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@super-repo/envx",
|
|
3
3
|
"description": "A global executable to run applications with the ENV variables witin a monorepo loaded by dotenvx",
|
|
4
|
-
"version": "0.2.3-b.
|
|
4
|
+
"version": "0.2.3-b.3",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"envx": "./dist/cli.js",
|
|
@@ -26,7 +26,9 @@
|
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"files": [
|
|
29
|
-
"dist"
|
|
29
|
+
"dist",
|
|
30
|
+
"docs",
|
|
31
|
+
"README.md"
|
|
30
32
|
],
|
|
31
33
|
"publishConfig": {
|
|
32
34
|
"access": "public"
|
|
@@ -58,8 +60,8 @@
|
|
|
58
60
|
"vite": "^8.0.11",
|
|
59
61
|
"vite-plugin-dts": "^5.0.0",
|
|
60
62
|
"vitest": "^4.1.5",
|
|
63
|
+
"@super-repo/cli": "0.2.3-b.3",
|
|
61
64
|
"@super-repo/envx-common": "0.0.1",
|
|
62
|
-
"@super-repo/envx-libs": "0.0.1"
|
|
63
|
-
"@super-repo/cli": "0.2.3-b.2"
|
|
65
|
+
"@super-repo/envx-libs": "0.0.1"
|
|
64
66
|
}
|
|
65
67
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"commands-B8vc6UKO.js","names":[],"sources":["../../src/commands/debug.ts","../../src/commands/decrypt.ts","../../src/commands/encrypt.ts","../../src/commands/expand.ts","../../src/commands/print.ts","../../src/commands/run.ts","../../src/commands/index.ts"],"sourcesContent":["import type { CommandModule } from \"yargs\";\n\nimport { resolveEnvPaths, validateCmdVariable } from \"@honeycluster/libs\";\nimport { log } from \"@honeycluster/common\";\n\n// #region -- debug command ---------------------------------\n\nexport const debugCommand: CommandModule = {\n command: \"debug\",\n describe:\n \"Show which env files would be loaded and which variables would be applied, without loading them.\",\n handler: (argv) => {\n const paths = resolveEnvPaths({\n envFiles: argv[\"env\"] as string[] | undefined,\n cascade: argv[\"cascade\"] as string | undefined,\n });\n\n const rawVars = argv[\"variables\"] as string[] | undefined;\n const variables = rawVars\n ? Object.fromEntries(rawVars.map(validateCmdVariable))\n : {};\n\n log.info(`Paths: ${JSON.stringify(paths)}`);\n log.info(`Variables: ${JSON.stringify(variables)}`);\n },\n};\n\n// #endregion -----------------------------------------------\n","import type { CommandModule } from \"yargs\";\n\nimport { decryptFiles, writeProcessed } from \"@honeycluster/libs\";\n\nimport { log } from \"@honeycluster/common\";\n\n// #region -- decrypt command -------------------------------\n\n/** Mirror of `dotenvx decrypt` from upstream — see also encrypt.ts. */\nexport const decryptCommand: CommandModule = {\n command: \"decrypt\",\n describe:\n \"Decrypt the values in one or more .env files in place using the matching private key in .env.keys.\",\n builder: (yargs) =>\n yargs\n .option(\"env-keys-file\", {\n alias: \"fk\",\n type: \"string\",\n describe:\n \"Path to the .env.keys file (default: ./.env.keys at cwd, regardless of --dir / --env-path).\",\n })\n .option(\"key\", {\n alias: \"k\",\n type: \"array\",\n string: true,\n describe:\n \"Specific keys (or picomatch globs) to decrypt. Default: all keys.\",\n })\n .option(\"exclude-key\", {\n alias: \"ek\",\n type: \"array\",\n string: true,\n describe: \"Keys (or picomatch globs) to leave encrypted.\",\n })\n .option(\"stdout\", {\n type: \"boolean\",\n default: false,\n describe:\n \"Write the decrypted env contents to stdout instead of saving in place.\",\n })\n .help(\"h\")\n .alias(\"h\", \"help\"),\n handler: (argv) => {\n const envFiles = (argv[\"env\"] as string[] | undefined) ?? [\".env\"];\n const keys = argv[\"key\"] as string[] | undefined;\n const excludeKeys = argv[\"exclude-key\"] as string[] | undefined;\n const envKeysFile = argv[\"env-keys-file\"] as string | undefined;\n const stdout = (argv[\"stdout\"] as boolean | undefined) ?? false;\n\n const result = decryptFiles({\n envFiles,\n ...(keys ? { keys } : {}),\n ...(excludeKeys ? { excludeKeys } : {}),\n ...(envKeysFile ? { envKeysFile } : {}),\n });\n\n let hadError = false;\n\n for (const processed of result.processedEnvs) {\n if (processed.error) {\n hadError = true;\n log.error(`${processed.envFilepath}: ${processed.error.message}`);\n if (processed.error.help) log.dim(processed.error.help);\n continue;\n }\n if (stdout) {\n process.stdout.write(processed.envSrc);\n }\n }\n\n if (!stdout) {\n const { written } = writeProcessed(result.processedEnvs);\n for (const w of written) log.success(`decrypted ${w}`);\n if (\n written.length === 0 &&\n result.unchangedFilepaths.length > 0 &&\n !hadError\n ) {\n log.dim(`no changes (${result.unchangedFilepaths.join(\", \")})`);\n }\n }\n\n if (hadError) process.exitCode = 1;\n },\n};\n\n// #endregion -----------------------------------------------\n","import type { CommandModule } from \"yargs\";\n\nimport { encryptFiles, writeProcessed } from \"@honeycluster/libs\";\n\nimport { log } from \"@honeycluster/common\";\n\n// #region -- encrypt command -------------------------------\n\n/**\n * Mirrors the upstream `dotenvx encrypt` flags so existing muscle\n * memory carries over. The `--env` global doubles as the file list\n * (the upstream calls it `--env-file`); this CLI keeps a single\n * canonical name across subcommands.\n */\nexport const encryptCommand: CommandModule = {\n command: \"encrypt\",\n describe:\n \"Encrypt the values in one or more .env files. Generates a private key in .env.keys on first run.\",\n builder: (yargs) =>\n yargs\n .option(\"env-keys-file\", {\n alias: \"fk\",\n type: \"string\",\n describe:\n \"Path to the .env.keys file (default: ./.env.keys at cwd, regardless of --dir / --env-path).\",\n })\n .option(\"key\", {\n alias: \"k\",\n type: \"array\",\n string: true,\n describe:\n \"Specific keys (or picomatch globs) to encrypt. Default: all keys.\",\n })\n .option(\"exclude-key\", {\n alias: \"ek\",\n type: \"array\",\n string: true,\n describe: \"Keys (or picomatch globs) to leave plaintext.\",\n })\n .option(\"stdout\", {\n type: \"boolean\",\n default: false,\n describe:\n \"Write the encrypted env contents to stdout instead of saving in place.\",\n })\n .help(\"h\")\n .alias(\"h\", \"help\"),\n handler: (argv) => {\n const envFiles = (argv[\"env\"] as string[] | undefined) ?? [\".env\"];\n const keys = argv[\"key\"] as string[] | undefined;\n const excludeKeys = argv[\"exclude-key\"] as string[] | undefined;\n const envKeysFile = argv[\"env-keys-file\"] as string | undefined;\n const stdout = (argv[\"stdout\"] as boolean | undefined) ?? false;\n\n const result = encryptFiles({\n envFiles,\n ...(keys ? { keys } : {}),\n ...(excludeKeys ? { excludeKeys } : {}),\n ...(envKeysFile ? { envKeysFile } : {}),\n });\n\n let hadError = false;\n\n for (const processed of result.processedEnvs) {\n if (processed.error) {\n hadError = true;\n log.error(`${processed.envFilepath}: ${processed.error.message}`);\n if (processed.error.help) log.dim(processed.error.help);\n continue;\n }\n\n if (stdout) {\n process.stdout.write(processed.envSrc);\n continue;\n }\n\n if (processed.privateKeyAdded) {\n log.success(\n `key added to .env.keys (${processed.privateKeyName ?? \"<unknown>\"})`,\n );\n }\n }\n\n if (!stdout) {\n const { written } = writeProcessed(result.processedEnvs);\n for (const w of written) log.success(`encrypted ${w}`);\n if (\n written.length === 0 &&\n result.unchangedFilepaths.length > 0 &&\n !hadError\n ) {\n log.dim(`no changes (${result.unchangedFilepaths.join(\", \")})`);\n }\n }\n\n if (hadError) process.exitCode = 1;\n },\n};\n\n// #endregion -----------------------------------------------\n","import * as fs from \"fs\";\nimport * as path from \"path\";\nimport type { CommandModule } from \"yargs\";\n\nimport {\n decryptFiles,\n expandEnvSrc,\n isEncrypted,\n parseEnv,\n} from \"@honeycluster/libs\";\n\nimport { log } from \"@honeycluster/common\";\n\n// #region -- expand command --------------------------------\n\n/**\n * Decrypts (when needed) and expands variable references in an env\n * file. Mirrors the workflow `decrypt-vault` action — but cycle-safe,\n * supports `${VAR:-default}` / `${VAR:?msg}`, and never silently\n * truncates after N passes.\n */\nexport const expandCommand: CommandModule = {\n command: \"expand\",\n describe:\n \"Decrypt (if needed) and expand ${VAR}/$VAR references in an env file. Outputs to stdout by default.\",\n builder: (yargs) =>\n yargs\n .option(\"output\", {\n type: \"string\",\n describe: \"Write the expanded result to this file (default: stdout).\",\n })\n .option(\"env-keys-file\", {\n alias: \"fk\",\n type: \"string\",\n describe:\n \"Path to .env.keys (default: ./.env.keys at cwd, regardless of --dir / --env-path).\",\n })\n .option(\"on-missing\", {\n type: \"string\",\n choices: [\"leave\", \"empty\", \"throw\"] as const,\n default: \"leave\" as const,\n describe:\n \"How to handle ${UNRESOLVED_VAR}: leave it literal, substitute empty, or fail.\",\n })\n .help(\"h\")\n .alias(\"h\", \"help\"),\n handler: (argv) => {\n const envFiles = (argv[\"env\"] as string[] | undefined) ?? [\".env\"];\n if (envFiles.length !== 1) {\n log.error(\n `expand operates on a single env file at a time; got ${String(envFiles.length)}.`,\n );\n process.exit(1);\n }\n const envFile = envFiles[0]!;\n const filepath = path.resolve(envFile);\n if (!fs.existsSync(filepath)) {\n log.error(`env file not found: ${envFile}`);\n process.exit(1);\n }\n\n let envSrc = fs.readFileSync(filepath, \"utf8\");\n const hasEncrypted = parseEnv(envSrc).some(\n (l) => l.type === \"kv\" && isEncrypted(l.value),\n );\n\n if (hasEncrypted) {\n const envKeysFile = argv[\"env-keys-file\"] as string | undefined;\n const dec = decryptFiles({\n envFiles: [envFile],\n ...(envKeysFile ? { envKeysFile } : {}),\n });\n const processed = dec.processedEnvs[0];\n if (processed?.error) {\n log.error(`${envFile}: ${processed.error.message}`);\n if (processed.error.help) log.dim(processed.error.help);\n process.exit(1);\n }\n envSrc = processed!.envSrc;\n }\n\n const onMissing = argv[\"on-missing\"] as \"leave\" | \"empty\" | \"throw\";\n const result = expandEnvSrc(envSrc, { onMissing });\n\n for (const v of result.unresolved) {\n log.warn(`unresolved variable: ${v}`);\n }\n for (const cycle of result.cycles) {\n log.warn(`cycle: ${cycle.join(\" → \")}`);\n }\n\n const output = argv[\"output\"] as string | undefined;\n if (output) {\n fs.writeFileSync(path.resolve(output), result.envSrc);\n log.success(`expanded ${envFile} → ${output}`);\n } else {\n process.stdout.write(result.envSrc);\n if (!result.envSrc.endsWith(\"\\n\")) process.stdout.write(\"\\n\");\n }\n },\n};\n\n// #endregion -----------------------------------------------\n","import type { CommandModule } from \"yargs\";\n\nimport { loadEnv } from \"@honeycluster/libs\";\n\n// #region -- print command ---------------------------------\n\nexport const printCommand: CommandModule = {\n command: \"print <variable>\",\n describe: \"Load env files and print the value of a single variable.\",\n builder: (yargs) =>\n yargs.positional(\"variable\", {\n describe: \"Name of the variable to print\",\n type: \"string\",\n demandOption: true,\n }),\n handler: (argv) => {\n loadEnv({\n envFiles: argv[\"env\"] as string[] | undefined,\n variables: argv[\"variables\"] as string[] | undefined,\n cascade: argv[\"cascade\"] as string | undefined,\n vault: argv[\"vault\"] as boolean | undefined,\n envPath: argv[\"env-path\"] as string | undefined,\n override: argv[\"override\"] as boolean | undefined,\n quiet: argv[\"quiet\"] as boolean | undefined,\n });\n\n const name = argv[\"variable\"] as string;\n const value = process.env[name];\n process.stdout.write(value != null ? `${value}\\n` : \"\\n\");\n },\n};\n\n// #endregion -----------------------------------------------\n","import { execSync } from \"child_process\";\nimport type { CommandModule } from \"yargs\";\n\nimport { loadEnv } from \"@honeycluster/libs\";\nimport { log } from \"@honeycluster/common\";\n\n// #region -- run command -----------------------------------\n\nexport const runCommand: CommandModule = {\n command: \"run [command..]\",\n aliases: [\"$0\"],\n describe:\n \"Load env files into process.env and execute a command. Default subcommand — `dotenvx-run [command]` is equivalent.\",\n builder: (yargs) =>\n yargs.positional(\"command\", {\n describe: \"Command to execute after loading env files\",\n type: \"string\",\n array: true,\n }),\n handler: (argv) => {\n const command = ((argv[\"command\"] as string[] | undefined) ?? []).join(\" \");\n\n loadEnv({\n envFiles: argv[\"env\"] as string[] | undefined,\n variables: argv[\"variables\"] as string[] | undefined,\n cascade: argv[\"cascade\"] as string | undefined,\n vault: argv[\"vault\"] as boolean | undefined,\n envPath: argv[\"env-path\"] as string | undefined,\n override: argv[\"override\"] as boolean | undefined,\n quiet: argv[\"quiet\"] as boolean | undefined,\n });\n\n if (!command) return;\n\n log.info(`Running: ${command}`);\n try {\n execSync(command, { stdio: \"inherit\" });\n log.success(`Command completed: ${command}`);\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n log.error(`Command failed: ${msg}`);\n process.exit(1);\n }\n },\n};\n\n// #endregion -----------------------------------------------\n","import yargs from \"yargs\";\nimport type { Argv } from \"yargs\";\n\nimport * as path from \"path\";\n\nimport {\n findWorkspaceRoot,\n listEnvFiles,\n loadDotenvxConfig,\n} from \"@honeycluster/libs\";\n\nimport { log } from \"@honeycluster/common\";\nimport { debugCommand } from \"./debug.js\";\nimport { decryptCommand } from \"./decrypt.js\";\nimport { encryptCommand } from \"./encrypt.js\";\nimport { expandCommand } from \"./expand.js\";\nimport { printCommand } from \"./print.js\";\nimport { runCommand } from \"./run.js\";\n\n// #region -- CLI Factory -----------------------------------\n\n/**\n * Build the yargs CLI for `dotenvx-run`. Global options (env, variables,\n * cascade, vault, override, quiet) are registered on the root so every\n * subcommand inherits them.\n */\nexport function createCli(argvInput: string[]): Argv {\n return yargs(argvInput)\n .scriptName(\"envx\")\n .usage(\"$0 <command> [options]\")\n .option(\"config\", {\n alias: \"c\",\n type: \"string\",\n describe:\n \"Path to an envx config file (default: discovered from package.json `envx.config` or envx.config.{ts,js,json}).\",\n })\n .option(\"env\", {\n alias: \"e\",\n type: \"array\",\n describe:\n \"Env files to load. Default: [\\\".env\\\"] (or `envFiles` from config; or every .env* under --env-path when set). Auto-detects environment when omitted.\",\n })\n .option(\"env-path\", {\n alias: [\"dir\", \"d\"],\n type: \"string\",\n describe:\n \"Subdirectory of the workspace root holding the env files (e.g. `vault`). When set and --env is omitted, every .env* file in that directory is included.\",\n })\n .option(\"variables\", {\n alias: \"v\",\n type: \"array\",\n describe: \"Inline variables in the form name=value (repeatable).\",\n default: [],\n })\n .option(\"cascade\", {\n type: \"string\",\n describe:\n \"Cascade load order: .env, .env.<cascade>, .env.local, .env.<cascade>.local\",\n })\n .option(\"vault\", {\n alias: \"va\",\n type: \"boolean\",\n describe: \"Shortcut for `--env-path vault`.\",\n })\n .option(\"override\", {\n alias: \"o\",\n type: \"boolean\",\n describe: \"Override existing process.env values. Conflicts with --cascade.\",\n })\n .option(\"quiet\", {\n alias: \"q\",\n type: \"boolean\",\n describe: \"Suppress dotenv's own output.\",\n })\n .middleware((argv) => {\n const configPath = argv[\"config\"] as string | undefined;\n let loaded;\n try {\n loaded = loadDotenvxConfig({\n ...(configPath ? { configPath } : {}),\n });\n } catch (e) {\n log.error(`config error: ${(e as Error).message}`);\n process.exit(1);\n }\n if (loaded.source) {\n log.dim(`config: ${loaded.source} (${loaded.origin})`);\n }\n const cfg = loaded.config;\n // Apply config defaults only where the CLI didn't supply a value.\n if (argv[\"cascade\"] === undefined && cfg.cascade !== undefined) {\n argv[\"cascade\"] = cfg.cascade;\n }\n if (argv[\"override\"] === undefined) {\n argv[\"override\"] = cfg.override ?? false;\n }\n if (argv[\"quiet\"] === undefined) {\n argv[\"quiet\"] = cfg.quiet ?? true;\n }\n if (argv[\"vault\"] === undefined) {\n argv[\"vault\"] = false;\n }\n if (argv[\"env-keys-file\"] === undefined && cfg.envKeysFile !== undefined) {\n argv[\"env-keys-file\"] = cfg.envKeysFile;\n }\n\n // env-path: CLI > config > --vault shortcut\n if (argv[\"env-path\"] === undefined) {\n if (cfg.envPath !== undefined) argv[\"env-path\"] = cfg.envPath;\n else if (argv[\"vault\"]) argv[\"env-path\"] = \"vault\";\n }\n\n // env files: CLI > config > \"all .env* in env-path\" > [\".env\"]\n const userPassedEnv = Array.isArray(argv[\"env\"]) && argv[\"env\"].length > 0;\n let files: string[];\n if (userPassedEnv) {\n files = argv[\"env\"] as string[];\n } else if (cfg.envFiles && cfg.envFiles.length > 0) {\n files = [...cfg.envFiles];\n } else if (typeof argv[\"env-path\"] === \"string\") {\n // \"all files in the env-path are included\" — discover them.\n const subdir = argv[\"env-path\"] as string;\n const wsRoot = findWorkspaceRoot();\n const dir = path.resolve(wsRoot, subdir);\n const discovered = listEnvFiles(dir);\n if (discovered.length > 0) {\n files = discovered;\n log.dim(\n `env-path ${subdir}: discovered ${String(discovered.length)} file(s) — ${discovered.join(\", \")}`,\n );\n } else {\n files = [\".env\"];\n log.warn(\n `env-path ${subdir}: no .env* files found; falling back to [\".env\"]`,\n );\n }\n } else {\n files = [\".env\"];\n }\n\n // When env-path is set, resolve every relative file against that\n // directory up-front. encrypt/decrypt/expand operate on the\n // resulting paths verbatim — no per-command envPath plumbing.\n if (typeof argv[\"env-path\"] === \"string\") {\n const wsRoot = findWorkspaceRoot();\n const dir = path.resolve(wsRoot, argv[\"env-path\"] as string);\n files = files.map((f) => (path.isAbsolute(f) ? f : path.join(dir, f)));\n }\n argv[\"env\"] = files;\n })\n .command(runCommand)\n .command(printCommand)\n .command(debugCommand)\n .command(encryptCommand)\n .command(decryptCommand)\n .command(expandCommand)\n .help(\"h\")\n .alias(\"h\", \"help\")\n .version()\n .strictCommands()\n .demandCommand(0)\n .recommendCommands()\n .epilog(\n \"Examples:\\n\" +\n \" envx -- node app.js # load .env (auto-detected) and run\\n\" +\n \" envx --env dev -- pnpm start # load .env.dev\\n\" +\n \" envx print DATABASE_URL # print one variable\\n\" +\n \" envx debug --cascade prod # show resolved paths\\n\" +\n \" envx encrypt -e .env.prod # encrypt values in .env.prod\\n\" +\n \" envx decrypt -e .env.prod -k FOO # decrypt only FOO\\n\" +\n \" envx expand -e vault/.env.prod # decrypt + expand to stdout\\n\" +\n \" envx -c ./my.config.json run -- node app.js\",\n );\n}\n\n// #endregion -----------------------------------------------\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"src-CDuEfaCY.js","names":[],"sources":["../../../libs/src/crypto.ts","../../../libs/src/parser.ts","../../../libs/src/keys.ts","../../../libs/src/match.ts","../../../libs/src/encrypt.ts","../../../libs/src/decrypt.ts","../../../libs/src/expand.ts","../../../libs/src/config.ts","../../../common/src/logger.ts","../../../libs/src/env.ts","../../../libs/src/index.ts"],"sourcesContent":["import * as crypto from \"crypto\";\nimport { PrivateKey, encrypt as eciesEncrypt, decrypt as eciesDecrypt } from \"eciesjs\";\n\n// #region -- Algorithm Parameters --------------------------\n\n/**\n * AES-256-GCM with a random 96-bit nonce. Format on the wire:\n *\n * \"encrypted:\" + base64( nonce(12) ‖ ciphertext ‖ tag(16) )\n *\n * The `encrypted:` prefix matches the visual convention used by\n * upstream dotenvx, so encrypted values are recognizable in diffs even\n * though our crypto scheme is independent.\n */\nconst ALGORITHM = \"aes-256-gcm\" as const;\nconst NONCE_BYTES = 12;\nconst TAG_BYTES = 16;\nconst KEY_BYTES = 32;\nexport const ENCRYPTED_PREFIX = \"encrypted:\";\n\n// #endregion -----------------------------------------------\n\n// #region -- Key Generation --------------------------------\n\n/** Generate a fresh 256-bit private key encoded as 64 hex chars. */\nexport function generatePrivateKey(): string {\n return crypto.randomBytes(KEY_BYTES).toString(\"hex\");\n}\n\nfunction decodeKey(keyHex: string): Buffer {\n const buf = Buffer.from(keyHex, \"hex\");\n if (buf.length !== KEY_BYTES) {\n throw new Error(\n `[INVALID_KEY] private key must decode to ${String(KEY_BYTES)} bytes, got ${String(buf.length)}`,\n );\n }\n return buf;\n}\n\n// #endregion -----------------------------------------------\n\n// #region -- Encrypt / Decrypt -----------------------------\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/** AES-256-GCM encrypt; returns the `encrypted:<base64>` form. */\nexport function encryptValue(plaintext: string, keyHex: string): string {\n const key = decodeKey(keyHex);\n const nonce = crypto.randomBytes(NONCE_BYTES);\n const cipher = crypto.createCipheriv(ALGORITHM, key, nonce);\n const ciphertext = Buffer.concat([\n cipher.update(plaintext, \"utf8\"),\n cipher.final(),\n ]);\n const tag = cipher.getAuthTag();\n return ENCRYPTED_PREFIX + Buffer.concat([nonce, ciphertext, tag]).toString(\"base64\");\n}\n\n/**\n * AES-256-GCM decrypt. Throws on a corrupt/truncated blob, an auth-tag\n * mismatch, or a wrong key. Throws Errors with `.code` set so callers\n * can distinguish failure modes.\n */\nexport function decryptValue(blob: string, keyHex: string): string {\n if (!isEncrypted(blob)) {\n throw makeError(\"INVALID_CIPHERTEXT\", `value does not start with \"${ENCRYPTED_PREFIX}\"`);\n }\n const key = decodeKey(keyHex);\n const data = Buffer.from(blob.slice(ENCRYPTED_PREFIX.length), \"base64\");\n if (data.length < NONCE_BYTES + TAG_BYTES) {\n throw makeError(\"INVALID_CIPHERTEXT\", `ciphertext too short (${String(data.length)} bytes)`);\n }\n const nonce = data.subarray(0, NONCE_BYTES);\n const tag = data.subarray(data.length - TAG_BYTES);\n const ciphertext = data.subarray(NONCE_BYTES, data.length - TAG_BYTES);\n const decipher = crypto.createDecipheriv(ALGORITHM, key, nonce);\n decipher.setAuthTag(tag);\n try {\n return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString(\"utf8\");\n } catch (e) {\n const code =\n (e as NodeJS.ErrnoException).code === \"ERR_OSSL_BAD_DECRYPT\" ||\n (e as NodeJS.ErrnoException).code === \"ERR_CRYPTO_INVALID_AUTH_TAG\"\n ? \"DECRYPTION_FAILED\"\n : \"INVALID_CIPHERTEXT\";\n throw makeError(code, \"decryption failed (wrong key or tampered ciphertext)\");\n }\n}\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\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 *\n * Wire format (delegated to eciesjs to match dotenvx):\n * `encrypted:<base64(ephemeral_pub || nonce || ciphertext || tag)>`\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","// #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","import * as fs from \"fs\";\nimport * as path from \"path\";\n\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`: at the caller's cwd, NOT alongside the\n * env file. Keeping the keys file at the project root makes `--dir vault`\n * (or `--env-path vault`) move the env files into a subdirectory while\n * leaving the keys file in a single, predictable spot.\n *\n * Callers can still pass an explicit path via `--env-keys-file` / config's\n * `envKeysFile` to override.\n */\nexport function defaultKeysPath(): string {\n return path.resolve(process.cwd(), \".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 encryptValue,\n encryptValueAsymmetric,\n generateKeyPair,\n generatePrivateKey,\n isEncrypted,\n} from \"./crypto.js\";\nimport {\n PUBLIC_KEY_BANNER,\n defaultKeysPath,\n privateKeyCandidateNamesFor,\n publicKeyCandidateNamesFor,\n publicKeyNameFor,\n readKeysFile,\n writeKeysFile,\n} from \"./keys.js\";\nimport { isSelected } from \"./match.js\";\nimport { parseEnv, serializeEnv, toRecord } 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. Pure: returns\n * new file contents but does not write to disk. Use {@link writeProcessed}\n * to persist.\n *\n * Two encryption modes, picked by the presence of an `ENVX_PUBLIC_KEY*`\n * (or legacy `DOTENV_PUBLIC_KEY*`) entry at the top of the env file:\n *\n * - **asymmetric (ECIES)** — preferred for new files. The public key\n * is committed alongside the env file; the matching private key\n * stays in `.env.keys` (gitignored). On first encrypt of a fresh\n * file, generates a keypair, prepends the public-key banner +\n * header to the file, and writes the private key to `.env.keys`.\n *\n * - **symmetric (AES-256-GCM)** — legacy mode for files without a\n * public-key header. The `.env.keys` entry is the AES key directly.\n * Existing files keep working untouched; users opt into asymmetric\n * by adding (or generating) a public-key header.\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 // When --env-keys-file is set, resolve it relative to each env file's\n // directory rather than cwd. Lets users say `-fk ../.env.keys` and have\n // it land next to the project root regardless of where the env files\n // live (`vault/`, `secrets/`, etc.). Absolute paths bypass this and are\n // taken verbatim.\n const keysPath = opts.envKeysFile\n ? path.isAbsolute(opts.envKeysFile)\n ? opts.envKeysFile\n : path.resolve(path.dirname(filepath), 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 = parseEnv(fs.readFileSync(filepath, \"utf8\"));\n const linesAsRecord = toRecord(lines);\n\n // Detect mode by looking for an existing public-key header.\n const pubCandidates = publicKeyCandidateNamesFor(envFilePath);\n const canonicalPubName = pubCandidates[0]!;\n let publicKeyHex: string | undefined;\n for (const name of pubCandidates) {\n const v = linesAsRecord[name];\n if (v && v.length > 0) {\n publicKeyHex = v;\n break;\n }\n }\n const useAsymmetric = publicKeyHex !== undefined || !filePresumedLegacy(lines);\n\n const keysMap = readKeysFile(keysPath);\n const privCandidates = privateKeyCandidateNamesFor(envFilePath);\n const canonicalPrivName = privCandidates[0]!;\n let keyName = canonicalPrivName;\n let privateKeyHex: string | undefined;\n for (const name of privCandidates) {\n const v = keysMap.get(name);\n if (v) {\n privateKeyHex = v;\n keyName = name;\n break;\n }\n }\n\n let privateKeyAdded = false;\n\n if (useAsymmetric) {\n // Asymmetric path. We need a public/private keypair. If the file\n // doesn't have a header yet (and we have no private key on disk),\n // generate fresh ones, write the header, persist the private key.\n if (publicKeyHex === undefined) {\n if (privateKeyHex !== undefined) {\n // Edge case: private key exists but no public-key header.\n // We can't derive the public key from a 32-byte symmetric AES\n // secret, so treat this as a different file (legacy AES) and\n // fall back. Caller can purge .env.keys if they want to reset.\n // — handled by setting useAsymmetric=false below at runtime.\n } else {\n const pair = generateKeyPair();\n publicKeyHex = pair.publicKey;\n privateKeyHex = pair.privateKey;\n keysMap.set(canonicalPrivName, privateKeyHex);\n keyName = canonicalPrivName;\n // Splice public-key banner + header into the parsed lines so\n // it lands at the top of the rewritten file.\n lines = injectPublicKeyHeader(lines, canonicalPubName, publicKeyHex);\n writeKeysFile(keysPath, keysMap, {\n sectionFor: name => sectionForKey(name),\n });\n privateKeyAdded = true;\n }\n }\n }\n\n // After the optional asymmetric setup above, decide the actual mode\n // by what we have on hand.\n const mode: \"asymmetric\" | \"symmetric\" =\n publicKeyHex !== undefined && privateKeyHex !== undefined\n ? \"asymmetric\"\n : \"symmetric\";\n\n if (mode === \"symmetric\" && privateKeyHex === undefined) {\n // Legacy path: no public-key header AND no private key in .env.keys.\n // Generate a fresh AES key like before.\n privateKeyHex = generatePrivateKey();\n keysMap.set(canonicalPrivName, privateKeyHex);\n keyName = canonicalPrivName;\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 === canonicalPubName || line.key === pubCandidates[1]) continue;\n\n line.value =\n mode === \"asymmetric\"\n ? encryptValueAsymmetric(line.value, publicKeyHex!)\n : encryptValue(line.value, privateKeyHex!);\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: keyName, 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 * Decide whether a file with no public-key header should be treated as\n * legacy AES. A file is \"presumed legacy\" when it already has encrypted\n * values present — promoting it to asymmetric would orphan those values.\n * Empty / fully-plaintext files default to asymmetric.\n */\nfunction filePresumedLegacy(lines: ReadonlyArray<import(\"./parser.js\").EnvLine>): boolean {\n for (const line of lines) {\n if (line.type !== \"kv\") continue;\n if (isEncrypted(line.value)) return true;\n }\n return false;\n}\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<import(\"./parser.js\").EnvLine>,\n publicKeyName: string,\n publicKeyHex: string,\n): import(\"./parser.js\").EnvLine[] {\n const out: import(\"./parser.js\").EnvLine[] = [];\n // Banner is 4 raw lines + a trailing newline; render each as a `raw`\n // line so the parser can round-trip them.\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 // Blank separator before the rest of the file.\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 { decryptValue, decryptValueAsymmetric, isEncrypted } from \"./crypto.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 ? path.isAbsolute(opts.envKeysFile)\n ? opts.envKeysFile\n : path.resolve(path.dirname(filepath), 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 // Mode is determined by the presence of a public-key header in the\n // env file. ENVX_PUBLIC_KEY* (or legacy DOTENV_PUBLIC_KEY*) → ECIES\n // (asymmetric); none → AES-256-GCM (symmetric, legacy). The env-file\n // record is the authoritative source — `.env.keys` only carries the\n // private secret, which is interpreted differently per mode.\n const linesAsRecord = toRecord(lines);\n const pubCandidates = publicKeyCandidateNamesFor(envFilePath);\n let mode: \"asymmetric\" | \"symmetric\" = \"symmetric\";\n for (const name of pubCandidates) {\n if (linesAsRecord[name] && linesAsRecord[name]!.length > 0) {\n mode = \"asymmetric\";\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 (!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 =\n mode === \"asymmetric\"\n ? decryptValueAsymmetric(line.value, keyHex)\n : decryptValue(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 { 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 { 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 of the workspace root where env files live (e.g. \"vault\").\n * When set, relative env file paths resolve to `<workspaceRoot>/<envPath>/<file>`\n * instead of `<workspaceRoot>/<file>`. Equivalent to passing `--vault`\n * (which is a shortcut for `envPath: \"vault\"`).\n */\n readonly envPath?: string;\n /** Cascade strategy ('prod', 'dev', etc.) — see loadEnv. */\n readonly cascade?: string;\n /** Path to the keys file. Defaults to .env.keys alongside each env file. */\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\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\"] === \"string\") 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 (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 -- 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 * as fs from \"fs\";\nimport * as path from \"path\";\nimport * as dotenv from \"@dotenvx/dotenvx\";\n\nimport { log } from \"@honeycluster/common\";\n\n// #region -- Environment Detection -------------------------\n\n/**\n * Detect the deployment environment from well-known platform variables.\n * Order of precedence: Vercel → Netlify → NODE_ENV → 'root'.\n */\nexport function detectEnvironment(): 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 switch (nodeEnv.toLowerCase()) {\n case \"production\":\n return \"prod\";\n case \"development\":\n return \"dev\";\n case \"local\":\n return \"local\";\n default:\n return \"root\";\n }\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// #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. Order is\n * least- to most-specific so the caller (which loads them in order with\n * later entries overriding earlier ones) ends up with the most specific\n * values winning.\n */\nexport function expandCascadePaths(\n paths: string[],\n cascade: string | boolean,\n): string[] {\n return paths.reduce<string[]>(\n (accumulator, p) =>\n accumulator.concat(\n typeof cascade === \"string\"\n ? [`${p}.${cascade}.local`, `${p}.local`, `${p}.${cascade}`, p]\n : [`${p}.local`, p],\n ),\n [],\n );\n}\n\n// #endregion -- Cascade Path Expansion ---------------------\n\n// #region -- Env Loading -----------------------------------\n\nexport interface ResolveEnvOptions {\n readonly envFiles?: string[] | string;\n readonly cascade?: string | boolean;\n readonly autoDetect?: boolean;\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 if (autoDetect && envFiles.length === 1 && envFiles[0] === \".env\") {\n const detected = detectEnvironment();\n if (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\n let paths = envFiles.map((e) =>\n path.isAbsolute(e) || e.startsWith(\".env\") ? e : `.env.${e}`,\n );\n if (cascade) paths = expandCascadePaths(paths, cascade);\n return paths;\n}\n\nexport interface LoadEnvOptions {\n readonly envFiles?: string[] | string;\n readonly variables?: string[] | string;\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}\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 } = 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 });\n const workspaceRoot = 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 return { paths };\n}\n\n// #endregion -- Env Loading --------------------------------\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 encryptValue,\n decryptValue,\n encryptValueAsymmetric,\n decryptValueAsymmetric,\n generateKeyPair,\n generatePrivateKey,\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\";\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 detectEnvironment,\n validateCmdVariable,\n findWorkspaceRoot,\n expandCascadePaths,\n listEnvFiles,\n resolveEnvPaths,\n loadEnv,\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":";;;;;;;;;;;;;;;;AAcA,IAAM,YAAY;AAClB,IAAM,cAAc;AACpB,IAAM,YAAY;AAClB,IAAM,YAAY;AAClB,IAAa,mBAAmB;;AAOhC,SAAgB,qBAA6B;CAC3C,OAAO,OAAO,YAAY,UAAU,CAAC,SAAS,MAAM;;AAGtD,SAAS,UAAU,QAAwB;CACzC,MAAM,MAAM,OAAO,KAAK,QAAQ,MAAM;CACtC,IAAI,IAAI,WAAW,WACjB,MAAM,IAAI,MACR,4CAA4C,OAAO,UAAU,CAAC,cAAc,OAAO,IAAI,OAAO,GAC/F;CAEH,OAAO;;;AAQT,SAAgB,YAAY,OAAwB;CAClD,OAAO,MAAM,WAAW,iBAAiB;;;AAI3C,SAAgB,aAAa,WAAmB,QAAwB;CACtE,MAAM,MAAM,UAAU,OAAO;CAC7B,MAAM,QAAQ,OAAO,YAAY,YAAY;CAC7C,MAAM,SAAS,OAAO,eAAe,WAAW,KAAK,MAAM;CAC3D,MAAM,aAAa,OAAO,OAAO,CAC/B,OAAO,OAAO,WAAW,OAAO,EAChC,OAAO,OAAO,CACf,CAAC;CACF,MAAM,MAAM,OAAO,YAAY;CAC/B,OAAO,mBAAmB,OAAO,OAAO;EAAC;EAAO;EAAY;EAAI,CAAC,CAAC,SAAS,SAAS;;;;;;;AAQtF,SAAgB,aAAa,MAAc,QAAwB;CACjE,IAAI,CAAC,YAAY,KAAK,EACpB,MAAM,UAAU,sBAAsB,8BAA8B,iBAAiB,GAAG;CAE1F,MAAM,MAAM,UAAU,OAAO;CAC7B,MAAM,OAAO,OAAO,KAAK,KAAK,MAAM,GAAwB,EAAE,SAAS;CACvE,IAAI,KAAK,SAAS,cAAc,WAC9B,MAAM,UAAU,sBAAsB,yBAAyB,OAAO,KAAK,OAAO,CAAC,SAAS;CAE9F,MAAM,QAAQ,KAAK,SAAS,GAAG,YAAY;CAC3C,MAAM,MAAM,KAAK,SAAS,KAAK,SAAS,UAAU;CAClD,MAAM,aAAa,KAAK,SAAS,aAAa,KAAK,SAAS,UAAU;CACtE,MAAM,WAAW,OAAO,iBAAiB,WAAW,KAAK,MAAM;CAC/D,SAAS,WAAW,IAAI;CACxB,IAAI;EACF,OAAO,OAAO,OAAO,CAAC,SAAS,OAAO,WAAW,EAAE,SAAS,OAAO,CAAC,CAAC,CAAC,SAAS,OAAO;UAC/E,GAAG;EAMV,MAAM,UAJH,EAA4B,SAAS,0BACrC,EAA4B,SAAS,gCAClC,sBACA,sBACgB,uDAAuD;;;AAQjF,SAAS,UAAU,MAAc,SAA8B;CAC7D,MAAM,IAAI,IAAI,MAAM,QAAQ;CAC5B,EAAE,OAAO;CACT,OAAO;;;;;;;;;;AAeT,SAAgB,kBAA6D;CAC3E,MAAM,KAAK,IAAI,YAAY;CAG3B,OAAO;EACL,YAAY,OAAO,KAAK,GAAG,OAAO,CAAC,SAAS,MAAM;EAClD,WAAW,OAAO,KAAK,GAAG,UAAU,WAAW,CAAC,SAAS,MAAM;EAChE;;;;;;;;;;AAWH,SAAgB,uBAAuB,WAAmB,cAA8B;CACtF,IAAI;EACF,MAAM,OAAO,QAAa,cAAc,OAAO,KAAK,WAAW,OAAO,CAAC;EACvE,OAAO,mBAAmB,OAAO,KAAK,KAAK,CAAC,SAAS,SAAS;UACvD,GAAG;EACV,MAAM,UAAU,qBAAqB,yBAA0B,EAAY,UAAU;;;;AAKzF,SAAgB,uBAAuB,MAAc,eAA+B;CAClF,IAAI,CAAC,YAAY,KAAK,EACpB,MAAM,UAAU,sBAAsB,8BAA8B,iBAAiB,GAAG;CAE1F,MAAM,OAAO,OAAO,KAAK,KAAK,MAAM,GAAwB,EAAE,SAAS;CACvE,IAAI;EACF,MAAM,MAAM,QAAa,eAAe,KAAK;EAC7C,OAAO,OAAO,KAAK,IAAI,CAAC,SAAS,OAAO;UACjC,GAAG;EACV,MAAM,UAAU,qBAAqB,yBAA0B,EAAY,UAAU;;;;;ACxHzF,IAAM,WAAW;;AAGjB,SAAgB,SAAS,SAA4B;CACnD,MAAM,QAAQ,QAAQ,MAAM,KAAK;CAGjC,MAAM,gBAAgB,MAAM,SAAS,KAAK,MAAM,MAAM,SAAS,OAAO;CAGtE,MAAM,OAFO,gBAAgB,MAAM,MAAM,GAAG,GAAG,GAAG,OAEtB,KAAK,SAAS;EACxC,IAAI,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM,CAAC,WAAW,IAAI,EACnD,OAAO;GAAE,MAAM;GAAO,KAAK;GAAM;EAEnC,MAAM,QAAQ,SAAS,KAAK,KAAK;EACjC,IAAI,CAAC,OAAO,OAAO;GAAE,MAAM;GAAO,KAAK;GAAM;EAE7C,MAAM,MAAM,MAAM;EAClB,MAAM,qBAAqB,MAAM;EACjC,MAAM,EAAE,UAAU,aAAa,qBAAqB,mBAAmB;EACvE,MAAM,EAAE,OAAO,UAAU,QAAQ,SAAS;EAE1C,OAAO;GAAE,MAAM;GAAM;GAAK,KAAK;GAAU;GAAO;GAAO;GAAU;GACjE;CAEF,IAAI,eAAe,IAAI,KAAK;EAAE,MAAM;EAAO,KAAK;EAAI,CAAC;CACrD,OAAO;;;AAIT,SAAgB,aAAa,OAAmC;CAC9D,OAAO,MACJ,KAAK,SAAS;EACb,IAAI,KAAK,SAAS,OAAO,OAAO,KAAK;EACrC,MAAM,YAAY,YAAY,KAAK,OAAO,KAAK,MAAM;EACrD,OAAO,GAAG,KAAK,IAAI,GAAG,YAAY,KAAK;GACvC,CACD,KAAK,KAAK;;AAOf,SAAS,qBAAqB,GAAmD;CAG/E,IAAI,EAAE,WAAW,KAAI,IAAI,EAAE,WAAW,IAAI,EAAE;EAC1C,MAAM,QAAQ,EAAE;EAChB,IAAI,IAAI;EACR,OAAO,IAAI,EAAE,QAAQ;GACnB,IAAI,EAAE,OAAO,MAAM;IACjB,KAAK;IACL;;GAEF,IAAI,EAAE,OAAO,OAAO;IAClB,KAAK;IACL;;GAEF,KAAK;;EAEP,OAAO;GAAE,UAAU,EAAE,MAAM,GAAG,EAAE;GAAE,UAAU,EAAE,MAAM,EAAE;GAAE;;CAG1D,MAAM,IAAI,UAAU,KAAK,EAAE;CAC3B,IAAI,CAAC,GAAG,OAAO;EAAE,UAAU;EAAG,UAAU;EAAI;CAC5C,OAAO;EACL,UAAU,EAAE,MAAM,GAAG,EAAE,MAAM;EAC7B,UAAU,EAAE,MAAM,EAAE,MAAM;EAC3B;;AAGH,SAAS,QAAQ,KAAuD;CACtE,IAAI,IAAI,UAAU,KAAK,IAAI,WAAW,KAAI,IAAI,IAAI,SAAS,KAAI,EAC7D,OAAO;EAAE,OAAO,qBAAqB,IAAI,MAAM,GAAG,GAAG,CAAC;EAAE,OAAO;EAAK;CAEtE,IAAI,IAAI,UAAU,KAAK,IAAI,WAAW,IAAI,IAAI,IAAI,SAAS,IAAI,EAC7D,OAAO;EAAE,OAAO,IAAI,MAAM,GAAG,GAAG;EAAE,OAAO;EAAK;CAEhD,OAAO;EAAE,OAAO;EAAK,OAAO;EAAI;;AAGlC,SAAS,qBAAqB,GAAmB;CAC/C,OAAO,EACJ,QAAQ,SAAS,KAAI,CACrB,QAAQ,QAAQ,KAAK,CACrB,QAAQ,QAAQ,KAAK,CACrB,QAAQ,QAAQ,IAAK,CACrB,QAAQ,QAAQ,KAAI,CACpB,QAAQ,MAAM,KAAK;;AAGxB,SAAS,mBAAmB,GAAmB;CAC7C,OAAO,EACJ,QAAQ,OAAO,OAAO,CACtB,QAAQ,MAAM,OAAM,CACpB,QAAQ,OAAO,MAAM,CACrB,QAAQ,OAAO,MAAM,CACrB,QAAQ,OAAO,MAAM;;AAG1B,SAAS,YAAY,OAAe,eAAuC;CAGzE,MAAM,cAAc,cAAc,KAAK,MAAM,IAAI,UAAU;CAC3D,IAAI,kBAAkB,OAAO,CAAC,MAAM,SAAS,IAAI,EAC/C,OAAO,IAAI,MAAM;CAEnB,IAAI,kBAAkB,OAAO,MAAM,SAAS,IAAI,EAE9C,OAAO,IAAI,mBAAmB,MAAM,CAAC;CAEvC,IAAI,kBAAkB,MACpB,OAAO,IAAI,mBAAmB,MAAM,CAAC;CAGvC,IAAI,CAAC,aAAa,OAAO;CACzB,OAAO,IAAI,mBAAmB,MAAM,CAAC;;;AAQvC,SAAgB,SAAS,OAAmD;CAC1E,MAAM,MAA8B,EAAE;CACtC,KAAK,MAAM,QAAQ,OACjB,IAAI,KAAK,SAAS,MAAM,IAAI,KAAK,OAAO,KAAK;CAE/C,OAAO;;;;;;;;ACtJT,IAAa,0BAA0B;;;;;;;AAQvC,IAAa,yBAAyB;;;;;;AAOtC,IAAa,4BAA4B;;AAGzC,IAAa,2BAA2B;;;;;;;;;;;;AAyBxC,SAAgB,4BACd,aACmB;CACnB,OAAO,CACL,4BAA4B,aAAa,wBAAwB,EACjE,4BAA4B,aAAa,0BAA0B,CACpE;;;;;;;;AAcH,SAAgB,2BACd,aACmB;CACnB,OAAO,CACL,4BAA4B,aAAa,uBAAuB,EAChE,4BAA4B,aAAa,yBAAyB,CACnE;;AAGH,SAAS,4BACP,aACA,QACQ;CACR,MAAM,WAAW,KAAK,SAAS,YAAY;CAC3C,IAAI,aAAa,QAAQ,OAAO;CAEhC,OAAO,GAAG,OAAO,GAAG,YADL,SAAS,QAAQ,YAAY,GACd,CAAO;;AAGvC,SAAS,YAAU,GAAmB;CACpC,OAAO,EAAE,aAAa,CAAC,QAAQ,cAAc,IAAI;;;AAQnD,SAAgB,aAAa,UAAuC;CAClE,IAAI,CAAC,GAAG,WAAW,SAAS,EAAE,uBAAO,IAAI,KAAK;CAC9C,MAAM,QAAQ,SAAS,GAAG,aAAa,UAAU,OAAO,CAAC;CACzD,OAAO,IAAI,IAAI,OAAO,QAAQ,SAAS,MAAM,CAAC,CAAC;;;;;;AAOjD,IAAa,mBACX;;;;;;AAUF,IAAa,oBACX;;;;;;;;;;AAcF,SAAgB,cACd,UACA,MACA,OAEI,EAAE,EACA;CACN,IAAI,KAAK,SAAS,GAAG;EACnB,GAAG,cAAc,UAAU,iBAAiB;EAC5C;;CAEF,MAAM,MAAgB,CAAC,iBAAiB;CACxC,IAAI;CACJ,KAAK,MAAM,CAAC,KAAK,UAAU,MAAM;EAC/B,MAAM,UAAU,KAAK,aAAa,IAAI;EACtC,IAAI,YAAY,KAAA,KAAa,YAAY,aAAa;GACpD,IAAI,KAAK,OAAO,QAAQ,IAAI;GAC5B,cAAc;SACT,IAAI,gBAAgB,KAAA,GAAW;GAGpC,IAAI,KAAK,KAAK;GACd,cAAc;;EAIhB,MAAM,OAAsC;GAC1C,MAAM;GACN;GACA;GACA,KAAK;GACL,OAAO;GACP,UAAU;GACX;EACD,IAAI,KAAK,aAAa,CAAC,KAAK,CAAC,GAAG,KAAK;;CAEvC,GAAG,cAAc,UAAU,IAAI,KAAK,GAAG,CAAC;;;;;;;;;;;AAY1C,SAAgB,kBAA0B;CACxC,OAAO,KAAK,QAAQ,QAAQ,KAAK,EAAE,YAAY;;;;;;;;;;ACnLjD,SAAS,QAAQ,SAAyB;CAExC,MAAM,OADU,QAAQ,QAAQ,qBAAqB,OACxC,CAAQ,QAAQ,OAAO,KAAK,CAAC,QAAQ,OAAO,IAAI;CAC7D,OAAO,IAAI,OAAO,IAAI,KAAK,GAAG;;AAGhC,SAAgB,WAAW,KAAa,UAAsC;CAC5E,KAAK,MAAM,WAAW,UACpB,IAAI,QAAQ,QAAQ,CAAC,KAAK,IAAI,EAAE,OAAO;CAEzC,OAAO;;;;;;AAOT,SAAgB,WACd,KACA,UACA,UACS;CACT,IAAI,SAAS,SAAS,KAAK,WAAW,KAAK,SAAS,EAAE,OAAO;CAC7D,IAAI,SAAS,WAAW,GAAG,OAAO;CAClC,OAAO,WAAW,KAAK,SAAS;;;;;;;;;;;;;;;;;;;;;;;ACalC,SAAgB,aAAa,MAA6B;CACxD,MAAM,WAAW,KAAK,QAAQ,EAAE;CAChC,MAAM,WAAW,KAAK,eAAe,EAAE;CACvC,MAAM,gBAAgC,EAAE;CACxC,MAAM,mBAA6B,EAAE;CACrC,MAAM,qBAA+B,EAAE;CAEvC,KAAK,MAAM,eAAe,KAAK,UAAU;EACvC,MAAM,WAAW,KAAK,QAAQ,YAAY;EAM1C,MAAM,WAAW,KAAK,cAClB,KAAK,WAAW,KAAK,YAAY,GAC/B,KAAK,cACL,KAAK,QAAQ,KAAK,QAAQ,SAAS,EAAE,KAAK,YAAY,GACxD,iBAAiB;EAErB,IAAI,CAAC,GAAG,WAAW,SAAS,EAAE;GAC5B,cAAc,KAAK;IACjB,aAAa;IACb;IACA,QAAQ;IACR,SAAS;IACT,MAAM,EAAE;IACR,OAAO;KACL,MAAM;KACN,SAAS,uBAAuB;KAChC,MAAM,sCAAsC,YAAY;KACzD;IACF,CAAC;GACF;;EAGF,IAAI,QAAQ,SAAS,GAAG,aAAa,UAAU,OAAO,CAAC;EACvD,MAAM,gBAAgB,SAAS,MAAM;EAGrC,MAAM,gBAAgB,2BAA2B,YAAY;EAC7D,MAAM,mBAAmB,cAAc;EACvC,IAAI;EACJ,KAAK,MAAM,QAAQ,eAAe;GAChC,MAAM,IAAI,cAAc;GACxB,IAAI,KAAK,EAAE,SAAS,GAAG;IACrB,eAAe;IACf;;;EAGJ,MAAM,gBAAgB,iBAAiB,KAAA,KAAa,CAAC,mBAAmB,MAAM;EAE9E,MAAM,UAAU,aAAa,SAAS;EACtC,MAAM,iBAAiB,4BAA4B,YAAY;EAC/D,MAAM,oBAAoB,eAAe;EACzC,IAAI,UAAU;EACd,IAAI;EACJ,KAAK,MAAM,QAAQ,gBAAgB;GACjC,MAAM,IAAI,QAAQ,IAAI,KAAK;GAC3B,IAAI,GAAG;IACL,gBAAgB;IAChB,UAAU;IACV;;;EAIJ,IAAI,kBAAkB;EAEtB,IAAI;OAIE,iBAAiB,KAAA,GACnB,IAAI,kBAAkB,KAAA,GAAW,QAM1B;IACL,MAAM,OAAO,iBAAiB;IAC9B,eAAe,KAAK;IACpB,gBAAgB,KAAK;IACrB,QAAQ,IAAI,mBAAmB,cAAc;IAC7C,UAAU;IAGV,QAAQ,sBAAsB,OAAO,kBAAkB,aAAa;IACpE,cAAc,UAAU,SAAS,EAC/B,aAAY,SAAQ,cAAc,KAAK,EACxC,CAAC;IACF,kBAAkB;;;EAOxB,MAAM,OACJ,iBAAiB,KAAA,KAAa,kBAAkB,KAAA,IAC5C,eACA;EAEN,IAAI,SAAS,eAAe,kBAAkB,KAAA,GAAW;GAGvD,gBAAgB,oBAAoB;GACpC,QAAQ,IAAI,mBAAmB,cAAc;GAC7C,UAAU;GACV,cAAc,UAAU,SAAS,EAC/B,aAAY,SAAQ,cAAc,KAAK,EACxC,CAAC;GACF,kBAAkB;;EAGpB,MAAM,WAAqB,EAAE;EAC7B,IAAI,UAAU;EAEd,KAAK,MAAM,QAAQ,OAAO;GACxB,IAAI,KAAK,SAAS,MAAM;GACxB,SAAS,KAAK,KAAK,IAAI;GACvB,IAAI,YAAY,KAAK,MAAM,EAAE;GAC7B,IAAI,CAAC,WAAW,KAAK,KAAK,UAAU,SAAS,EAAE;GAE/C,IAAI,KAAK,QAAQ,oBAAoB,KAAK,QAAQ,cAAc,IAAI;GAEpE,KAAK,QACH,SAAS,eACL,uBAAuB,KAAK,OAAO,aAAc,GACjD,aAAa,KAAK,OAAO,cAAe;GAC9C,KAAK,QAAQ;GACb,UAAU;;EAIZ,MAAM,YAA0B;GAC9B,aAAa;GACb;GACA,QAJa,aAAa,MAI1B;GACA;GACA,MAAM;GACN,GAAI,kBACA;IAAE,iBAAiB;IAAM,gBAAgB;IAAS,YAAY;IAAe,GAC7E,EAAE;GACP;EACD,cAAc,KAAK,UAAU;EAC7B,IAAI,SAAS,iBAAiB,KAAK,SAAS;OACvC,mBAAmB,KAAK,SAAS;;CAGxC,OAAO;EAAE;EAAe;EAAkB;EAAoB;;;;;;;;AAWhE,SAAS,mBAAmB,OAA8D;CACxF,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,KAAK,SAAS,MAAM;EACxB,IAAI,YAAY,KAAK,MAAM,EAAE,OAAO;;CAEtC,OAAO;;;;;;;AAQT,SAAS,sBACP,OACA,eACA,cACiC;CACjC,MAAM,MAAuC,EAAE;CAG/C,KAAK,MAAM,OAAO,kBAAkB,MAAM,KAAK,EAAE;EAC/C,IAAI,IAAI,WAAW,GAAG;EACtB,IAAI,KAAK;GAAE,MAAM;GAAO;GAAK,CAAC;;CAEhC,IAAI,KAAK;EACP,MAAM;EACN,KAAK;EACL,OAAO;EACP,KAAK;EACL,OAAO;EACP,UAAU;EACX,CAAC;CAEF,IAAI,KAAK;EAAE,MAAM;EAAO,KAAK;EAAI,CAAC;CAClC,KAAK,MAAM,QAAQ,OAAO,IAAI,KAAK,KAAK;CACxC,OAAO;;;;;;;AAQT,SAAS,cAAc,SAAqC;CAC1D,MAAM,IAAI,QAAQ,MAAM,0CAA0C;CAClE,IAAI,CAAC,GAAG,OAAO,KAAA;CACf,MAAM,SAAS,EAAE;CACjB,OAAO,SAAS,QAAQ,OAAO,aAAa,KAAK;;;;ACjPnD,SAAS,YAAY,OAAwB;CAC3C,OAAO,cAAc,KAAK,MAAM,IAAI,UAAU;;;;;;;;;AAahD,SAAgB,aAAa,MAA6B;CACxD,MAAM,WAAW,KAAK,QAAQ,EAAE;CAChC,MAAM,WAAW,KAAK,eAAe,EAAE;CACvC,MAAM,gBAAgC,EAAE;CACxC,MAAM,mBAA6B,EAAE;CACrC,MAAM,qBAA+B,EAAE;CAEvC,KAAK,MAAM,eAAe,KAAK,UAAU;EACvC,MAAM,WAAW,KAAK,QAAQ,YAAY;EAE1C,MAAM,WAAW,KAAK,cAClB,KAAK,WAAW,KAAK,YAAY,GAC/B,KAAK,cACL,KAAK,QAAQ,KAAK,QAAQ,SAAS,EAAE,KAAK,YAAY,GACxD,iBAAiB;EAErB,IAAI,CAAC,GAAG,WAAW,SAAS,EAAE;GAC5B,cAAc,KAAK;IACjB,aAAa;IACb;IACA,QAAQ;IACR,SAAS;IACT,MAAM,EAAE;IACR,OAAO;KACL,MAAM;KACN,SAAS,uBAAuB;KACjC;IACF,CAAC;GACF;;EAGF,MAAM,UAAU,aAAa,SAAS;EAKtC,MAAM,iBAAiB,4BAA4B,YAAY;EAE/D,MAAM,UADgB,eAAe;EAErC,IAAI;EACJ,KAAK,MAAM,QAAQ,gBAAgB;GACjC,MAAM,IAAI,QAAQ,IAAI,KAAK;GAC3B,IAAI,GAAG;IACL,SAAS;IACT;;;EAIJ,MAAM,QAAQ,SAAS,GAAG,aAAa,UAAU,OAAO,CAAC;EACzD,MAAM,WAAqB,EAAE;EAC7B,IAAI,UAAU;EACd,IAAI;EAOJ,MAAM,gBAAgB,SAAS,MAAM;EACrC,MAAM,gBAAgB,2BAA2B,YAAY;EAC7D,IAAI,OAAmC;EACvC,KAAK,MAAM,QAAQ,eACjB,IAAI,cAAc,SAAS,cAAc,MAAO,SAAS,GAAG;GAC1D,OAAO;GACP;;EAIJ,KAAK,MAAM,QAAQ,OAAO;GACxB,IAAI,KAAK,SAAS,MAAM;GACxB,SAAS,KAAK,KAAK,IAAI;GACvB,IAAI,CAAC,YAAY,KAAK,MAAM,EAAE;GAC9B,IAAI,CAAC,WAAW,KAAK,KAAK,UAAU,SAAS,EAAE;GAC/C,IAAI,CAAC,QAAQ;IACX,eAAe;KACb,MAAM;KACN,SAAS,MAAM,QAAQ,YAAY;KACnC,MAAM,OAAO,QAAQ;KACtB;IACD;;GAEF,IAAI;IACF,KAAK,QACH,SAAS,eACL,uBAAuB,KAAK,OAAO,OAAO,GAC1C,aAAa,KAAK,OAAO,OAAO;IAMtC,KAAK,QAAQ,YAAY,KAAK,MAAM,GAAG,OAAM;IAC7C,UAAU;YACH,GAAG;IACV,eAAe;KACb,MACG,EAAwB,SAAS,sBAC9B,sBACA;KACN,SAAS,GAAG,YAAY,IAAK,EAAY,QAAQ,QAAQ,KAAK,IAAI;KACnE;IACD;;;EAIJ,MAAM,SAAS,aAAa,MAAM;EAClC,MAAM,YAA0B;GAC9B,aAAa;GACb;GACA,QAAQ,eAAe,GAAG,aAAa,UAAU,OAAO,GAAG;GAC3D,SAAS,CAAC,gBAAgB;GAC1B,MAAM;GACN,GAAI,eAAe,EAAE,OAAO,cAAc,GAAG,EAAE;GAChD;EACD,cAAc,KAAK,UAAU;EAC7B,IAAI,UAAU,SAAS,iBAAiB,KAAK,SAAS;OACjD,IAAI,CAAC,cAAc,mBAAmB,KAAK,SAAS;;CAG3D,OAAO;EAAE;EAAe;EAAkB;EAAoB;;;;;ACpGhE,SAAgB,aACd,QACA,OAAsB,EAAE,EACsD;CAC9E,MAAM,YAAY,KAAK,aAAa;CACpC,MAAM,WAAW,KAAK,YAAY,EAAE;CAIpC,MAAM,WAAmC,EAAE;CAC3C,MAAM,2BAAW,IAAI,KAAa;CAClC,MAAM,6BAAa,IAAI,KAAa;CACpC,MAAM,SAAqB,EAAE;CAE7B,SAAS,UAAU,MAAuB;EACxC,IAAI,OAAO,UAAU,eAAe,KAAK,QAAQ,KAAK,EAAE,OAAO;EAC/D,IAAI,OAAO,UAAU,eAAe,KAAK,UAAU,KAAK,IAAI,SAAS,UAAU,KAAA,GAC7E,OAAO;EAET,OAAO;;CAGT,SAAS,OAAO,MAAc,OAAyB;EACrD,IAAI,OAAO,UAAU,eAAe,KAAK,UAAU,KAAK,EAAE,OAAO,SAAS;EAC1E,IAAI,SAAS,IAAI,KAAK,EAAE;GACtB,MAAM,aAAa,MAAM,QAAQ,KAAK;GACtC,OAAO,KAAK,MAAM,MAAM,eAAe,KAAK,IAAI,WAAW,CAAC,OAAO,KAAK,CAAC;GACzE,OAAO;;EAET,IAAI,OAAO,UAAU,eAAe,KAAK,QAAQ,KAAK,EAAE;GACtD,SAAS,IAAI,KAAK;GAClB,MAAM,SAAS,aAAa,OAAO,OAAQ,CAAC,GAAG,OAAO,KAAK,CAAC;GAC5D,SAAS,OAAO,KAAK;GACrB,SAAS,QAAQ;GACjB,OAAO;;EAET,IAAI,OAAO,UAAU,eAAe,KAAK,UAAU,KAAK,EAAE;GACxD,MAAM,IAAI,SAAS;GACnB,IAAI,MAAM,KAAA,GAAW;IACnB,SAAS,QAAQ;IACjB,OAAO;;;EAGX,OAAO;;CAGT,SAAS,aAAa,OAAe,OAAyB;EAC5D,OAAO,WAAW,QAAQ,SAAS,iBAAiB;GAIlD,IAAI,CAHY,UAAU,QAGrB,EAAS;IACZ,IAAI,cAAc,SAAS,WACzB,OAAO,aAAa,aAAa,OAAO,MAAM;IAEhD,IAAI,cAAc,SAAS,SACzB,MAAM,IAAI,MACR,IAAI,QAAQ,MAAM,aAAa,SAAS,+BACzC;IAEH,WAAW,IAAI,QAAQ;IACvB,IAAI,cAAc,SAAS,OAAO;IAClC,OAAO,OAAO,UAAU;;GAG1B,MAAM,IAAI,OAAO,SAAS,MAAM;GAEhC,IAAI,MAAM,MAAM,cAAc,SAAS,WACrC,OAAO,aAAa,aAAa,OAAO,MAAM;GAEhD,IAAI,MAAM,MAAM,cAAc,SAAS,SACrC,MAAM,IAAI,MACR,IAAI,QAAQ,MAAM,aAAa,SAAS,+BACzC;GAEH,OAAO;IACP;;CAGJ,KAAK,MAAM,OAAO,OAAO,KAAK,OAAO,EACnC,IAAI,SAAS,SAAS,KAAA,KAAa,CAAC,SAAS,IAAI,IAAI,EAAE;EACrD,SAAS,IAAI,IAAI;EACjB,IAAI;GACF,SAAS,OAAO,aAAa,OAAO,MAAO,CAAC,IAAI,CAAC;YACzC;GACR,SAAS,OAAO,IAAI;;;CAK1B,IAAI,cAAc,YAAY,WAAW,OAAO,KAAK,OAAO,SAAS,IAAI;EACvE,MAAM,QAAkB,EAAE;EAC1B,IAAI,WAAW,OAAO,GAAG,MAAM,KAAK,eAAe,CAAC,GAAG,WAAW,CAAC,KAAK,KAAK,GAAG;EAChF,IAAI,OAAO,SAAS,GAClB,MAAM,KAAK,WAAW,OAAO,KAAK,MAAM,EAAE,KAAK,MAAM,CAAC,CAAC,KAAK,KAAK,GAAG;EAEtE,MAAM,IAAI,MAAM,sBAAsB,MAAM,KAAK,KAAK,GAAG;;CAG3D,OAAO;EAAE,QAAQ;EAAU,YAAY,CAAC,GAAG,WAAW;EAAE;EAAQ;;;;;;;AAQlE,SAAgB,aAAa,QAAgB,OAAsB,EAAE,EAAgB;CACnF,MAAM,QAAQ,SAAS,OAAO;CAC9B,MAAM,SAAiC,EAAE;CACzC,KAAK,MAAM,QAAQ,OACjB,IAAI,KAAK,SAAS,MAAM,OAAO,KAAK,OAAO,KAAK;CAElD,MAAM,WAAW,KAAK,YAAY,QAAQ;CAC1C,MAAM,SAAS,aAAa,QAAQ;EAAE,GAAG;EAAM;EAAU,CAAC;CAC1D,KAAK,MAAM,QAAQ,OACjB,IAAI,KAAK,SAAS,MAAM,KAAK,QAAQ,OAAO,OAAO,KAAK,QAAQ,KAAK;CAEvE,OAAO;EACL,QAAQ,OAAO;EACf,QAAQ,aAAa,MAAM;EAC3B,YAAY,OAAO;EACnB,QAAQ,OAAO;EAChB;;;;;;;;;AA4BH,SAAS,WAAW,OAAe,aAAkC;CACnE,IAAI,MAAM;CACV,IAAI,IAAI;CACR,OAAO,IAAI,MAAM,QAAQ;EACvB,MAAM,IAAI,MAAM;EAChB,IAAI,MAAM,SAAS,MAAM,IAAI,OAAO,OAAO,MAAM,IAAI,OAAO,OAAO;GACjE,OAAO,MAAM,IAAI;GACjB,KAAK;GACL;;EAEF,IAAI,MAAM,KAAK;GACb,OAAO;GACP,KAAK;GACL;;EAGF,MAAM,OAAO,MAAM,IAAI;EACvB,IAAI,SAAS,KAAK;GAChB,MAAM,QAAQ,kBAAkB,OAAO,IAAI,EAAE;GAC7C,IAAI,UAAU,IAAI;IAEhB,OAAO;IACP,KAAK;IACL;;GAGF,MAAM,EAAE,MAAM,aAAa,SADb,MAAM,MAAM,IAAI,GAAG,MACG,CAAM;GAC1C,IAAI,CAAC,YAAY,KAAK,EACpB,OAAO,MAAM,MAAM,GAAG,QAAQ,EAAE;QAEhC,OAAO,YAAY,MAAM,SAAS;GAEpC,IAAI,QAAQ;GACZ;;EAEF,IAAI,SAAS,KAAA,KAAa,YAAY,KAAK,KAAK,EAAE;GAChD,IAAI,IAAI,IAAI;GACZ,OAAO,IAAI,MAAM,UAAU,eAAe,KAAK,MAAM,GAAI,EAAE;GAC3D,MAAM,OAAO,MAAM,MAAM,IAAI,GAAG,EAAE;GAClC,OAAO,YAAY,MAAM,KAAA,EAAU;GACnC,IAAI;GACJ;;EAGF,OAAO;EACP,KAAK;;CAEP,OAAO;;AAGT,SAAS,kBAAkB,GAAW,SAAyB;CAC7D,IAAI,QAAQ;CACZ,KAAK,IAAI,IAAI,SAAS,IAAI,EAAE,QAAQ,KAClC,IAAI,EAAE,OAAO,KAAK;MACb,IAAI,EAAE,OAAO,KAAK;EACrB;EACA,IAAI,UAAU,GAAG,OAAO;;CAG5B,OAAO;;AAGT,SAAS,SAAS,OAAyD;CAEzE,IAAI,MAAM,MAAM,QAAQ,KAAK;CAC7B,IAAI,QAAQ,IACV,OAAO;EACL,MAAM,MAAM,MAAM,GAAG,IAAI;EACzB,UAAU;GAAE,MAAM;GAAW,OAAO,MAAM,MAAM,MAAM,EAAE;GAAE;EAC3D;CAGH,MAAM,MAAM,QAAQ,KAAK;CACzB,IAAI,QAAQ,IACV,OAAO;EACL,MAAM,MAAM,MAAM,GAAG,IAAI;EACzB,UAAU;GAAE,MAAM;GAAS,OAAO,MAAM,MAAM,MAAM,EAAE;GAAE;EACzD;CAEH,OAAO;EAAE,MAAM;EAAO,UAAU,KAAA;EAAW;;AAG7C,SAAS,YAAY,MAAuB;CAC1C,OAAO,2BAA2B,KAAK,KAAK;;;;AC3N9C,IAAM,aAAa;CACjB;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;;;AAWD,SAAgB,kBACd,OAA0B,EAAE,EACd;CACd,MAAM,MAAM,KAAK,OAAO,QAAQ,KAAK;CAErC,IAAI,KAAK,YAAY;EACnB,MAAM,WAAW,KAAK,QAAQ,KAAK,KAAK,WAAW;EACnD,OAAO;GACL,QAAQ,eAAe,SAAS;GAChC,QAAQ;GACR,QAAQ;GACT;;CAGH,MAAM,kBAAkB,kBAAkB,IAAI;CAC9C,IAAI,iBACF,OAAO;EACL,QAAQ,eAAe,gBAAgB,KAAK;EAC5C,QAAQ,gBAAgB;EACxB,QAAQ;EACT;CAGH,MAAM,WAAW,eAAe,IAAI;CACpC,IAAI,UACF,OAAO;EACL,QAAQ,eAAe,SAAS;EAChC,QAAQ;EACR,QAAQ;EACT;CAGH,OAAO;EAAE,QAAQ,EAAE;EAAE,QAAQ;EAAM,QAAQ;EAAY;;AAOzD,SAAS,kBACP,UAC0C;CAC1C,IAAI,MAAM;CACV,MAAM,OAAO,KAAK,MAAM,IAAI,CAAC;CAC7B,OAAO,MAAM;EACX,MAAM,UAAU,KAAK,KAAK,KAAK,eAAe;EAC9C,IAAI,GAAG,WAAW,QAAQ,EACxB,IAAI;GACF,MAAM,MAAM,KAAK,MAAM,GAAG,aAAa,SAAS,OAAO,CAAC;GAKxD,MAAM,MAAM,IAAI,MAAM,UAAU,IAAI,SAAS;GAC7C,IAAI,OAAO,QAAQ,YAAY,IAAI,SAAS,GAC1C,OAAO;IAAE,MAAM,KAAK,QAAQ,KAAK,IAAI;IAAE;IAAS;UAE5C;EAIV,IAAI,QAAQ,MAAM,OAAO;EACzB,MAAM,SAAS,KAAK,QAAQ,IAAI;EAChC,IAAI,WAAW,KAAK,OAAO;EAC3B,MAAM;;;AAIV,SAAS,eAAe,UAAiC;CACvD,KAAK,MAAM,QAAQ,YAAY;EAC7B,MAAM,IAAI,KAAK,KAAK,UAAU,KAAK;EACnC,IAAI,GAAG,WAAW,EAAE,EAAE,OAAO;;CAE/B,OAAO;;AAOT,IAAM,WAAW,cAAc,OAAO,KAAK,IAAI;AAE/C,SAAS,eAAe,UAAiC;CACvD,IAAI,CAAC,GAAG,WAAW,SAAS,EAC1B,MAAM,IAAI,MAAM,mCAAmC,WAAW;CAEhE,MAAM,MAAM,KAAK,QAAQ,SAAS,CAAC,aAAa;CAChD,QAAQ,KAAR;EACE,KAAK,SACH,OAAO,UAAU,KAAK,MAAM,GAAG,aAAa,UAAU,OAAO,CAAC,CAAC;EACjE,KAAK,QACH,OAAO,UAAU,SAAS,SAAS,CAAC;EACtC,KAAK;EACL,KAAK,QAKH,OAAO,UAAU,WAAW,SAAS,CAAC;EACxC,KAAK;EACL,KAAK,QAIH,OAAO,UAAU,WAAW,SAAS,CAAC;EACxC,SACE,MAAM,IAAI,MAAM,kDAAkD,MAAM;;;AAI9E,SAAS,WAAW,UAA2B;CAC7C,IAAI;EACF,MAAM,MAAM,SAAS,SAAS;EAC9B,OAAQ,IAA8B,WAAW;UAC1C,GAAG;EACV,IAAK,EAA4B,SAAS,mBACxC,MAAM,IAAI,MACR,+BAA+B,SAAS,+GAEzC;EAEH,MAAM;;;AAIV,SAAS,WAAW,UAA2B;CAG7C,IAAI;EACF,SAAS,UAAU;SACb;EACN,MAAM,IAAI,MACR,iCAAiC,SAAS,kGAE3C;;CAEH,MAAM,MAAM,SAAS,SAAS;CAC9B,OAAQ,IAA8B,WAAW;;AAGnD,SAAS,UAAU,KAA6B;CAC9C,IAAI,QAAQ,QAAQ,OAAO,QAAQ,UACjC,MAAM,IAAI,MAAM,kDAAkD;CAGpE,MAAM,IAAI;CACV,MAAM,MAAkE,EAAE;CAC1E,IAAI,MAAM,QAAQ,EAAE,YAAY,EAC9B,IAAI,WAAW,EAAE,YAAY,QAAQ,MAAmB,OAAO,MAAM,SAAS;CAEhF,IAAI,OAAO,EAAE,eAAe,UAAU,IAAI,UAAU,EAAE;CACtD,IAAI,OAAO,EAAE,eAAe,UAAU,IAAI,UAAU,EAAE;CACtD,IAAI,OAAO,EAAE,mBAAmB,UAAU,IAAI,cAAc,EAAE;CAC9D,IAAI,OAAO,EAAE,gBAAgB,WAAW,IAAI,WAAW,EAAE;CACzD,IAAI,OAAO,EAAE,aAAa,WAAW,IAAI,QAAQ,EAAE;CACnD,IAAI,EAAE,cAAc,OAAO,EAAE,eAAe,UAAU;EACpD,MAAM,UAAoC,EAAE;EAC5C,KAAK,MAAM,CAAC,MAAM,SAAS,OAAO,QAAQ,EAAE,WAAsC,EAChF,IAAI,MAAM,QAAQ,KAAK,EACrB,QAAQ,QAAQ,KAAK,QAAQ,MAAmB,OAAO,MAAM,SAAS;EAG1E,IAAI,UAAU;;CAEhB,OAAO;;;;ACjPT,IAAM,QAAQ;AACd,IAAM,MAAM;AACZ,IAAM,QAAQ;AACd,IAAM,SAAS;AACf,IAAM,OAAO;AACb,IAAM,MAAM;AAkBZ,IAAa,MAAc;CACzB,UAAU,QAAsB;EAC9B,QAAQ,IAAI,GAAG,QAAQ,MAAM,QAAQ;;CAEvC,QAAQ,QAAsB;EAC5B,QAAQ,MAAM,GAAG,MAAM,MAAM,QAAQ;;CAEvC,OAAO,QAAsB;EAC3B,QAAQ,IAAI,GAAG,SAAS,MAAM,QAAQ;;CAExC,OAAO,QAAsB;EAC3B,QAAQ,IAAI,GAAG,OAAO,MAAM,QAAQ;;CAEtC,MAAM,QAAsB;EAC1B,QAAQ,IAAI,GAAG,MAAM,MAAM,QAAQ;;CAEtC;;;;;;;AC7BD,SAAgB,oBAA4B;CAC1C,IAAI,QAAQ,IAAI,WAAW;EACzB,IAAI,QAAQ,IAAI,kBAAkB,cAAc,OAAO;EACvD,OAAO;;CAGT,IAAI,QAAQ,IAAI,YAAY;EAC1B,IAAI,QAAQ,IAAI,eAAe,cAAc,OAAO;EACpD,IACE,QAAQ,IAAI,eAAe,oBAC3B,QAAQ,IAAI,eAAe,iBAE3B,OAAO;EAET,OAAO;;CAGT,MAAM,UAAA,QAAA,IAAA;CACN,IAAI,SACF,QAAQ,QAAQ,aAAa,EAA7B;EACE,KAAK,cACH,OAAO;EACT,KAAK,eACH,OAAO;EACT,KAAK,SACH,OAAO;EACT,SACE,OAAO;;CAIb,OAAO;;;;;;AAWT,SAAgB,oBAAoB,OAAiC;CACnE,MAAM,QAAQ,MAAM,MAAM,qBAAqB;CAC/C,IAAI,CAAC,OAAO;EACV,IAAI,MACF,6DAA6D,MAAM,KACpE;EACD,QAAQ,KAAK,EAAE;;CAEjB,MAAM,GAAG,KAAK,OAAO;CACrB,IAAI,CAAC,OAAO,CAAC,KAAK;EAChB,IAAI,MACF,6DAA6D,MAAM,KACpE;EACD,QAAQ,KAAK,EAAE;;CAEjB,OAAO,CAAC,KAAK,IAAI;;AAOnB,IAAM,uBAAuB;CAC3B;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;AAQD,SAAgB,kBAAkB,WAAmB,QAAQ,KAAK,EAAU;CAC1E,MAAM,OAAO,KAAK,MAAM,SAAS,CAAC;CAElC,IAAI,aAAa;CACjB,OAAO,eAAe,MAAM;EAC1B,MAAM,kBAAkB,KAAK,KAAK,YAAY,eAAe;EAC7D,IAAI;GACF,MAAM,qBAAqB,GAAG,aAAa,iBAAiB,OAAO;GACnE,MAAM,cAAc,KAAK,MAAM,mBAAmB;GAClD,IAAI,YAAY,cAAc,YAAY,MAAM,YAC9C,OAAO;UAEH;EAGR,aAAa,KAAK,QAAQ,WAAW;;CAGvC,aAAa;CACb,OAAO,eAAe,MAAM;EAC1B,KAAK,MAAM,aAAa,sBACtB,IAAI,GAAG,WAAW,KAAK,KAAK,YAAY,UAAU,CAAC,EACjD,OAAO;EAGX,aAAa,KAAK,QAAQ,WAAW;;CAGvC,OAAO;;;;;;;;;;AAeT,SAAgB,aAAa,KAAuB;CAClD,IAAI,CAAC,GAAG,WAAW,IAAI,IAAI,CAAC,GAAG,SAAS,IAAI,CAAC,aAAa,EAAE,OAAO,EAAE;CACrE,OAAO,GACJ,YAAY,KAAK,EAAE,eAAe,MAAM,CAAC,CACzC,QAAQ,MAAM,EAAE,QAAQ,CAAC,CACzB,KAAK,MAAM,EAAE,KAAK,CAClB,QAAQ,MAAM,MAAM,UAAU,EAAE,WAAW,QAAQ,CAAC,CACpD,QAAQ,MAAM,MAAM,YAAY,CAChC,MAAM;;;;;;;;AAaX,SAAgB,mBACd,OACA,SACU;CACV,OAAO,MAAM,QACV,aAAa,MACZ,YAAY,OACV,OAAO,YAAY,WACf;EAAC,GAAG,EAAE,GAAG,QAAQ;EAAS,GAAG,EAAE;EAAS,GAAG,EAAE,GAAG;EAAW;EAAE,GAC7D,CAAC,GAAG,EAAE,SAAS,EAAE,CACtB,EACH,EAAE,CACH;;;;;;;AAkBH,SAAgB,gBAAgB,MAAmC;CACjE,MAAM,EAAE,SAAS,aAAa,SAAS;CACvC,MAAM,WAAW,KAAK,YAAY,CAAC,OAAO;CAC1C,MAAM,WAAW,MAAM,QAAQ,SAAS,GAAG,CAAC,GAAG,SAAS,GAAG,CAAC,SAAS;CAErE,IAAI,cAAc,SAAS,WAAW,KAAK,SAAS,OAAO,QAAQ;EACjE,MAAM,WAAW,mBAAmB;EACpC,IAAI,aAAa,QAAQ;GACvB,SAAS,KAAK,QAAQ;GACtB,IAAI,IACF,8BAA8B,SAAS,aAAA,QAAA,IAAA,YACG,YAAY,WAC1C,QAAQ,IAAI,aAAa,YAAY,eACjC,QAAQ,IAAI,iBAAiB,YAAY,YAC5C,QAAQ,IAAI,cAAc,YAAY,YACtC,QAAQ,IAAI,cAAc,YAAY,GACpD;;;CAIL,IAAI,QAAQ,SAAS,KAAK,MACxB,KAAK,WAAW,EAAE,IAAI,EAAE,WAAW,OAAO,GAAG,IAAI,QAAQ,IAC1D;CACD,IAAI,SAAS,QAAQ,mBAAmB,OAAO,QAAQ;CACvD,OAAO;;;;;;;AAwBT,SAAgB,QAAQ,MAA2C;CACjE,MAAM,EACJ,SACA,QAAQ,OACR,SACA,WAAW,OACX,QAAQ,MACR,cACE;CAEJ,IAAI,WAAW,UAAU;EACvB,IAAI,MAAM,0DAA0D;EACpE,QAAQ,KAAK,EAAE;;CAIjB,MAAM,SAAS,YAAY,QAAQ,UAAU;CAE7C,MAAM,QAAQ,gBAAgB;EAC5B,GAAI,KAAK,aAAa,KAAA,IAAY,EAAE,UAAU,KAAK,UAAU,GAAG,EAAE;EAClE,GAAI,YAAY,KAAA,IAAY,EAAE,SAAS,GAAG,EAAE;EAC7C,CAAC;CACF,MAAM,gBAAgB,mBAAmB;CAEzC,MAAM,SAAS,QAAQ;EACrB,IAAI,KAAK,WAAW,IAAI,EAAE;GACxB,IAAI,IAAI,6BAA6B,MAAM;GAC3C,OAAO,OAAO;IAAE,MAAM;IAAK;IAAU;IAAO,CAAC;GAC7C;;EAGF,MAAM,gBAAgB,SAClB,KAAK,QAAQ,eAAe,QAAQ,IAAI,GACxC,KAAK,QAAQ,eAAe,IAAI;EACpC,MAAM,oBAAoB,KAAK,QAAQ,eAAe,YAAY;EAClE,MAAM,iBAAiB,SACnB,KAAK,QAAQ,QAAQ,KAAK,EAAE,QAAQ,IAAI,GACxC,KAAK,QAAQ,QAAQ,KAAK,EAAE,IAAI;EACpC,MAAM,qBAAqB,KAAK,QAAQ,QAAQ,KAAK,EAAE,YAAY;EAEnE,MAAM,kBAAkB,GAAG,WAAW,cAAc;EACpD,MAAM,mBAAmB,GAAG,WAAW,eAAe;EAEtD,IAAI,iBAAiB;GACnB,IAAI,IAAI,6BAA6B,gBAAgB;GACrD,OAAO,OAAO;IACZ,MAAM;IACN,aAAa;IACb;IACA;IACD,CAAC;;EAGJ,IAAI,kBAAkB;GACpB,IAAI,IAAI,6BAA6B,iBAAiB;GACtD,OAAO,OAAO;IACZ,MAAM;IACN,aAAa;IACb;IACA;IACD,CAAC;;EAGJ,IAAI,CAAC,mBAAmB,CAAC,kBAAkB;GACzC,IAAI,IAAI,6BAA6B,gBAAgB;GACrD,OAAO,OAAO;IACZ,MAAM;IACN,aAAa;IACb;IACA;IACD,CAAC;;GAEJ;CAEF,IAAI,cAAc,KAAA,GAAW;EAE3B,MAAM,SAAS,OAAO,aADT,MAAM,QAAQ,UAAU,GAAG,YAAY,CAAC,UAAU,EACxB,IAAI,oBAAoB,CAAC;EAChE,OAAO,OAAO,QAAQ,KAAK,OAAO;;CAGpC,OAAO,EAAE,OAAO;;;;;;;;;ACtNlB,SAAgB,eACd,WACuB;CACvB,MAAM,UAAoB,EAAE;CAC5B,KAAK,MAAM,KAAK,WAAW;EACzB,IAAI,EAAE,SAAS,CAAC,EAAE,SAAS;EAC3B,GAAG,cAAc,EAAE,UAAU,EAAE,OAAO;EACtC,QAAQ,KAAK,EAAE,SAAS;;CAE1B,OAAO,EAAE,SAAS"}
|