@karmaniverous/get-dotenv 7.0.7 → 7.0.8

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-CUPiFVdZ.mjs} +24 -9
  2. package/dist/chunks/index-CNXekCQC.mjs +96 -0
  3. package/dist/chunks/{index-BzoCat8h.mjs → index-DnG3N6yj.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-BT1C87_u.mjs} +41 -14
  7. package/dist/chunks/{resolveCliOptions-CR-BEUmS.mjs → resolveCliOptions-BbfouWSK.mjs} +1 -1
  8. package/dist/chunks/{spawnEnv-CKgnHGpr.mjs → spawnEnv-DvisqPiU.mjs} +28 -3
  9. package/dist/chunks/{types-poB1VAs_.mjs → types-BkQxnyZK.mjs} +1 -1
  10. package/dist/cli.d.ts +9 -5
  11. package/dist/cli.mjs +11 -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 +11 -15
  19. package/dist/index.d.ts +10 -6
  20. package/dist/index.mjs +31 -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 +10 -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-BT1C87_u.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-BbfouWSK.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-CNXekCQC.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-DnG3N6yj.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,9 +82,10 @@ 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);
90
91
  // Refresh dynamic help text using the resolved config slices.
@@ -115,8 +116,8 @@ function installRootHooks(program, defaults) {
115
116
  /* tolerate non-strict flows */
116
117
  }
117
118
  });
