@karmaniverous/get-dotenv 5.2.6 → 6.0.0-1

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 (55) hide show
  1. package/README.md +106 -70
  2. package/dist/cliHost.d.ts +232 -226
  3. package/dist/cliHost.mjs +777 -545
  4. package/dist/config.d.ts +7 -2
  5. package/dist/env-overlay.d.ts +21 -9
  6. package/dist/env-overlay.mjs +14 -19
  7. package/dist/getdotenv.cli.mjs +1366 -1163
  8. package/dist/index.d.ts +415 -242
  9. package/dist/index.mjs +1364 -1414
  10. package/dist/plugins-aws.d.ts +149 -94
  11. package/dist/plugins-aws.mjs +307 -195
  12. package/dist/plugins-batch.d.ts +153 -99
  13. package/dist/plugins-batch.mjs +277 -95
  14. package/dist/plugins-cmd.d.ts +140 -94
  15. package/dist/plugins-cmd.mjs +636 -502
  16. package/dist/plugins-demo.d.ts +140 -94
  17. package/dist/plugins-demo.mjs +237 -46
  18. package/dist/plugins-init.d.ts +140 -94
  19. package/dist/plugins-init.mjs +129 -12
  20. package/dist/plugins.d.ts +166 -103
  21. package/dist/plugins.mjs +977 -840
  22. package/package.json +15 -53
  23. package/templates/cli/ts/plugins/hello.ts +27 -6
  24. package/templates/config/js/getdotenv.config.js +1 -1
  25. package/templates/config/ts/getdotenv.config.ts +9 -2
  26. package/dist/cliHost.cjs +0 -1875
  27. package/dist/cliHost.d.cts +0 -409
  28. package/dist/cliHost.d.mts +0 -409
  29. package/dist/config.cjs +0 -252
  30. package/dist/config.d.cts +0 -55
  31. package/dist/config.d.mts +0 -55
  32. package/dist/env-overlay.cjs +0 -163
  33. package/dist/env-overlay.d.cts +0 -50
  34. package/dist/env-overlay.d.mts +0 -50
  35. package/dist/index.cjs +0 -4140
  36. package/dist/index.d.cts +0 -457
  37. package/dist/index.d.mts +0 -457
  38. package/dist/plugins-aws.cjs +0 -667
  39. package/dist/plugins-aws.d.cts +0 -158
  40. package/dist/plugins-aws.d.mts +0 -158
  41. package/dist/plugins-batch.cjs +0 -616
  42. package/dist/plugins-batch.d.cts +0 -180
  43. package/dist/plugins-batch.d.mts +0 -180
  44. package/dist/plugins-cmd.cjs +0 -1113
  45. package/dist/plugins-cmd.d.cts +0 -178
  46. package/dist/plugins-cmd.d.mts +0 -178
  47. package/dist/plugins-demo.cjs +0 -307
  48. package/dist/plugins-demo.d.cts +0 -158
  49. package/dist/plugins-demo.d.mts +0 -158
  50. package/dist/plugins-init.cjs +0 -289
  51. package/dist/plugins-init.d.cts +0 -162
  52. package/dist/plugins-init.d.mts +0 -162
  53. package/dist/plugins.cjs +0 -2283
  54. package/dist/plugins.d.cts +0 -210
  55. package/dist/plugins.d.mts +0 -210
