@karmaniverous/get-dotenv 6.3.0 → 6.4.0

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 (45) hide show
  1. package/dist/chunks/{AwsRestJsonProtocol-Dv5q8CFK.mjs → AwsRestJsonProtocol-fYZqn-kW.mjs} +2 -2
  2. package/dist/chunks/{createCli-BSn6Be40.mjs → createCli-BnRdfRRL.mjs} +5 -5
  3. package/dist/chunks/{externalDataInterceptor-pqHO-Qmn.mjs → externalDataInterceptor-CILOLqbB.mjs} +2 -2
  4. package/dist/chunks/{getSSOTokenFromFile-otmZHSRV.mjs → getSSOTokenFromFile-BwMkZ_yT.mjs} +1 -1
  5. package/dist/chunks/{index-CGg5wWCm.mjs → index-0-nP97ri.mjs} +5 -5
  6. package/dist/chunks/{index-BNcKuiBy.mjs → index-70Dm0f1N.mjs} +9 -9
  7. package/dist/chunks/{index-DLQEHTw4.mjs → index-BhVOypA1.mjs} +7 -7
  8. package/dist/chunks/{index-C6uLiKpC.mjs → index-CcwT4HJK.mjs} +4 -4
  9. package/dist/chunks/{index-B18W-ELX.mjs → index-D8UL3w94.mjs} +4 -4
  10. package/dist/chunks/{index-CYoFYXZv.mjs → index-DLNhHC15.mjs} +7 -7
  11. package/dist/chunks/{index-C4Ac6feq.mjs → index-DWqbxY8Y.mjs} +9 -9
  12. package/dist/chunks/{index-CXpZ0pei.mjs → index-Du51s-Z0.mjs} +6 -6
  13. package/dist/chunks/{index-DFNcs3pR.mjs → index-DuSz0ul6.mjs} +6 -6
  14. package/dist/chunks/{index-DtRaL61T.mjs → index-OeNCYa8T.mjs} +4 -4
  15. package/dist/chunks/{index-Bi0RIILn.mjs → index-_FP0whjC.mjs} +4 -4
  16. package/dist/chunks/{index-eZMlmESW.mjs → index-o5zJ9PWL.mjs} +14 -14
  17. package/dist/chunks/{index-BqZ3PB6c.mjs → index-r0Me7-sT.mjs} +109 -4
  18. package/dist/chunks/{loadSso-CJ_XUhEj.mjs → loadSso-CLR1fKci.mjs} +6 -6
  19. package/dist/chunks/{parseKnownFiles-B6x1cUmR.mjs → parseKnownFiles-BQvmJ0HK.mjs} +1 -1
  20. package/dist/chunks/readDotenvCascade-DfFkWMjs.mjs +546 -0
  21. package/dist/chunks/{readMergedOptions-DLBDzpXX.mjs → readMergedOptions-B7VdLROn.mjs} +60 -271
  22. package/dist/chunks/{resolveCliOptions-_qtsVxda.mjs → resolveCliOptions-pgUXHJtj.mjs} +1 -1
  23. package/dist/chunks/{sdk-stream-mixin-DCdC70Up.mjs → sdk-stream-mixin-ecbbBR0l.mjs} +1 -1
  24. package/dist/chunks/{types-DdqcXCV1.mjs → types-CVDR-Sjk.mjs} +1 -1
  25. package/dist/cli.d.ts +218 -84
  26. package/dist/cli.mjs +7 -7
  27. package/dist/cliHost.d.ts +218 -84
  28. package/dist/cliHost.mjs +5 -5
  29. package/dist/env-overlay.d.ts +304 -2
  30. package/dist/env-overlay.mjs +37 -1
  31. package/dist/getdotenv.cli.mjs +7 -7
  32. package/dist/index.d.ts +218 -84
  33. package/dist/index.mjs +199 -42
  34. package/dist/plugins-aws.d.ts +153 -19
  35. package/dist/plugins-aws.mjs +2 -2
  36. package/dist/plugins-batch.d.ts +153 -19
  37. package/dist/plugins-batch.mjs +2 -2
  38. package/dist/plugins-cmd.d.ts +153 -19
  39. package/dist/plugins-cmd.mjs +4 -4
  40. package/dist/plugins-init.d.ts +153 -19
  41. package/dist/plugins-init.mjs +2 -2
  42. package/dist/plugins.d.ts +153 -19
  43. package/dist/plugins.mjs +6 -6
  44. package/package.json +1 -1
  45. package/dist/chunks/overlayEnv-Bqh_kPGA.mjs +0 -235