118
- // Hook: preAction — root-only and parent-alias flows.
119
- program.hook('preAction', async (thisCommand) => {
119
+ // Hook: preAction — root-only and parent-alias flows + scoped afterResolve.
120
+ program.hook('preAction', async (thisCommand, actionCommand) => {
120
121
  const sources = await resolveGetDotenvConfigSources(import.meta.url);
121
122
  const rawArgs = thisCommand.rawArgs ?? [];
122
123
  dbg('preAction:rawArgs', rawArgs);
@@ -162,6 +163,20 @@ function installRootHooks(program, defaults) {
162
163
  catch {
163
164
  /* tolerate non-strict flows */
164
165
  }
166
+ // Run afterResolve scoped to the invoked command branch.
167
+ // Walk actionCommand.parent chain to build the full plugin path.
168
+ // Always true after resolution above, but satisfies the type checker.
169
+ const ctx = program.hasCtx() ? program.getCtx() : undefined;
170
+ if (ctx) {
171
+ const segments = [];
172
+ let node = actionCommand;
173
+ while (node && node !== thisCommand) {
174
+ segments.unshift(node.name());
175
+ node = node
176
+ .parent;
177
+ }
178
+ await program._runAfterResolve(ctx, segments.length > 0 ? segments : undefined);
179
+ }
165
180
  });
166
181
  return program;
167
182
  }
@@ -0,0 +1,96 @@
1
+ import { d as definePlugin } from './readMergedOptions-BT1C87_u.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
+ import { STSClient, GetCallerIdentityCommand } from '@aws-sdk/client-sts';
18
+
19
+ /**
20
+ * Attach the default action for the `aws whoami` command.
21
+ *
22
+ * This behavior executes only when `aws whoami` is invoked without a subcommand.
23
+ *
24
+ * @param cli - The `whoami` command mount.
25
+ * @returns Nothing.
26
+ */
27
+ function attachWhoamiDefaultAction(cli) {
28
+ cli.action(async () => {
29
+ // The AWS SDK default providers will read credentials from process.env,
30
+ // which the aws parent has already populated.
31
+ const client = new STSClient();
32
+ const result = await client.send(new GetCallerIdentityCommand());
33
+ console.log(JSON.stringify(result, null, 2));
34
+ });
35
+ }
36
+
37
+ /**
38
+ * Attach options/arguments for the `aws whoami` plugin mount.
39
+ *
40
+ * This subcommand currently takes no flags/args; this module exists to keep the
41
+ * wiring layout consistent across shipped plugins (options vs actions).
42
+ *
43
+ * Note: the plugin description is owned by `src/plugins/aws/whoami/index.ts` and
44
+ * must not be set here.
45
+ *
46
+ * @param cli - The `whoami` command mount under `aws`.
47
+ * @returns The same `cli` instance for chaining.
48
+ *
49
+ * @internal
50
+ */
51
+ function attachWhoamiOptions(cli) {
52
+ return cli;
53
+ }
54
+
55
+ /**
56
+ * Attach the `really` subcommand under `aws whoami`.
57
+ *
58
+ * Reads `SECRET_IDENTITY` from the resolved get-dotenv context (`cli.getCtx().dotenv`).
59
+ *
60
+ * @param cli - The `whoami` command mount.
61
+ * @returns Nothing.
62
+ */
63
+ function attachWhoamiReallyAction(cli) {
64
+ const really = cli
65
+ .ns('really')
66
+ .description('Print SECRET_IDENTITY from the resolved dotenv context');
67
+ really.action(() => {
68
+ const secretIdentity = really.getCtx().dotenv.SECRET_IDENTITY;
69
+ console.log(`Your secret identity is ${secretIdentity ?? 'still a secret'}.`);
70
+ });
71
+ }
72
+
73
+ /**
74
+ * AWS Whoami plugin factory.
75
+ *
76
+ * This plugin demonstrates a “bucket of subcommands” pattern:
77
+ * - Subcommand behavior is articulated in separate modules as `attach*` helpers.
78
+ * - Those helpers are not individually composable plugins; they are internal wiring for one plugin instance.
79
+ *
80
+ * @returns A plugin instance mounted at `aws whoami`.
81
+ */
82
+ const awsWhoamiPlugin = () => definePlugin({
83
+ ns: 'whoami',
84
+ setup(cli) {
85
+ cli.description('Print AWS caller identity (uses parent aws session)');
86
+ // Options/args (none today, but keep layout consistent with other plugins).
87
+ const whoami = attachWhoamiOptions(cli);
88
+ // Default behavior: `getdotenv aws whoami`
89
+ attachWhoamiDefaultAction(whoami);
90
+ // Subcommand behavior: `getdotenv aws whoami really`
91
+ attachWhoamiReallyAction(whoami);
92
+ return undefined;
93
+ },
94
+ });
95
+
96
+ 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-BT1C87_u.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-BbfouWSK.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
  /**
@@ -947,7 +947,7 @@ function buildHelpInformation(base, cmd) {
947
947
  }
948
948
  const marker = '\nCommands:';
949
949
  const idx = base.indexOf(marker);
950
- let out = base;
950
+ let out;
951
951
  if (idx >= 0) {
952
952
  const toInsert = groups.startsWith('\n') ? groups : `\n${groups}`;
953
953
  out = `${base.slice(0, idx)}${toInsert}${base.slice(idx)}`;
@@ -1278,7 +1278,10 @@ class GetDotenvCli extends Command {
1278
1278
  // Ensure plugins are installed exactly once, then run afterResolve.
1279
1279
  await this.install();
1280
1280
  if (opts?.runAfterResolve ?? true) {
1281
- await this._runAfterResolve(ctx, opts?.invokedSubcommand);
1281
+ const path = opts?.invokedSubcommand
1282
+ ? [opts.invokedSubcommand]
1283
+ : undefined;
1284
+ await this._runAfterResolve(ctx, path);
1282
1285
  }
1283
1286
  return ctx;
1284
1287
  }
@@ -1438,16 +1441,40 @@ class GetDotenvCli extends Command {
1438
1441
  }
1439
1442
  /**
1440
1443
  * 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).
1444
+ * When {@link commandPath} is provided, only the matching branch of the
1445
+ * plugin tree runs; otherwise all plugins run (backward compatibility).
1446
+ *
1447
+ * The path is walked segment-by-segment against the plugin tree.
1448
+ * Intermediate matches run their own afterResolve hook.
1449
+ * The deepest match runs afterResolve for itself and all its descendants.
1443
1450
  */
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);
1451
+ async _runAfterResolve(ctx, commandPath) {
1452
+ if (!commandPath || commandPath.length === 0) {
1453
+ await runAfterResolveTree(this, this._plugins.map((e) => e.plugin), ctx);
1454
+ return;
1455
+ }
1456
+ // Walk the plugin tree along the command path, scoping to the invoked branch.
1457
+ let entries = this._plugins;
1458
+ for (let i = 0; i < commandPath.length; i++) {
1459
+ const segment = commandPath[i];
1460
+ const entry = entries.find((e) => effectiveNs(e) === segment);
1461
+ if (!entry)
1462
+ return;
1463
+ const isLast = i === commandPath.length - 1;
1464
+ const nextSegment = commandPath[i + 1];
1465
+ const hasMatchingChild = !isLast &&
1466
+ entry.plugin.children.some((e) => effectiveNs(e) === nextSegment);
1467
+ if (isLast || !hasMatchingChild) {
1468
+ // Deepest match: run this plugin + all its descendants.
1469
+ await runAfterResolveTree(this, [entry.plugin], ctx);
1470
+ return;
1471
+ }
1472
+ // Intermediate match: run only this plugin's own afterResolve.
1473
+ if (entry.plugin.afterResolve) {
1474
+ await entry.plugin.afterResolve(this, ctx);
1475
+ }
1476
+ entries = entry.plugin.children;
1477
+ }
1451
1478
  }
1452
1479
  }
1453
1480
 
@@ -1477,4 +1504,4 @@ const readMergedOptions = (cmd) => {
1477
1504
  return bag;
1478
1505
  };
