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

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.
Files changed (42) hide show
  1. package/README.md +970 -104
  2. package/dist/auto.js.map +1 -1
  3. package/dist/chunks/commands-D3eQPQO6.js +1502 -0
  4. package/dist/chunks/commands-D3eQPQO6.js.map +1 -0
  5. package/dist/chunks/{src-CDuEfaCY.js → src-D0n2wHDg.js} +0 -0
  6. package/dist/chunks/src-D0n2wHDg.js.map +1 -0
  7. package/dist/cli.js +1 -1
  8. package/dist/commands/audit.d.ts +13 -0
  9. package/dist/commands/audit.d.ts.map +1 -0
  10. package/dist/commands/bake.d.ts +18 -0
  11. package/dist/commands/bake.d.ts.map +1 -0
  12. package/dist/commands/diff.d.ts +16 -0
  13. package/dist/commands/diff.d.ts.map +1 -0
  14. package/dist/commands/doctor.d.ts +16 -0
  15. package/dist/commands/doctor.d.ts.map +1 -0
  16. package/dist/commands/encrypt.d.ts.map +1 -1
  17. package/dist/commands/hook.d.ts +18 -0
  18. package/dist/commands/hook.d.ts.map +1 -0
  19. package/dist/commands/index.d.ts.map +1 -1
  20. package/dist/commands/index.js +1 -1
  21. package/dist/commands/info.d.ts +10 -0
  22. package/dist/commands/info.d.ts.map +1 -0
  23. package/dist/commands/rotate.d.ts +13 -0
  24. package/dist/commands/rotate.d.ts.map +1 -0
  25. package/dist/commands/run.d.ts.map +1 -1
  26. package/dist/commands/template.d.ts +13 -0
  27. package/dist/commands/template.d.ts.map +1 -0
  28. package/dist/commands/types.d.ts +14 -0
  29. package/dist/commands/types.d.ts.map +1 -0
  30. package/dist/commands/watch.d.ts +14 -0
  31. package/dist/commands/watch.d.ts.map +1 -0
  32. package/dist/index.d.ts +16 -4
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +57 -28
  35. package/dist/index.js.map +1 -1
  36. package/docs/auto-detection.md +217 -0
  37. package/docs/configuration.md +224 -0
  38. package/docs/recipes.md +234 -0
  39. package/package.json +6 -4
  40. package/dist/chunks/commands-B8vc6UKO.js +0 -354
  41. package/dist/chunks/commands-B8vc6UKO.js.map +0 -1
  42. package/dist/chunks/src-CDuEfaCY.js.map +0 -1
