@karmaniverous/get-dotenv 7.0.7 → 7.0.9

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 (51) hide show
  1. package/dist/chunks/{createCli-DRgcaM2D.mjs → createCli-CCxTLJ1j.mjs} +57 -10
  2. package/dist/chunks/index-Cay5Gzhu.mjs +111 -0
  3. package/dist/chunks/{index-BzoCat8h.mjs → index-xqvxTkr9.mjs} +6 -6
  4. package/dist/chunks/{loader-V1vbmtyw.mjs → loader-C3DtD6HB.mjs} +4 -2
  5. package/dist/chunks/{readDotenvCascade-Dgx4SC1p.mjs → readDotenvCascade-CfFPgLCp.mjs} +52 -21
  6. package/dist/chunks/{readMergedOptions-x80ltQO_.mjs → readMergedOptions-_hjyCNZ7.mjs} +54 -14
  7. package/dist/chunks/{resolveCliOptions-CR-BEUmS.mjs → resolveCliOptions-Dp7wPY1K.mjs} +1 -1
  8. package/dist/chunks/{spawnEnv-CKgnHGpr.mjs → spawnEnv-DvisqPiU.mjs} +28 -3
  9. package/dist/chunks/{types-poB1VAs_.mjs → types-zXDNhcST.mjs} +1 -1
  10. package/dist/cli.d.ts +9 -5
  11. package/dist/cli.mjs +10 -15
  12. package/dist/cliHost.d.ts +9 -5
  13. package/dist/cliHost.mjs +6 -6
  14. package/dist/config.d.ts +1 -1
  15. package/dist/config.mjs +1 -1
  16. package/dist/env-overlay.d.ts +13 -9
  17. package/dist/env-overlay.mjs +2 -2
  18. package/dist/getdotenv.cli.mjs +10 -15
  19. package/dist/index.d.ts +10 -6
  20. package/dist/index.mjs +30 -23
  21. package/dist/plugins-aws.d.ts +1 -1
  22. package/dist/plugins-aws.mjs +4 -4
  23. package/dist/plugins-batch.d.ts +17 -1
  24. package/dist/plugins-batch.mjs +202 -68
  25. package/dist/plugins-cmd.d.ts +1 -1
  26. package/dist/plugins-cmd.mjs +6 -6
  27. package/dist/plugins-init.d.ts +1 -1
  28. package/dist/plugins-init.mjs +3 -3
  29. package/dist/plugins.d.ts +3 -1
  30. package/dist/plugins.mjs +9 -14
  31. package/package.json +40 -40
  32. package/schema/getdotenv.config.schema.json +207 -0
  33. package/dist/chunks/AwsRestJsonProtocol-BWWvLZiw.mjs +0 -1026
  34. package/dist/chunks/externalDataInterceptor-Bbvq4sdd.mjs +0 -19
  35. package/dist/chunks/getSSOTokenFromFile-ClTzvS3i.mjs +0 -22
  36. package/dist/chunks/index-4kbkrHS9.mjs +0 -12529
  37. package/dist/chunks/index-B5GwHCSX.mjs +0 -669
  38. package/dist/chunks/index-Cl6wXPYD.mjs +0 -82
  39. package/dist/chunks/index-D7Lv-lxm.mjs +0 -349
  40. package/dist/chunks/index-DFNP_Nrx.mjs +0 -188
  41. package/dist/chunks/index-DO68RbZ8.mjs +0 -103
  42. package/dist/chunks/index-Db08BBL5.mjs +0 -519
  43. package/dist/chunks/index-De2jIOhi.mjs +0 -541
  44. package/dist/chunks/index-IOQ1o3w3.mjs +0 -290
  45. package/dist/chunks/index-Tm4WDj9R.mjs +0 -383
  46. package/dist/chunks/index-fNrNPp4e.mjs +0 -946
  47. package/dist/chunks/index-w8gK2SKP.mjs +0 -31
  48. package/dist/chunks/loadSso-Ce3ChPPj.mjs +0 -488
  49. package/dist/chunks/package-DbbYaehr.mjs +0 -5
  50. package/dist/chunks/parseKnownFiles-BCL0L7aP.mjs +0 -23
  51. package/dist/chunks/sdk-stream-mixin-B_ajKWho.mjs +0 -307
@@ -1,24 +1,24 @@
1
1
  import 'zod';
2
2
  import 'path';
3
- import { r as resolveGetDotenvConfigSources } from './loader-V1vbmtyw.mjs';
3
+ import { r as resolveGetDotenvConfigSources } from './loader-C3DtD6HB.mjs';
4
4
  import 'nanoid';
5
5
  import 'fs-extra';
6
6
  import 'node:path';
7
7
  import 'radash';
8
8
  import 'node:buffer';