@@ -1,13 +1,13 @@
1
1
  import { z } from 'zod';
2
2
  import { Option, Command } from '@commander-js/extra-typings';
3
- import { d as dotenvExpand, b as dotenvExpandAll, l as loadAndApplyDynamic, a as applyDynamicMap, o as overlayEnv, c as dotenvExpandFromProcessEnv } from './overlayEnv-Bqh_kPGA.mjs';
4
- import 'node:path';
3
+ import { g as dotenvExpand, r as readDotenvCascadeWithProvenance, o as overlayEnvWithProvenance, b as applyDynamicMapWithProvenance, l as loadDynamicModuleDefault, h as dotenvExpandFromProcessEnv } from './readDotenvCascade-DfFkWMjs.mjs';
5
4
  import fs from 'fs-extra';
6
- import { nanoid } from 'nanoid';
5
+ import 'node:path';
6
+ import 'nanoid';
7
7
  import path from 'path';
8
8
  import 'crypto';
9
9
  import { fileURLToPath } from 'url';
10
- import { parse } from 'dotenv';
10
+ import 'dotenv';
11
11
  import { g as getDotenvOptionsSchemaResolved, r as resolveGetDotenvConfigSources } from './loader-CePOf74i.mjs';
12
12
  import { packageDirectory } from 'package-directory';
13
13
 