@@ -0,0 +1,234 @@
1
+ # Monorepo recipes
2
+
3
+ Concrete layouts for the most common envx-in-a-monorepo setups. Each recipe lists the directory tree, the config files, and the commands you'd run.
4
+
5
+ ## Recipe 1: Single workspace `.env.keys`, vault subdirectory
6
+
7
+ The most common shape — encrypted env files live in `vault/` at the workspace root, with one `.env.keys` (gitignored) covering everything.
8
+
9
+ ```
10
+ /repo
11
+ ├── .env.keys ← gitignored, holds private keys for every vault file
12
+ ├── pnpm-workspace.yaml
13
+ ├── envx.config.ts ← workspace-level defaults
14
+ ├── vault/
15
+ │ ├── .env ← committed, encrypted
16
+ │ ├── .env.dev ← committed, encrypted
17
+ │ ├── .env.prod ← committed, encrypted
18
+ │ └── .env.staging ← committed, encrypted
19
+ └── packages/
20
+ ├── api/
21
+ │ └── package.json
22
+ └── web/
23
+ └── package.json
24
+ ```
25
+
26
+ ```ts
27
+ // /repo/envx.config.ts
28
+ export default {
29
+ envPath: "vault", // every command pulls from vault/
30
+ }
31
+ ```
32
+
33
+ ```sh
34
+ # Commands run from any package directory — envx walks up to find the config.
35
+ cd /repo/packages/api
36
+
37
+ envx encrypt -e .env.prod # writes vault/.env.prod + updates /repo/.env.keys
38
+ envx -- pnpm start # auto-detects environment, loads vault/.env.<detected>
39
+ envx --cascade prod -- node app.js # layers vault/.env, vault/.env.prod, etc.
40
+ envx rotate # rotates the keypair for vault/.env
41
+ envx info # shows what envx resolved
42
+ ```
43
+
44
+ `.gitignore` should contain `.env.keys` (and any `.env.*.local` if you use cascades).
45
+
46
+ ## Recipe 2: Per-package overrides on top of workspace defaults
47
+
48
+ Some packages have additional environment files that don't belong at the workspace root (e.g. a Docker image-builder that pulls from a separate vault).
49
+
50
+ ```
51
+ /repo
52
+ ├── envx.config.ts ← workspace defaults
53
+ ├── vault/
54
+ │ └── .env ← shared by most packages
55
+ └── packages/
56
+ ├── api/ ← inherits /repo/envx.config.ts
57
+ │ └── package.json
58
+ └── docker-builder/
59
+ ├── envx.config.ts ← package-local override
60
+ ├── vault/
61
+ │ └── .env.image ← only relevant to this package
62
+ └── package.json
63
+ ```
64
+
65
+ ```ts
66
+ // /repo/envx.config.ts
67
+ export default {
68
+ envPath: "vault",
69
+ }
70
+ ```
71
+
72
+ ```ts
73
+ // /repo/packages/docker-builder/envx.config.ts
74
+ export default {
75
+ envFiles: [".env.image"],
76
+ envPath: "vault",
77
+ }
78
+ ```
79
+
80
+ When run from `packages/docker-builder/`, envx finds the package-local config first and uses it. From `packages/api/`, envx walks up and uses the workspace config. Configs **don't merge** across the walk — the closest one wins entirely.
81
+
82
+ ## Recipe 3: NODE_ENV-driven environments without `--env` everywhere
83
+
84
+ You want `pnpm start` to load `.env.dev`, `pnpm start:prod` to load `.env.prod`, and `NODE_ENV=staging pnpm test` to load `.env.staging` — all without typing `--env` each time.
85
+
86
+ ```ts
87
+ // /repo/envx.config.ts
88
+ export default {
89
+ // No envFiles → auto-detect kicks in.
90
+ // No nodeEnvMap override needed — staging passes through automatically.
91
+ }
92
+ ```
93
+
94
+ ```jsonc
95
+ // /repo/package.json
96
+ {
97
+ "scripts": {
98
+ "start": "envx -- node app.js", // → .env (no NODE_ENV)
99
+ "start:dev": "NODE_ENV=development envx -- node app.js", // → .env.dev
100
+ "start:prod": "NODE_ENV=production envx -- node app.js", // → .env.prod
101
+ "test:staging": "NODE_ENV=staging envx -- vitest run" // → .env.staging
102
+ }
103
+ }
104
+ ```
105
+
106
+ If your team uses non-canonical names (`qa`, `preview`, …), envx loads `.env.<value>` automatically — no config change needed.
107
+
108
+ ## Recipe 4: Custom `NODE_ENV` mapping
109
+
110
+ You want `NODE_ENV=qa` to load `.env.qa-prod` (because QA actually shares prod's external services), and `NODE_ENV=preview` to load just `.env` (no per-environment overrides).
111
+
112
+ ```ts
113
+ // /repo/envx.config.ts
114
+ export default {
115
+ envPath: "vault",
116
+ nodeEnvMap: {
117
+ qa: "qa-prod",
118
+ preview: "", // empty = no suffix → .env
119
+ },
120
+ }
121
+ ```
122
+
123
+ ```sh
124
+ NODE_ENV=qa envx -- node app.js
125
+ # Loads vault/.env.qa-prod (or walks up if missing)
126
+
127
+ NODE_ENV=preview envx -- node app.js
128
+ # Loads vault/.env (no preview-specific file)
129
+ ```
130
+
131
+ Verify with `envx info`:
132
+
133
+ ```
134
+ NODE_ENV → suffix mapping
135
+ development → .env.dev (default)
136
+ local → .env.local (default)
137
+ production → .env.prod (default)
138
+ qa → .env.qa-prod (config)
139
+ preview → (no suffix → .env) (config)
140
+ ```
141
+
142
+ ## Recipe 5: CI without committing `.env.keys`
143
+
144
+ In CI (GitHub Actions, Vercel, Netlify, …), the `.env.keys` file isn't checked in. Inject the private keys via the platform's secret store and write them out at job start.
145
+
146
+ ### GitHub Actions
147
+
148
+ ```yaml
149
+ # .github/workflows/ci.yml
150
+ jobs:
151
+ test:
152
+ runs-on: ubuntu-latest
153
+ steps:
154
+ - uses: actions/checkout@v4
155
+ - uses: pnpm/action-setup@v4
156
+ - uses: actions/setup-node@v4
157
+ with:
158
+ node-version: 22
159
+
160
+ - run: pnpm install --frozen-lockfile
161
+
162
+ # Materialize .env.keys from secrets — one entry per encrypted file.
163
+ - name: Write .env.keys
164
+ run: |
165
+ cat > .env.keys <<EOF
166
+ ENVX_PRIVATE_KEY="${{ secrets.ENVX_PRIVATE_KEY }}"
167
+ ENVX_PRIVATE_KEY_PROD="${{ secrets.ENVX_PRIVATE_KEY_PROD }}"
168
+ ENVX_PRIVATE_KEY_STAGING="${{ secrets.ENVX_PRIVATE_KEY_STAGING }}"
169
+ EOF
170
+
171
+ - run: NODE_ENV=staging pnpm test
172
+ ```
173
+
174
+ The `.env.keys` lands at the repo root; envx finds it via the cwd-first walk-up regardless of which package the test runs in.
175
+
176
+ ### Vercel / Netlify
177
+
178
+ These platforms set `VERCEL_ENV` / `CONTEXT` automatically, so envx auto-detects the right `.env.<env>` without `NODE_ENV`. Inject `.env.keys` via their secret-file or env-var mechanism.
179
+
180
+ For per-environment private keys, set them as project env vars and write `.env.keys` in your build step:
181
+
182
+ ```sh
183
+ # Vercel build command
184
+ echo "ENVX_PRIVATE_KEY_PROD=$ENVX_PRIVATE_KEY_PROD" > .env.keys && pnpm build
185
+ ```
186
+
187
+ ## Recipe 6: Programmatic embed with custom config
188
+
189
+ You're building a CLI of your own and want to embed envx's loading + auto-detect without the user having to install envx separately.
190
+
191
+ ```ts
192
+ import envx from "@super-repo/envx";
193
+
194
+ // Same shape as envx.config.ts:
195
+ envx({
196
+ envFiles: ["vault/.env.prod"],
197
+ envPath: "vault",
198
+ override: true,
199
+ autoDetect: false, // we're picking files explicitly
200
+ variables: ["MY_TOOL_VERSION=1.2.3"],
201
+ });
202
+
203
+ // process.env is now populated; carry on.
204
+ ```
205
+
206
+ For full control over which yargs commands you re-export, pull `createCli` from the `/commands` subpath:
207
+
208
+ ```ts
209
+ import { createCli } from "@super-repo/envx/commands";
210
+
211
+ createCli(process.argv.slice(2)).parseSync();
212
+ ```
213
+
214
+ ## Recipe 7: Migrating an existing dotenvx project
215
+
216
+ You have a project using `@dotenvx/dotenvx` directly and want to switch.
217
+
218
+ 1. **Install:**
219
+ ```sh
220
+ pnpm add @super-repo/envx
221
+ pnpm remove @dotenvx/dotenvx
222
+ ```
223
+
224
+ 2. **Rename references:** `dotenvx encrypt` → `envx encrypt`, `dotenvx run` → `envx run`. The `dotenvx-proxy` bin in this package is a direct passthrough to the upstream dotenvx CLI if you need to keep specific commands working during migration.
225
+
226
+ 3. **Keys file:** existing `.env.keys` files **work as-is**. envx reads `DOTENV_PRIVATE_KEY*` and `DOTENV_PUBLIC_KEY*` as fallbacks — no rename needed. New encrypts will use the canonical `ENVX_PRIVATE_KEY*` / `ENVX_PUBLIC_KEY*` names; you can migrate the existing entries opportunistically (or never).
227
+
228
+ 4. **Verify:**
229
+ ```sh
230
+ envx info
231
+ envx -- node app.js # same behavior as before
232
+ ```
233
+
234
+ If anything decrypts under dotenvx but not envx (or vice versa), they're not actually wire-compatible and we should know — open an issue.
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.2",
4
+ "version": "0.2.3-b.4",
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",
61
- "@super-repo/envx-common": "0.0.1",
62
63
  "@super-repo/envx-libs": "0.0.1",
63
- "@super-repo/cli": "0.2.3-b.2"
64
+ "@super-repo/cli": "0.2.3-b.4",
65
+ "@super-repo/envx-common": "0.0.1"
64
66
  }
