@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
package/dist/index.d.ts CHANGED
@@ -2,6 +2,218 @@ import { z, ZodObject } from 'zod';
2
2
  export { z } from 'zod';
3
3
  import { OptionValues, Command, InferCommandArguments, Option, CommandUnknownOpts } from '@commander-js/extra-typings';
4
4
 
5
+ /**
6
+ * Dotenv provenance model (descriptor-only).
7
+ *
8
+ * Requirements addressed:
9
+ * - Host ctx carries a dotenv provenance mapping describing per-key origin and override history.
10
+ * - Provenance is descriptor-only (no value payloads).
11
+ * - Provenance is an ordered stack per key in ascending precedence; the last entry is effective.
12
+ * - Explicit unsets are represented as `op: 'unset'` without requiring deletion of keys from the env map.
13
+ */
14
+ /**
15
+ * Provenance kind for an entry in {@link DotenvProvenance}.
16
+ *
17
+ * @public
18
+ */
19
+ type DotenvProvenanceKind = 'file' | 'config' | 'vars' | 'dynamic';
20
+ /**
21
+ * Operation represented by a provenance entry.
22
+ *
23
+ * @public
24
+ */
25
+ type DotenvProvenanceOp = 'set' | 'unset';
26
+ /**
27
+ * Base shape for all provenance entries.
28
+ *
29
+ * @public
30
+ */
31
+ interface DotenvProvenanceEntryBase {
32
+ /**
33
+ * The kind of provenance entry.
34
+ */
35
+ kind: DotenvProvenanceKind;
36
+ /**
37
+ * The operation applied at this layer.
38
+ */
39
+ op: DotenvProvenanceOp;
40
+ }
41
+ /**
42
+ * Provenance entry representing a value sourced from a dotenv file in the cascade.
43
+ *
44
+ * @public
45
+ */
46
+ interface DotenvFileProvenanceEntry extends DotenvProvenanceEntryBase {
47
+ /** Discriminator. */
48
+ kind: 'file';
49
+ /** Global vs env-scoped file. */
50
+ scope: 'global' | 'env';
51
+ /** Public vs private file. */
52
+ privacy: 'public' | 'private';
53
+ /** Environment name (required when scope is `env`). */
54
+ env?: string;
55
+ /**
56
+ * The corresponding `paths[]` entry as provided by the caller/CLI.
57
+ * This is not an absolute path by policy.
58
+ */
59
+ path: string;
60
+ /**
61
+ * The computed dotenv filename token (e.g., `.env`, `.env.dev`, `.env.local`, `.env.dev.local`).
62
+ */
63
+ file: string;
64
+ }
65
+ /**
66
+ * Provenance entry representing a value sourced from config overlays (`vars` / `envVars`).
67
+ *
68
+ * @public
69
+ */
70
+ interface DotenvConfigProvenanceEntry extends DotenvProvenanceEntryBase {
71
+ /** Discriminator. */
72
+ kind: 'config';
73
+ /** Global vs env-scoped config slice. */
74
+ scope: 'global' | 'env';
75
+ /** Public vs private on the privacy axis (`local` config maps to `private`). */
76
+ privacy: 'public' | 'private';
77
+ /** Environment name (required when scope is `env`). */
78
+ env?: string;
79
+ /** Packaged vs project config origin. */
80
+ configScope: 'packaged' | 'project';
81
+ /** Public vs local config file. */
82
+ configPrivacy: 'public' | 'local';
83
+ }
84
+ /**
85
+ * Provenance entry representing explicit variables overrides.
86
+ *
87
+ * Notes:
88
+ * - This kind represents values injected via `vars` (CLI/programmatic), not dotenv files.
89
+ *
90
+ * @public
91
+ */
92
+ interface DotenvVarsProvenanceEntry extends DotenvProvenanceEntryBase {
93
+ /** Discriminator. */
94
+ kind: 'vars';
95
+ }
96
+ /**
97
+ * Source tier for dynamic variables.
98
+ *
99
+ * @public
100
+ */
101
+ type DotenvDynamicSource = 'config' | 'programmatic' | 'dynamicPath';
102
+ /**
103
+ * Provenance entry representing dynamic variables.
104
+ *
105
+ * @public
106
+ */
107
+ interface DotenvDynamicProvenanceEntry extends DotenvProvenanceEntryBase {
108
+ /** Discriminator. */
109
+ kind: 'dynamic';
110
+ /** Dynamic source tier. */
111
+ dynamicSource: DotenvDynamicSource;
112
+ /**
113
+ * The `dynamicPath` value as provided by the caller/CLI when `dynamicSource` is `dynamicPath`.
114
+ * This is not resolved to an absolute path by provenance policy.
115
+ */
116
+ dynamicPath?: string;
117
+ }
118
+ /**
119
+ * Union of all supported provenance entry shapes.
120
+ *
121
+ * @public
122
+ */
123
+ type DotenvProvenanceEntry = DotenvFileProvenanceEntry | DotenvConfigProvenanceEntry | DotenvVarsProvenanceEntry | DotenvDynamicProvenanceEntry;
124
+ /**
125
+ * Per-key provenance history.
126
+ *
127
+ * Each key maps to an array of entries ordered in ascending precedence (lower to higher).
128
+ *
129
+ * @public
130
+ */
131
+ type DotenvProvenance = Record<string, DotenvProvenanceEntry[]>;
132
+
133
+ /**
134
+ * Resolved CLI options schema.
135
+ * For the current step this mirrors the RAW schema; later stages may further
136
+ * narrow types post-resolution in the host pipeline.
137
+ */
138
+ /**
139
+ * CLI options schema (resolved).
140
+ *
141
+ * Today this mirrors {@link getDotenvCliOptionsSchemaRaw}, but is kept as a distinct export
142
+ * so future resolution steps can narrow or materialize defaults without breaking the API.
143
+ *
144
+ * @public
145
+ */
146
+ declare const getDotenvCliOptionsSchemaResolved: z.ZodObject<{
147
+ defaultEnv: z.ZodOptional<z.ZodString>;
148
+ dotenvToken: z.ZodOptional<z.ZodString>;
149
+ dynamicPath: z.ZodOptional<z.ZodString>;
150
+ dynamic: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
151
+ env: z.ZodOptional<z.ZodString>;
152
+ excludeDynamic: z.ZodOptional<z.ZodBoolean>;
153
+ excludeEnv: z.ZodOptional<z.ZodBoolean>;
154
+ excludeGlobal: z.ZodOptional<z.ZodBoolean>;
155
+ excludePrivate: z.ZodOptional<z.ZodBoolean>;
156
+ excludePublic: z.ZodOptional<z.ZodBoolean>;
157
+ loadProcess: z.ZodOptional<z.ZodBoolean>;
158
+ log: z.ZodOptional<z.ZodBoolean>;
159
+ logger: z.ZodDefault<z.ZodUnknown>;
160
+ outputPath: z.ZodOptional<z.ZodString>;
161
+ privateToken: z.ZodOptional<z.ZodString>;
162
+ debug: z.ZodOptional<z.ZodBoolean>;
163
+ strict: z.ZodOptional<z.ZodBoolean>;
164
+ capture: z.ZodOptional<z.ZodBoolean>;
165
+ trace: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodArray<z.ZodString>]>>;
166
+ redact: z.ZodOptional<z.ZodBoolean>;
167
+ warnEntropy: z.ZodOptional<z.ZodBoolean>;
168
+ entropyThreshold: z.ZodOptional<z.ZodNumber>;
169
+ entropyMinLength: z.ZodOptional<z.ZodNumber>;
170
+ entropyWhitelist: z.ZodOptional<z.ZodArray<z.ZodString>>;
171
+ redactPatterns: z.ZodOptional<z.ZodArray<z.ZodString>>;
172
+ paths: z.ZodOptional<z.ZodString>;
173
+ pathsDelimiter: z.ZodOptional<z.ZodString>;
174
+ pathsDelimiterPattern: z.ZodOptional<z.ZodString>;
175
+ scripts: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
176
+ shell: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>;
177
+ vars: z.ZodOptional<z.ZodString>;
178
+ varsAssignor: z.ZodOptional<z.ZodString>;
179
+ varsAssignorPattern: z.ZodOptional<z.ZodString>;
180
+ varsDelimiter: z.ZodOptional<z.ZodString>;
181
+ varsDelimiterPattern: z.ZodOptional<z.ZodString>;
182
+ }, z.core.$strip>;
183
+
184
+ /**
185
+ * Resolved programmatic options schema (post-inheritance).
186
+ * For now, this mirrors the RAW schema; future stages may materialize defaults
187
+ * and narrow shapes as resolution is wired into the host.
188
+ */
189
+ /**
190
+ * Programmatic options schema (resolved).
191
+ *
192
+ * Today this mirrors {@link getDotenvOptionsSchemaRaw}, but is kept as a distinct export
193
+ * so future resolution steps can narrow or materialize defaults without breaking the API.
194
+ *
195
+ * @public
196
+ */
197
+ declare const getDotenvOptionsSchemaResolved: z.ZodObject<{
198
+ defaultEnv: z.ZodOptional<z.ZodString>;
199
+ dotenvToken: z.ZodOptional<z.ZodString>;
200
+ dynamicPath: z.ZodOptional<z.ZodString>;
201
+ dynamic: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
202
+ env: z.ZodOptional<z.ZodString>;
203
+ excludeDynamic: z.ZodOptional<z.ZodBoolean>;
204
+ excludeEnv: z.ZodOptional<z.ZodBoolean>;
205
+ excludeGlobal: z.ZodOptional<z.ZodBoolean>;
206
+ excludePrivate: z.ZodOptional<z.ZodBoolean>;
207
+ excludePublic: z.ZodOptional<z.ZodBoolean>;
208
+ loadProcess: z.ZodOptional<z.ZodBoolean>;
209
+ log: z.ZodOptional<z.ZodBoolean>;
210
+ logger: z.ZodDefault<z.ZodUnknown>;
211
+ outputPath: z.ZodOptional<z.ZodString>;
212
+ paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
213
+ privateToken: z.ZodOptional<z.ZodString>;
214
+ vars: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodOptional<z.ZodString>>>;
215
+ }, z.core.$strip>;
216
+
5
217
  /**
6
218
  * Minimal root options shape shared by CLI and generator layers.
7
219
  * Keep keys optional to respect exactOptionalPropertyTypes semantics.
@@ -121,6 +333,12 @@ interface GetDotenvCliCtx<TOptions extends GetDotenvOptions = GetDotenvOptions>
121
333
  * Final composed dotenv environment for this invocation.
122
334
  */
