@karmaniverous/get-dotenv 5.2.3 → 5.2.5
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.
- package/README.md +5 -5
- package/dist/cliHost.cjs +422 -21
- package/dist/cliHost.d.cts +95 -8
- package/dist/cliHost.d.mts +95 -8
- package/dist/cliHost.d.ts +95 -8
- package/dist/cliHost.mjs +423 -22
- package/dist/getdotenv.cli.mjs +10 -6
- package/dist/index.cjs +11 -6
- package/dist/index.d.cts +19 -1
- package/dist/index.d.mts +19 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.mjs +11 -7
- package/dist/plugins-aws.cjs +7 -0
- package/dist/plugins-aws.d.cts +99 -162
- package/dist/plugins-aws.d.mts +99 -162
- package/dist/plugins-aws.d.ts +99 -162
- package/dist/plugins-aws.mjs +7 -0
- package/dist/plugins-batch.cjs +7 -0
- package/dist/plugins-batch.d.cts +99 -162
- package/dist/plugins-batch.d.mts +99 -162
- package/dist/plugins-batch.d.ts +99 -162
- package/dist/plugins-batch.mjs +7 -0
- package/dist/plugins-cmd.cjs +9 -5
- package/dist/plugins-cmd.d.cts +99 -162
- package/dist/plugins-cmd.d.mts +99 -162
- package/dist/plugins-cmd.d.ts +99 -162
- package/dist/plugins-cmd.mjs +9 -5
- package/dist/plugins-demo.cjs +7 -0
- package/dist/plugins-demo.d.cts +99 -162
- package/dist/plugins-demo.d.mts +99 -162
- package/dist/plugins-demo.d.ts +99 -162
- package/dist/plugins-demo.mjs +7 -0
- package/dist/plugins-init.cjs +7 -0
- package/dist/plugins-init.d.cts +99 -162
- package/dist/plugins-init.d.mts +99 -162
- package/dist/plugins-init.d.ts +99 -162
- package/dist/plugins-init.mjs +7 -0
- package/dist/plugins.cjs +9 -5
- package/dist/plugins.d.cts +99 -162
- package/dist/plugins.d.mts +99 -162
- package/dist/plugins.d.ts +99 -162
- package/dist/plugins.mjs +9 -5
- package/package.json +1 -1
package/dist/cliHost.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
1
|
+
import { Command, Option } from 'commander';
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
3
|
import { packageDirectory } from 'package-directory';
|
|
4
4
|
import url, { fileURLToPath, pathToFileURL } from 'url';
|
|
@@ -9,27 +9,6 @@ import { nanoid } from 'nanoid';
|
|
|
9
9
|
import { parse } from 'dotenv';
|
|
10
10
|
import { createHash } from 'crypto';
|
|
11
11
|
|
|
12
|
-
/**
|
|
13
|
-
* Define a GetDotenv CLI plugin with compositional helpers.
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* const parent = definePlugin(\{ id: 'p', setup(cli) \{ /* ... *\/ \} \})
|
|
17
|
-
* .use(childA)
|
|
18
|
-
* .use(childB);
|
|
19
|
-
*/
|
|
20
|
-
const definePlugin = (spec) => {
|
|
21
|
-
const { children = [], ...rest } = spec;
|
|
22
|
-
const plugin = {
|
|
23
|
-
...rest,
|
|
24
|
-
children: [...children],
|
|
25
|
-
use(child) {
|
|
26
|
-
this.children.push(child);
|
|
27
|
-
return this;
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
return plugin;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
12
|
// Base root CLI defaults (shared; kept untyped here to avoid cross-layer deps).
|
|
34
13
|
const baseRootOptionDefaults = {
|
|
35
14
|
dotenvToken: '.env',
|
|
@@ -586,6 +565,22 @@ const dotenvExpandAll = (values = {}, options = {}) => Object.keys(values).reduc
|
|
|
586
565
|
});
|
|
587
566
|
return acc;
|
|
588
567
|
}, {});
|
|
568
|
+
/**
|
|
569
|
+
* Recursively expands environment variables in a string using `process.env` as
|
|
570
|
+
* the expansion reference. Variables may be presented with optional default as
|
|
571
|
+
* `$VAR[:default]` or `${VAR[:default]}`. Unknown variables will expand to an
|
|
572
|
+
* empty string.
|
|
573
|
+
*
|
|
574
|
+
* @param value - The string to expand.
|
|
575
|
+
* @returns The expanded string.
|
|
576
|
+
*
|
|
577
|
+
* @example
|
|
578
|
+
* ```ts
|
|
579
|
+
* process.env.FOO = 'bar';
|
|
580
|
+
* dotenvExpandFromProcessEnv('Hello $FOO'); // "Hello bar"
|
|
581
|
+
* ```
|
|
582
|
+
*/
|
|
583
|
+
const dotenvExpandFromProcessEnv = (value) => dotenvExpand(value, process.env);
|
|
589
584
|
|
|
590
585
|
const applyKv = (current, kv) => {
|
|
591
586
|
if (!kv || Object.keys(kv).length === 0)
|
|
@@ -1436,6 +1431,412 @@ class GetDotenvCli extends Command {
|
|
|
1436
1431
|
}
|
|
1437
1432
|
}
|
|
1438
1433
|
|
|
1434
|
+
/**
|
|
1435
|
+
* Validate a composed env against config-provided validation surfaces.
|
|
1436
|
+
* Precedence for validation definitions:
|
|
1437
|
+
* project.local -\> project.public -\> packaged
|
|
1438
|
+
*
|
|
1439
|
+
* Behavior:
|
|
1440
|
+
* - If a JS/TS `schema` is present, use schema.safeParse(finalEnv).
|
|
1441
|
+
* - Else if `requiredKeys` is present, check presence (value !== undefined).
|
|
1442
|
+
* - Returns a flat list of issue strings; caller decides warn vs fail.
|
|
1443
|
+
*/
|
|
1444
|
+
const validateEnvAgainstSources = (finalEnv, sources) => {
|
|
1445
|
+
const pick = (getter) => {
|
|
1446
|
+
const pl = sources.project?.local;
|
|
1447
|
+
const pp = sources.project?.public;
|
|
1448
|
+
const pk = sources.packaged;
|
|
1449
|
+
return ((pl && getter(pl)) ||
|
|
1450
|
+
(pp && getter(pp)) ||
|
|
1451
|
+
(pk && getter(pk)) ||
|
|
1452
|
+
undefined);
|
|
1453
|
+
};
|
|
1454
|
+
const schema = pick((cfg) => cfg['schema']);
|
|
1455
|
+
if (schema &&
|
|
1456
|
+
typeof schema.safeParse === 'function') {
|
|
1457
|
+
try {
|
|
1458
|
+
const parsed = schema.safeParse(finalEnv);
|
|
1459
|
+
if (!parsed.success) {
|
|
1460
|
+
// Try to render zod-style issues when available.
|
|
1461
|
+
const err = parsed.error;
|
|
1462
|
+
const issues = Array.isArray(err.issues) && err.issues.length > 0
|
|
1463
|
+
? err.issues.map((i) => {
|
|
1464
|
+
const path = Array.isArray(i.path) ? i.path.join('.') : '';
|
|
1465
|
+
const msg = i.message ?? 'Invalid value';
|
|
1466
|
+
return path ? `[schema] ${path}: ${msg}` : `[schema] ${msg}`;
|
|
1467
|
+
})
|
|
1468
|
+
: ['[schema] validation failed'];
|
|
1469
|
+
return issues;
|
|
1470
|
+
}
|
|
1471
|
+
return [];
|
|
1472
|
+
}
|
|
1473
|
+
catch {
|
|
1474
|
+
// If schema invocation fails, surface a single diagnostic.
|
|
1475
|
+
return [
|
|
1476
|
+
'[schema] validation failed (unable to execute schema.safeParse)',
|
|
1477
|
+
];
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
const requiredKeys = pick((cfg) => cfg['requiredKeys']);
|
|
1481
|
+
if (Array.isArray(requiredKeys) && requiredKeys.length > 0) {
|
|
1482
|
+
const missing = requiredKeys.filter((k) => finalEnv[k] === undefined);
|
|
1483
|
+
if (missing.length > 0) {
|
|
1484
|
+
return missing.map((k) => `[requiredKeys] missing: ${k}`);
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
return [];
|
|
1488
|
+
};
|
|
1489
|
+
|
|
1490
|
+
/**
|
|
1491
|
+
* Attach legacy root flags to a Commander program.
|
|
1492
|
+
* Uses provided defaults to render help labels without coupling to generators.
|
|
1493
|
+
*/
|
|
1494
|
+
const attachRootOptions = (program, defaults, opts) => {
|
|
1495
|
+
// Install temporary wrappers to tag all options added here as "base".
|
|
1496
|
+
const GROUP = 'base';
|
|
1497
|
+
const tagLatest = (cmd, group) => {
|
|
1498
|
+
const optsArr = cmd.options;
|
|
1499
|
+
if (Array.isArray(optsArr) && optsArr.length > 0) {
|
|
1500
|
+
const last = optsArr[optsArr.length - 1];
|
|
1501
|
+
last.__group = group;
|
|
1502
|
+
}
|
|
1503
|
+
};
|
|
1504
|
+
const originalAddOption = program.addOption.bind(program);
|
|
1505
|
+
const originalOption = program.option.bind(program);
|
|
1506
|
+
program.addOption = function patchedAdd(opt) {
|
|
1507
|
+
// Tag before adding, in case consumers inspect the Option directly.
|
|
1508
|
+
opt.__group = GROUP;
|
|
1509
|
+
const ret = originalAddOption(opt);
|
|
1510
|
+
return ret;
|
|
1511
|
+
};
|
|
1512
|
+
program.option = function patchedOption(...args) {
|
|
1513
|
+
const ret = originalOption(...args);
|
|
1514
|
+
tagLatest(this, GROUP);
|
|
1515
|
+
return ret;
|
|
1516
|
+
};
|
|
1517
|
+
const { defaultEnv, dotenvToken, dynamicPath, env, excludeDynamic, excludeEnv, excludeGlobal, excludePrivate, excludePublic, loadProcess, log, outputPath, paths, pathsDelimiter, pathsDelimiterPattern, privateToken, scripts, shell, varsAssignor, varsAssignorPattern, varsDelimiter, varsDelimiterPattern, } = defaults ?? {};
|
|
1518
|
+
const va = typeof defaults?.varsAssignor === 'string' ? defaults.varsAssignor : '=';
|
|
1519
|
+
const vd = typeof defaults?.varsDelimiter === 'string' ? defaults.varsDelimiter : ' ';
|
|
1520
|
+
// Build initial chain.
|
|
1521
|
+
let p = program
|
|
1522
|
+
.enablePositionalOptions()
|
|
1523
|
+
.passThroughOptions()
|
|
1524
|
+
.option('-e, --env <string>', `target environment (dotenv-expanded)`, dotenvExpandFromProcessEnv, env);
|
|
1525
|
+
p = p.option('-v, --vars <string>', `extra variables expressed as delimited key-value pairs (dotenv-expanded): ${[
|
|
1526
|
+
['KEY1', 'VAL1'],
|
|
1527
|
+
['KEY2', 'VAL2'],
|
|
1528
|
+
]
|
|
1529
|
+
.map((v) => v.join(va))
|
|
1530
|
+
.join(vd)}`, dotenvExpandFromProcessEnv);
|
|
1531
|
+
// Optional legacy root command flag (kept for generated CLI compatibility).
|
|
1532
|
+
// Default is OFF; the generator opts in explicitly.
|
|
1533
|
+
if (opts?.includeCommandOption === true) {
|
|
1534
|
+
p = p.option('-c, --command <string>', 'command executed according to the --shell option, conflicts with cmd subcommand (dotenv-expanded)', dotenvExpandFromProcessEnv);
|
|
1535
|
+
}
|
|
1536
|
+
p = p
|
|
1537
|
+
.option('-o, --output-path <string>', 'consolidated output file (dotenv-expanded)', dotenvExpandFromProcessEnv, outputPath)
|
|
1538
|
+
.addOption(new Option('-s, --shell [string]', (() => {
|
|
1539
|
+
let defaultLabel = '';
|
|
1540
|
+
if (shell !== undefined) {
|
|
1541
|
+
if (typeof shell === 'boolean') {
|
|
1542
|
+
defaultLabel = ' (default OS shell)';
|
|
1543
|
+
}
|
|
1544
|
+
else if (typeof shell === 'string') {
|
|
1545
|
+
// Safe string interpolation
|
|
1546
|
+
defaultLabel = ` (default ${shell})`;
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
return `command execution shell, no argument for default OS shell or provide shell string${defaultLabel}`;
|
|
1550
|
+
})()).conflicts('shellOff'))
|
|
1551
|
+
.addOption(new Option('-S, --shell-off', `command execution shell OFF${!shell ? ' (default)' : ''}`).conflicts('shell'))
|
|
1552
|
+
.addOption(new Option('-p, --load-process', `load variables to process.env ON${loadProcess ? ' (default)' : ''}`).conflicts('loadProcessOff'))
|
|
1553
|
+
.addOption(new Option('-P, --load-process-off', `load variables to process.env OFF${!loadProcess ? ' (default)' : ''}`).conflicts('loadProcess'))
|
|
1554
|
+
.addOption(new Option('-a, --exclude-all', `exclude all dotenv variables from loading ON${excludeDynamic &&
|
|
1555
|
+
((excludeEnv && excludeGlobal) || (excludePrivate && excludePublic))
|
|
1556
|
+
? ' (default)'
|
|
1557
|
+
: ''}`).conflicts('excludeAllOff'))
|
|
1558
|
+
.addOption(new Option('-A, --exclude-all-off', `exclude all dotenv variables from loading OFF (default)`).conflicts('excludeAll'))
|
|
1559
|
+
.addOption(new Option('-z, --exclude-dynamic', `exclude dynamic dotenv variables from loading ON${excludeDynamic ? ' (default)' : ''}`).conflicts('excludeDynamicOff'))
|
|
1560
|
+
.addOption(new Option('-Z, --exclude-dynamic-off', `exclude dynamic dotenv variables from loading OFF${!excludeDynamic ? ' (default)' : ''}`).conflicts('excludeDynamic'))
|
|
1561
|
+
.addOption(new Option('-n, --exclude-env', `exclude environment-specific dotenv variables from loading${excludeEnv ? ' (default)' : ''}`).conflicts('excludeEnvOff'))
|
|
1562
|
+
.addOption(new Option('-N, --exclude-env-off', `exclude environment-specific dotenv variables from loading OFF${!excludeEnv ? ' (default)' : ''}`).conflicts('excludeEnv'))
|
|
1563
|
+
.addOption(new Option('-g, --exclude-global', `exclude global dotenv variables from loading ON${excludeGlobal ? ' (default)' : ''}`).conflicts('excludeGlobalOff'))
|
|
1564
|
+
.addOption(new Option('-G, --exclude-global-off', `exclude global dotenv variables from loading OFF${!excludeGlobal ? ' (default)' : ''}`).conflicts('excludeGlobal'))
|
|
1565
|
+
.addOption(new Option('-r, --exclude-private', `exclude private dotenv variables from loading ON${excludePrivate ? ' (default)' : ''}`).conflicts('excludePrivateOff'))
|
|
1566
|
+
.addOption(new Option('-R, --exclude-private-off', `exclude private dotenv variables from loading OFF${!excludePrivate ? ' (default)' : ''}`).conflicts('excludePrivate'))
|
|
1567
|
+
.addOption(new Option('-u, --exclude-public', `exclude public dotenv variables from loading ON${excludePublic ? ' (default)' : ''}`).conflicts('excludePublicOff'))
|
|
1568
|
+
.addOption(new Option('-U, --exclude-public-off', `exclude public dotenv variables from loading OFF${!excludePublic ? ' (default)' : ''}`).conflicts('excludePublic'))
|
|
1569
|
+
.addOption(new Option('-l, --log', `console log loaded variables ON${log ? ' (default)' : ''}`).conflicts('logOff'))
|
|
1570
|
+
.addOption(new Option('-L, --log-off', `console log loaded variables OFF${!log ? ' (default)' : ''}`).conflicts('log'))
|
|
1571
|
+
.option('--capture', 'capture child process stdio for commands (tests/CI)')
|
|
1572
|
+
.option('--redact', 'mask secret-like values in logs/trace (presentation-only)')
|
|
1573
|
+
.option('--default-env <string>', 'default target environment', dotenvExpandFromProcessEnv, defaultEnv)
|
|
1574
|
+
.option('--dotenv-token <string>', 'dotenv-expanded token indicating a dotenv file', dotenvExpandFromProcessEnv, dotenvToken)
|
|
1575
|
+
.option('--dynamic-path <string>', 'dynamic variables path (.js or .ts; .ts is auto-compiled when esbuild is available, otherwise precompile)', dotenvExpandFromProcessEnv, dynamicPath)
|
|
1576
|
+
.option('--paths <string>', 'dotenv-expanded delimited list of paths to dotenv directory', dotenvExpandFromProcessEnv, paths)
|
|
1577
|
+
.option('--paths-delimiter <string>', 'paths delimiter string', pathsDelimiter)
|
|
1578
|
+
.option('--paths-delimiter-pattern <string>', 'paths delimiter regex pattern', pathsDelimiterPattern)
|
|
1579
|
+
.option('--private-token <string>', 'dotenv-expanded token indicating private variables', dotenvExpandFromProcessEnv, privateToken)
|
|
1580
|
+
.option('--vars-delimiter <string>', 'vars delimiter string', varsDelimiter)
|
|
1581
|
+
.option('--vars-delimiter-pattern <string>', 'vars delimiter regex pattern', varsDelimiterPattern)
|
|
1582
|
+
.option('--vars-assignor <string>', 'vars assignment operator string', varsAssignor)
|
|
1583
|
+
.option('--vars-assignor-pattern <string>', 'vars assignment operator regex pattern', varsAssignorPattern)
|
|
1584
|
+
// Hidden scripts pipe-through (stringified)
|
|
1585
|
+
.addOption(new Option('--scripts <string>')
|
|
1586
|
+
.default(JSON.stringify(scripts))
|
|
1587
|
+
.hideHelp());
|
|
1588
|
+
// Diagnostics: opt-in tracing; optional variadic keys after the flag.
|
|
1589
|
+
p = p.option('--trace [keys...]', 'emit diagnostics for child env composition (optional keys)');
|
|
1590
|
+
// Validation: strict mode fails on env validation issues (warn by default).
|
|
1591
|
+
p = p.option('--strict', 'fail on env validation errors (schema/requiredKeys)');
|
|
1592
|
+
// Entropy diagnostics (presentation-only)
|
|
1593
|
+
p = p
|
|
1594
|
+
.addOption(new Option('--entropy-warn', 'enable entropy warnings (default on)').conflicts('entropyWarnOff'))
|
|
1595
|
+
.addOption(new Option('--entropy-warn-off', 'disable entropy warnings').conflicts('entropyWarn'))
|
|
1596
|
+
.option('--entropy-threshold <number>', 'entropy bits/char threshold (default 3.8)')
|
|
1597
|
+
.option('--entropy-min-length <number>', 'min length to examine for entropy (default 16)')
|
|
1598
|
+
.option('--entropy-whitelist <pattern...>', 'suppress entropy warnings when key matches any regex pattern')
|
|
1599
|
+
.option('--redact-pattern <pattern...>', 'additional key-match regex patterns to trigger redaction');
|
|
1600
|
+
// Restore original methods to avoid tagging future additions outside base.
|
|
1601
|
+
program.addOption = originalAddOption;
|
|
1602
|
+
program.option = originalOption;
|
|
1603
|
+
return p;
|
|
1604
|
+
};
|
|
1605
|
+
|
|
1606
|
+
/**
|
|
1607
|
+
* Resolve a tri-state optional boolean flag under exactOptionalPropertyTypes.
|
|
1608
|
+
* - If the user explicitly enabled the flag, return true.
|
|
1609
|
+
* - If the user explicitly disabled (the "...-off" variant), return undefined (unset).
|
|
1610
|
+
* - Otherwise, adopt the default (true → set; false/undefined → unset).
|
|
1611
|
+
*
|
|
1612
|
+
* @param exclude - The "on" flag value as parsed by Commander.
|
|
1613
|
+
* @param excludeOff - The "off" toggle (present when specified) as parsed by Commander.
|
|
1614
|
+
* @param defaultValue - The generator default to adopt when no explicit toggle is present.
|
|
1615
|
+
* @returns boolean | undefined — use `undefined` to indicate "unset" (do not emit).
|
|
1616
|
+
*
|
|
1617
|
+
* @example
|
|
1618
|
+
* ```ts
|
|
1619
|
+
* resolveExclusion(undefined, undefined, true); // => true
|
|
1620
|
+
* ```
|
|
1621
|
+
*/
|
|
1622
|
+
const resolveExclusion = (exclude, excludeOff, defaultValue) => exclude ? true : excludeOff ? undefined : defaultValue ? true : undefined;
|
|
1623
|
+
/**
|
|
1624
|
+
* Resolve an optional flag with "--exclude-all" overrides.
|
|
1625
|
+
* If excludeAll is set and the individual "...-off" is not, force true.
|
|
1626
|
+
* If excludeAllOff is set and the individual flag is not explicitly set, unset.
|
|
1627
|
+
* Otherwise, adopt the default (true → set; false/undefined → unset).
|
|
1628
|
+
*
|
|
1629
|
+
* @param exclude - Individual include/exclude flag.
|
|
1630
|
+
* @param excludeOff - Individual "...-off" flag.
|
|
1631
|
+
* @param defaultValue - Default for the individual flag.
|
|
1632
|
+
* @param excludeAll - Global "exclude-all" flag.
|
|
1633
|
+
* @param excludeAllOff - Global "exclude-all-off" flag.
|
|
1634
|
+
*
|
|
1635
|
+
* @example
|
|
1636
|
+
* resolveExclusionAll(undefined, undefined, false, true, undefined) =\> true
|
|
1637
|
+
*/
|
|
1638
|
+
const resolveExclusionAll = (exclude, excludeOff, defaultValue, excludeAll, excludeAllOff) =>
|
|
1639
|
+
// Order of precedence:
|
|
1640
|
+
// 1) Individual explicit "on" wins outright.
|
|
1641
|
+
// 2) Individual explicit "off" wins over any global.
|
|
1642
|
+
// 3) Global exclude-all forces true when not explicitly turned off.
|
|
1643
|
+
// 4) Global exclude-all-off unsets when the individual wasn't explicitly enabled.
|
|
1644
|
+
// 5) Fall back to the default (true => set; false/undefined => unset).
|
|
1645
|
+
(() => {
|
|
1646
|
+
// Individual "on"
|
|
1647
|
+
if (exclude === true)
|
|
1648
|
+
return true;
|
|
1649
|
+
// Individual "off"
|
|
1650
|
+
if (excludeOff === true)
|
|
1651
|
+
return undefined;
|
|
1652
|
+
// Global "exclude-all" ON (unless explicitly turned off)
|
|
1653
|
+
if (excludeAll === true)
|
|
1654
|
+
return true;
|
|
1655
|
+
// Global "exclude-all-off" (unless explicitly enabled)
|
|
1656
|
+
if (excludeAllOff === true)
|
|
1657
|
+
return undefined;
|
|
1658
|
+
// Default
|
|
1659
|
+
return defaultValue ? true : undefined;
|
|
1660
|
+
})();
|
|
1661
|
+
/**
|
|
1662
|
+
* exactOptionalPropertyTypes-safe setter for optional boolean flags:
|
|
1663
|
+
* delete when undefined; assign when defined — without requiring an index signature on T.
|
|
1664
|
+
*
|
|
1665
|
+
* @typeParam T - Target object type.
|
|
1666
|
+
* @param obj - The object to write to.
|
|
1667
|
+
* @param key - The optional boolean property key of {@link T}.
|
|
1668
|
+
* @param value - The value to set or `undefined` to unset.
|
|
1669
|
+
*
|
|
1670
|
+
* @remarks
|
|
1671
|
+
* Writes through a local `Record<string, unknown>` view to avoid requiring an index signature on {@link T}.
|
|
1672
|
+
*/
|
|
1673
|
+
const setOptionalFlag = (obj, key, value) => {
|
|
1674
|
+
const target = obj;
|
|
1675
|
+
const k = key;
|
|
1676
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
1677
|
+
if (value === undefined)
|
|
1678
|
+
delete target[k];
|
|
1679
|
+
else
|
|
1680
|
+
target[k] = value;
|
|
1681
|
+
};
|
|
1682
|
+
|
|
1683
|
+
/**
|
|
1684
|
+
* Merge and normalize raw Commander options (current + parent + defaults)
|
|
1685
|
+
* into a GetDotenvCliOptions-like object. Types are intentionally wide to
|
|
1686
|
+
* avoid cross-layer coupling; callers may cast as needed.
|
|
1687
|
+
*/
|
|
1688
|
+
const resolveCliOptions = (rawCliOptions, defaults, parentJson) => {
|
|
1689
|
+
const parent = typeof parentJson === 'string' && parentJson.length > 0
|
|
1690
|
+
? JSON.parse(parentJson)
|
|
1691
|
+
: undefined;
|
|
1692
|
+
const { command, debugOff, excludeAll, excludeAllOff, excludeDynamicOff, excludeEnvOff, excludeGlobalOff, excludePrivateOff, excludePublicOff, loadProcessOff, logOff, entropyWarn, entropyWarnOff, scripts, shellOff, ...rest } = rawCliOptions;
|
|
1693
|
+
const current = { ...rest };
|
|
1694
|
+
if (typeof scripts === 'string') {
|
|
1695
|
+
try {
|
|
1696
|
+
current.scripts = JSON.parse(scripts);
|
|
1697
|
+
}
|
|
1698
|
+
catch {
|
|
1699
|
+
// ignore parse errors; leave scripts undefined
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
const merged = defaultsDeep({}, defaults, parent ?? {}, current);
|
|
1703
|
+
const d = defaults;
|
|
1704
|
+
setOptionalFlag(merged, 'debug', resolveExclusion(merged.debug, debugOff, d.debug));
|
|
1705
|
+
setOptionalFlag(merged, 'excludeDynamic', resolveExclusionAll(merged.excludeDynamic, excludeDynamicOff, d.excludeDynamic, excludeAll, excludeAllOff));
|
|
1706
|
+
setOptionalFlag(merged, 'excludeEnv', resolveExclusionAll(merged.excludeEnv, excludeEnvOff, d.excludeEnv, excludeAll, excludeAllOff));
|
|
1707
|
+
setOptionalFlag(merged, 'excludeGlobal', resolveExclusionAll(merged.excludeGlobal, excludeGlobalOff, d.excludeGlobal, excludeAll, excludeAllOff));
|
|
1708
|
+
setOptionalFlag(merged, 'excludePrivate', resolveExclusionAll(merged.excludePrivate, excludePrivateOff, d.excludePrivate, excludeAll, excludeAllOff));
|
|
1709
|
+
setOptionalFlag(merged, 'excludePublic', resolveExclusionAll(merged.excludePublic, excludePublicOff, d.excludePublic, excludeAll, excludeAllOff));
|
|
1710
|
+
setOptionalFlag(merged, 'log', resolveExclusion(merged.log, logOff, d.log));
|
|
1711
|
+
setOptionalFlag(merged, 'loadProcess', resolveExclusion(merged.loadProcess, loadProcessOff, d.loadProcess));
|
|
1712
|
+
// warnEntropy (tri-state)
|
|
1713
|
+
setOptionalFlag(merged, 'warnEntropy', resolveExclusion(merged.warnEntropy, entropyWarnOff, d.warnEntropy));
|
|
1714
|
+
// Normalize shell for predictability: explicit default shell per OS.
|
|
1715
|
+
const defaultShell = process.platform === 'win32' ? 'powershell.exe' : '/bin/bash';
|
|
1716
|
+
let resolvedShell = merged.shell;
|
|
1717
|
+
if (shellOff)
|
|
1718
|
+
resolvedShell = false;
|
|
1719
|
+
else if (resolvedShell === true || resolvedShell === undefined) {
|
|
1720
|
+
resolvedShell = defaultShell;
|
|
1721
|
+
}
|
|
1722
|
+
else if (typeof resolvedShell !== 'string' &&
|
|
1723
|
+
typeof defaults.shell === 'string') {
|
|
1724
|
+
resolvedShell = defaults.shell;
|
|
1725
|
+
}
|
|
1726
|
+
merged.shell = resolvedShell;
|
|
1727
|
+
const cmd = typeof command === 'string' ? command : undefined;
|
|
1728
|
+
return cmd !== undefined ? { merged, command: cmd } : { merged };
|
|
1729
|
+
};
|
|
1730
|
+
|
|
1731
|
+
GetDotenvCli.prototype.attachRootOptions = function (defaults, opts) {
|
|
1732
|
+
const d = (defaults ?? baseRootOptionDefaults);
|
|
1733
|
+
attachRootOptions(this, d, opts);
|
|
1734
|
+
return this;
|
|
1735
|
+
};
|
|
1736
|
+
GetDotenvCli.prototype.passOptions = function (defaults) {
|
|
1737
|
+
const d = (defaults ?? baseRootOptionDefaults);
|
|
1738
|
+
this.hook('preSubcommand', async (thisCommand) => {
|
|
1739
|
+
const raw = thisCommand.opts();
|
|
1740
|
+
const { merged } = resolveCliOptions(raw, d, process.env.getDotenvCliOptions);
|
|
1741
|
+
// Persist merged options for nested invocations (batch exec).
|
|
1742
|
+
thisCommand.getDotenvCliOptions =
|
|
1743
|
+
merged;
|
|
1744
|
+
// Also store on the host for downstream ergonomic accessors.
|
|
1745
|
+
this._setOptionsBag(merged);
|
|
1746
|
+
// Build service options and compute context (always-on config loader path).
|
|
1747
|
+
const serviceOptions = getDotenvCliOptions2Options(merged);
|
|
1748
|
+
await this.resolveAndLoad(serviceOptions);
|
|
1749
|
+
// Global validation: once after Phase C using config sources.
|
|
1750
|
+
try {
|
|
1751
|
+
const ctx = this.getCtx();
|
|
1752
|
+
const dotenv = (ctx?.dotenv ?? {});
|
|
1753
|
+
const sources = await resolveGetDotenvConfigSources(import.meta.url);
|
|
1754
|
+
const issues = validateEnvAgainstSources(dotenv, sources);
|
|
1755
|
+
if (Array.isArray(issues) && issues.length > 0) {
|
|
1756
|
+
const logger = (merged.logger ??
|
|
1757
|
+
console);
|
|
1758
|
+
const emit = logger.error ?? logger.log;
|
|
1759
|
+
issues.forEach((m) => {
|
|
1760
|
+
emit(m);
|
|
1761
|
+
});
|
|
1762
|
+
if (merged.strict) {
|
|
1763
|
+
// Deterministic failure under strict mode
|
|
1764
|
+
process.exit(1);
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
catch {
|
|
1769
|
+
// Be tolerant: validation errors reported above; unexpected failures here
|
|
1770
|
+
// should not crash non-strict flows.
|
|
1771
|
+
}
|
|
1772
|
+
});
|
|
1773
|
+
// Also handle root-level flows (no subcommand) so option-aliases can run
|
|
1774
|
+
// with the same merged options and context without duplicating logic.
|
|
1775
|
+
this.hook('preAction', async (thisCommand) => {
|
|
1776
|
+
const raw = thisCommand.opts();
|
|
1777
|
+
const { merged } = resolveCliOptions(raw, d, process.env.getDotenvCliOptions);
|
|
1778
|
+
thisCommand.getDotenvCliOptions =
|
|
1779
|
+
merged;
|
|
1780
|
+
this._setOptionsBag(merged);
|
|
1781
|
+
// Avoid duplicate heavy work if a context is already present.
|
|
1782
|
+
if (!this.getCtx()) {
|
|
1783
|
+
const serviceOptions = getDotenvCliOptions2Options(merged);
|
|
1784
|
+
await this.resolveAndLoad(serviceOptions);
|
|
1785
|
+
try {
|
|
1786
|
+
const ctx = this.getCtx();
|
|
1787
|
+
const dotenv = (ctx?.dotenv ?? {});
|
|
1788
|
+
const sources = await resolveGetDotenvConfigSources(import.meta.url);
|
|
1789
|
+
const issues = validateEnvAgainstSources(dotenv, sources);
|
|
1790
|
+
if (Array.isArray(issues) && issues.length > 0) {
|
|
1791
|
+
const logger = (merged
|
|
1792
|
+
.logger ?? console);
|
|
1793
|
+
const emit = logger.error ?? logger.log;
|
|
1794
|
+
issues.forEach((m) => {
|
|
1795
|
+
emit(m);
|
|
1796
|
+
});
|
|
1797
|
+
if (merged.strict) {
|
|
1798
|
+
process.exit(1);
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
catch {
|
|
1803
|
+
// Tolerate validation side-effects in non-strict mode
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
});
|
|
1807
|
+
return this;
|
|
1808
|
+
};
|
|
1809
|
+
|
|
1810
|
+
/** src/cliHost/definePlugin.ts
|
|
1811
|
+
* Plugin contracts for the GetDotenv CLI host.
|
|
1812
|
+
*
|
|
1813
|
+
* This module exposes a structural public interface for the host that plugins
|
|
1814
|
+
* should use (GetDotenvCliPublic). Using a structural type at the seam avoids
|
|
1815
|
+
* nominal class identity issues (private fields) in downstream consumers.
|
|
1816
|
+
*/
|
|
1817
|
+
/**
|
|
1818
|
+
* Define a GetDotenv CLI plugin with compositional helpers.
|
|
1819
|
+
*
|
|
1820
|
+
* @example
|
|
1821
|
+
* const parent = definePlugin(\{ id: 'p', setup(cli) \{ /* ... *\/ \} \})
|
|
1822
|
+
* .use(childA)
|
|
1823
|
+
* .use(childB);
|
|
1824
|
+
*/
|
|
1825
|
+
const definePlugin = (spec) => {
|
|
1826
|
+
const { children = [], ...rest } = spec;
|
|
1827
|
+
const plugin = {
|
|
1828
|
+
...rest,
|
|
1829
|
+
children: [...children],
|
|
1830
|
+
use(child) {
|
|
1831
|
+
this.children.push(child);
|
|
1832
|
+
return this;
|
|
1833
|
+
},
|
|
1834
|
+
};
|
|
1835
|
+
return plugin;
|
|
1836
|
+
};
|
|
1837
|
+
|
|
1838
|
+
// Ensure attachRootOptions() and passOptions() are available whenever the
|
|
1839
|
+
// /cliHost subpath is imported (unconditional for downstream hosts).
|
|
1439
1840
|
/**
|
|
1440
1841
|
* Helper to retrieve the merged root options bag from any action handler
|
|
1441
1842
|
* that only has access to thisCommand. Avoids structural casts.
|
package/dist/getdotenv.cli.mjs
CHANGED
|
@@ -2066,6 +2066,13 @@ const buildSpawnEnv = (base, overlay) => {
|
|
|
2066
2066
|
return out;
|
|
2067
2067
|
};
|
|
2068
2068
|
|
|
2069
|
+
/** src/cliHost/definePlugin.ts
|
|
2070
|
+
* Plugin contracts for the GetDotenv CLI host.
|
|
2071
|
+
*
|
|
2072
|
+
* This module exposes a structural public interface for the host that plugins
|
|
2073
|
+
* should use (GetDotenvCliPublic). Using a structural type at the seam avoids
|
|
2074
|
+
* nominal class identity issues (private fields) in downstream consumers.
|
|
2075
|
+
*/
|
|
2069
2076
|
/**
|
|
2070
2077
|
* Define a GetDotenv CLI plugin with compositional helpers.
|
|
2071
2078
|
*
|
|
@@ -3202,11 +3209,8 @@ const cmdPlugin = (options = {}) => definePlugin({
|
|
|
3202
3209
|
const { logger: _omit, ...envBag } = merged;
|
|
3203
3210
|
const capture = process.env.GETDOTENV_STDIO === 'pipe' ||
|
|
3204
3211
|
Boolean(merged.capture);
|
|
3205
|
-
// Prefer explicit env injection
|
|
3206
|
-
|
|
3207
|
-
// exclusions (e.g., --exclude-private) are in effect.
|
|
3208
|
-
const host = cli;
|
|
3209
|
-
const ctx = host.getCtx();
|
|
3212
|
+
// Prefer explicit env injection using the resolved dotenv map.
|
|
3213
|
+
const ctx = cli.getCtx();
|
|
3210
3214
|
const dotenv = (ctx?.dotenv ?? {});
|
|
3211
3215
|
// Diagnostics: --trace [keys...] (space-delimited keys if provided; all keys when true)
|
|
3212
3216
|
const traceOpt = merged.trace;
|
|
@@ -3748,7 +3752,7 @@ new Command()
|
|
|
3748
3752
|
|
|
3749
3753
|
new Command()
|
|
3750
3754
|
.name('cmd')
|
|
3751
|
-
.description('
|
|
3755
|
+
.description('Execute command according to the --shell option, conflicts with --command option (default subcommand)')
|
|
3752
3756
|
.configureHelp({ showGlobalOptions: true })
|
|
3753
3757
|
.enablePositionalOptions()
|
|
3754
3758
|
.passThroughOptions()
|
package/dist/index.cjs
CHANGED
|
@@ -2075,6 +2075,13 @@ const buildSpawnEnv = (base, overlay) => {
|
|
|
2075
2075
|
return out;
|
|
2076
2076
|
};
|
|
2077
2077
|
|
|
2078
|
+
/** src/cliHost/definePlugin.ts
|
|
2079
|
+
* Plugin contracts for the GetDotenv CLI host.
|
|
2080
|
+
*
|
|
2081
|
+
* This module exposes a structural public interface for the host that plugins
|
|
2082
|
+
* should use (GetDotenvCliPublic). Using a structural type at the seam avoids
|
|
2083
|
+
* nominal class identity issues (private fields) in downstream consumers.
|
|
2084
|
+
*/
|
|
2078
2085
|
/**
|
|
2079
2086
|
* Define a GetDotenv CLI plugin with compositional helpers.
|
|
2080
2087
|
*
|
|
@@ -3211,11 +3218,8 @@ const cmdPlugin = (options = {}) => definePlugin({
|
|
|
3211
3218
|
const { logger: _omit, ...envBag } = merged;
|
|
3212
3219
|
const capture = process.env.GETDOTENV_STDIO === 'pipe' ||
|
|
3213
3220
|
Boolean(merged.capture);
|
|
3214
|
-
// Prefer explicit env injection
|
|
3215
|
-
|
|
3216
|
-
// exclusions (e.g., --exclude-private) are in effect.
|
|
3217
|
-
const host = cli;
|
|
3218
|
-
const ctx = host.getCtx();
|
|
3221
|
+
// Prefer explicit env injection using the resolved dotenv map.
|
|
3222
|
+
const ctx = cli.getCtx();
|
|
3219
3223
|
const dotenv = (ctx?.dotenv ?? {});
|
|
3220
3224
|
// Diagnostics: --trace [keys...] (space-delimited keys if provided; all keys when true)
|
|
3221
3225
|
const traceOpt = merged.trace;
|
|
@@ -3757,7 +3761,7 @@ const batchCommand = new commander.Command()
|
|
|
3757
3761
|
|
|
3758
3762
|
const cmdCommand = new commander.Command()
|
|
3759
3763
|
.name('cmd')
|
|
3760
|
-
.description('
|
|
3764
|
+
.description('Execute command according to the --shell option, conflicts with --command option (default subcommand)')
|
|
3761
3765
|
.configureHelp({ showGlobalOptions: true })
|
|
3762
3766
|
.enablePositionalOptions()
|
|
3763
3767
|
.passThroughOptions()
|
|
@@ -4108,6 +4112,7 @@ function createCli(opts = {}) {
|
|
|
4108
4112
|
};
|
|
4109
4113
|
}
|
|
4110
4114
|
|
|
4115
|
+
exports.buildSpawnEnv = buildSpawnEnv;
|
|
4111
4116
|
exports.createCli = createCli;
|
|
4112
4117
|
exports.defineDynamic = defineDynamic;
|
|
4113
4118
|
exports.dotenvExpand = dotenvExpand;
|
package/dist/index.d.cts
CHANGED
|
@@ -70,6 +70,24 @@ declare module '../cliHost/GetDotenvCli' {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/** src/cliCore/spawnEnv.ts
|
|
74
|
+
* Build a sanitized environment bag for child processes.
|
|
75
|
+
*
|
|
76
|
+
* Requirements addressed:
|
|
77
|
+
* - Provide a single helper (buildSpawnEnv) to normalize/dedupe child env.
|
|
78
|
+
* - Drop undefined values (exactOptional semantics).
|
|
79
|
+
* - On Windows, dedupe keys case-insensitively and prefer the last value,
|
|
80
|
+
* preserving the latest key's casing. Ensure HOME fallback from USERPROFILE.
|
|
81
|
+
* Normalize TMP/TEMP consistency when either is present.
|
|
82
|
+
* - On POSIX, keep keys as-is; when a temp dir key is present (TMPDIR/TMP/TEMP),
|
|
83
|
+
* ensure TMPDIR exists for downstream consumers that expect it.
|
|
84
|
+
*
|
|
85
|
+
* Adapter responsibility: pure mapping; no business logic.
|
|
86
|
+
*/
|
|
87
|
+
type SpawnEnv = Readonly<Partial<Record<string, string>>>;
|
|
88
|
+
/** Build a sanitized env for child processes from base + overlay. */
|
|
89
|
+
declare const buildSpawnEnv: (base?: NodeJS.ProcessEnv, overlay?: Record<string, string | undefined>) => SpawnEnv;
|
|
90
|
+
|
|
73
91
|
type RootOptionsShapeCompat = Omit<RootOptionsShape, 'vars' | 'paths'> & {
|
|
74
92
|
vars?: string | Record<string, string | undefined>;
|
|
75
93
|
paths?: string | string[];
|
|
@@ -456,5 +474,5 @@ declare function createCli(opts?: CreateCliOptions): {
|
|
|
456
474
|
run: (argv: string[]) => Promise<void>;
|
|
457
475
|
};
|
|
458
476
|
|
|
459
|
-
export { createCli, defineDynamic, dotenvExpand, dotenvExpandAll, dotenvExpandFromProcessEnv, generateGetDotenvCli, getDotenv, getDotenvCliOptions2Options, interpolateDeep };
|
|
477
|
+
export { buildSpawnEnv, createCli, defineDynamic, dotenvExpand, dotenvExpandAll, dotenvExpandFromProcessEnv, generateGetDotenvCli, getDotenv, getDotenvCliOptions2Options, interpolateDeep };
|
|
460
478
|
export type { CreateCliOptions, GetDotenvDynamic, GetDotenvOptions, ProcessEnv };
|
package/dist/index.d.mts
CHANGED
|
@@ -70,6 +70,24 @@ declare module '../cliHost/GetDotenvCli' {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/** src/cliCore/spawnEnv.ts
|
|
74
|
+
* Build a sanitized environment bag for child processes.
|
|
75
|
+
*
|
|
76
|
+
* Requirements addressed:
|
|
77
|
+
* - Provide a single helper (buildSpawnEnv) to normalize/dedupe child env.
|
|
78
|
+
* - Drop undefined values (exactOptional semantics).
|
|
79
|
+
* - On Windows, dedupe keys case-insensitively and prefer the last value,
|
|
80
|
+
* preserving the latest key's casing. Ensure HOME fallback from USERPROFILE.
|
|
81
|
+
* Normalize TMP/TEMP consistency when either is present.
|
|
82
|
+
* - On POSIX, keep keys as-is; when a temp dir key is present (TMPDIR/TMP/TEMP),
|
|
83
|
+
* ensure TMPDIR exists for downstream consumers that expect it.
|
|
84
|
+
*
|
|
85
|
+
* Adapter responsibility: pure mapping; no business logic.
|
|
86
|
+
*/
|
|
87
|
+
type SpawnEnv = Readonly<Partial<Record<string, string>>>;
|
|
88
|
+
/** Build a sanitized env for child processes from base + overlay. */
|
|
89
|
+
declare const buildSpawnEnv: (base?: NodeJS.ProcessEnv, overlay?: Record<string, string | undefined>) => SpawnEnv;
|
|
90
|
+
|
|
73
91
|
type RootOptionsShapeCompat = Omit<RootOptionsShape, 'vars' | 'paths'> & {
|
|
74
92
|
vars?: string | Record<string, string | undefined>;
|
|
75
93
|
paths?: string | string[];
|
|
@@ -456,5 +474,5 @@ declare function createCli(opts?: CreateCliOptions): {
|
|
|
456
474
|
run: (argv: string[]) => Promise<void>;
|
|
457
475
|
};
|
|
458
476
|
|
|
459
|
-
export { createCli, defineDynamic, dotenvExpand, dotenvExpandAll, dotenvExpandFromProcessEnv, generateGetDotenvCli, getDotenv, getDotenvCliOptions2Options, interpolateDeep };
|
|
477
|
+
export { buildSpawnEnv, createCli, defineDynamic, dotenvExpand, dotenvExpandAll, dotenvExpandFromProcessEnv, generateGetDotenvCli, getDotenv, getDotenvCliOptions2Options, interpolateDeep };
|
|
460
478
|
export type { CreateCliOptions, GetDotenvDynamic, GetDotenvOptions, ProcessEnv };
|
package/dist/index.d.ts
CHANGED
|
@@ -70,6 +70,24 @@ declare module '../cliHost/GetDotenvCli' {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/** src/cliCore/spawnEnv.ts
|
|
74
|
+
* Build a sanitized environment bag for child processes.
|
|
75
|
+
*
|
|
76
|
+
* Requirements addressed:
|
|
77
|
+
* - Provide a single helper (buildSpawnEnv) to normalize/dedupe child env.
|
|
78
|
+
* - Drop undefined values (exactOptional semantics).
|
|
79
|
+
* - On Windows, dedupe keys case-insensitively and prefer the last value,
|
|
80
|
+
* preserving the latest key's casing. Ensure HOME fallback from USERPROFILE.
|
|
81
|
+
* Normalize TMP/TEMP consistency when either is present.
|
|
82
|
+
* - On POSIX, keep keys as-is; when a temp dir key is present (TMPDIR/TMP/TEMP),
|
|
83
|
+
* ensure TMPDIR exists for downstream consumers that expect it.
|
|
84
|
+
*
|
|
85
|
+
* Adapter responsibility: pure mapping; no business logic.
|
|
86
|
+
*/
|
|
87
|
+
type SpawnEnv = Readonly<Partial<Record<string, string>>>;
|
|
88
|
+
/** Build a sanitized env for child processes from base + overlay. */
|
|
89
|
+
declare const buildSpawnEnv: (base?: NodeJS.ProcessEnv, overlay?: Record<string, string | undefined>) => SpawnEnv;
|
|
90
|
+
|
|
73
91
|
type RootOptionsShapeCompat = Omit<RootOptionsShape, 'vars' | 'paths'> & {
|
|
74
92
|
vars?: string | Record<string, string | undefined>;
|
|
75
93
|
paths?: string | string[];
|
|
@@ -456,5 +474,5 @@ declare function createCli(opts?: CreateCliOptions): {
|
|
|
456
474
|
run: (argv: string[]) => Promise<void>;
|
|
457
475
|
};
|
|
458
476
|
|
|
459
|
-
export { createCli, defineDynamic, dotenvExpand, dotenvExpandAll, dotenvExpandFromProcessEnv, generateGetDotenvCli, getDotenv, getDotenvCliOptions2Options, interpolateDeep };
|
|
477
|
+
export { buildSpawnEnv, createCli, defineDynamic, dotenvExpand, dotenvExpandAll, dotenvExpandFromProcessEnv, generateGetDotenvCli, getDotenv, getDotenvCliOptions2Options, interpolateDeep };
|
|
460
478
|
export type { CreateCliOptions, GetDotenvDynamic, GetDotenvOptions, ProcessEnv };
|