65
67
  }
@@ -1,354 +0,0 @@
1
- import { a as resolveEnvPaths, c as loadDotenvxConfig, d as encryptFiles, f as parseEnv, i as loadEnv, l as expandEnvSrc, n as findWorkspaceRoot, o as validateCmdVariable, p as isEncrypted, r as listEnvFiles, s as log, t as writeProcessed, u as decryptFiles } from "./src-CDuEfaCY.js";
2
- import * as fs from "fs";
3
- import * as path from "path";
4
- import yargs from "yargs";
5
- import { execSync } from "child_process";
6
- //#region src/commands/debug.ts
7
- var debugCommand = {
8
- command: "debug",
9
- describe: "Show which env files would be loaded and which variables would be applied, without loading them.",
10
- handler: (argv) => {
11
- const paths = resolveEnvPaths({
12
- envFiles: argv["env"],
13
- cascade: argv["cascade"]
14
- });
15
- const rawVars = argv["variables"];
16
- const variables = rawVars ? Object.fromEntries(rawVars.map(validateCmdVariable)) : {};
17
- log.info(`Paths: ${JSON.stringify(paths)}`);
18
- log.info(`Variables: ${JSON.stringify(variables)}`);
19
- }
20
- };
21
- //#endregion
22
- //#region src/commands/decrypt.ts
23
- /** Mirror of `dotenvx decrypt` from upstream — see also encrypt.ts. */
24
- var decryptCommand = {
25
- command: "decrypt",
26
- describe: "Decrypt the values in one or more .env files in place using the matching private key in .env.keys.",
27
- builder: (yargs) => yargs.option("env-keys-file", {
28
- alias: "fk",
29
- type: "string",
30
- describe: "Path to the .env.keys file (default: ./.env.keys at cwd, regardless of --dir / --env-path)."
31
- }).option("key", {
32
- alias: "k",
33
- type: "array",
34
- string: true,
35
- describe: "Specific keys (or picomatch globs) to decrypt. Default: all keys."
36
- }).option("exclude-key", {
37
- alias: "ek",
38
- type: "array",
39
- string: true,
40
- describe: "Keys (or picomatch globs) to leave encrypted."
41
- }).option("stdout", {
42
- type: "boolean",
43
- default: false,
44
- describe: "Write the decrypted env contents to stdout instead of saving in place."
45
- }).help("h").alias("h", "help"),
46
- handler: (argv) => {
47
- const envFiles = argv["env"] ?? [".env"];
48
- const keys = argv["key"];
49
- const excludeKeys = argv["exclude-key"];
50
- const envKeysFile = argv["env-keys-file"];
51
- const stdout = argv["stdout"] ?? false;
52
- const result = decryptFiles({
53
- envFiles,
54
- ...keys ? { keys } : {},
55
- ...excludeKeys ? { excludeKeys } : {},
56
- ...envKeysFile ? { envKeysFile } : {}
57
- });
58
- let hadError = false;
59
- for (const processed of result.processedEnvs) {
60
- if (processed.error) {
61
- hadError = true;
62
- log.error(`${processed.envFilepath}: ${processed.error.message}`);
63
- if (processed.error.help) log.dim(processed.error.help);
64
- continue;
65
- }
66
- if (stdout) process.stdout.write(processed.envSrc);
67
- }
68
- if (!stdout) {
69
- const { written } = writeProcessed(result.processedEnvs);
70
- for (const w of written) log.success(`decrypted ${w}`);
71
- if (written.length === 0 && result.unchangedFilepaths.length > 0 && !hadError) log.dim(`no changes (${result.unchangedFilepaths.join(", ")})`);
72
- }
73
- if (hadError) process.exitCode = 1;
74
- }
75
- };
76
- //#endregion
77
- //#region src/commands/encrypt.ts
78
- /**
79
- * Mirrors the upstream `dotenvx encrypt` flags so existing muscle
80
- * memory carries over. The `--env` global doubles as the file list
81
- * (the upstream calls it `--env-file`); this CLI keeps a single
82
- * canonical name across subcommands.
83
- */
84
- var encryptCommand = {
85
- command: "encrypt",
86
- describe: "Encrypt the values in one or more .env files. Generates a private key in .env.keys on first run.",
87
- builder: (yargs) => yargs.option("env-keys-file", {
88
- alias: "fk",
89
- type: "string",
90
- describe: "Path to the .env.keys file (default: ./.env.keys at cwd, regardless of --dir / --env-path)."
91
- }).option("key", {
92
- alias: "k",
93
- type: "array",
94
- string: true,
95
- describe: "Specific keys (or picomatch globs) to encrypt. Default: all keys."
96
- }).option("exclude-key", {
97
- alias: "ek",
98
- type: "array",
99
- string: true,
100
- describe: "Keys (or picomatch globs) to leave plaintext."
101
- }).option("stdout", {
102
- type: "boolean",
103
- default: false,
104
- describe: "Write the encrypted env contents to stdout instead of saving in place."
105
- }).help("h").alias("h", "help"),
106
- handler: (argv) => {
107
- const envFiles = argv["env"] ?? [".env"];
108
- const keys = argv["key"];
109
- const excludeKeys = argv["exclude-key"];
110
- const envKeysFile = argv["env-keys-file"];
111
- const stdout = argv["stdout"] ?? false;
112
- const result = encryptFiles({
113
- envFiles,
114
- ...keys ? { keys } : {},
115
- ...excludeKeys ? { excludeKeys } : {},
116
- ...envKeysFile ? { envKeysFile } : {}
117
- });
118
- let hadError = false;
119
- for (const processed of result.processedEnvs) {
120
- if (processed.error) {
121
- hadError = true;
122
- log.error(`${processed.envFilepath}: ${processed.error.message}`);
123
- if (processed.error.help) log.dim(processed.error.help);
124
- continue;
125
- }
126
- if (stdout) {
127
- process.stdout.write(processed.envSrc);
128
- continue;
129
- }
130
- if (processed.privateKeyAdded) log.success(`key added to .env.keys (${processed.privateKeyName ?? "<unknown>"})`);
131
- }
132
- if (!stdout) {
133
- const { written } = writeProcessed(result.processedEnvs);
134
- for (const w of written) log.success(`encrypted ${w}`);
135
- if (written.length === 0 && result.unchangedFilepaths.length > 0 && !hadError) log.dim(`no changes (${result.unchangedFilepaths.join(", ")})`);
136
- }
137
- if (hadError) process.exitCode = 1;
138
- }
139
- };
140
- //#endregion
141
- //#region src/commands/expand.ts
142
- /**
143
- * Decrypts (when needed) and expands variable references in an env
144
- * file. Mirrors the workflow `decrypt-vault` action — but cycle-safe,
145
- * supports `${VAR:-default}` / `${VAR:?msg}`, and never silently
146
- * truncates after N passes.
147
- */
148
- var expandCommand = {
149
- command: "expand",
150
- describe: "Decrypt (if needed) and expand ${VAR}/$VAR references in an env file. Outputs to stdout by default.",
151
- builder: (yargs) => yargs.option("output", {
152
- type: "string",
153
- describe: "Write the expanded result to this file (default: stdout)."
154
- }).option("env-keys-file", {
155
- alias: "fk",
156
- type: "string",
157
- describe: "Path to .env.keys (default: ./.env.keys at cwd, regardless of --dir / --env-path)."
158
- }).option("on-missing", {
159
- type: "string",
160
- choices: [
161
- "leave",
162
- "empty",
163
- "throw"
164
- ],
165
- default: "leave",
166
- describe: "How to handle ${UNRESOLVED_VAR}: leave it literal, substitute empty, or fail."
167
- }).help("h").alias("h", "help"),
168
- handler: (argv) => {
169
- const envFiles = argv["env"] ?? [".env"];
170
- if (envFiles.length !== 1) {
171
- log.error(`expand operates on a single env file at a time; got ${String(envFiles.length)}.`);
172
- process.exit(1);
173
- }
174
- const envFile = envFiles[0];
175
- const filepath = path.resolve(envFile);
176
- if (!fs.existsSync(filepath)) {
177
- log.error(`env file not found: ${envFile}`);
178
- process.exit(1);
179
- }
180
- let envSrc = fs.readFileSync(filepath, "utf8");
181
- if (parseEnv(envSrc).some((l) => l.type === "kv" && isEncrypted(l.value))) {
182
- const envKeysFile = argv["env-keys-file"];
183
- const processed = decryptFiles({
184
- envFiles: [envFile],
185
- ...envKeysFile ? { envKeysFile } : {}
186
- }).processedEnvs[0];
187
- if (processed?.error) {
188
- log.error(`${envFile}: ${processed.error.message}`);
189
- if (processed.error.help) log.dim(processed.error.help);
190
- process.exit(1);
191
- }
192
- envSrc = processed.envSrc;
193
- }
194
- const onMissing = argv["on-missing"];
195
- const result = expandEnvSrc(envSrc, { onMissing });
196
- for (const v of result.unresolved) log.warn(`unresolved variable: ${v}`);
197
- for (const cycle of result.cycles) log.warn(`cycle: ${cycle.join(" → ")}`);
198
- const output = argv["output"];
199
- if (output) {
200
- fs.writeFileSync(path.resolve(output), result.envSrc);
201
- log.success(`expanded ${envFile} → ${output}`);
202
- } else {
203
- process.stdout.write(result.envSrc);
204
- if (!result.envSrc.endsWith("\n")) process.stdout.write("\n");
205
- }
206
- }
207
- };
208
- //#endregion
209
- //#region src/commands/print.ts
210
- var printCommand = {
211
- command: "print <variable>",
212
- describe: "Load env files and print the value of a single variable.",
213
- builder: (yargs) => yargs.positional("variable", {
214
- describe: "Name of the variable to print",
215
- type: "string",
216
- demandOption: true
217
- }),
218
- handler: (argv) => {
219
- loadEnv({
220
- envFiles: argv["env"],
221
- variables: argv["variables"],
222
- cascade: argv["cascade"],
223
- vault: argv["vault"],
224
- envPath: argv["env-path"],
225
- override: argv["override"],
226
- quiet: argv["quiet"]
227
- });
228
- const name = argv["variable"];
229
- const value = process.env[name];
230
- process.stdout.write(value != null ? `${value}\n` : "\n");
231
- }
232
- };
233
- //#endregion
234
- //#region src/commands/run.ts
235
- var runCommand = {
236
- command: "run [command..]",
237
- aliases: ["$0"],
238
- describe: "Load env files into process.env and execute a command. Default subcommand — `dotenvx-run [command]` is equivalent.",
239
- builder: (yargs) => yargs.positional("command", {
240
- describe: "Command to execute after loading env files",
241
- type: "string",
242
- array: true
243
- }),
244
- handler: (argv) => {
245
- const command = (argv["command"] ?? []).join(" ");
246
- loadEnv({
247
- envFiles: argv["env"],
248
- variables: argv["variables"],
249
- cascade: argv["cascade"],
250
- vault: argv["vault"],
251
- envPath: argv["env-path"],
252
- override: argv["override"],
253
- quiet: argv["quiet"]
254
- });
255
- if (!command) return;
256
- log.info(`Running: ${command}`);
257
- try {
258
- execSync(command, { stdio: "inherit" });
259
- log.success(`Command completed: ${command}`);
260
- } catch (error) {
261
- const msg = error instanceof Error ? error.message : String(error);
262
- log.error(`Command failed: ${msg}`);
263
- process.exit(1);
264
- }
265
- }
266
- };
267
- //#endregion
268
- //#region src/commands/index.ts
269
- /**
270
- * Build the yargs CLI for `dotenvx-run`. Global options (env, variables,
271
- * cascade, vault, override, quiet) are registered on the root so every
272
- * subcommand inherits them.
273
- */
274
- function createCli(argvInput) {
275
- return yargs(argvInput).scriptName("envx").usage("$0 <command> [options]").option("config", {
276
- alias: "c",
277
- type: "string",
278
- describe: "Path to an envx config file (default: discovered from package.json `envx.config` or envx.config.{ts,js,json})."
279
- }).option("env", {
280
- alias: "e",
281
- type: "array",
282
- describe: "Env files to load. Default: [\".env\"] (or `envFiles` from config; or every .env* under --env-path when set). Auto-detects environment when omitted."
283
- }).option("env-path", {
284
- alias: ["dir", "d"],
285
- type: "string",
286
- describe: "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."
287
- }).option("variables", {
288
- alias: "v",
289
- type: "array",
290
- describe: "Inline variables in the form name=value (repeatable).",
291
- default: []
292
- }).option("cascade", {
293
- type: "string",
294
- describe: "Cascade load order: .env, .env.<cascade>, .env.local, .env.<cascade>.local"
295
- }).option("vault", {
296
- alias: "va",
297
- type: "boolean",
298
- describe: "Shortcut for `--env-path vault`."
299
- }).option("override", {
300
- alias: "o",
301
- type: "boolean",
302
- describe: "Override existing process.env values. Conflicts with --cascade."
303
- }).option("quiet", {
304
- alias: "q",
305
- type: "boolean",
306
- describe: "Suppress dotenv's own output."
307
- }).middleware((argv) => {
308
- const configPath = argv["config"];
309
- let loaded;
310
- try {
311
- loaded = loadDotenvxConfig({ ...configPath ? { configPath } : {} });
312
- } catch (e) {
313
- log.error(`config error: ${e.message}`);
314
- process.exit(1);
315
- }
316
- if (loaded.source) log.dim(`config: ${loaded.source} (${loaded.origin})`);
317
- const cfg = loaded.config;
318
- if (argv["cascade"] === void 0 && cfg.cascade !== void 0) argv["cascade"] = cfg.cascade;
319
- if (argv["override"] === void 0) argv["override"] = cfg.override ?? false;
320
- if (argv["quiet"] === void 0) argv["quiet"] = cfg.quiet ?? true;
321
- if (argv["vault"] === void 0) argv["vault"] = false;
322
- if (argv["env-keys-file"] === void 0 && cfg.envKeysFile !== void 0) argv["env-keys-file"] = cfg.envKeysFile;
323
- if (argv["env-path"] === void 0) {
324
- if (cfg.envPath !== void 0) argv["env-path"] = cfg.envPath;
325
- else if (argv["vault"]) argv["env-path"] = "vault";
326
- }
327
- const userPassedEnv = Array.isArray(argv["env"]) && argv["env"].length > 0;
328
- let files;
329
- if (userPassedEnv) files = argv["env"];
330
- else if (cfg.envFiles && cfg.envFiles.length > 0) files = [...cfg.envFiles];
331
- else if (typeof argv["env-path"] === "string") {
332
- const subdir = argv["env-path"];
333
- const wsRoot = findWorkspaceRoot();
334
- const discovered = listEnvFiles(path.resolve(wsRoot, subdir));
335
- if (discovered.length > 0) {
336
- files = discovered;
337
- log.dim(`env-path ${subdir}: discovered ${String(discovered.length)} file(s) — ${discovered.join(", ")}`);
338
- } else {
339
- files = [".env"];
340
- log.warn(`env-path ${subdir}: no .env* files found; falling back to [".env"]`);
341
- }
342
- } else files = [".env"];
343
- if (typeof argv["env-path"] === "string") {
344
- const wsRoot = findWorkspaceRoot();
345
- const dir = path.resolve(wsRoot, argv["env-path"]);
346
- files = files.map((f) => path.isAbsolute(f) ? f : path.join(dir, f));
347
- }
348
- argv["env"] = files;
349
- }).command(runCommand).command(printCommand).command(debugCommand).command(encryptCommand).command(decryptCommand).command(expandCommand).help("h").alias("h", "help").version().strictCommands().demandCommand(0).recommendCommands().epilog("Examples:\n envx -- node app.js # load .env (auto-detected) and run\n envx --env dev -- pnpm start # load .env.dev\n envx print DATABASE_URL # print one variable\n envx debug --cascade prod # show resolved paths\n envx encrypt -e .env.prod # encrypt values in .env.prod\n envx decrypt -e .env.prod -k FOO # decrypt only FOO\n envx expand -e vault/.env.prod # decrypt + expand to stdout\n envx -c ./my.config.json run -- node app.js");
350
- }
351
- //#endregion
352
- export { createCli as t };
353
-
354
- //# sourceMappingURL=commands-B8vc6UKO.js.map