@@ -1,667 +0,0 @@
1
- 'use strict';
2
-
3
- var execa = require('execa');
4
- var zod = require('zod');
5
-
6
- // Minimal tokenizer for shell-off execution:
7
- // Splits by whitespace while preserving quoted segments (single or double quotes).
8
- const tokenize = (command) => {
9
- const out = [];
10
- let cur = '';
11
- let quote = null;
12
- for (let i = 0; i < command.length; i++) {
13
- const c = command.charAt(i);
14
- if (quote) {
15
- if (c === quote) {
16
- // Support doubled quotes inside a quoted segment (Windows/PowerShell style):
17
- // "" -> " and '' -> '
18
- const next = command.charAt(i + 1);
19
- if (next === quote) {
20
- cur += quote;
21
- i += 1; // skip the second quote
22
- }
23
- else {
24
- // end of quoted segment
25
- quote = null;
26
- }
27
- }
28
- else {
29
- cur += c;
30
- }
31
- }
32
- else {
33
- if (c === '"' || c === "'") {
34
- quote = c;
35
- }
36
- else if (/\s/.test(c)) {
37
- if (cur) {
38
- out.push(cur);
39
- cur = '';
40
- }
41
- }
42
- else {
43
- cur += c;
44
- }
45
- }
46
- }
47
- if (cur)
48
- out.push(cur);
49
- return out;
50
- };
51
-
52
- const dbg = (...args) => {
53
- if (process.env.GETDOTENV_DEBUG) {
54
- // Use stderr to avoid interfering with stdout assertions
55
- console.error('[getdotenv:run]', ...args);
56
- }
57
- };
58
- // Strip repeated symmetric outer quotes (single or double) until stable.
59
- // This is safe for argv arrays passed to execa (no quoting needed) and avoids
60
- // passing quote characters through to Node (e.g., for `node -e "<code>"`).
61
- // Handles stacked quotes from shells like PowerShell: """code""" -> code.
62
- const stripOuterQuotes = (s) => {
63
- let out = s;
64
- // Repeatedly trim only when the entire string is wrapped in matching quotes.
65
- // Stop as soon as the ends are asymmetric or no quotes remain.
66
- while (out.length >= 2) {
67
- const a = out.charAt(0);
68
- const b = out.charAt(out.length - 1);
69
- const symmetric = (a === '"' && b === '"') || (a === "'" && b === "'");
70
- if (!symmetric)
71
- break;
72
- out = out.slice(1, -1);
73
- }
74
- return out;
75
- };
76
- // Extract exitCode/stdout/stderr from execa result or error in a tolerant way.
77
- const pickResult = (r) => {
78
- const exit = r.exitCode;
79
- const stdoutVal = r.stdout;
80
- const stderrVal = r.stderr;
81
- return {
82
- exitCode: typeof exit === 'number' ? exit : Number.NaN,
83
- stdout: typeof stdoutVal === 'string' ? stdoutVal : '',
84
- stderr: typeof stderrVal === 'string' ? stderrVal : '',
85
- };
86
- };
87
- // Convert NodeJS.ProcessEnv (string | undefined values) to the shape execa
88
- // expects (Readonly<Partial<Record<string, string>>>), dropping undefineds.
89
- const sanitizeEnv = (env) => {
90
- if (!env)
91
- return undefined;
92
- const entries = Object.entries(env).filter((e) => typeof e[1] === 'string');
93
- return entries.length > 0 ? Object.fromEntries(entries) : undefined;
94
- };
95
- /**
96
- * Execute a command and capture stdout/stderr (buffered).
97
- * - Preserves plain vs shell behavior and argv/string normalization.
98
- * - Never re-emits stdout/stderr to parent; returns captured buffers.
99
- * - Supports optional timeout (ms).
100
- */
101
- const runCommandResult = async (command, shell, opts = {}) => {
102
- const envSan = sanitizeEnv(opts.env);
103
- {
104
- let file;
105
- let args = [];
106
- if (Array.isArray(command)) {
107
- file = command[0];
108
- args = command.slice(1).map(stripOuterQuotes);
109
- }
110
- else {
111
- const tokens = tokenize(command);
112
- file = tokens[0];
113
- args = tokens.slice(1);
114
- }
115
- if (!file)
116
- return { exitCode: 0, stdout: '', stderr: '' };
117
- dbg('exec:capture (plain)', { file, args });
118
- try {
119
- const result = await execa.execa(file, args, {
120
- ...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}),
121
- ...(envSan !== undefined ? { env: envSan } : {}),
122
- stdio: 'pipe',
123
- ...(opts.timeoutMs !== undefined
124
- ? { timeout: opts.timeoutMs, killSignal: 'SIGKILL' }
125
- : {}),
126
- });
127
- const ok = pickResult(result);
128
- dbg('exit:capture (plain)', { exitCode: ok.exitCode });
129
- return ok;
130
- }
131
- catch (err) {
132
- const out = pickResult(err);
133
- dbg('exit:capture:error (plain)', { exitCode: out.exitCode });
134
- return out;
135
- }
136
- }
137
- };
138
- const runCommand = async (command, shell, opts) => {
139
- if (shell === false) {
140
- let file;
141
- let args = [];
142
- if (Array.isArray(command)) {
143
- file = command[0];
144
- args = command.slice(1).map(stripOuterQuotes);
145
- }
146
- else {
147
- const tokens = tokenize(command);
148
- file = tokens[0];
149
- args = tokens.slice(1);
150
- }
151
- if (!file)
152
- return 0;
153
- dbg('exec (plain)', { file, args, stdio: opts.stdio });
154
- // Build options without injecting undefined properties (exactOptionalPropertyTypes).
155
- const envSan = sanitizeEnv(opts.env);
156
- const plainOpts = {};
157
- if (opts.cwd !== undefined)
158
- plainOpts.cwd = opts.cwd;
159
- if (envSan !== undefined)
160
- plainOpts.env = envSan;
161
- if (opts.stdio !== undefined)
162
- plainOpts.stdio = opts.stdio;
163
- const result = await execa.execa(file, args, plainOpts);
164
- if (opts.stdio === 'pipe' && result.stdout) {
165
- process.stdout.write(result.stdout + (result.stdout.endsWith('\n') ? '' : '\n'));
166
- }
167
- const exit = result?.exitCode;
168
- dbg('exit (plain)', { exitCode: exit });
169
- return typeof exit === 'number' ? exit : Number.NaN;
170
- }
171
- else {
172
- const commandStr = Array.isArray(command) ? command.join(' ') : command;
173
- dbg('exec (shell)', {
174
- shell: typeof shell === 'string' ? shell : 'custom',
175
- stdio: opts.stdio,
176
- command: commandStr,
177
- });
178
- const envSan = sanitizeEnv(opts.env);
179
- const shellOpts = { shell };
180
- if (opts.cwd !== undefined)
181
- shellOpts.cwd = opts.cwd;
182
- if (envSan !== undefined)
183
- shellOpts.env = envSan;
184
- if (opts.stdio !== undefined)
185
- shellOpts.stdio = opts.stdio;
186
- const result = await execa.execaCommand(commandStr, shellOpts);
187
- const out = result?.stdout;
188
- if (opts.stdio === 'pipe' && out) {
189
- process.stdout.write(out + (out.endsWith('\n') ? '' : '\n'));
190
- }
191
- const exit = result?.exitCode;
192
- dbg('exit (shell)', { exitCode: exit });
193
- return typeof exit === 'number' ? exit : Number.NaN;
194
- }
195
- };
196
-
197
- const dropUndefined = (bag) => Object.fromEntries(Object.entries(bag).filter((e) => typeof e[1] === 'string'));
198
- /** Build a sanitized env for child processes from base + overlay. */
199
- const buildSpawnEnv = (base, overlay) => {
200
- const raw = {
201
- ...(base ?? {}),
202
- ...(overlay ?? {}),
203
- };
204
- // Drop undefined first
205
- const entries = Object.entries(dropUndefined(raw));
206
- if (process.platform === 'win32') {
207
- // Windows: keys are case-insensitive; collapse duplicates
208
- const byLower = new Map();
209
- for (const [k, v] of entries) {
210
- byLower.set(k.toLowerCase(), [k, v]); // last wins; preserve latest casing
211
- }
212
- const out = {};
213
- for (const [, [k, v]] of byLower)
214
- out[k] = v;
215
- // HOME fallback from USERPROFILE (common expectation)
216
- if (!Object.prototype.hasOwnProperty.call(out, 'HOME')) {
217
- const up = out['USERPROFILE'];
218
- if (typeof up === 'string' && up.length > 0)
219
- out['HOME'] = up;
220
- }
221
- // Normalize TMP/TEMP coherence (pick any present; reflect to both)
222
- const tmp = out['TMP'] ?? out['TEMP'];
223
- if (typeof tmp === 'string' && tmp.length > 0) {
224
- out['TMP'] = tmp;
225
- out['TEMP'] = tmp;
226
- }
227
- return out;
228
- }
229
- // POSIX: keep keys as-is
230
- const out = Object.fromEntries(entries);
231
- // Ensure TMPDIR exists when any temp key is present (best-effort)
232
- const tmpdir = out['TMPDIR'] ?? out['TMP'] ?? out['TEMP'];
233
- if (typeof tmpdir === 'string' && tmpdir.length > 0) {
234
- out['TMPDIR'] = tmpdir;
235
- }
236
- return out;
237
- };
238
-
239
- /** src/cliHost/definePlugin.ts
240
- * Plugin contracts for the GetDotenv CLI host.
241
- *
242
- * This module exposes a structural public interface for the host that plugins
243
- * should use (GetDotenvCliPublic). Using a structural type at the seam avoids
244
- * nominal class identity issues (private fields) in downstream consumers.
245
- */
246
- /**
247
- * Define a GetDotenv CLI plugin with compositional helpers.
248
- *
249
- * @example
250
- * const parent = definePlugin(\{ id: 'p', setup(cli) \{ /* ... *\/ \} \})
251
- * .use(childA)
252
- * .use(childB);
253
- */
254
- const definePlugin = (spec) => {
255
- const { children = [], ...rest } = spec;
256
- const plugin = {
257
- ...rest,
258
- children: [...children],
259
- use(child) {
260
- this.children.push(child);
261
- return this;
262
- },
263
- };
264
- return plugin;
265
- };
266
-
267
- /**
268
- * Batch services (neutral): resolve command and shell settings.
269
- * Shared by the generator path and the batch plugin to avoid circular deps.
270
- */
271
- /**
272
- * Resolve a command string from the {@link Scripts} table.
273
- * A script may be expressed as a string or an object with a `cmd` property.
274
- *
275
- * @param scripts - Optional scripts table.
276
- * @param command - User-provided command name or string.
277
- * @returns Resolved command string (falls back to the provided command).
278
- */
279
- /**
280
- * Resolve the shell setting for a given command:
281
- * - If the script entry is an object, prefer its `shell` override.
282
- * - Otherwise use the provided `shell` (string | boolean).
283
- *
284
- * @param scripts - Optional scripts table.
285
- * @param command - User-provided command name or string.
286
- * @param shell - Global shell preference (string | boolean).
287
- */
288
- const resolveShell = (scripts, command, shell) => scripts && typeof scripts[command] === 'object'
289
- ? (scripts[command].shell ?? false)
290
- : (shell ?? false);
291
-
292
- const DEFAULT_TIMEOUT_MS = 15_000;
293
- const trim = (s) => (typeof s === 'string' ? s.trim() : '');
294
- const unquote = (s) => s.length >= 2 &&
295
- ((s.startsWith('"') && s.endsWith('"')) ||
296
- (s.startsWith("'") && s.endsWith("'")))
297
- ? s.slice(1, -1)
298
- : s;
299
- const parseExportCredentialsJson = (txt) => {
300
- try {
301
- const obj = JSON.parse(txt);
302
- const src = obj.Credentials ?? obj;
303
- const ak = src.AccessKeyId;
304
- const sk = src.SecretAccessKey;
305
- const tk = src.SessionToken;
306
- if (ak && sk)
307
- return {
308
- accessKeyId: ak,
309
- secretAccessKey: sk,
310
- ...(tk ? { sessionToken: tk } : {}),
311
- };
312
- }
313
- catch {
314
- /* ignore */
315
- }
316
- return undefined;
317
- };
318
- const parseExportCredentialsEnv = (txt) => {
319
- const lines = txt.split(/\r?\n/);
320
- let id;
321
- let secret;
322
- let token;
323
- for (const raw of lines) {
324
- const line = raw.trim();
325
- if (!line)
326
- continue;
327
- // POSIX: export AWS_ACCESS_KEY_ID=..., export AWS_SECRET_ACCESS_KEY=..., export AWS_SESSION_TOKEN=...
328
- let m = /^export\s+([A-Z0-9_]+)\s*=\s*(.+)$/.exec(line);
329
- if (!m) {
330
- // PowerShell: $Env:AWS_ACCESS_KEY_ID="...", etc.
331
- m = /^\$Env:([A-Z0-9_]+)\s*=\s*(.+)$/.exec(line);
332
- }
333
- if (!m)
334
- continue;
335
- const k = m[1];
336
- const valRaw = m[2];
337
- if (typeof valRaw !== 'string')
338
- continue;
339
- let v = unquote(valRaw.trim());
340
- // Drop trailing semicolons if present (some shells)
341
- v = v.replace(/;$/, '');
342
- if (k === 'AWS_ACCESS_KEY_ID')
343
- id = v;
344
- else if (k === 'AWS_SECRET_ACCESS_KEY')
345
- secret = v;
346
- else if (k === 'AWS_SESSION_TOKEN')
347
- token = v;
348
- }
349
- if (id && secret)
350
- return {
351
- accessKeyId: id,
352
- secretAccessKey: secret,
353
- ...(token ? { sessionToken: token } : {}),
354
- };
355
- return undefined;
356
- };
357
- const getAwsConfigure = async (key, profile, timeoutMs = DEFAULT_TIMEOUT_MS) => {
358
- const r = await runCommandResult(['aws', 'configure', 'get', key, '--profile', profile], false, {
359
- env: process.env,
360
- timeoutMs,
361
- });
362
- // Guard for mocked undefined in tests; keep narrow lint scope.
363
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
364
- if (!r || typeof r.exitCode !== 'number')
365
- return undefined;
366
- if (r.exitCode === 0) {
367
- const v = trim(r.stdout);
368
- return v.length > 0 ? v : undefined;
369
- }
370
- return undefined;
371
- };
372
- const exportCredentials = async (profile, timeoutMs = DEFAULT_TIMEOUT_MS) => {
373
- // Try JSON format first (AWS CLI v2)
374
- const rJson = await runCommandResult([
375
- 'aws',
376
- 'configure',
377
- 'export-credentials',
378
- '--profile',
379
- profile,
380
- '--format',
381
- 'json',
382
- ], false, { env: process.env, timeoutMs });
383
- if (rJson.exitCode === 0) {
384
- const creds = parseExportCredentialsJson(rJson.stdout);
385
- if (creds)
386
- return creds;
387
- }
388
- // Fallback: env lines
389
- const rEnv = await runCommandResult(['aws', 'configure', 'export-credentials', '--profile', profile], false, { env: process.env, timeoutMs });
390
- if (rEnv.exitCode === 0) {
391
- const creds = parseExportCredentialsEnv(rEnv.stdout);
392
- if (creds)
393
- return creds;
394
- }
395
- return undefined;
396
- };
397
- const resolveAwsContext = async ({ dotenv, cfg, }) => {
398
- const profileKey = cfg.profileKey ?? 'AWS_LOCAL_PROFILE';
399
- const profileFallbackKey = cfg.profileFallbackKey ?? 'AWS_PROFILE';
400
- const regionKey = cfg.regionKey ?? 'AWS_REGION';
401
- const profile = cfg.profile ??
402
- dotenv[profileKey] ??
403
- dotenv[profileFallbackKey] ??
404
- undefined;
405
- let region = cfg.region ?? dotenv[regionKey] ?? undefined;
406
- // Short-circuit when strategy is disabled.
407
- if (cfg.strategy === 'none') {
408
- // If region is still missing and we have a profile, try best-effort region resolve.
409
- if (!region && profile)
410
- region = await getAwsConfigure('region', profile);
411
- if (!region && cfg.defaultRegion)
412
- region = cfg.defaultRegion;
413
- const out = {};
414
- if (profile !== undefined)
415
- out.profile = profile;
416
- if (region !== undefined)
417
- out.region = region;
418
- return out;
419
- }
420
- // Env-first credentials.
421
- let credentials;
422
- const envId = trim(process.env.AWS_ACCESS_KEY_ID);
423
- const envSecret = trim(process.env.AWS_SECRET_ACCESS_KEY);
424
- const envToken = trim(process.env.AWS_SESSION_TOKEN);
425
- if (envId && envSecret) {
426
- credentials = {
427
- accessKeyId: envId,
428
- secretAccessKey: envSecret,
429
- ...(envToken ? { sessionToken: envToken } : {}),
430
- };
431
- }
432
- else if (profile) {
433
- // Try export-credentials
434
- credentials = await exportCredentials(profile);
435
- // On failure, detect SSO and optionally login then retry
436
- if (!credentials) {
437
- const ssoSession = await getAwsConfigure('sso_session', profile);
438
- const looksSSO = typeof ssoSession === 'string' && ssoSession.length > 0;
439
- if (looksSSO && cfg.loginOnDemand) {
440
- // Best-effort login, then retry export once.
441
- await runCommandResult(['aws', 'sso', 'login', '--profile', profile], false, {
442
- env: process.env,
443
- timeoutMs: DEFAULT_TIMEOUT_MS,
444
- });
445
- credentials = await exportCredentials(profile);
446
- }
447
- }
448
- // Static fallback if still missing.
449
- if (!credentials) {
450
- const id = await getAwsConfigure('aws_access_key_id', profile);
451
- const secret = await getAwsConfigure('aws_secret_access_key', profile);
452
- const token = await getAwsConfigure('aws_session_token', profile);
453
- if (id && secret) {
454
- credentials = {
455
- accessKeyId: id,
456
- secretAccessKey: secret,
457
- ...(token ? { sessionToken: token } : {}),
458
- };
459
- }
460
- }
461
- }
462
- // Final region resolution
463
- if (!region && profile)
464
- region = await getAwsConfigure('region', profile);
465
- if (!region && cfg.defaultRegion)
466
- region = cfg.defaultRegion;
467
- const out = {};
468
- if (profile !== undefined)
469
- out.profile = profile;
470
- if (region !== undefined)
471
- out.region = region;
472
- if (credentials)
473
- out.credentials = credentials;
474
- return out;
475
- };
476
-
477
- const AwsPluginConfigSchema = zod.z.object({
478
- profile: zod.z.string().optional(),
479
- region: zod.z.string().optional(),
480
- defaultRegion: zod.z.string().optional(),
481
- profileKey: zod.z.string().default('AWS_LOCAL_PROFILE').optional(),
482
- profileFallbackKey: zod.z.string().default('AWS_PROFILE').optional(),
483
- regionKey: zod.z.string().default('AWS_REGION').optional(),
484
- strategy: zod.z.enum(['cli-export', 'none']).default('cli-export').optional(),
485
- loginOnDemand: zod.z.boolean().default(false).optional(),
486
- setEnv: zod.z.boolean().default(true).optional(),
487
- addCtx: zod.z.boolean().default(true).optional(),
488
- });
489
-
490
- const awsPlugin = () => definePlugin({
491
- id: 'aws',
492
- // Host validates this slice when the loader path is active.
493
- configSchema: AwsPluginConfigSchema,
494
- setup(cli) {
495
- // Subcommand: aws
496
- cli
497
- .ns('aws')
498
- .description('Establish an AWS session and optionally forward to the AWS CLI')
499
- .configureHelp({ showGlobalOptions: true })
500
- .enablePositionalOptions()
501
- .passThroughOptions()
502
- .allowUnknownOption(true)
503
- // Boolean toggles
504
- .option('--login-on-demand', 'attempt aws sso login on-demand')
505
- .option('--no-login-on-demand', 'disable sso login on-demand')
506
- .option('--set-env', 'write resolved values into process.env')
507
- .option('--no-set-env', 'do not write resolved values into process.env')
508
- .option('--add-ctx', 'mirror results under ctx.plugins.aws')
509
- .option('--no-add-ctx', 'do not mirror results under ctx.plugins.aws')
510
- // Strings / enums
511
- .option('--profile <string>', 'AWS profile name')
512
- .option('--region <string>', 'AWS region')
513
- .option('--default-region <string>', 'fallback region')
514
- .option('--strategy <string>', 'credential acquisition strategy: cli-export|none')
515
- // Advanced key overrides
516
- .option('--profile-key <string>', 'dotenv/config key for local profile')
517
- .option('--profile-fallback-key <string>', 'fallback dotenv/config key for profile')
518
- .option('--region-key <string>', 'dotenv/config key for region')
519
- // Accept any extra operands so Commander does not error when tokens appear after "--".
520
- .argument('[args...]')
521
- .action(async (args, opts, thisCommand) => {
522
- const self = thisCommand;
523
- const parent = (self.parent ?? null);
524
- // Access merged root CLI options (installed by passOptions())
525
- const rootOpts = (parent?.getDotenvCliOptions ?? {});
526
- const capture = process.env.GETDOTENV_STDIO === 'pipe' ||
527
- Boolean(rootOpts?.capture);
528
- const underTests = process.env.GETDOTENV_TEST === '1' ||
529
- typeof process.env.VITEST_WORKER_ID === 'string';
530
- // Build overlay cfg from subcommand flags layered over discovered config.
531
- const ctx = cli.getCtx();
532
- const cfgBase = (ctx?.pluginConfigs?.['aws'] ??
533
- {});
534
- const overlay = {};
535
- // Map boolean toggles (respect explicit --no-*)
536
- if (Object.prototype.hasOwnProperty.call(opts, 'loginOnDemand'))
537
- overlay.loginOnDemand = Boolean(opts.loginOnDemand);
538
- if (Object.prototype.hasOwnProperty.call(opts, 'setEnv'))
539
- overlay.setEnv = Boolean(opts.setEnv);
540
- if (Object.prototype.hasOwnProperty.call(opts, 'addCtx'))
541
- overlay.addCtx = Boolean(opts.addCtx);
542
- // Strings/enums
543
- if (typeof opts.profile === 'string')
544
- overlay.profile = opts.profile;
545
- if (typeof opts.region === 'string')
546
- overlay.region = opts.region;
547
- if (typeof opts.defaultRegion === 'string')
548
- overlay.defaultRegion = opts.defaultRegion;
549
- if (typeof opts.strategy === 'string')
550
- overlay.strategy =
551
- opts.strategy;
552
- // Advanced key overrides
553
- if (typeof opts.profileKey === 'string')
554
- overlay.profileKey = opts.profileKey;
555
- if (typeof opts.profileFallbackKey === 'string')
556
- overlay.profileFallbackKey = opts.profileFallbackKey;
557
- if (typeof opts.regionKey === 'string')
558
- overlay.regionKey = opts.regionKey;
559
- const cfg = {
560
- ...cfgBase,
561
- ...overlay,
562
- };
563
- // Resolve current context with overrides
564
- const out = await resolveAwsContext({
565
- dotenv: ctx?.dotenv ?? {},
566
- cfg,
567
- });
568
- // Apply env/ctx mirrors per toggles
569
- if (cfg.setEnv !== false) {
570
- if (out.region) {
571
- process.env.AWS_REGION = out.region;
572
- if (!process.env.AWS_DEFAULT_REGION)
573
- process.env.AWS_DEFAULT_REGION = out.region;
574
- }
575
- if (out.credentials) {
576
- process.env.AWS_ACCESS_KEY_ID = out.credentials.accessKeyId;
577
- process.env.AWS_SECRET_ACCESS_KEY =
578
- out.credentials.secretAccessKey;
579
- if (out.credentials.sessionToken !== undefined) {
580
- process.env.AWS_SESSION_TOKEN = out.credentials.sessionToken;
581
- }
582
- }
583
- }
584
- if (cfg.addCtx !== false) {
585
- if (ctx) {
586
- ctx.plugins ??= {};
587
- ctx.plugins['aws'] = {
588
- ...(out.profile ? { profile: out.profile } : {}),
589
- ...(out.region ? { region: out.region } : {}),
590
- ...(out.credentials ? { credentials: out.credentials } : {}),
591
- };
592
- }
593
- }
594
- // Forward when positional args are present; otherwise session-only.
595
- if (Array.isArray(args) && args.length > 0) {
596
- const argv = ['aws', ...args];
597
- const shellSetting = resolveShell(rootOpts?.scripts, 'aws', rootOpts?.shell);
598
- const ctxDotenv = (ctx?.dotenv ?? {});
599
- const exit = await runCommand(argv, shellSetting, {
600
- env: buildSpawnEnv(process.env, ctxDotenv),
601
- stdio: capture ? 'pipe' : 'inherit',
602
- });
603
- // Deterministic termination (suppressed under tests)
604
- if (!underTests) {
605
- process.exit(typeof exit === 'number' ? exit : 0);
606
- }
607
- return;
608
- }
609
- else {
610
- // Session only: low-noise breadcrumb under debug
611
- if (process.env.GETDOTENV_DEBUG) {
612
- const log = console;
613
- log.log('[aws] session established', {
614
- profile: out.profile,
615
- region: out.region,
616
- hasCreds: Boolean(out.credentials),
617
- });
618
- }
619
- if (!underTests)
620
- process.exit(0);
621
- return;
622
- }
623
- });
624
- },
625
- async afterResolve(_cli, ctx) {
626
- const log = console;
627
- const cfgRaw = (ctx.pluginConfigs?.['aws'] ?? {});
628
- const cfg = (cfgRaw || {});
629
- const out = await resolveAwsContext({
630
- dotenv: ctx.dotenv,
631
- cfg,
632
- });
633
- const { profile, region, credentials } = out;
634
- if (cfg.setEnv !== false) {
635
- if (region) {
636
- process.env.AWS_REGION = region;
637
- if (!process.env.AWS_DEFAULT_REGION)
638
- process.env.AWS_DEFAULT_REGION = region;
639
- }
640
- if (credentials) {
641
- process.env.AWS_ACCESS_KEY_ID = credentials.accessKeyId;
642
- process.env.AWS_SECRET_ACCESS_KEY = credentials.secretAccessKey;
643
- if (credentials.sessionToken !== undefined) {
644
- process.env.AWS_SESSION_TOKEN = credentials.sessionToken;
645
- }
646
- }
647
- }
648
- if (cfg.addCtx !== false) {
649
- ctx.plugins ??= {};
650
- ctx.plugins['aws'] = {
651
- ...(profile ? { profile } : {}),
652
- ...(region ? { region } : {}),
653
- ...(credentials ? { credentials } : {}),
654
- };
655
- }
656
- // Optional: low-noise breadcrumb for diagnostics
657
- if (process.env.GETDOTENV_DEBUG) {
658
- log.log('[aws] afterResolve', {
659
- profile,
660
- region,
661
- hasCreds: Boolean(credentials),
662
- });
663
- }
664
- },
665
- });
666
-
667
- exports.awsPlugin = awsPlugin;