123
335
  dotenv: ProcessEnv;
336
+ /**
337
+ * Dotenv provenance history for {@link GetDotenvCliCtx.dotenv}.
338
+ *
339
+ * Descriptor-only: does not include value payloads.
340
+ */
341
+ dotenvProvenance: DotenvProvenance;
124
342
  /**
125
343
  * Optional runtime plugin state bag. Plugins may publish non-sensitive metadata here.
126
344
  */
@@ -148,90 +366,6 @@ interface BrandOptions {
148
366
  helpHeader?: string;
149
367
  }
150
368
 
151
- /**
152
- * Resolved CLI options schema.
153
- * For the current step this mirrors the RAW schema; later stages may further
154
- * narrow types post-resolution in the host pipeline.
155
- */
156
- /**
157
- * CLI options schema (resolved).
158
- *
159
- * Today this mirrors {@link getDotenvCliOptionsSchemaRaw}, but is kept as a distinct export
160
- * so future resolution steps can narrow or materialize defaults without breaking the API.
161
- *
162
- * @public
163
- */
164
- declare const getDotenvCliOptionsSchemaResolved: z.ZodObject<{
165
- defaultEnv: z.ZodOptional<z.ZodString>;
166
- dotenvToken: z.ZodOptional<z.ZodString>;
167
- dynamicPath: z.ZodOptional<z.ZodString>;
168
- dynamic: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
169
- env: z.ZodOptional<z.ZodString>;
170
- excludeDynamic: z.ZodOptional<z.ZodBoolean>;
171
- excludeEnv: z.ZodOptional<z.ZodBoolean>;
172
- excludeGlobal: z.ZodOptional<z.ZodBoolean>;
173
- excludePrivate: z.ZodOptional<z.ZodBoolean>;
174
- excludePublic: z.ZodOptional<z.ZodBoolean>;
175
- loadProcess: z.ZodOptional<z.ZodBoolean>;
176
- log: z.ZodOptional<z.ZodBoolean>;
177
- logger: z.ZodDefault<z.ZodUnknown>;
178
- outputPath: z.ZodOptional<z.ZodString>;
179
- privateToken: z.ZodOptional<z.ZodString>;
180
- debug: z.ZodOptional<z.ZodBoolean>;
181
- strict: z.ZodOptional<z.ZodBoolean>;
182
- capture: z.ZodOptional<z.ZodBoolean>;
183
- trace: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodArray<z.ZodString>]>>;
184
- redact: z.ZodOptional<z.ZodBoolean>;
185
- warnEntropy: z.ZodOptional<z.ZodBoolean>;
186
- entropyThreshold: z.ZodOptional<z.ZodNumber>;
187
- entropyMinLength: z.ZodOptional<z.ZodNumber>;
188
- entropyWhitelist: z.ZodOptional<z.ZodArray<z.ZodString>>;
189
- redactPatterns: z.ZodOptional<z.ZodArray<z.ZodString>>;
190
- paths: z.ZodOptional<z.ZodString>;
191
- pathsDelimiter: z.ZodOptional<z.ZodString>;
192
- pathsDelimiterPattern: z.ZodOptional<z.ZodString>;
193
- scripts: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
194
- shell: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>;
195
- vars: z.ZodOptional<z.ZodString>;
196
- varsAssignor: z.ZodOptional<z.ZodString>;
197
- varsAssignorPattern: z.ZodOptional<z.ZodString>;
198
- varsDelimiter: z.ZodOptional<z.ZodString>;
199
- varsDelimiterPattern: z.ZodOptional<z.ZodString>;
200
- }, z.core.$strip>;
201
-
202
- /**
203
- * Resolved programmatic options schema (post-inheritance).
204
- * For now, this mirrors the RAW schema; future stages may materialize defaults
205
- * and narrow shapes as resolution is wired into the host.
206
- */
207
- /**
208
- * Programmatic options schema (resolved).
209
- *
210
- * Today this mirrors {@link getDotenvOptionsSchemaRaw}, but is kept as a distinct export
211
- * so future resolution steps can narrow or materialize defaults without breaking the API.
212
- *
213
- * @public
214
- */
215
- declare const getDotenvOptionsSchemaResolved: z.ZodObject<{
216
- defaultEnv: z.ZodOptional<z.ZodString>;
217
- dotenvToken: z.ZodOptional<z.ZodString>;
218
- dynamicPath: z.ZodOptional<z.ZodString>;
219
- dynamic: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
220
- env: z.ZodOptional<z.ZodString>;
221
- excludeDynamic: z.ZodOptional<z.ZodBoolean>;
222
- excludeEnv: z.ZodOptional<z.ZodBoolean>;
223
- excludeGlobal: z.ZodOptional<z.ZodBoolean>;
224
- excludePrivate: z.ZodOptional<z.ZodBoolean>;
225
- excludePublic: z.ZodOptional<z.ZodBoolean>;
226
- loadProcess: z.ZodOptional<z.ZodBoolean>;
227
- log: z.ZodOptional<z.ZodBoolean>;
228
- logger: z.ZodDefault<z.ZodUnknown>;
229
- outputPath: z.ZodOptional<z.ZodString>;
230
- paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
231
- privateToken: z.ZodOptional<z.ZodString>;
232
- vars: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodOptional<z.ZodString>>>;
233
- }, z.core.$strip>;
234
-
235
369
  /**
236
370
  * Canonical programmatic options and helpers for get-dotenv.
237
371
  *
package/dist/index.mjs CHANGED
@@ -1,28 +1,31 @@
1
- export { c as createCli } from './chunks/createCli-BSn6Be40.mjs';
2
- export { G as GetDotenvCli, b as baseRootOptionDefaults, e as defineDynamic, f as defineGetDotenvConfig, d as definePlugin, h as getDotenv, g as getDotenvCliOptions2Options, k as interpolateDeep, m as maybeWarnEntropy, r as readMergedOptions, i as redactDisplay, j as redactObject } from './chunks/readMergedOptions-DLBDzpXX.mjs';
1
+ export { c as createCli } from './chunks/createCli-BnRdfRRL.mjs';
2
+ import { e as resolveGetDotenvOptions, w as writeDotenvFile } from './chunks/readMergedOptions-B7VdLROn.mjs';
3
+ export { G as GetDotenvCli, b as baseRootOptionDefaults, f as defineDynamic, h as defineGetDotenvConfig, d as definePlugin, g as getDotenvCliOptions2Options, i as interpolateDeep, r as readMergedOptions } from './chunks/readMergedOptions-B7VdLROn.mjs';
3
4
  export { d as buildSpawnEnv, s as shouldCapture } from './chunks/spawnEnv-CQwFu7ZJ.mjs';
4
- export { d as defineScripts, g as groupPlugins } from './chunks/types-DdqcXCV1.mjs';
5
+ export { d as defineScripts, g as groupPlugins } from './chunks/types-CVDR-Sjk.mjs';
5
6
  import fs from 'fs-extra';
6
7
  import 'crypto';
7
- import 'path';
8
+ import path$1 from 'path';
8
9
  import 'url';
9
10
  export { z } from 'zod';
10
- import 'dotenv';
11
- export { t as traceChildEnv } from './chunks/index-BqZ3PB6c.mjs';
12
- export { d as dotenvExpand, b as dotenvExpandAll, c as dotenvExpandFromProcessEnv } from './chunks/overlayEnv-Bqh_kPGA.mjs';
11
+ import { nanoid } from 'nanoid';
12
+ import { r as redactObject, m as maybeWarnEntropy } from './chunks/index-r0Me7-sT.mjs';
13
+ export { a as redactDisplay, t as traceChildEnv } from './chunks/index-r0Me7-sT.mjs';
14
+ import { f as readDotenv, d as dotenvExpandAll, a as applyDynamicMap, c as loadAndApplyDynamic } from './chunks/readDotenvCascade-DfFkWMjs.mjs';
15
+ export { g as dotenvExpand, h as dotenvExpandFromProcessEnv } from './chunks/readDotenvCascade-DfFkWMjs.mjs';
13
16
  import path from 'node:path';
17
+ import 'dotenv';
14
18
  import './chunks/loader-CePOf74i.mjs';
15
19
  import 'package-directory';
16
20
  import 'yaml';
17
21
  import './chunks/loadModuleDefault-Dj8B3Stt.mjs';
18
- import 'nanoid';
19
22
  import 'execa';
20
23
  import './chunks/helpConfig-CGejgwWW.mjs';
21
- import './chunks/resolveCliOptions-_qtsVxda.mjs';
24
+ import './chunks/resolveCliOptions-pgUXHJtj.mjs';
22
25
  import './chunks/validate-CDl0rE6k.mjs';
23
26
  import './plugins-aws.mjs';
24
27
  import '@commander-js/extra-typings';
25
- import './chunks/index-BNcKuiBy.mjs';
28
+ import './chunks/index-70Dm0f1N.mjs';
26
29
  import 'buffer';
27
30
  import 'os';
28
31
  import 'node:fs/promises';
@@ -620,56 +623,100 @@ function editDotenvText(text, updates, options = {}) {
620
623
  }
621
624
 
622
625
  /**
623
- * FS-level dotenv editor adapter (target resolution + template bootstrap).
626
+ * @packageDocumentation
627
+ * Deterministic dotenv target selection across a multi-path cascade.
624
628
  *
625
- * Requirements addressed:
626
- * - Deterministic target selection across getdotenv `paths` only.
627
- * - Scope axis (global|env) × privacy axis (public|private).
628
- * - Template bootstrap: copy `<target>.<templateExtension>` to `<target>` when needed.
629
- * - Edit in place while preserving formatting via the pure text editor.
629
+ * This module extracts the “which file should we edit?” logic from the FS-level editor
630
+ * so plugins/tools can reuse the same selection semantics as {@link editDotenvFile}.
630
631
  *
631
- * @packageDocumentation
632
+ * Notes:
633
+ * - Selection is based on `paths` only (directories), consistent with get-dotenv overlay precedence.
634
+ * - Default search order is reverse (last path wins).
635
+ * - Template discovery is supported via `<target>.<templateExtension>` and returns the template path
636
+ * when the concrete target is missing.
632
637
  */