9
- import { d as defaultsDeep, h as getDotenvCliOptions2Options, c as baseRootOptionDefaults, G as GetDotenvCli, b as attachRootOptions } from './readMergedOptions-x80ltQO_.mjs';
9
+ import { a as defaultsDeep, g as getDotenvCliOptions2Options, b as baseRootOptionDefaults, G as GetDotenvCli, c as attachRootOptions } from './readMergedOptions-_hjyCNZ7.mjs';
10
10
  import 'crypto';
11
11
  import 'url';
12
12
  import '@commander-js/extra-typings';
13
13
  import 'dotenv';
14
14
  import 'execa';
15
15
  import { t as toHelpConfig } from './helpConfig-CGejgwWW.mjs';
16
- import { r as resolveCliOptions } from './resolveCliOptions-CR-BEUmS.mjs';
16
+ import { r as resolveCliOptions } from './resolveCliOptions-Dp7wPY1K.mjs';
17
17
  import { v as validateEnvAgainstSources } from './validate-CDl0rE6k.mjs';
18
18
  import { awsPlugin } from '../plugins-aws.mjs';
19
- import { R as awsWhoamiPlugin } from './index-4kbkrHS9.mjs';
19
+ import { a as awsWhoamiPlugin } from './index-Cay5Gzhu.mjs';
20
20
  import { batchPlugin } from '../plugins-batch.mjs';
21
- import { c as cmdPlugin } from './index-BzoCat8h.mjs';
21
+ import { c as cmdPlugin } from './index-xqvxTkr9.mjs';
22
22
  import { initPlugin } from '../plugins-init.mjs';
23
23
 
24
24
  const dbg = (...args) => {
@@ -66,7 +66,7 @@ function installRootHooks(program, defaults) {
66
66
  }
67
67
  };
68
68
  // Hook: preSubcommand — always runs for subcommand flows.
