@karmaniverous/get-dotenv 6.2.4 → 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.
- package/README.md +1 -0
- package/dist/chunks/{AwsRestJsonProtocol-Bq1HE-Ln.mjs → AwsRestJsonProtocol-fYZqn-kW.mjs} +2 -2
- package/dist/chunks/{createCli-BY6_cfZr.mjs → createCli-BnRdfRRL.mjs} +7 -6
- package/dist/chunks/{externalDataInterceptor-CbsdEYa-.mjs → externalDataInterceptor-CILOLqbB.mjs} +2 -2
- package/dist/chunks/{getSSOTokenFromFile-hUSpR7Wf.mjs → getSSOTokenFromFile-BwMkZ_yT.mjs} +1 -1
- package/dist/chunks/{index-C_wqbTwI.mjs → index-0-nP97ri.mjs} +7 -6
- package/dist/chunks/{index-CeCufHlm.mjs → index-70Dm0f1N.mjs} +11 -10
- package/dist/chunks/{index-BPYF6K_G.mjs → index-BhVOypA1.mjs} +9 -8
- package/dist/chunks/{index-cIunyiUQ.mjs → index-CcwT4HJK.mjs} +6 -5
- package/dist/chunks/{index-BpCF5UKx.mjs → index-D8UL3w94.mjs} +6 -5
- package/dist/chunks/{index-Cu7rdyqN.mjs → index-DLNhHC15.mjs} +9 -8
- package/dist/chunks/{index-Dp1Ip6Ra.mjs → index-DWqbxY8Y.mjs} +11 -10
- package/dist/chunks/{index-c7zKtEuy.mjs → index-Du51s-Z0.mjs} +8 -7
- package/dist/chunks/{index-B5JKTBOL.mjs → index-DuSz0ul6.mjs} +8 -7
- package/dist/chunks/{index-DWAtHEA-.mjs → index-OeNCYa8T.mjs} +6 -5
- package/dist/chunks/{index-DyU5pKKi.mjs → index-_FP0whjC.mjs} +6 -5
- package/dist/chunks/{index-BEJFiHMX.mjs → index-o5zJ9PWL.mjs} +15 -15
- package/dist/chunks/{index-Bc3h0a95.mjs → index-r0Me7-sT.mjs} +112 -6
- package/dist/chunks/{loadSso-w1eTVg0O.mjs → loadSso-CLR1fKci.mjs} +8 -7
- package/dist/chunks/{loader-DnhPeGfq.mjs → loader-CePOf74i.mjs} +1 -0
- package/dist/chunks/{parseKnownFiles-B9cDK21V.mjs → parseKnownFiles-BQvmJ0HK.mjs} +1 -1
- package/dist/chunks/readDotenvCascade-DfFkWMjs.mjs +546 -0
- package/dist/chunks/{readMergedOptions-Nt0TR7dX.mjs → readMergedOptions-B7VdLROn.mjs} +62 -272
- package/dist/chunks/{resolveCliOptions-TFRzhB2c.mjs → resolveCliOptions-pgUXHJtj.mjs} +2 -1
- package/dist/chunks/{sdk-stream-mixin-BZoJ5jy9.mjs → sdk-stream-mixin-ecbbBR0l.mjs} +1 -1
- package/dist/chunks/{spawnEnv-CN8a7cNR.mjs → spawnEnv-CQwFu7ZJ.mjs} +2 -1
- package/dist/chunks/{types-DJ-BGABd.mjs → types-CVDR-Sjk.mjs} +1 -1
- package/dist/cli.d.ts +218 -84
- package/dist/cli.mjs +10 -10
- package/dist/cliHost.d.ts +218 -84
- package/dist/cliHost.mjs +8 -7
- package/dist/config.mjs +2 -1
- package/dist/env-overlay.d.ts +304 -2
- package/dist/env-overlay.mjs +38 -1
- package/dist/getdotenv.cli.mjs +10 -10
- package/dist/index.d.ts +703 -86
- package/dist/index.mjs +862 -13
- package/dist/plugins-aws.d.ts +153 -19
- package/dist/plugins-aws.mjs +5 -4
- package/dist/plugins-batch.d.ts +153 -19
- package/dist/plugins-batch.mjs +5 -4
- package/dist/plugins-cmd.d.ts +153 -19
- package/dist/plugins-cmd.mjs +7 -6
- package/dist/plugins-init.d.ts +153 -19
- package/dist/plugins-init.mjs +4 -4
- package/dist/plugins.d.ts +153 -19
- package/dist/plugins.mjs +9 -9
- package/package.json +1 -1
- package/dist/chunks/overlayEnv-Bs2kVayG.mjs +0 -234
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { l as loadModuleDefault } from './loadModuleDefault-Dj8B3Stt.mjs';
|
|
4
|
+
import { parse } from 'dotenv';
|
|
5
|
+
import 'crypto';
|
|
6
|
+
import 'path';
|
|
7
|
+
import 'url';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Dotenv expansion utilities.
|
|
11
|
+
*
|
|
12
|
+
* This module implements recursive expansion of environment-variable
|
|
13
|
+
* references in strings and records. It supports both whitespace and
|
|
14
|
+
* bracket syntaxes with optional defaults:
|
|
15
|
+
*
|
|
16
|
+
* - Whitespace: `$VAR[:default]`
|
|
17
|
+
* - Bracketed: `${VAR[:default]}`
|
|
18
|
+
*
|
|
19
|
+
* Escaped dollar signs (`\$`) are preserved.
|
|
20
|
+
* Unknown variables resolve to empty string unless a default is provided.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Like String.prototype.search but returns the last index.
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
const searchLast = (str, rgx) => {
|
|
27
|
+
const matches = Array.from(str.matchAll(rgx));
|
|
28
|
+
return matches.length > 0 ? (matches.slice(-1)[0]?.index ?? -1) : -1;
|
|
29
|
+
};
|
|
30
|
+
const replaceMatch = (value, match, ref) => {
|
|
31
|
+
/**
|
|
32
|
+
* @internal
|
|
33
|
+
*/
|
|
34
|
+
const group = match[0];
|
|
35
|
+
const key = match[1];
|
|
36
|
+
const defaultValue = match[2];
|
|
37
|
+
if (!key)
|
|
38
|
+
return value;
|
|
39
|
+
const replacement = value.replace(group, ref[key] ?? defaultValue ?? '');
|
|
40
|
+
return interpolate(replacement, ref);
|
|
41
|
+
};
|
|
42
|
+
const interpolate = (value = '', ref = {}) => {
|
|
43
|
+
/**
|
|
44
|
+
* @internal
|
|
45
|
+
*/
|
|
46
|
+
// if value is falsy, return it as is
|
|
47
|
+
if (!value)
|
|
48
|
+
return value;
|
|
49
|
+
// get position of last unescaped dollar sign
|
|
50
|
+
const lastUnescapedDollarSignIndex = searchLast(value, /(?!(?<=\\))\$/g);
|
|
51
|
+
// return value if none found
|
|
52
|
+
if (lastUnescapedDollarSignIndex === -1)
|
|
53
|
+
return value;
|
|
54
|
+
// evaluate the value tail
|
|
55
|
+
const tail = value.slice(lastUnescapedDollarSignIndex);
|
|
56
|
+
// find whitespace pattern: $KEY:DEFAULT
|
|
57
|
+
const whitespacePattern = /^\$([\w]+)(?::([^\s]*))?/;
|
|
58
|
+
const whitespaceMatch = whitespacePattern.exec(tail);
|
|
59
|
+
if (whitespaceMatch != null)
|
|
60
|
+
return replaceMatch(value, whitespaceMatch, ref);
|
|
61
|
+
else {
|
|
62
|
+
// find bracket pattern: ${KEY:DEFAULT}
|
|
63
|
+
const bracketPattern = /^\${([\w]+)(?::([^}]*))?}/;
|
|
64
|
+
const bracketMatch = bracketPattern.exec(tail);
|
|
65
|
+
if (bracketMatch != null)
|
|
66
|
+
return replaceMatch(value, bracketMatch, ref);
|
|
67
|
+
}
|
|
68
|
+
return value;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Recursively expands environment variables in a string. Variables may be
|
|
72
|
+
* presented with optional default as `$VAR[:default]` or `${VAR[:default]}`.
|
|
73
|
+
* Unknown variables will expand to an empty string.
|
|
74
|
+
*
|
|
75
|
+
* @param value - The string to expand.
|
|
76
|
+
* @param ref - The reference object to use for variable expansion.
|
|
77
|
+
* @returns The expanded string.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```ts
|
|
81
|
+
* process.env.FOO = 'bar';
|
|
82
|
+
* dotenvExpand('Hello $FOO'); // "Hello bar"
|
|
83
|
+
* dotenvExpand('Hello $BAZ:world'); // "Hello world"
|
|
84
|
+
* ```
|
|
85
|
+
*
|
|
86
|
+
* @remarks
|
|
87
|
+
* The expansion is recursive. If a referenced variable itself contains
|
|
88
|
+
* references, those will also be expanded until a stable value is reached.
|
|
89
|
+
* Escaped references (e.g. `\$FOO`) are preserved as literals.
|
|
90
|
+
*/
|
|
91
|
+
const dotenvExpand = (value, ref = process.env) => {
|
|
92
|
+
const result = interpolate(value, ref);
|
|
93
|
+
return result ? result.replace(/\\\$/g, '$') : undefined;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Recursively expands environment variables in the values of a JSON object.
|
|
97
|
+
* Variables may be presented with optional default as `$VAR[:default]` or
|
|
98
|
+
* `${VAR[:default]}`. Unknown variables will expand to an empty string.
|
|
99
|
+
*
|
|
100
|
+
* @param values - The values object to expand.
|
|
101
|
+
* @param options - Expansion options.
|
|
102
|
+
* @returns The value object with expanded string values.
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```ts
|
|
106
|
+
* process.env.FOO = 'bar';
|
|
107
|
+
* dotenvExpandAll({ A: '$FOO', B: 'x${FOO}y' });
|
|
108
|
+
* // => { A: "bar", B: "xbary" }
|
|
109
|
+
* ```
|
|
110
|
+
*
|
|
111
|
+
* @remarks
|
|
112
|
+
* Options:
|
|
113
|
+
* - ref: The reference object to use for expansion (defaults to process.env).
|
|
114
|
+
* - progressive: Whether to progressively add expanded values to the set of
|
|
115
|
+
* reference keys.
|
|
116
|
+
*
|
|
117
|
+
* When `progressive` is true, each expanded key becomes available for
|
|
118
|
+
* subsequent expansions in the same object (left-to-right by object key order).
|
|
119
|
+
*/
|
|
120
|
+
function dotenvExpandAll(values, options = {}) {
|
|
121
|
+
const { ref = process.env, progressive = false, } = options;
|
|
122
|
+
const out = Object.keys(values).reduce((acc, key) => {
|
|
123
|
+
acc[key] = dotenvExpand(values[key], {
|
|
124
|
+
...ref,
|
|
125
|
+
...(progressive ? acc : {}),
|
|
126
|
+
});
|
|
127
|
+
return acc;
|
|
128
|
+
}, {});
|
|
129
|
+
// Key-preserving return with a permissive index signature to allow later additions.
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Recursively expands environment variables in a string using `process.env` as
|
|
134
|
+
* the expansion reference. Variables may be presented with optional default as
|
|
135
|
+
* `$VAR[:default]` or `${VAR[:default]}`. Unknown variables will expand to an
|
|
136
|
+
* empty string.
|
|
137
|
+
*
|
|
138
|
+
* @param value - The string to expand.
|
|
139
|
+
* @returns The expanded string.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```ts
|
|
143
|
+
* process.env.FOO = 'bar';
|
|
144
|
+
* dotenvExpandFromProcessEnv('Hello $FOO'); // "Hello bar"
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
const dotenvExpandFromProcessEnv = (value) => dotenvExpand(value, process.env);
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Dotenv provenance model (descriptor-only).
|
|
151
|
+
*
|
|
152
|
+
* Requirements addressed:
|
|
153
|
+
* - Host ctx carries a dotenv provenance mapping describing per-key origin and override history.
|
|
154
|
+
* - Provenance is descriptor-only (no value payloads).
|
|
155
|
+
* - Provenance is an ordered stack per key in ascending precedence; the last entry is effective.
|
|
156
|
+
* - Explicit unsets are represented as `op: 'unset'` without requiring deletion of keys from the env map.
|
|
157
|
+
*/
|
|
158
|
+
/**
|
|
159
|
+
* Create an empty provenance map.
|
|
160
|
+
*
|
|
161
|
+
* @public
|
|
162
|
+
*/
|
|
163
|
+
function createDotenvProvenance() {
|
|
164
|
+
return {};
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Append a provenance entry for a key.
|
|
168
|
+
*
|
|
169
|
+
* @param prov - Provenance map to mutate.
|
|
170
|
+
* @param key - Variable name.
|
|
171
|
+
* @param entry - Descriptor-only provenance entry.
|
|
172
|
+
*
|
|
173
|
+
* @public
|
|
174
|
+
*/
|
|
175
|
+
function pushDotenvProvenance(prov, key, entry) {
|
|
176
|
+
(prov[key] ??= []).push(entry);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** src/env/dynamic.ts
|
|
180
|
+
* Helpers for applying and loading dynamic variables (JS/TS).
|
|
181
|
+
*
|
|
182
|
+
* Requirements addressed:
|
|
183
|
+
* - Single service to apply a dynamic map progressively.
|
|
184
|
+
* - Single service to load a JS/TS dynamic module with robust fallbacks (util/loadModuleDefault).
|
|
185
|
+
* - Unify error messaging so callers show consistent guidance.
|
|
186
|
+
*/
|
|
187
|
+
/**
|
|
188
|
+
* Apply a dynamic map to the target progressively.
|
|
189
|
+
* - Functions receive (target, env) and may return string | undefined.
|
|
190
|
+
* - Literals are assigned directly (including undefined).
|
|
191
|
+
*
|
|
192
|
+
* @param target - Mutable target environment to assign into.
|
|
193
|
+
* @param map - Dynamic map to apply (functions and/or literal values).
|
|
194
|
+
* @param env - Selected environment name (if any) passed through to dynamic functions.
|
|
195
|
+
* @returns Nothing.
|
|
196
|
+
*/
|
|
197
|
+
function applyDynamicMap(target, map, env) {
|
|
198
|
+
if (!map)
|
|
199
|
+
return;
|
|
200
|
+
for (const key of Object.keys(map)) {
|
|
201
|
+
const val = typeof map[key] === 'function'
|
|
202
|
+
? map[key](target, env)
|
|
203
|
+
: map[key];
|
|
204
|
+
Object.assign(target, { [key]: val });
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Load a default-export dynamic map from a JS/TS file (without applying it).
|
|
209
|
+
*
|
|
210
|
+
* Uses util/loadModuleDefault for robust TS handling (direct import, esbuild,
|
|
211
|
+
* typescript.transpile fallback).
|
|
212
|
+
*
|
|
213
|
+
* Error behavior:
|
|
214
|
+
* - On failure to load/compile/evaluate the module, throws a unified message:
|
|
215
|
+
* "Unable to load dynamic TypeScript file: <absPath>. Install 'esbuild'..."
|
|
216
|
+
*
|
|
217
|
+
* @param absPath - Absolute path to the dynamic module file.
|
|
218
|
+
* @param cacheDirName - Cache subdirectory under `.tsbuild/` for compiled artifacts.
|
|
219
|
+
* @returns A `Promise\<GetDotenvDynamic | undefined\>` resolving to the module default export (if present).
|
|
220
|
+
*
|
|
221
|
+
* @public
|
|
222
|
+
*/
|
|
223
|
+
async function loadDynamicModuleDefault(absPath, cacheDirName) {
|
|
224
|
+
if (!(await fs.exists(absPath)))
|
|
225
|
+
return undefined;
|
|
226
|
+
try {
|
|
227
|
+
return await loadModuleDefault(absPath, cacheDirName);
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
throw new Error(`Unable to load dynamic TypeScript file: ${absPath}. ` +
|
|
231
|
+
`Install 'esbuild' (devDependency) to enable TypeScript dynamic modules.`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Apply a dynamic map and append provenance entries per key.
|
|
236
|
+
*
|
|
237
|
+
* Requirements addressed:
|
|
238
|
+
* - Dynamic provenance entries record `dynamicSource` and optional `dynamicPath` (as provided).
|
|
239
|
+
* - Record `op: 'unset'` when the dynamic evaluation yields `undefined`.
|
|
240
|
+
*
|
|
241
|
+
* @param target - Mutable env map to assign into.
|
|
242
|
+
* @param map - Dynamic map (functions and/or literals).
|
|
243
|
+
* @param env - Selected environment name (if any).
|
|
244
|
+
* @param prov - Provenance map to append into.
|
|
245
|
+
* @param meta - Dynamic provenance metadata (source tier and optional dynamicPath).
|
|
246
|
+
*
|
|
247
|
+
* @public
|
|
248
|
+
*/
|
|
249
|
+
function applyDynamicMapWithProvenance(target, map, env, prov, meta) {
|
|
250
|
+
if (!map)
|
|
251
|
+
return;
|
|
252
|
+
for (const key of Object.keys(map)) {
|
|
253
|
+
const val = typeof map[key] === 'function'
|
|
254
|
+
? map[key](target, env)
|
|
255
|
+
: map[key];
|
|
256
|
+
Object.assign(target, { [key]: val });
|
|
257
|
+
pushDotenvProvenance(prov, key, {
|
|
258
|
+
kind: 'dynamic',
|
|
259
|
+
op: typeof val === 'string' ? 'set' : 'unset',
|
|
260
|
+
dynamicSource: meta.dynamicSource,
|
|
261
|
+
...(meta.dynamicSource === 'dynamicPath' &&
|
|
262
|
+
typeof meta.dynamicPath === 'string' &&
|
|
263
|
+
meta.dynamicPath.length > 0
|
|
264
|
+
? { dynamicPath: meta.dynamicPath }
|
|
265
|
+
: {}),
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Load a default-export dynamic map from a JS/TS file and apply it.
|
|
271
|
+
* Uses util/loadModuleDefault for robust TS handling (direct import, esbuild,
|
|
272
|
+
* typescript.transpile fallback).
|
|
273
|
+
*
|
|
274
|
+
* Error behavior:
|
|
275
|
+
* - On failure to load/compile/evaluate the module, throws a unified message:
|
|
276
|
+
* "Unable to load dynamic TypeScript file: <absPath>. Install 'esbuild'..."
|
|
277
|
+
*
|
|
278
|
+
* @param target - Mutable target environment to assign into.
|
|
279
|
+
* @param absPath - Absolute path to the dynamic module file.
|
|
280
|
+
* @param env - Selected environment name (if any).
|
|
281
|
+
* @param cacheDirName - Cache subdirectory under `.tsbuild/` for compiled artifacts.
|
|
282
|
+
* @returns A `Promise\<void\>` which resolves after the module (if present) has been applied.
|
|
283
|
+
*/
|
|
284
|
+
async function loadAndApplyDynamic(target, absPath, env, cacheDirName) {
|
|
285
|
+
const dyn = await loadDynamicModuleDefault(absPath, cacheDirName);
|
|
286
|
+
if (!dyn)
|
|
287
|
+
return;
|
|
288
|
+
applyDynamicMap(target, dyn, env);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Asynchronously read a dotenv file & parse it into an object.
|
|
293
|
+
*
|
|
294
|
+
* @param path - Path to dotenv file.
|
|
295
|
+
* @returns The parsed dotenv object.
|
|
296
|
+
*/
|
|
297
|
+
const readDotenv = async (path) => {
|
|
298
|
+
try {
|
|
299
|
+
return (await fs.exists(path)) ? parse(await fs.readFile(path)) : {};
|
|
300
|
+
}
|
|
301
|
+
catch {
|
|
302
|
+
return {};
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Overlay config-provided values onto a base env while recording provenance.
|
|
308
|
+
*
|
|
309
|
+
* Requirements addressed:
|
|
310
|
+
* - Provenance entries for `kind: 'config'` include scope/privacy/env/configScope/configPrivacy.
|
|
311
|
+
* - Provenance entries for `kind: 'vars'` represent explicit vars overlays.
|
|
312
|
+
* - Provenance is recorded as layers are applied (no post-hoc reconstruction).
|
|
313
|
+
* - Record `op: 'unset'` when a layer writes an undefined result (e.g., expansion yields empty).
|
|
314
|
+
*/
|
|
315
|
+
const applyKv = (current, kv) => {
|
|
316
|
+
if (!kv || Object.keys(kv).length === 0)
|
|
317
|
+
return { env: current, expanded: {} };
|
|
318
|
+
const expanded = dotenvExpandAll(kv, { ref: current, progressive: true });
|
|
319
|
+
return { env: { ...current, ...expanded }, expanded };
|
|
320
|
+
};
|
|
321
|
+
const opFrom = (v) => typeof v === 'string' ? 'set' : 'unset';
|
|
322
|
+
const applyConfigSlice = (current, cfg, env, prov, meta) => {
|
|
323
|
+
if (!cfg)
|
|
324
|
+
return current;
|
|
325
|
+
// kind axis: global then env (env overrides global)
|
|
326
|
+
{
|
|
327
|
+
const { env: next, expanded } = applyKv(current, cfg.vars);
|
|
328
|
+
for (const key of Object.keys(expanded)) {
|
|
329
|
+
pushDotenvProvenance(prov, key, {
|
|
330
|
+
kind: 'config',
|
|
331
|
+
op: opFrom(expanded[key]),
|
|
332
|
+
scope: 'global',
|
|
333
|
+
privacy: meta.privacy,
|
|
334
|
+
configScope: meta.configScope,
|
|
335
|
+
configPrivacy: meta.configPrivacy,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
current = next;
|
|
339
|
+
}
|
|
340
|
+
if (env && cfg.envVars) {
|
|
341
|
+
const envKv = cfg.envVars[env];
|
|
342
|
+
const { env: next, expanded } = applyKv(current, envKv);
|
|
343
|
+
for (const key of Object.keys(expanded)) {
|
|
344
|
+
pushDotenvProvenance(prov, key, {
|
|
345
|
+
kind: 'config',
|
|
346
|
+
op: opFrom(expanded[key]),
|
|
347
|
+
scope: 'env',
|
|
348
|
+
privacy: meta.privacy,
|
|
349
|
+
env,
|
|
350
|
+
configScope: meta.configScope,
|
|
351
|
+
configPrivacy: meta.configPrivacy,
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
current = next;
|
|
355
|
+
}
|
|
356
|
+
return current;
|
|
357
|
+
};
|
|
358
|
+
/**
|
|
359
|
+
* Overlay config-provided values onto a base ProcessEnv using precedence axes:
|
|
360
|
+
* - kind: env \> global
|
|
361
|
+
* - privacy: local \> public
|
|
362
|
+
* - source: project \> packaged \> base
|
|
363
|
+
*
|
|
364
|
+
* Programmatic explicit vars override all config slices.
|
|
365
|
+
*
|
|
366
|
+
* @param args - Overlay inputs.
|
|
367
|
+
* @returns The overlaid env and the updated provenance mapping.
|
|
368
|
+
*
|
|
369
|
+
* @public
|
|
370
|
+
*/
|
|
371
|
+
function overlayEnvWithProvenance(args) {
|
|
372
|
+
const { base, env, configs } = args;
|
|
373
|
+
const prov = args.provenance ?? createDotenvProvenance();
|
|
374
|
+
let current = { ...base };
|
|
375
|
+
// Source: packaged (public only)
|
|
376
|
+
current = applyConfigSlice(current, configs.packaged, env, prov, {
|
|
377
|
+
privacy: 'public',
|
|
378
|
+
configScope: 'packaged',
|
|
379
|
+
configPrivacy: 'public',
|
|
380
|
+
});
|
|
381
|
+
// Source: project (public -> local)
|
|
382
|
+
current = applyConfigSlice(current, configs.project?.public, env, prov, {
|
|
383
|
+
privacy: 'public',
|
|
384
|
+
configScope: 'project',
|
|
385
|
+
configPrivacy: 'public',
|
|
386
|
+
});
|
|
387
|
+
current = applyConfigSlice(current, configs.project?.local, env, prov, {
|
|
388
|
+
privacy: 'private',
|
|
389
|
+
configScope: 'project',
|
|
390
|
+
configPrivacy: 'local',
|
|
391
|
+
});
|
|
392
|
+
// Programmatic explicit vars (top of static tier)
|
|
393
|
+
const pv = args.programmaticVars ?? {};
|
|
394
|
+
const varsKv = {};
|
|
395
|
+
const unsetKeys = [];
|
|
396
|
+
for (const [k, v] of Object.entries(pv)) {
|
|
397
|
+
if (typeof v === 'string')
|
|
398
|
+
varsKv[k] = v;
|
|
399
|
+
else
|
|
400
|
+
unsetKeys.push(k);
|
|
401
|
+
}
|
|
402
|
+
if (Object.keys(varsKv).length > 0) {
|
|
403
|
+
const { env: next, expanded } = applyKv(current, varsKv);
|
|
404
|
+
for (const key of Object.keys(expanded)) {
|
|
405
|
+
const op = opFrom(expanded[key]);
|
|
406
|
+
pushDotenvProvenance(prov, key, { kind: 'vars', op });
|
|
407
|
+
}
|
|
408
|
+
current = next;
|
|
409
|
+
}
|
|
410
|
+
// Explicit unsets (rare in current CLI flow; supported for provenance completeness).
|
|
411
|
+
for (const key of unsetKeys) {
|
|
412
|
+
current[key] = undefined;
|
|
413
|
+
pushDotenvProvenance(prov, key, { kind: 'vars', op: 'unset' });
|
|
414
|
+
}
|
|
415
|
+
return { env: current, provenance: prov };
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Read dotenv files from the deterministic cascade and record provenance.
|
|
420
|
+
*
|
|
421
|
+
* Requirements addressed:
|
|
422
|
+
* - Provenance entries for `kind: 'file'` include scope/privacy/env/path/file (descriptor-only).
|
|
423
|
+
* - Provenance is ordered in ascending precedence as layers are applied.
|
|
424
|
+
* - Uses the same file naming convention as get-dotenv (public/private × global/env).
|
|
425
|
+
*/
|
|
426
|
+
const resolveEnvName = (env, defaultEnv) => typeof env === 'string' && env.length > 0
|
|
427
|
+
? env
|
|
428
|
+
: typeof defaultEnv === 'string' && defaultEnv.length > 0
|
|
429
|
+
? defaultEnv
|
|
430
|
+
: undefined;
|
|
431
|
+
const buildFileToken = (args) => {
|
|
432
|
+
const { dotenvToken, privateToken, scope, privacy, envName } = args;
|
|
433
|
+
const parts = [dotenvToken];
|
|
434
|
+
if (scope === 'env')
|
|
435
|
+
parts.push(envName ?? '');
|
|
436
|
+
if (privacy === 'private')
|
|
437
|
+
parts.push(privateToken);
|
|
438
|
+
return parts.join('.');
|
|
439
|
+
};
|
|
440
|
+
const recordFileLayer = (prov, vars, entry) => {
|
|
441
|
+
for (const key of Object.keys(vars)) {
|
|
442
|
+
const v = vars[key];
|
|
443
|
+
// Record empty-string values as an explicit unset to align with dotenvExpand semantics
|
|
444
|
+
// (empty string expands to `undefined` in the final progressive expansion step).
|
|
445
|
+
const op = typeof v === 'string' && v.length > 0 ? 'set' : 'unset';
|
|
446
|
+
pushDotenvProvenance(prov, key, { kind: 'file', op, ...entry });
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
/**
|
|
450
|
+
* Read and expand dotenv vars from the deterministic file cascade, recording file provenance.
|
|
451
|
+
*
|
|
452
|
+
* @param args - Cascade selector options (tokens/paths/excludes/env).
|
|
453
|
+
* @returns Expanded dotenv and provenance.
|
|
454
|
+
*
|
|
455
|
+
* @public
|
|
456
|
+
*/
|
|
457
|
+
async function readDotenvCascadeWithProvenance(args) {
|
|
458
|
+
const dotenvToken = args.dotenvToken ?? '.env';
|
|
459
|
+
const privateToken = args.privateToken ?? 'local';
|
|
460
|
+
const envName = resolveEnvName(args.env, args.defaultEnv);
|
|
461
|
+
const prov = createDotenvProvenance();
|
|
462
|
+
const shouldPublicGlobal = !(args.excludePublic || args.excludeGlobal);
|
|
463
|
+
const shouldPublicEnv = !(args.excludePublic || args.excludeEnv) && !!envName;
|
|
464
|
+
const shouldPrivateGlobal = !(args.excludePrivate || args.excludeGlobal);
|
|
465
|
+
const shouldPrivateEnv = !(args.excludePrivate || args.excludeEnv) && !!envName;
|
|
466
|
+
let loaded = {};
|
|
467
|
+
for (const p of args.paths) {
|
|
468
|
+
const readOne = async (fileToken) => {
|
|
469
|
+
const abs = path.resolve(p, fileToken);
|
|
470
|
+
return readDotenv(abs);
|
|
471
|
+
};
|
|
472
|
+
if (shouldPublicGlobal) {
|
|
473
|
+
const token = buildFileToken({
|
|
474
|
+
dotenvToken,
|
|
475
|
+
privateToken,
|
|
476
|
+
scope: 'global',
|
|
477
|
+
privacy: 'public',
|
|
478
|
+
});
|
|
479
|
+
const vars = await readOne(token);
|
|
480
|
+
recordFileLayer(prov, vars, {
|
|
481
|
+
scope: 'global',
|
|
482
|
+
privacy: 'public',
|
|
483
|
+
path: p,
|
|
484
|
+
file: token,
|
|
485
|
+
});
|
|
486
|
+
loaded = { ...loaded, ...vars };
|
|
487
|
+
}
|
|
488
|
+
if (shouldPublicEnv && envName) {
|
|
489
|
+
const token = buildFileToken({
|
|
490
|
+
dotenvToken,
|
|
491
|
+
privateToken,
|
|
492
|
+
scope: 'env',
|
|
493
|
+
privacy: 'public',
|
|
494
|
+
envName,
|
|
495
|
+
});
|
|
496
|
+
const vars = await readOne(token);
|
|
497
|
+
recordFileLayer(prov, vars, {
|
|
498
|
+
scope: 'env',
|
|
499
|
+
privacy: 'public',
|
|
500
|
+
env: envName,
|
|
501
|
+
path: p,
|
|
502
|
+
file: token,
|
|
503
|
+
});
|
|
504
|
+
loaded = { ...loaded, ...vars };
|
|
505
|
+
}
|
|
506
|
+
if (shouldPrivateGlobal) {
|
|
507
|
+
const token = buildFileToken({
|
|
508
|
+
dotenvToken,
|
|
509
|
+
privateToken,
|
|
510
|
+
scope: 'global',
|
|
511
|
+
privacy: 'private',
|
|
512
|
+
});
|
|
513
|
+
const vars = await readOne(token);
|
|
514
|
+
recordFileLayer(prov, vars, {
|
|
515
|
+
scope: 'global',
|
|
516
|
+
privacy: 'private',
|
|
517
|
+
path: p,
|
|
518
|
+
file: token,
|
|
519
|
+
});
|
|
520
|
+
loaded = { ...loaded, ...vars };
|
|
521
|
+
}
|
|
522
|
+
if (shouldPrivateEnv && envName) {
|
|
523
|
+
const token = buildFileToken({
|
|
524
|
+
dotenvToken,
|
|
525
|
+
privateToken,
|
|
526
|
+
scope: 'env',
|
|
527
|
+
privacy: 'private',
|
|
528
|
+
envName,
|
|
529
|
+
});
|
|
530
|
+
const vars = await readOne(token);
|
|
531
|
+
recordFileLayer(prov, vars, {
|
|
532
|
+
scope: 'env',
|
|
533
|
+
privacy: 'private',
|
|
534
|
+
env: envName,
|
|
535
|
+
path: p,
|
|
536
|
+
file: token,
|
|
537
|
+
});
|
|
538
|
+
loaded = { ...loaded, ...vars };
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
// Match getDotenv behavior: expand progressively across the merged file map.
|
|
542
|
+
const expanded = dotenvExpandAll(loaded, { progressive: true });
|
|
543
|
+
return { dotenv: expanded, provenance: prov };
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
export { applyDynamicMap as a, applyDynamicMapWithProvenance as b, loadAndApplyDynamic as c, dotenvExpandAll as d, createDotenvProvenance as e, readDotenv as f, dotenvExpand as g, dotenvExpandFromProcessEnv as h, loadDynamicModuleDefault as l, overlayEnvWithProvenance as o, pushDotenvProvenance as p, readDotenvCascadeWithProvenance as r };
|