633
- const defaultFs = {
634
- pathExists: async (p) => fs.pathExists(p),
635
- readFile: async (p) => fs.readFile(p, 'utf-8'),
636
- writeFile: async (p, contents) => fs.writeFile(p, contents, 'utf-8'),
637
- copyFile: async (src, dest) => fs.copyFile(src, dest),
638
- };
639
638
  const resolveEnvName = (env, defaultEnv) => typeof env === 'string' && env.length > 0
640
639
  ? env
641
640
  : typeof defaultEnv === 'string' && defaultEnv.length > 0
642
641
  ? defaultEnv
643
642
  : undefined;
644
- const buildTargetFilename = (opts) => {
645
- const { dotenvToken, privateToken, scope, privacy, envName } = opts;
643
+ /**
644
+ * Build the dotenv filename for a selector (scope × privacy) using the same naming
645
+ * convention as get-dotenv.
646
+ *
647
+ * @param opts - Filename selector options.
648
+ * @returns The filename token (for example, `.env.dev.local`).
649
+ * @throws Error when `scope` is `'env'` and neither `env` nor `defaultEnv` is provided.
650
+ *
651
+ * @public
652
+ */
653
+ function buildDotenvTargetFilename(opts) {
654
+ const dotenvToken = opts.dotenvToken ?? '.env';
655
+ const privateToken = opts.privateToken ?? 'local';
656
+ const envName = resolveEnvName(opts.env, opts.defaultEnv);
646
657
  const parts = [dotenvToken];
647
- if (scope === 'env') {
658
+ if (opts.scope === 'env') {
648
659
  if (!envName) {
649
660
  throw new Error(`Unable to resolve env-scoped dotenv filename: env is required.`);
650
661
  }
651
662
  parts.push(envName);
652
663
  }
653
- if (privacy === 'private')
664
+ if (opts.privacy === 'private')
654
665
  parts.push(privateToken);
655
666
  return parts.join('.');
656
- };
667
+ }
657
668
  const orderedPaths = (pathsIn, order) => {
658
669
  const list = pathsIn.slice();
659
670
  return order === 'forward' ? list : list.reverse();
660
671
  };
661
- const resolveTargetAcrossPaths = async (fsPort, opts) => {
662
- const { filename, templateExtension, searchOrder } = opts;
672
+ /**
673
+ * Resolve a deterministic dotenv target across `paths` based on scope/privacy selectors.
674
+ *
675
+ * Selection rules:
676
+ * - Iterates `paths` in the requested search order.
677
+ * - Returns the first existing target file.
678
+ * - Otherwise, returns the first sibling template file (`<target>.<templateExtension>`) when present.
679
+ * - Throws when neither a target nor a template exists anywhere under `paths`.
680
+ *
681
+ * @param opts - Resolver options (paths + selector + fs port).
682
+ * @returns The resolved target path and optional template path.
683
+ *
684
+ * @public
685
+ */
686
+ async function resolveDotenvTarget(opts) {
687
+ const filename = buildDotenvTargetFilename(opts);
688
+ const templateExtension = opts.templateExtension ?? 'template';
689
+ const searchOrder = opts.searchOrder ?? 'reverse';
663
690
  const pathsOrdered = orderedPaths(opts.paths, searchOrder);
664
691
  for (const dir of pathsOrdered) {
665
692
  const targetPath = path.resolve(dir, filename);
666
- if (await fsPort.pathExists(targetPath))
667
- return { targetPath };
693
+ if (await opts.fs.pathExists(targetPath)) {
694
+ return { targetPath, filename };
695
+ }
668
696
  const templatePath = `${targetPath}.${templateExtension}`;
669
- if (await fsPort.pathExists(templatePath))
670
- return { targetPath, templatePath };
697
+ if (await opts.fs.pathExists(templatePath)) {
698
+ return { targetPath, templatePath, filename };
699
+ }
671
700
  }
672
701
  throw new Error(`Unable to locate dotenv target "${filename}" under provided paths, and no template was found.`);
702
+ }
703
+
704
+ /**
705
+ * FS-level dotenv editor adapter (target resolution + template bootstrap).
706
+ *
707
+ * Requirements addressed:
708
+ * - Deterministic target selection across getdotenv `paths` only.
709
+ * - Scope axis (global|env) × privacy axis (public|private).
710
+ * - Template bootstrap: copy `<target>.<templateExtension>` to `<target>` when needed.
711
+ * - Edit in place while preserving formatting via the pure text editor.
712
+ *
713
+ * @packageDocumentation
714
+ */
715
+ const defaultFs = {
716
+ pathExists: async (p) => fs.pathExists(p),
717
+ readFile: async (p) => fs.readFile(p, 'utf-8'),
718
+ writeFile: async (p, contents) => fs.writeFile(p, contents, 'utf-8'),
719
+ copyFile: async (src, dest) => fs.copyFile(src, dest),
673
720
  };
674
721
  /**
675
722
  * Edit a dotenv file selected by scope/privacy across a list of search paths.
@@ -686,17 +733,19 @@ async function editDotenvFile(updates, options) {
686
733
  const privateToken = options.privateToken ?? 'local';
687
734
  const templateExtension = options.templateExtension ?? 'template';
688
735
  const searchOrder = options.searchOrder ?? 'reverse';
689
- const envName = resolveEnvName(options.env, options.defaultEnv);
690
- const filename = buildTargetFilename({
736
+ const resolved = await resolveDotenvTarget({
737
+ fs: fsPort,
738
+ paths: options.paths,
691
739
  dotenvToken,
692
740
  privateToken,
741
+ ...(typeof options.env === 'string' && options.env.length > 0
742
+ ? { env: options.env }
743
+ : {}),
744
+ ...(typeof options.defaultEnv === 'string' && options.defaultEnv.length > 0
745
+ ? { defaultEnv: options.defaultEnv }
746
+ : {}),
693
747
  scope: options.scope,
694
748
  privacy: options.privacy,
695
- ...(envName ? { envName } : {}),
696
- });
697
- const resolved = await resolveTargetAcrossPaths(fsPort, {
698
- paths: options.paths,
699
- filename,
700
749
  templateExtension,
701
750
  searchOrder,
702
751
  });
@@ -729,4 +778,112 @@ async function editDotenvFile(updates, options) {
729
778
  return { path: resolved.targetPath, createdFromTemplate, changed };
730
779
  }
731
780
 
732
- export { applyDotenvEdits, editDotenvFile, editDotenvText, parseDotenvDocument, renderDotenvDocument };
781
+ async function getDotenv(options = {}) {
782
+ // Apply defaults.
783
+ 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);
784
+ // Read .env files.
785
+ const loaded = paths.length
786
+ ? await paths.reduce(async (e, p) => {
787
+ const publicGlobal = excludePublic || excludeGlobal
788
+ ? Promise.resolve({})
789
+ : readDotenv(path$1.resolve(p, dotenvToken));
790
+ const publicEnv = excludePublic || excludeEnv || (!env && !defaultEnv)
791
+ ? Promise.resolve({})
792
+ : readDotenv(path$1.resolve(p, `${dotenvToken}.${env ?? defaultEnv ?? ''}`));
793
+ const privateGlobal = excludePrivate || excludeGlobal
794
+ ? Promise.resolve({})
795
+ : readDotenv(path$1.resolve(p, `${dotenvToken}.${privateToken}`));
796
+ const privateEnv = excludePrivate || excludeEnv || (!env && !defaultEnv)
797
+ ? Promise.resolve({})
798
+ : readDotenv(path$1.resolve(p, `${dotenvToken}.${env ?? defaultEnv ?? ''}.${privateToken}`));
799
+ const [eResolved, publicGlobalResolved, publicEnvResolved, privateGlobalResolved, privateEnvResolved,] = await Promise.all([
800
+ e,
801
+ publicGlobal,
802
+ publicEnv,
803
+ privateGlobal,
804
+ privateEnv,
805
+ ]);
806
+ return {
807
+ ...eResolved,
808
+ ...publicGlobalResolved,
809
+ ...publicEnvResolved,
810
+ ...privateGlobalResolved,
811
+ ...privateEnvResolved,
812
+ };
813
+ }, Promise.resolve({}))
814
+ : {};
815
+ const outputKey = nanoid();
816
+ const dotenv = dotenvExpandAll({
817
+ ...loaded,
818
+ ...vars,
819
+ ...(outputPath ? { [outputKey]: outputPath } : {}),
820
+ }, { progressive: true });
821
+ // Process dynamic variables. Programmatic option takes precedence over path.
822
+ if (!excludeDynamic) {
823
+ // A2 precedence: programmatic dynamic < dynamicPath (dynamicPath wins when present)
824
+ if (options.dynamic && Object.keys(options.dynamic).length > 0) {
825
+ try {
826
+ applyDynamicMap(dotenv, options.dynamic, env ?? defaultEnv);
827
+ }
828
+ catch {
829
+ throw new Error(`Unable to evaluate dynamic variables.`);
830
+ }
831
+ }
832
+ // dynamicPath is evaluated even when programmatic `dynamic` is present.
833
+ if (dynamicPath) {
834
+ const absDynamicPath = path$1.resolve(dynamicPath);
835
+ await loadAndApplyDynamic(dotenv, absDynamicPath, env ?? defaultEnv, 'getdotenv-dynamic');
836
+ }
837
+ }
838
+ // Write output file.
839
+ let resultDotenv = dotenv;
840
+ if (outputPath) {
841
+ const outputPathResolved = dotenv[outputKey];
842
+ if (!outputPathResolved)
843
+ throw new Error('Output path not found.');
844
+ const { [outputKey]: _omitted, ...dotenvForOutput } = dotenv;
845
+ await writeDotenvFile(outputPathResolved, dotenvForOutput);
846
+ resultDotenv = dotenvForOutput;
847
+ }
848
+ // Log result.
849
+ if (log) {
850
+ const redactFlag = options.redact ?? false;
851
+ const redactPatterns = options.redactPatterns ?? undefined;
852
+ const redOpts = {};
853
+ if (redactFlag)
854
+ redOpts.redact = true;
855
+ if (redactFlag && Array.isArray(redactPatterns))
856
+ redOpts.redactPatterns = redactPatterns;
857
+ const bag = redactFlag
858
+ ? redactObject(resultDotenv, redOpts)
859
+ : { ...resultDotenv };
860
+ logger.log(bag);
861
+ // Entropy warnings: once-per-key-per-run (presentation only)
862
+ const warnEntropyVal = options.warnEntropy ?? true;
863
+ const entropyThresholdVal = options
864
+ .entropyThreshold;
865
+ const entropyMinLengthVal = options
866
+ .entropyMinLength;
867
+ const entropyWhitelistVal = options.entropyWhitelist;
868
+ const entOpts = {};
869
+ if (typeof warnEntropyVal === 'boolean')
870
+ entOpts.warnEntropy = warnEntropyVal;
871
+ if (typeof entropyThresholdVal === 'number')
872
+ entOpts.entropyThreshold = entropyThresholdVal;
873
+ if (typeof entropyMinLengthVal === 'number')
874
+ entOpts.entropyMinLength = entropyMinLengthVal;
875
+ if (Array.isArray(entropyWhitelistVal))
876
+ entOpts.entropyWhitelist = entropyWhitelistVal;
877
+ for (const [k, v] of Object.entries(resultDotenv)) {
878
+ maybeWarnEntropy(k, v, v !== undefined ? 'dotenv' : 'unset', entOpts, (line) => {
879
+ logger.log(line);
880
+ });
881
+ }
882
+ }
883
+ // Load process.env.
884
+ if (loadProcess)
885
+ Object.assign(process.env, resultDotenv);
886
+ return resultDotenv;
887
+ }
888
+
889
+ export { applyDotenvEdits, dotenvExpandAll, editDotenvFile, editDotenvText, getDotenv, maybeWarnEntropy, parseDotenvDocument, redactObject, renderDotenvDocument };