1479
1506
 
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 };
1507
+ 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-BT1C87_u.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-BT1C87_u.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
  /**
package/dist/cli.mjs CHANGED
@@ -1,7 +1,7 @@
1
- export { c as createCli } from './chunks/createCli-DRgcaM2D.mjs';
1
+ export { c as createCli } from './chunks/createCli-CUPiFVdZ.mjs';
2
2
  import 'zod';
3
3
  import 'path';
4
- import './chunks/loader-V1vbmtyw.mjs';
4
+ import './chunks/loader-C3DtD6HB.mjs';
5
5
  import 'fs-extra';
6
6
  import 'package-directory';
7
7
  import 'url';
@@ -13,27 +13,23 @@ import './chunks/loadModuleDefault-Dj8B3Stt.mjs';
13
13
  import 'crypto';
14
14
  import '@commander-js/extra-typings';
15
15
  import 'nanoid';
16
- import './chunks/readMergedOptions-x80ltQO_.mjs';
17
- import './chunks/readDotenvCascade-Dgx4SC1p.mjs';
16
+ import './chunks/readMergedOptions-BT1C87_u.mjs';
17
+ import './chunks/readDotenvCascade-CfFPgLCp.mjs';
18
18
  import 'dotenv';
19
19
  import 'execa';
20
20
  import './chunks/helpConfig-CGejgwWW.mjs';
21
- import './chunks/resolveCliOptions-CR-BEUmS.mjs';
21
+ import './chunks/resolveCliOptions-BbfouWSK.mjs';
22
22
  import './chunks/validate-CDl0rE6k.mjs';
23
23
  import './plugins-aws.mjs';
24
- import './chunks/spawnEnv-CKgnHGpr.mjs';
25
- import './chunks/index-4kbkrHS9.mjs';
26
- import 'node:os';
27
- import 'node:fs/promises';
28
- import 'node:stream';
29
- import 'node:crypto';
30
- import 'node:fs';
31
- import 'node:https';
32
- import 'node:process';
24
+ import './chunks/spawnEnv-DvisqPiU.mjs';
25
+ import './chunks/index-CNXekCQC.mjs';
26
+ import '@aws-sdk/client-sts';
33
27
  import './plugins-batch.mjs';
34
28
  import './chunks/invoke-DuRPU1oC.mjs';
35
29
  import 'globby';
36
- import './chunks/index-BzoCat8h.mjs';
30
+ import 'os';
31
+ import './chunks/index-DnG3N6yj.mjs';
37
32
  import './plugins-init.mjs';
33
+ import 'node:process';
38
34
  import 'readline/promises';
39
35
  import 'node:url';
package/dist/cliHost.d.ts CHANGED
@@ -287,7 +287,7 @@ interface RootOptionsShape {
287
287
  */
288
288
  trace?: boolean | string[];
289
289
  /** Paths to search for dotenv files (space-delimited string or array). */
290
- paths?: string;
290
+ paths?: string | string[];
291
291
  /** Delimiter for paths string (default: space). */
292
292
  pathsDelimiter?: string;
293
293
  /** Regex pattern for paths delimiter. */
@@ -384,7 +384,7 @@ type ProcessEnv = Record<string, string | undefined>;
384
384
  * and the selected environment (if any), and returns either a string to set
385
385
  * or `undefined` to unset/skip the variable.
386
386
  */
387
- type GetDotenvDynamicFunction = (vars: ProcessEnv, env: string | undefined) => string | undefined;
387
+ type GetDotenvDynamicFunction = (vars: ProcessEnv, env: string | undefined) => string | null | undefined;
388
388
  /**
389
389
  * A map of dynamic variable definitions.
390
390
  * Keys are variable names; values are either literal strings or functions.
@@ -903,10 +903,14 @@ declare class GetDotenvCli<TOptions extends GetDotenvOptions = GetDotenvOptions,
903
903
  install(): Promise<void>;
904
904
  /**
905
905
  * Run afterResolve hooks for registered plugins (parent → children).
906
- * When {@link invokedSubcommand} is provided, only the matching plugin
907
- * subtree runs; otherwise all plugins run (backward compatibility).
906
+ * When {@link commandPath} is provided, only the matching branch of the
907
+ * plugin tree runs; otherwise all plugins run (backward compatibility).
908
+ *
909
+ * The path is walked segment-by-segment against the plugin tree.
910
+ * Intermediate matches run their own afterResolve hook.
911
+ * The deepest match runs afterResolve for itself and all its descendants.
908
912
  */
909
- private _runAfterResolve;
913
+ _runAfterResolve(ctx: GetDotenvCliCtx<TOptions>, commandPath?: string[]): Promise<void>;
910
914
  }
911
915
 
912
916
  /** src/cliHost/getRootCommand.ts