69
- program.hook('preSubcommand', async (thisCommand, actionCommand) => {
69
+ program.hook('preSubcommand', async (thisCommand, _actionCommand) => {
70
70
  const sources = await resolveGetDotenvConfigSources(import.meta.url);
71
71
  const rawArgs = thisCommand.rawArgs ?? [];
72
72
  dbg('preSubcommand:rawArgs', rawArgs);
@@ -82,11 +82,16 @@ function installRootHooks(program, defaults) {
82
82
  thisCommand.getDotenvCliOptions = merged;
83
83
  program._setOptionsBag(merged);
84
84
  // Resolve context for this run via programmatic converter.
85
+ // afterResolve is deferred to preAction where the full command path is known.
85
86
  const serviceOptions = getDotenvCliOptions2Options(merged);
86
87
  await program.resolveAndLoad(serviceOptions, {
87
- invokedSubcommand: actionCommand.name(),
88
+ runAfterResolve: false,
88
89
  });
89
90
  propagateResolvedEnv(merged);
91
+ // Propagate debug flag to env so dbg() helpers and error boundary see it.
92
+ if (merged.debug) {
93
+ process.env.GETDOTENV_DEBUG = '1';
94
+ }
90
95
  // Refresh dynamic help text using the resolved config slices.
91
96
  try {
92
97
  const ctx = program.getCtx();
@@ -115,8 +120,8 @@ function installRootHooks(program, defaults) {
115
120
  /* tolerate non-strict flows */
116
121
  }
117
122
  });
118
- // Hook: preAction — root-only and parent-alias flows.
119
- program.hook('preAction', async (thisCommand) => {
123
+ // Hook: preAction — root-only and parent-alias flows + scoped afterResolve.
124
+ program.hook('preAction', async (thisCommand, actionCommand) => {
120
125
  const sources = await resolveGetDotenvConfigSources(import.meta.url);
121
126
  const rawArgs = thisCommand.rawArgs ?? [];
122
127
  dbg('preAction:rawArgs', rawArgs);
@@ -139,6 +144,10 @@ function installRootHooks(program, defaults) {
139
144
  // builds a fresh `merged` and overwrites the options bag, so the env
140
145
  // propagated by preSubcommand is lost without this call.
141
146
  propagateResolvedEnv(merged);
147
+ // Propagate debug flag to env so dbg() helpers and error boundary see it.
148
+ if (merged.debug) {
149
+ process.env.GETDOTENV_DEBUG = '1';
150
+ }
142
151
  try {
143
152
  const ctx = program.getCtx();
144
153
  const helpCfg = toHelpConfig(merged, ctx.pluginConfigs);
@@ -162,6 +171,20 @@ function installRootHooks(program, defaults) {
162
171
  catch {
163
172
  /* tolerate non-strict flows */
164
173
  }
174
+ // Run afterResolve scoped to the invoked command branch.
175
+ // Walk actionCommand.parent chain to build the full plugin path.
176
+ // Always true after resolution above, but satisfies the type checker.
177
+ const ctx = program.hasCtx() ? program.getCtx() : undefined;
178
+ if (ctx) {
179
+ const segments = [];
180
+ let node = actionCommand;
181
+ while (node && node !== thisCommand) {
182
+ segments.unshift(node.name());
183
+ node = node
184
+ .parent;
185
+ }
186
+ await program._runAfterResolve(ctx, segments.length > 0 ? segments : undefined);
187
+ }
165
188
  });
166
189
  return program;
167
190
  }
@@ -258,6 +281,25 @@ function applyRootVisibility(program, visibility) {
258
281
  }
259
282
  }
260
283
 
284
+ /**
285
+ * Top-level error boundary for the CLI runner.
286
+ *
287
+ * Presents errors as clean one-line messages by default.
288
+ * When GETDOTENV_DEBUG is set, prints the full stack trace.
289
+ */
290
+ function handleCliError(err, argv) {
291
+ const isDebug = process.env.GETDOTENV_DEBUG === '1' ||
292
+ process.env.GETDOTENV_DEBUG === 'true' ||
293
+ (Array.isArray(argv) && argv.includes('--debug'));
294
+ const message = err instanceof Error ? err.message : String(err);
295
+ if (isDebug) {
296
+ console.error(err instanceof Error ? (err.stack ?? message) : message);
297
+ }
298
+ else {
299
+ console.error(`Error: ${message}`);
300
+ }
301
+ process.exitCode = 1;
302
+ }
261
303
  /**
262
304
  * Create a configured get-dotenv CLI host.
263
305
  * Applies defaults, installs root hooks, and composes plugins.
@@ -453,7 +495,12 @@ function createCli(opts = {}) {
453
495
  ? { helpHeader: opts.branding }
454
496
  : {}),
455
497
  });
456
- await program.parseAsync(['node', alias, ...argv]);
498
+ try {
499
+ await program.parseAsync(['node', alias, ...argv]);
500
+ }
501
+ catch (err) {
502
+ handleCliError(err, argv);
503
+ }
457
504
  };
458
505
  }
459
506
 
@@ -0,0 +1,111 @@
1
+ import { d as definePlugin } from './readMergedOptions-_hjyCNZ7.mjs';
2
+ import 'execa';
3
+ import 'radash';
4
+ import 'node:buffer';
5
+ import 'fs-extra';
6
+ import 'node:path';
7
+ import 'crypto';
8
+ import 'path';
9
+ import 'url';
10
+ import '@commander-js/extra-typings';
11
+ import 'nanoid';
12
+ import 'dotenv';
13
+ import './loader-C3DtD6HB.mjs';
14
+ import 'package-directory';
15
+ import 'yaml';
16
+ import 'zod';
17
+
18
+ /**
19
+ * Attach the default action for the `aws whoami` command.
20
+ *
21
+ * This behavior executes only when `aws whoami` is invoked without a subcommand.
22
+ *
23
+ * @param cli - The `whoami` command mount.
24
+ * @returns Nothing.
25
+ */
26
+ function attachWhoamiDefaultAction(cli) {
27
+ cli.action(async () => {
28
+ // Dynamic import: @aws-sdk/client-sts is an optional peer dependency.
29
+ // A static import would cause Node to fail at startup (even for -h)
30
+ // when the SDK is not installed in the consumer's project.
31
+ let GetCallerIdentityCommand;
32
+ let STSClient;
33
+ try {
34
+ const mod = await import('@aws-sdk/client-sts');
35
+ GetCallerIdentityCommand = mod.GetCallerIdentityCommand;
36
+ STSClient = mod.STSClient;
37
+ }
38
+ catch {
39
+ console.error('The aws whoami command requires @aws-sdk/client-sts.\n' +
40
+ 'Install it with: npm install @aws-sdk/client-sts');
41
+ process.exitCode = 1;
42
+ return;
43
+ }
44
+ // The AWS SDK default providers will read credentials from process.env,
45
+ // which the aws parent has already populated.
46
+ const client = new STSClient();
47
+ const result = await client.send(new GetCallerIdentityCommand());
48
+ console.log(JSON.stringify(result, null, 2));
49
+ });
50
+ }
51
+
52
+ /**
53
+ * Attach options/arguments for the `aws whoami` plugin mount.
54
+ *
55
+ * This subcommand currently takes no flags/args; this module exists to keep the
56
+ * wiring layout consistent across shipped plugins (options vs actions).
57
+ *
58
+ * Note: the plugin description is owned by `src/plugins/aws/whoami/index.ts` and
59
+ * must not be set here.
60
+ *
61
+ * @param cli - The `whoami` command mount under `aws`.
62
+ * @returns The same `cli` instance for chaining.
63
+ *
64
+ * @internal
65
+ */
66
+ function attachWhoamiOptions(cli) {
67
+ return cli;
68
+ }
69
+
70
+ /**
71
+ * Attach the `really` subcommand under `aws whoami`.
72
+ *
73
+ * Reads `SECRET_IDENTITY` from the resolved get-dotenv context (`cli.getCtx().dotenv`).
74
+ *
75
+ * @param cli - The `whoami` command mount.
76
+ * @returns Nothing.
77
+ */
78
+ function attachWhoamiReallyAction(cli) {
79
+ const really = cli
80
+ .ns('really')
81
+ .description('Print SECRET_IDENTITY from the resolved dotenv context');
82
+ really.action(() => {
83
+ const secretIdentity = really.getCtx().dotenv.SECRET_IDENTITY;
84
+ console.log(`Your secret identity is ${secretIdentity ?? 'still a secret'}.`);
85
+ });
86
+ }
87
+
88
+ /**
89
+ * AWS Whoami plugin factory.
90
+ *
91
+ * This plugin demonstrates a “bucket of subcommands” pattern:
92
+ * - Subcommand behavior is articulated in separate modules as `attach*` helpers.
93
+ * - Those helpers are not individually composable plugins; they are internal wiring for one plugin instance.
94
+ *
95
+ * @returns A plugin instance mounted at `aws whoami`.
96
+ */
97
+ const awsWhoamiPlugin = () => definePlugin({
98
+ ns: 'whoami',
99
+ setup(cli) {
100
+ cli.description('Print AWS caller identity (uses parent aws session)');
101
+ // Options/args (none today, but keep layout consistent with other plugins).
102
+ const whoami = attachWhoamiOptions(cli);
103
+ // Default behavior: `getdotenv aws whoami`
104
+ attachWhoamiDefaultAction(whoami);
105
+ // Subcommand behavior: `getdotenv aws whoami really`
106
+ attachWhoamiReallyAction(whoami);
107
+ return undefined;
108
+ },
109
+ });
110
+
111
+ export { awsWhoamiPlugin as a };
@@ -1,5 +1,5 @@
1
1
  import { camel } from 'radash';
2
- import { r as readMergedOptions, d as defaultsDeep, h as getDotenvCliOptions2Options, g as definePlugin } from './readMergedOptions-x80ltQO_.mjs';
2
+ import { r as readMergedOptions, a as defaultsDeep, g as getDotenvCliOptions2Options, d as definePlugin } from './readMergedOptions-_hjyCNZ7.mjs';
3
3
  import 'execa';
4
4
  import 'node:buffer';
5
5
  import 'fs-extra';
@@ -10,14 +10,14 @@ import 'url';
10
10
  import '@commander-js/extra-typings';
11
11
  import 'nanoid';
12
12
  import 'dotenv';
13
- import { r as resolveGetDotenvConfigSources } from './loader-V1vbmtyw.mjs';
13
+ import { r as resolveGetDotenvConfigSources } from './loader-C3DtD6HB.mjs';
14
14
  import 'package-directory';
15
15
  import 'yaml';
16
16
  import { z } from 'zod';
17
- import { r as resolveCommand, a as resolveShell, t as tokenize, s as shouldCapture, c as runCommand, b as buildSpawnEnv } from './spawnEnv-CKgnHGpr.mjs';
17
+ import { r as resolveCommand, a as resolveShell, t as tokenize, s as shouldCapture, c as runCommand, b as buildSpawnEnv } from './spawnEnv-DvisqPiU.mjs';
18
18
  import { m as maybePreserveNodeEvalArgv, c as composeNestedEnv, s as stripOne } from './invoke-DuRPU1oC.mjs';
19
- import { f as dotenvExpandFromProcessEnv } from './readDotenvCascade-Dgx4SC1p.mjs';
20
- import { b as baseGetDotenvCliOptions, r as resolveCliOptions } from './resolveCliOptions-CR-BEUmS.mjs';
19
+ import { f as dotenvExpandFromProcessEnv } from './readDotenvCascade-CfFPgLCp.mjs';
20
+ import { b as baseGetDotenvCliOptions, r as resolveCliOptions } from './resolveCliOptions-Dp7wPY1K.mjs';
21
21
 
22
22
  /** src/diagnostics/entropy.ts
23
23
  * Entropy diagnostics (presentation-only).
@@ -480,4 +480,4 @@ const cmdPlugin = (options = {}) => {
480
480
  return plugin;
481
481
  };
482
482
 
483
- export { redactObject as a, cmdPlugin as c, maybeWarnEntropy as m, redactDisplay as r, traceChildEnv as t };
483
+ export { redactDisplay as a, cmdPlugin as c, maybeWarnEntropy as m, redactObject as r, traceChildEnv as t };
@@ -288,7 +288,7 @@ const discoverConfigFiles = async (importMetaUrl) => {
288
288
  * For JS/TS: default export is loaded; "dynamic" is allowed.
289
289
  */
290
290
  const loadConfigFile = async (filePath) => {
291
- let raw = {};
291
+ let raw;
292
292
  try {
293
293
  const abs = path.resolve(filePath);
294
294
  if (isJsOrTs(abs)) {
@@ -302,7 +302,9 @@ const loadConfigFile = async (filePath) => {
302
302
  }
303
303
  }
304
304
  catch (err) {
305
- throw new Error(`Failed to read/parse config: ${filePath}. ${String(err)}`);
305
+ throw new Error(`Failed to read/parse config: ${filePath}.`, {
306
+ cause: err,
307
+ });
306
308
  }
307
309
  // Validate RAW
308
310
  const parsed = getDotenvConfigSchemaRaw.safeParse(raw);
@@ -189,21 +189,31 @@ function pushDotenvProvenance(prov, key, entry) {
189
189
  */
190
190
  /**
191
191
  * Apply a dynamic map to the target progressively.
192
- * - Functions receive (target, env) and may return string | undefined.
193
- * - Literals are assigned directly (including undefined).
192
+ * - Functions receive (target, env) and may return string | null | undefined.
193
+ * - string set the key to that value.
194
+ * - undefined → no-op, leave existing value unchanged.
195
+ * - null → delete the key from the target.
194
196
  *
195
197
  * @param target - Mutable target environment to assign into.
196
198
  * @param map - Dynamic map to apply (functions and/or literal values).
197
199
  * @param env - Selected environment name (if any) passed through to dynamic functions.
198
- * @returns Nothing.
200
+ * @returns Set of keys that were deleted (value was null).
199
201
  */
200
202
  function applyDynamicMap(target, map, env) {
203
+ const deleted = new Set();
201
204
  if (!map)
202
- return;
205
+ return deleted;
203
206
  for (const key of Object.keys(map)) {
204
207
  const val = typeof map[key] === 'function' ? map[key](target, env) : map[key];
205
- Object.assign(target, { [key]: val });
208
+ if (val === null) {
209
+ Reflect.deleteProperty(target, key);
210
+ deleted.add(key);
211
+ }
212
+ else if (val !== undefined)
213
+ target[key] = val;
214
+ // undefined → no-op
206
215
  }
216
+ return deleted;
207
217
  }
208
218
  /**
209
219
  * Load a default-export dynamic map from a JS/TS file (without applying it).
@@ -245,25 +255,46 @@ async function loadDynamicModuleDefault(absPath, cacheDirName) {
245
255
  * @param prov - Provenance map to append into.
246
256
  * @param meta - Dynamic provenance metadata (source tier and optional dynamicPath).
247
257
  *
258
+ * @returns Set of keys that were deleted (value was null).
259
+ *
248
260
  * @public
249
261
  */
250
262
  function applyDynamicMapWithProvenance(target, map, env, prov, meta) {
263
+ const deleted = new Set();
251
264
  if (!map)
252
- return;
265
+ return deleted;
253
266
  for (const key of Object.keys(map)) {
254
267
  const val = typeof map[key] === 'function' ? map[key](target, env) : map[key];
255
- Object.assign(target, { [key]: val });
256
- pushDotenvProvenance(prov, key, {
257
- kind: 'dynamic',
258
- op: typeof val === 'string' ? 'set' : 'unset',
259
- dynamicSource: meta.dynamicSource,
260
- ...(meta.dynamicSource === 'dynamicPath' &&
261
- typeof meta.dynamicPath === 'string' &&
262
- meta.dynamicPath.length > 0
263
- ? { dynamicPath: meta.dynamicPath }
264
- : {}),
265
- });
268
+ if (val === null) {
269
+ Reflect.deleteProperty(target, key);
270
+ deleted.add(key);
271
+ pushDotenvProvenance(prov, key, {
272
+ kind: 'dynamic',
273
+ op: 'unset',
274
+ dynamicSource: meta.dynamicSource,
275
+ ...(meta.dynamicSource === 'dynamicPath' &&
276
+ typeof meta.dynamicPath === 'string' &&
277
+ meta.dynamicPath.length > 0
278
+ ? { dynamicPath: meta.dynamicPath }
279
+ : {}),
280
+ });
281
+ }
282
+ else if (val !== undefined) {
283
+ target[key] = val;
284
+ pushDotenvProvenance(prov, key, {
285
+ kind: 'dynamic',
286
+ op: 'set',
287
+ dynamicSource: meta.dynamicSource,
288
+ ...(meta.dynamicSource === 'dynamicPath' &&
289
+ typeof meta.dynamicPath === 'string' &&
290
+ meta.dynamicPath.length > 0
291
+ ? { dynamicPath: meta.dynamicPath }
292
+ : {}),
293
+ });
294
+ }
295
+ // undefined → no-op, no provenance entry
266
296
  }
297
+ return deleted;
267
298
  }
268
299
  /**
269
300
  * Load a default-export dynamic map from a JS/TS file and apply it.
@@ -278,13 +309,13 @@ function applyDynamicMapWithProvenance(target, map, env, prov, meta) {
278
309
  * @param absPath - Absolute path to the dynamic module file.
279
310
  * @param env - Selected environment name (if any).
280
311
  * @param cacheDirName - Cache subdirectory under `.tsbuild/` for compiled artifacts.
281
- * @returns A `Promise\<void\>` which resolves after the module (if present) has been applied.
312
+ * @returns A `Promise\<Set\<string\>\>` resolving to the set of deleted keys.
282
313
  */
283
314
  async function loadAndApplyDynamic(target, absPath, env, cacheDirName) {
284
315
  const dyn = await loadDynamicModuleDefault(absPath, cacheDirName);
285
316
  if (!dyn)
286
- return;
287
- applyDynamicMap(target, dyn, env);
317
+ return new Set();
318
+ return applyDynamicMap(target, dyn, env);
288
319
  }
289
320
 
290
321
  /**
@@ -542,4 +573,4 @@ async function readDotenvCascadeWithProvenance(args) {
542
573
  return { dotenv: expanded, provenance: prov };
543
574
  }
544
575
 
545
- export { applyDynamicMap as a, applyDynamicMapWithProvenance as b, createDotenvProvenance as c, dotenvExpand as d, dotenvExpandAll as e, dotenvExpandFromProcessEnv as f, loadDynamicModuleDefault as g, readDotenvCascadeWithProvenance as h, loadAndApplyDynamic as l, overlayEnvWithProvenance as o, pushDotenvProvenance as p, readDotenv as r };
576
+ export { applyDynamicMap as a, applyDynamicMapWithProvenance as b, createDotenvProvenance as c, dotenvExpandAll as d, loadDynamicModuleDefault as e, dotenvExpandFromProcessEnv as f, readDotenv as g, dotenvExpand as h, loadAndApplyDynamic as l, overlayEnvWithProvenance as o, pushDotenvProvenance as p, readDotenvCascadeWithProvenance as r };
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { Option, Command } from '@commander-js/extra-typings';
3
- import { d as dotenvExpand, h as readDotenvCascadeWithProvenance, o as overlayEnvWithProvenance, b as applyDynamicMapWithProvenance, g as loadDynamicModuleDefault, f as dotenvExpandFromProcessEnv } from './readDotenvCascade-Dgx4SC1p.mjs';
3
+ import { h as dotenvExpand, r as readDotenvCascadeWithProvenance, o as overlayEnvWithProvenance, b as applyDynamicMapWithProvenance, e as loadDynamicModuleDefault, f as dotenvExpandFromProcessEnv } from './readDotenvCascade-CfFPgLCp.mjs';
4
4
  import fs from 'fs-extra';
5
5
  import 'node:path';
6
6
  import 'nanoid';
@@ -10,7 +10,7 @@ import 'node:buffer';
10
10
  import 'crypto';
11
11
  import { fileURLToPath } from 'url';
12
12
  import 'dotenv';
13
- import { g as getDotenvOptionsSchemaResolved, r as resolveGetDotenvConfigSources } from './loader-V1vbmtyw.mjs';
13
+ import { g as getDotenvOptionsSchemaResolved, r as resolveGetDotenvConfigSources } from './loader-C3DtD6HB.mjs';
14
14
  import { packageDirectory } from 'package-directory';
15
15
 
16
16
  /**
@@ -737,6 +737,19 @@ const attachRootOptions = (program, defaults) => {
737
737
  program.addOption(lf);
738
738
  program.setOptionGroup(lf, GROUP);
739
739
  }
740
+ // Debug ON/OFF (dynamic)
741
+ {
742
+ const dOn = program
743
+ .createDynamicOption('--debug', (cfg) => `enable debug logging to stderr ${onOff(true, Boolean(cfg.debug))}`)
744
+ .conflicts('debugOff');
745
+ program.addOption(dOn);
746
+ program.setOptionGroup(dOn, GROUP);
747
+ const dOff = program
748
+ .createDynamicOption('--debug-off', (cfg) => `enable debug logging to stderr ${onOff(false, !cfg.debug)}`)
749
+ .conflicts('debug');
750
+ program.addOption(dOff);
751
+ program.setOptionGroup(dOff, GROUP);
752
+ }
740
753
  // Capture flag (no default display; static)
741
754
  {
742
755
  const opt = new Option('--capture', 'capture child process stdio for commands (tests/CI)');
@@ -947,7 +960,7 @@ function buildHelpInformation(base, cmd) {
947
960
  }
948
961
  const marker = '\nCommands:';
949
962
  const idx = base.indexOf(marker);
950
- let out = base;
963
+ let out;
951
964
  if (idx >= 0) {
952
965
  const toInsert = groups.startsWith('\n') ? groups : `\n${groups}`;
953
966
  out = `${base.slice(0, idx)}${toInsert}${base.slice(idx)}`;
@@ -1278,7 +1291,10 @@ class GetDotenvCli extends Command {
1278
1291
  // Ensure plugins are installed exactly once, then run afterResolve.
1279
1292
  await this.install();
1280
1293
  if (opts?.runAfterResolve ?? true) {
1281
- await this._runAfterResolve(ctx, opts?.invokedSubcommand);
1294
+ const path = opts?.invokedSubcommand
1295
+ ? [opts.invokedSubcommand]
1296
+ : undefined;
1297
+ await this._runAfterResolve(ctx, path);
1282
1298
  }
1283
1299
  return ctx;
1284
1300
  }
@@ -1438,16 +1454,40 @@ class GetDotenvCli extends Command {
1438
1454
  }
1439
1455
  /**
1440
1456
  * Run afterResolve hooks for registered plugins (parent → children).
1441
- * When {@link invokedSubcommand} is provided, only the matching plugin
1442
- * subtree runs; otherwise all plugins run (backward compatibility).
1457
+ * When {@link commandPath} is provided, only the matching branch of the
1458
+ * plugin tree runs; otherwise all plugins run (backward compatibility).
1459
+ *
1460
+ * The path is walked segment-by-segment against the plugin tree.
1461
+ * Intermediate matches run their own afterResolve hook.
1462
+ * The deepest match runs afterResolve for itself and all its descendants.
1443
1463
  */
1444
- async _runAfterResolve(ctx, invokedSubcommand) {
1445
- const plugins = invokedSubcommand
1446
- ? this._plugins
1447
- .filter((e) => effectiveNs(e) === invokedSubcommand)
1448
- .map((e) => e.plugin)
1449
- : this._plugins.map((e) => e.plugin);
1450
- await runAfterResolveTree(this, plugins, ctx);
1464
+ async _runAfterResolve(ctx, commandPath) {
1465
+ if (!commandPath || commandPath.length === 0) {
1466
+ await runAfterResolveTree(this, this._plugins.map((e) => e.plugin), ctx);
1467
+ return;
1468
+ }
1469
+ // Walk the plugin tree along the command path, scoping to the invoked branch.
1470
+ let entries = this._plugins;
1471
+ for (let i = 0; i < commandPath.length; i++) {
1472
+ const segment = commandPath[i];
1473
+ const entry = entries.find((e) => effectiveNs(e) === segment);
1474
+ if (!entry)
1475
+ return;
1476
+ const isLast = i === commandPath.length - 1;
1477
+ const nextSegment = commandPath[i + 1];
1478
+ const hasMatchingChild = !isLast &&
1479
+ entry.plugin.children.some((e) => effectiveNs(e) === nextSegment);
1480
+ if (isLast || !hasMatchingChild) {
1481
+ // Deepest match: run this plugin + all its descendants.
1482
+ await runAfterResolveTree(this, [entry.plugin], ctx);
1483
+ return;
1484
+ }
1485
+ // Intermediate match: run only this plugin's own afterResolve.
1486
+ if (entry.plugin.afterResolve) {
1487
+ await entry.plugin.afterResolve(this, ctx);
1488
+ }
1489
+ entries = entry.plugin.children;
1490
+ }
1451
1491
  }
1452
1492
  }
1453
1493
 
@@ -1477,4 +1517,4 @@ const readMergedOptions = (cmd) => {
1477
1517
  return bag;
1478
1518
  };
1479
1519
 
1480
- export { GetDotenvCli as G, assertLogger as a, attachRootOptions as b, baseRootOptionDefaults as c, defaultsDeep as d, defineDynamic as e, defineGetDotenvConfig as f, definePlugin as g, getDotenvCliOptions2Options as h, interpolateDeep as i, resolveGetDotenvOptions as j, readMergedOptions as r, writeDotenvFile as w };
1520
+ export { GetDotenvCli as G, defaultsDeep as a, baseRootOptionDefaults as b, attachRootOptions as c, definePlugin as d, resolveGetDotenvOptions as e, assertLogger as f, getDotenvCliOptions2Options as g, defineDynamic as h, defineGetDotenvConfig as i, interpolateDeep as j, readMergedOptions as r, writeDotenvFile as w };
@@ -1,4 +1,4 @@
1
- import { c as baseRootOptionDefaults, d as defaultsDeep } from './readMergedOptions-x80ltQO_.mjs';
1
+ import { b as baseRootOptionDefaults, a as defaultsDeep } from './readMergedOptions-_hjyCNZ7.mjs';
2
2
  import 'radash';
3
3
  import 'node:buffer';
4
4
  import 'fs-extra';
@@ -84,6 +84,26 @@ const shouldCapture = (bagCapture) => process.env.GETDOTENV_STDIO === 'pipe' ||
84
84
  // This is safe for argv arrays passed to execa (no quoting needed) and avoids
85
85
  // passing quote characters through to Node (e.g., for `node -e "<code>"`).
86
86
  // Handles stacked quotes from shells like PowerShell: """code""" -> code.
87
+ // Shell-quote a single token for safe interpolation when joining an argv
88
+ // array into a command string destined for a shell. On Windows (cmd.exe)
89
+ // wrap in double quotes when the token contains shell metacharacters; on
90
+ // Unix wrap in single quotes with escaped embedded singles.
91
+ const shellQuoteToken = (s) => {
92
+ if (process.platform === 'win32') {
93
+ // cmd.exe: double-quote tokens with special chars. Inner double
94
+ // quotes are escaped with backslash (Node/libuv convention).
95
+ if (/[\s"'&|<>^()!%+,;=]/.test(s)) {
96
+ return '"' + s.replace(/"/g, '\\"') + '"';
97
+ }
98
+ return s;
99
+ }
100
+ // POSIX: single-quote tokens with special chars. Embedded single
101
+ // quotes break out, add an escaped single, and re-enter.
102
+ if (/[\s"'&|<>()!$\\`~#;{}[\]*?+]/.test(s)) {
103
+ return "'" + s.replace(/'/g, "'\\''") + "'";
104
+ }
105
+ return s;
106
+ };
87
107
  const stripOuterQuotes = (s) => {
88
108
  let out = s;
89
109
  // Repeatedly trim only when the entire string is wrapped in matching quotes.
@@ -129,7 +149,7 @@ async function _execNormalized(command, shell, opts = {}) {
129
149
  const stdio = opts.stdio ?? 'pipe';
130
150
  if (shell === false) {
131
151
  let file;
132
- let args = [];
152
+ let args;
133
153
  if (typeof command === 'string') {
134
154
  const tokens = tokenize(command);
135
155
  file = tokens[0];
@@ -158,8 +178,13 @@ async function _execNormalized(command, shell, opts = {}) {
158
178
  return out;
159
179
  }
160
180
  }
161
- // Shell path (string|true|URL): execaCommand handles shell resolution.
162
- const commandStr = typeof command === 'string' ? command : command.join(' ');
181
+ // Shell path (string|true|URL): build a single command string for the
182
+ // target shell. When the command is an array we shell-quote each token
183
+ // individually before joining so that metacharacters (spaces, quotes, +,
184
+ // parentheses, etc.) survive the round-trip through cmd.exe / sh.
185
+ const commandStr = typeof command === 'string'
186
+ ? command
187
+ : command.map(shellQuoteToken).join(' ');
163
188
  dbg('exec (shell)', {
164
189
  command: commandStr,
165
190
  shell: typeof shell === 'string' ? shell : 'custom',
@@ -1,4 +1,4 @@
1
- import { g as definePlugin } from './readMergedOptions-x80ltQO_.mjs';
1
+ import { d as definePlugin } from './readMergedOptions-_hjyCNZ7.mjs';
2
2
 
3
3
  /**
4
4
  * Create a namespace-only parent plugin (a group command) for composing plugins
package/dist/cli.d.ts CHANGED
@@ -286,7 +286,7 @@ interface RootOptionsShape {
286
286
  */
287
287
  trace?: boolean | string[];
288
288
  /** Paths to search for dotenv files (space-delimited string or array). */
289
- paths?: string;
289
+ paths?: string | string[];
290
290
  /** Delimiter for paths string (default: space). */
291
291
  pathsDelimiter?: string;
292
292
  /** Regex pattern for paths delimiter. */
@@ -378,7 +378,7 @@ type ProcessEnv = Record<string, string | undefined>;
378
378
  * and the selected environment (if any), and returns either a string to set
379
379
  * or `undefined` to unset/skip the variable.
380
380
  */
381
- type GetDotenvDynamicFunction = (vars: ProcessEnv, env: string | undefined) => string | undefined;
381
+ type GetDotenvDynamicFunction = (vars: ProcessEnv, env: string | undefined) => string | null | undefined;
382
382
  /**
383
383
  * A map of dynamic variable definitions.
384
384
  * Keys are variable names; values are either literal strings or functions.
@@ -713,10 +713,14 @@ declare class GetDotenvCli<TOptions extends GetDotenvOptions = GetDotenvOptions,
713
713
  install(): Promise<void>;
714
714
  /**
715
715
  * Run afterResolve hooks for registered plugins (parent → children).
716
- * When {@link invokedSubcommand} is provided, only the matching plugin
717
- * subtree runs; otherwise all plugins run (backward compatibility).
716
+ * When {@link commandPath} is provided, only the matching branch of the
717
+ * plugin tree runs; otherwise all plugins run (backward compatibility).
718
+ *
719
+ * The path is walked segment-by-segment against the plugin tree.
720
+ * Intermediate matches run their own afterResolve hook.
721
+ * The deepest match runs afterResolve for itself and all its descendants.
718
722
  */
719
- private _runAfterResolve;
723
+ _runAfterResolve(ctx: GetDotenvCliCtx<TOptions>, commandPath?: string[]): Promise<void>;
720
724
  }
721
725
 
722
726
  /**