@@ -114,14 +114,6 @@ const interpolateDeep = (value, envRef) => {
114
114
  * @param obj - Object to filter.
115
115
  * @returns A shallow copy of `obj` without keys whose value is `undefined`.
116
116
  */
117
- function omitUndefined(obj) {
118
- const out = {};
119
- for (const [k, v] of Object.entries(obj)) {
120
- if (v !== undefined)
121
- out[k] = v;
122
- }
123
- return out;
124
- }
125
117
  /**
126
118
  * Specialized helper for env-like maps: drop undefined and return string-only.
127
119
  *
@@ -138,111 +130,6 @@ function omitUndefinedRecord(obj) {
138
130
  return out;
139
131
  }
140
132
 
141
- /** src/diagnostics/entropy.ts
142
- * Entropy diagnostics (presentation-only).
143
- * - Gated by min length and printable ASCII.
144
- * - Warn once per key per run when bits/char \>= threshold.
145
- * - Supports whitelist patterns to suppress known-noise keys.
146
- */
147
- const warned = new Set();
148
- const isPrintableAscii = (s) => /^[\x20-\x7E]+$/.test(s);
149
- const compile$1 = (patterns) => (patterns ?? []).map((p) => (typeof p === 'string' ? new RegExp(p, 'i') : p));
150
- const whitelisted = (key, regs) => regs.some((re) => re.test(key));
151
- const shannonBitsPerChar = (s) => {
152
- const freq = new Map();
153
- for (const ch of s)
154
- freq.set(ch, (freq.get(ch) ?? 0) + 1);
155
- const n = s.length;
156
- let h = 0;
157
- for (const c of freq.values()) {
158
- const p = c / n;
159
- h -= p * Math.log2(p);
160
- }
161
- return h;
162
- };
163
- /**
164
- * Maybe emit a one-line entropy warning for a key.
165
- * Caller supplies an `emit(line)` function; the helper ensures once-per-key.
166
- */
167
- const maybeWarnEntropy = (key, value, origin, opts, emit) => {
168
- if (!opts || opts.warnEntropy === false)
169
- return;
170
- if (warned.has(key))
171
- return;
172
- const v = value ?? '';
173
- const minLen = Math.max(0, opts.entropyMinLength ?? 16);
174
- const threshold = opts.entropyThreshold ?? 3.8;
175
- if (v.length < minLen)
176
- return;
177
- if (!isPrintableAscii(v))
178
- return;
179
- const wl = compile$1(opts.entropyWhitelist);
180
- if (whitelisted(key, wl))
181
- return;
182
- const bpc = shannonBitsPerChar(v);
183
- if (bpc >= threshold) {
184
- warned.add(key);
185
- emit(`[entropy] key=${key} score=${bpc.toFixed(2)} len=${String(v.length)} origin=${origin}`);
186
- }
187
- };
188
-
189
- const DEFAULT_PATTERNS = [
190
- '\\bsecret\\b',
191
- '\\btoken\\b',
192
- '\\bpass(word)?\\b',
193
- '\\bapi[_-]?key\\b',
194
- '\\bkey\\b',
195
- ];
196
- const compile = (patterns) => (patterns && patterns.length > 0 ? patterns : DEFAULT_PATTERNS).map((p) => typeof p === 'string' ? new RegExp(p, 'i') : p);
197
- const shouldRedactKey = (key, regs) => regs.some((re) => re.test(key));
198
- const MASK = '[redacted]';
199
- /**
200
- * Redact a single displayed value according to key/patterns.
201
- * Returns the original value when redaction is disabled or key is not matched.
202
- */
203
- const redactDisplay = (key, value, opts) => {
204
- if (!value)
205
- return value;
206
- if (!opts?.redact)
207
- return value;
208
- const regs = compile(opts.redactPatterns);
209
- return shouldRedactKey(key, regs) ? MASK : value;
210
- };
211
- /**
212
- * Produce a shallow redacted copy of an env-like object for display.
213
- */
214
- const redactObject = (obj, opts) => {
215
- if (!opts?.redact)
216
- return { ...obj };
217
- const regs = compile(opts.redactPatterns);
218
- const out = {};
219
- for (const [k, v] of Object.entries(obj)) {
220
- out[k] = v && shouldRedactKey(k, regs) ? MASK : v;
221
- }
222
- return out;
223
- };
224
- /**
225
- * Utility to redact three related displayed values (parent/dotenv/final)
226
- * consistently for trace lines.
227
- */
228
- const redactTriple = (key, triple, opts) => {
229
- if (!opts?.redact)
230
- return triple;
231
- const regs = compile(opts.redactPatterns);
232
- const maskIf = (v) => (v && shouldRedactKey(key, regs) ? MASK : v);
233
- const out = {};
234
- const p = maskIf(triple.parent);
235
- const d = maskIf(triple.dotenv);
236
- const f = maskIf(triple.final);
237
- if (p !== undefined)
238
- out.parent = p;
239
- if (d !== undefined)
240
- out.dotenv = d;
241
- if (f !== undefined)
242
- out.final = f;
243
- return out;
244
- };
245
-
246
133
  /**
247
134
  * Base root CLI defaults (shared; kept untyped here to avoid cross-layer deps).
248
135
  * Used as the bottom layer for CLI option resolution.
@@ -383,131 +270,6 @@ const resolveGetDotenvOptions = (customOptions) => {
383
270
  });
384
271
  };
385
272
 
386
- /**
387
- * Asynchronously read a dotenv file & parse it into an object.
388
- *
389
- * @param path - Path to dotenv file.
390
- * @returns The parsed dotenv object.
391
- */
392
- const readDotenv = async (path) => {
393
- try {
394
- return (await fs.exists(path)) ? parse(await fs.readFile(path)) : {};
395
- }
396
- catch {
397
- return {};
398
- }
399
- };
400
-
401
- async function getDotenv(options = {}) {
402
- // Apply defaults.
403
- const { defaultEnv, dotenvToken = '.env', dynamicPath, env, excludeDynamic = false, excludeEnv = false, excludeGlobal = false, excludePrivate = false, excludePublic = false, loadProcess = false, log = false, logger = console, outputPath, paths = [], privateToken = 'local', vars = {}, } = await resolveGetDotenvOptions(options);
404
- // Read .env files.
405
- const loaded = paths.length
406
- ? await paths.reduce(async (e, p) => {
407
- const publicGlobal = excludePublic || excludeGlobal
408
- ? Promise.resolve({})
409
- : readDotenv(path.resolve(p, dotenvToken));
410
- const publicEnv = excludePublic || excludeEnv || (!env && !defaultEnv)
411
- ? Promise.resolve({})
412
- : readDotenv(path.resolve(p, `${dotenvToken}.${env ?? defaultEnv ?? ''}`));
413
- const privateGlobal = excludePrivate || excludeGlobal
414
- ? Promise.resolve({})
415
- : readDotenv(path.resolve(p, `${dotenvToken}.${privateToken}`));
416
- const privateEnv = excludePrivate || excludeEnv || (!env && !defaultEnv)
417
- ? Promise.resolve({})
418
- : readDotenv(path.resolve(p, `${dotenvToken}.${env ?? defaultEnv ?? ''}.${privateToken}`));
419
- const [eResolved, publicGlobalResolved, publicEnvResolved, privateGlobalResolved, privateEnvResolved,] = await Promise.all([
420
- e,
421
- publicGlobal,
422
- publicEnv,
423
- privateGlobal,
424
- privateEnv,
425
- ]);
426
- return {
427
- ...eResolved,
428
- ...publicGlobalResolved,
429
- ...publicEnvResolved,
430
- ...privateGlobalResolved,
431
- ...privateEnvResolved,
432
- };
433
- }, Promise.resolve({}))
434
- : {};
435
- const outputKey = nanoid();
436
- const dotenv = dotenvExpandAll({
437
- ...loaded,
438
- ...vars,
439
- ...(outputPath ? { [outputKey]: outputPath } : {}),
440
- }, { progressive: true });
441
- // Process dynamic variables. Programmatic option takes precedence over path.
442
- if (!excludeDynamic) {
443
- let dynamic = undefined;
444
- if (options.dynamic && Object.keys(options.dynamic).length > 0) {
445
- dynamic = options.dynamic;
446
- }
447
- else if (dynamicPath) {
448
- const absDynamicPath = path.resolve(dynamicPath);
449
- await loadAndApplyDynamic(dotenv, absDynamicPath, env ?? defaultEnv, 'getdotenv-dynamic');
450
- }
451
- if (dynamic) {
452
- try {
453
- applyDynamicMap(dotenv, dynamic, env ?? defaultEnv);
454
- }
455
- catch {
456
- throw new Error(`Unable to evaluate dynamic variables.`);
457
- }
458
- }
459
- }
460
- // Write output file.
461
- let resultDotenv = dotenv;
462
- if (outputPath) {
463
- const outputPathResolved = dotenv[outputKey];
464
- if (!outputPathResolved)
465
- throw new Error('Output path not found.');
466
- const { [outputKey]: _omitted, ...dotenvForOutput } = dotenv;
467
- await writeDotenvFile(outputPathResolved, dotenvForOutput);
468
- resultDotenv = dotenvForOutput;
469
- }
470
- // Log result.
471
- if (log) {
472
- const redactFlag = options.redact ?? false;
473
- const redactPatterns = options.redactPatterns ?? undefined;
474
- const redOpts = {};
475
- if (redactFlag)
476
- redOpts.redact = true;
477
- if (redactFlag && Array.isArray(redactPatterns))
478
- redOpts.redactPatterns = redactPatterns;
479
- const bag = redactFlag
480
- ? redactObject(resultDotenv, redOpts)
481
- : { ...resultDotenv };
482
- logger.log(bag);
483
- // Entropy warnings: once-per-key-per-run (presentation only)
484
- const warnEntropyVal = options.warnEntropy ?? true;
485
- const entropyThresholdVal = options
486
- .entropyThreshold;
487
- const entropyMinLengthVal = options
488
- .entropyMinLength;
489
- const entropyWhitelistVal = options.entropyWhitelist;
490
- const entOpts = {};
491
- if (typeof warnEntropyVal === 'boolean')
492
- entOpts.warnEntropy = warnEntropyVal;
493
- if (typeof entropyThresholdVal === 'number')
494
- entOpts.entropyThreshold = entropyThresholdVal;
495
- if (typeof entropyMinLengthVal === 'number')
496
- entOpts.entropyMinLength = entropyMinLengthVal;
497
- if (Array.isArray(entropyWhitelistVal))
498
- entOpts.entropyWhitelist = entropyWhitelistVal;
499
- for (const [k, v] of Object.entries(resultDotenv)) {
500
- maybeWarnEntropy(k, v, v !== undefined ? 'dotenv' : 'unset', entOpts, (line) => {
501
- logger.log(line);
502
- });
503
- }
504
- }
505
- // Load process.env.
506
- if (loadProcess)
507
- Object.assign(process.env, resultDotenv);
508
- return resultDotenv;
509
- }
510
-
511
273
  /**
512
274
  * Compute the realized path for a command mount (leaf-up to root).
513
275
  * Excludes the root application alias.
@@ -568,37 +330,63 @@ const computeContext = async (customOptions, plugins, hostMetaUrl) => {
568
330
  // Zod boundary: parse returns the schema-derived shape; we adopt our public
569
331
  // GetDotenvOptions overlay (logger/dynamic typing) for internal processing.
570
332
  const validated = getDotenvOptionsSchemaResolved.parse(optionsResolved);
571
- // Build a pure base without side effects or logging (no dynamics, no programmatic vars).
572
- const cleanedValidated = omitUndefined(validated);
573
- const base = await getDotenv({
574
- ...cleanedValidated,
575
- excludeDynamic: true,
576
- vars: {},
577
- log: false,
578
- loadProcess: false,
579
- });
580
- // Discover config sources and overlay with progressive expansion per slice.
333
+ // Discover config sources.
581
334
  const sources = await resolveGetDotenvConfigSources(hostMetaUrl);
582
- const dotenvOverlaid = overlayEnv({
583
- base,
584
- env: validated.env ?? validated.defaultEnv,
335
+ // Base dotenv from files (with file provenance; no dynamics; no programmatic vars; no side effects).
336
+ const envName = validated.env ?? validated.defaultEnv;
337
+ const fileRes = await readDotenvCascadeWithProvenance({
338
+ paths: Array.isArray(validated.paths) ? validated.paths : [],
339
+ ...(typeof validated.dotenvToken === 'string'
340
+ ? { dotenvToken: validated.dotenvToken }
341
+ : {}),
342
+ ...(typeof validated.privateToken === 'string'
343
+ ? { privateToken: validated.privateToken }
344
+ : {}),
345
+ ...(typeof validated.env === 'string' ? { env: validated.env } : {}),
346
+ ...(typeof validated.defaultEnv === 'string'
347
+ ? { defaultEnv: validated.defaultEnv }
348
+ : {}),
349
+ ...(validated.excludeEnv === true ? { excludeEnv: true } : {}),
350
+ ...(validated.excludeGlobal === true ? { excludeGlobal: true } : {}),
351
+ ...(validated.excludePrivate === true ? { excludePrivate: true } : {}),
352
+ ...(validated.excludePublic === true ? { excludePublic: true } : {}),
353
+ });
354
+ // Overlay configs + vars with provenance.
355
+ const overlaid = overlayEnvWithProvenance({
356
+ base: fileRes.dotenv,
357
+ env: envName,
585
358
  configs: sources,
586
- ...(validated.vars ? { programmaticVars: validated.vars } : {}),
359
+ programmaticVars: validated.vars ?? {},
360
+ provenance: fileRes.provenance,
587
361
  });
588
- const dotenv = { ...dotenvOverlaid };
589
- // Programmatic dynamic variables (when provided)
590
- applyDynamicMap(dotenv, validated.dynamic, validated.env ?? validated.defaultEnv);
591
- // Packaged/project dynamics
592
- const packagedDyn = (sources.packaged?.dynamic ?? undefined);
593
- const publicDyn = (sources.project?.public?.dynamic ?? undefined);
594
- const localDyn = (sources.project?.local?.dynamic ?? undefined);
595
- applyDynamicMap(dotenv, packagedDyn, validated.env ?? validated.defaultEnv);
596
- applyDynamicMap(dotenv, publicDyn, validated.env ?? validated.defaultEnv);
597
- applyDynamicMap(dotenv, localDyn, validated.env ?? validated.defaultEnv);
598
- // file dynamicPath (lowest)
599
- if (validated.dynamicPath) {
600
- const absDynamicPath = path.resolve(validated.dynamicPath);
601
- await loadAndApplyDynamic(dotenv, absDynamicPath, validated.env ?? validated.defaultEnv, 'getdotenv-dynamic-host');
362
+ const dotenv = { ...overlaid.env };
363
+ const dotenvProvenance = overlaid.provenance;
364
+ // Dynamic precedence (A2): config dynamic < programmatic dynamic < dynamicPath
365
+ if (!validated.excludeDynamic) {
366
+ // Config dynamics (JS/TS configs only), ordered by source precedence.
367
+ const packagedDyn = (sources.packaged?.dynamic ?? undefined);
368
+ const publicDyn = (sources.project?.public?.dynamic ?? undefined);
369
+ const localDyn = (sources.project?.local?.dynamic ?? undefined);
370
+ applyDynamicMapWithProvenance(dotenv, packagedDyn, envName, dotenvProvenance, {
371
+ dynamicSource: 'config',
372
+ });
373
+ applyDynamicMapWithProvenance(dotenv, publicDyn, envName, dotenvProvenance, {
374
+ dynamicSource: 'config',
375
+ });
376
+ applyDynamicMapWithProvenance(dotenv, localDyn, envName, dotenvProvenance, {
377
+ dynamicSource: 'config',
378
+ });
379
+ // Programmatic dynamic (overrides config dynamic)
380
+ applyDynamicMapWithProvenance(dotenv, validated.dynamic, envName, dotenvProvenance, { dynamicSource: 'programmatic' });
381
+ // dynamicPath (highest dynamic tier; always evaluated when present)
382
+ if (validated.dynamicPath) {
383
+ const absDynamicPath = path.resolve(validated.dynamicPath);
384
+ const dyn = await loadDynamicModuleDefault(absDynamicPath, 'getdotenv-dynamic-host');
385
+ applyDynamicMapWithProvenance(dotenv, dyn, envName, dotenvProvenance, {
386
+ dynamicSource: 'dynamicPath',
387
+ dynamicPath: validated.dynamicPath,
388
+ });
389
+ }
602
390
  }
603
391
  // Effects:
604
392
  if (validated.outputPath) {
@@ -658,6 +446,7 @@ const computeContext = async (customOptions, plugins, hostMetaUrl) => {
658
446
  return {
659
447
  optionsResolved: validated,
660
448
  dotenv,
449
+ dotenvProvenance,
661
450
  plugins: {},
662
451
  pluginConfigs: mergedPluginConfigsByPath,
663
452
  };
@@ -1624,4 +1413,4 @@ const readMergedOptions = (cmd) => {
1624
1413
  return bag;
1625
1414
  };
1626
1415
 
1627
- export { GetDotenvCli as G, defaultsDeep as a, baseRootOptionDefaults as b, attachRootOptions as c, definePlugin as d, defineDynamic as e, defineGetDotenvConfig as f, getDotenvCliOptions2Options as g, getDotenv as h, redactDisplay as i, redactObject as j, interpolateDeep as k, redactTriple as l, maybeWarnEntropy as m, readMergedOptions as r };
1416
+ export { GetDotenvCli as G, defaultsDeep as a, baseRootOptionDefaults as b, attachRootOptions as c, definePlugin as d, resolveGetDotenvOptions as e, defineDynamic as f, getDotenvCliOptions2Options as g, defineGetDotenvConfig as h, interpolateDeep as i, readMergedOptions as r, writeDotenvFile as w };
@@ -1,4 +1,4 @@
1
- import { b as baseRootOptionDefaults, a as defaultsDeep } from './readMergedOptions-DLBDzpXX.mjs';
1
+ import { b as baseRootOptionDefaults, a as defaultsDeep } from './readMergedOptions-B7VdLROn.mjs';
2
2
  import 'fs-extra';
3
3
  import 'node:path';
4
4
  import 'crypto';
@@ -1,4 +1,4 @@
1
- import { m as fromBase64, n as toBase64, o as toHex, t as toUtf8, q as fromArrayBuffer, r as streamCollector$1 } from './index-BNcKuiBy.mjs';
1
+ import { m as fromBase64, n as toBase64, o as toHex, t as toUtf8, q as fromArrayBuffer, r as streamCollector$1 } from './index-70Dm0f1N.mjs';
2
2
  import { Readable } from 'stream';
3
3
 
4
4
  const isReadableStream = (stream) => typeof ReadableStream === "function" &&
@@ -1,4 +1,4 @@
1
- import { d as definePlugin } from './readMergedOptions-DLBDzpXX.mjs';
1
+ import { d as definePlugin } from './readMergedOptions-B7VdLROn.mjs';
2
2
 
3
3
  /**
4
4
  * Create a namespace-only parent plugin (a group command) for composing plugins