@pawells/config-provider-env 3.0.0-rc1
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/LICENSE +21 -0
- package/README.md +210 -0
- package/dist/env-utils.d.ts +90 -0
- package/dist/env-utils.d.ts.map +1 -0
- package/dist/env-utils.js +185 -0
- package/dist/env-utils.js.map +1 -0
- package/dist/environment-provider.d.ts +226 -0
- package/dist/environment-provider.d.ts.map +1 -0
- package/dist/environment-provider.js +278 -0
- package/dist/environment-provider.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +64 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Phillip Aaron Wells
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# @pawells/config-provider-env
|
|
2
|
+
|
|
3
|
+
[](https://github.com/PhillipAWells/config/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@pawells/config-provider-env)
|
|
5
|
+
[](https://nodejs.org)
|
|
6
|
+
[](../../LICENSE)
|
|
7
|
+
|
|
8
|
+
## Description
|
|
9
|
+
|
|
10
|
+
`@pawells/config-provider-env` is a configuration provider for `@pawells/config` that loads settings from `process.env` and an optional `.env` file. Values in the dotenv file overwrite `process.env` for the same key. Environment variable strings are parsed to their native JavaScript types (numbers, booleans, arrays, `null`) before being handed to `ConfigManager` for schema validation.
|
|
11
|
+
|
|
12
|
+
The provider also supports writing configuration back to disk (`Save`), making it suitable for generating `.env.example` templates and snapshotting live values.
|
|
13
|
+
|
|
14
|
+
See the [workspace README](../../README.md) for an end-to-end quick start. See [CHANGELOG.md](../../CHANGELOG.md) for version history.
|
|
15
|
+
|
|
16
|
+
## Requirements
|
|
17
|
+
|
|
18
|
+
- Node.js `>=22.0.0`
|
|
19
|
+
- `@pawells/config` (peer dependency, installed as a direct dependency via `workspace:*` in the monorepo)
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
yarn add @pawells/config-provider-env @pawells/config
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
All packages are ESM-only (`"type": "module"`).
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { ConfigManager, RegisterConfigSchema, Secret } from '@pawells/config';
|
|
33
|
+
import { ConfigEnvironmentProvider } from '@pawells/config-provider-env';
|
|
34
|
+
import { z } from 'zod';
|
|
35
|
+
|
|
36
|
+
// Register the provider BEFORE importing any schema modules.
|
|
37
|
+
// The factory method creates, registers, and returns the provider instance.
|
|
38
|
+
const envProvider = await ConfigEnvironmentProvider.Register({ path: '.env' });
|
|
39
|
+
|
|
40
|
+
// Define a schema — provider values are already available.
|
|
41
|
+
const AppConfig = RegisterConfigSchema('App', z.object({
|
|
42
|
+
HOST: z.string().min(1).default('localhost'),
|
|
43
|
+
PORT: z.coerce.number().int().positive().default(3000),
|
|
44
|
+
API_KEY: Secret(z.string().min(1)).default(''),
|
|
45
|
+
}));
|
|
46
|
+
|
|
47
|
+
// Read typed values.
|
|
48
|
+
const host = AppConfig.Get('HOST'); // string
|
|
49
|
+
const port = AppConfig.Get('PORT'); // number
|
|
50
|
+
|
|
51
|
+
// Generate .env.example — defaults written; secrets blank.
|
|
52
|
+
await ConfigManager.Save(envProvider, { path: '.env.example' });
|
|
53
|
+
|
|
54
|
+
// Snapshot current runtime values — secrets included.
|
|
55
|
+
await ConfigManager.Save(envProvider, { path: '.env.snapshot', useCurrentValues: true });
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## API Reference
|
|
59
|
+
|
|
60
|
+
### `ConfigEnvironmentProvider`
|
|
61
|
+
|
|
62
|
+
An async configuration provider that extends `ConfigProvider` from `@pawells/config`.
|
|
63
|
+
|
|
64
|
+
#### `static Register(options?)`
|
|
65
|
+
|
|
66
|
+
Convenience factory: creates a `ConfigEnvironmentProvider` instance, registers it with `ConfigManager`, and returns it.
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
static async Register(
|
|
70
|
+
options?: Partial<TConfigENVProviderOptions>
|
|
71
|
+
): Promise<ConfigEnvironmentProvider>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Unspecified options use schema defaults (`name: 'environment'`, `path: <cwd>/.env`).
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
// Register with all defaults
|
|
78
|
+
const provider = await ConfigEnvironmentProvider.Register();
|
|
79
|
+
|
|
80
|
+
// Register with a custom path
|
|
81
|
+
const provider = await ConfigEnvironmentProvider.Register({
|
|
82
|
+
path: '.env.production'
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
#### Constructor
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
new ConfigEnvironmentProvider(options: TConfigENVProviderOptions)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
After constructing, pass the instance to `ConfigManager.RegisterProvider` manually if you need the instance before registration:
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
import { ConfigManager } from '@pawells/config';
|
|
96
|
+
import { ConfigEnvironmentProvider } from '@pawells/config-provider-env';
|
|
97
|
+
|
|
98
|
+
const provider = new ConfigEnvironmentProvider({ name: 'env', path: '.env' });
|
|
99
|
+
await ConfigManager.RegisterProvider(provider);
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
#### `Load()`
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
async Load(): Promise<Record<string, unknown>>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Loads configuration from `process.env` and optionally the `.env` file at `options.path`:
|
|
109
|
+
|
|
110
|
+
1. All entries in `process.env` are read first.
|
|
111
|
+
2. If `options.path` is set, the dotenv file is parsed and its entries overwrite `process.env` values for the same key.
|
|
112
|
+
3. All string values are passed through an internal parser that converts JSON-encoded booleans, numbers, arrays, and `null` to their native JavaScript types. Plain strings are returned unchanged.
|
|
113
|
+
4. If the dotenv file is absent (`ENOENT`), it is silently skipped — only `process.env` is returned.
|
|
114
|
+
5. Security exceptions (symlink path, path-traversal sequence) are propagated as errors.
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
// .env: APP_PORT=8080
|
|
118
|
+
// process.env: APP_HOST=prod.example.com
|
|
119
|
+
const values = await provider.Load();
|
|
120
|
+
// → { APP_HOST: 'prod.example.com', APP_PORT: 8080 }
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Throws** `ConfigError` when the dotenv path is a symlink or contains a `..` traversal sequence.
|
|
124
|
+
|
|
125
|
+
#### `Save(entries, options?)`
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
async Save(
|
|
129
|
+
entries: readonly ConfigSaveEntry[],
|
|
130
|
+
options?: TConfigENVProviderSaveOptions
|
|
131
|
+
): Promise<void>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Writes configuration entries to a `.env`-format file. Call via `ConfigManager.Save` rather than directly.
|
|
135
|
+
|
|
136
|
+
**Template mode** (`useCurrentValues: false`, the default):
|
|
137
|
+
- Each entry is written as `KEY=<default value>`.
|
|
138
|
+
- Entries where `entry.isSecret` is `true` are written as `KEY=` (blank value), regardless of their default.
|
|
139
|
+
- This is suitable for generating `.env.example` files safe to commit.
|
|
140
|
+
|
|
141
|
+
**Current-values mode** (`useCurrentValues: true`):
|
|
142
|
+
- All entries including secrets are written with their live resolved values.
|
|
143
|
+
- Use for runtime snapshots or debugging.
|
|
144
|
+
|
|
145
|
+
If a Zod `.describe()` annotation is present on the field, it is emitted as a `# comment` line immediately before the key.
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
// Template output in .env.example:
|
|
149
|
+
// # Server port
|
|
150
|
+
// APP_PORT=3000
|
|
151
|
+
// # API key
|
|
152
|
+
// APP_API_KEY=
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Options:**
|
|
156
|
+
|
|
157
|
+
| Field | Type | Default | Description |
|
|
158
|
+
|---|---|---|---|
|
|
159
|
+
| `path` | `string` (optional) | `this.options.path` | Output file path; overrides the constructor path if provided. |
|
|
160
|
+
| `useCurrentValues` | `boolean` (optional) | `false` | `false` = registered defaults, secrets blank; `true` = live resolved values. |
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
### Options types
|
|
165
|
+
|
|
166
|
+
#### `TConfigENVProviderOptions`
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
type TConfigENVProviderOptions = {
|
|
170
|
+
name: string // Default: 'environment'
|
|
171
|
+
path: string // Default: <cwd>/.env; '..' sequences are rejected
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Validated by `CONFIG_ENV_PROVIDER_OPTIONS_SCHEMA`.
|
|
176
|
+
|
|
177
|
+
#### `TConfigENVProviderSaveOptions`
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
type TConfigENVProviderSaveOptions = {
|
|
181
|
+
path?: string // Overrides constructor path if provided; '..' sequences are rejected
|
|
182
|
+
useCurrentValues?: boolean // Default: false
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Validated by `CONFIG_ENV_PROVIDER_SAVE_OPTIONS_SCHEMA`.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
### Assertion and validation utilities
|
|
191
|
+
|
|
192
|
+
| Export | Description |
|
|
193
|
+
|---|---|
|
|
194
|
+
| `CONFIG_ENV_PROVIDER_OPTIONS_SCHEMA` | Zod schema for `TConfigENVProviderOptions` |
|
|
195
|
+
| `AssertConfigENVProviderOptions(options)` | Asserts conformance; throws `ZodError` otherwise |
|
|
196
|
+
| `ValidateConfigENVProviderOptions(options)` | Returns `true` if valid; `false` otherwise |
|
|
197
|
+
| `CONFIG_ENV_PROVIDER_SAVE_OPTIONS_SCHEMA` | Zod schema for `TConfigENVProviderSaveOptions` |
|
|
198
|
+
| `AssertConfigENVProviderSaveOptions(options)` | Asserts conformance; throws `ZodError` otherwise |
|
|
199
|
+
| `ValidateConfigENVProviderSaveOptions(options)` | Returns `true` if valid; `false` otherwise |
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
### Security
|
|
204
|
+
|
|
205
|
+
- **Symlink rejection** — The dotenv path is checked; if it resolves to a symlink, `Load()` throws a `ConfigError`.
|
|
206
|
+
- **Path-traversal protection** — Any path containing `..` is rejected by the options schema at construction time and at save time.
|
|
207
|
+
|
|
208
|
+
## License
|
|
209
|
+
|
|
210
|
+
MIT — See [LICENSE](../../LICENSE) for details.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intelligently parses an environment variable string to its native JavaScript type.
|
|
3
|
+
*
|
|
4
|
+
* Attempts `JSON.parse()` to convert encoded booleans (`'true'`), numbers (`'42'`),
|
|
5
|
+
* arrays (`'["a","b"]'`), and `null` to their native JavaScript equivalents.
|
|
6
|
+
* Falls back to returning the original string unchanged if JSON parsing fails.
|
|
7
|
+
* Uses a fast-path optimization to skip `JSON.parse()` for plain strings.
|
|
8
|
+
*
|
|
9
|
+
* @param envVarValue - Raw string value read from `process.env`
|
|
10
|
+
* @returns Parsed value; a native JS type if JSON-parseable, otherwise the original string
|
|
11
|
+
*
|
|
12
|
+
* @remarks
|
|
13
|
+
* The string `"null"` is parsed by `JSON.parse()` and returns the JavaScript `null` value
|
|
14
|
+
* (not the string `"null"`). Schemas that expect a string will reject this value via `safeParse()`.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* ParseEnvVarValue('true') // → true (boolean)
|
|
19
|
+
* ParseEnvVarValue('42') // → 42 (number)
|
|
20
|
+
* ParseEnvVarValue('["a","b"]') // → ['a', 'b'] (string[])
|
|
21
|
+
* ParseEnvVarValue('hello world') // → 'hello world' (string, unchanged)
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare function ParseEnvVarValue(envVarValue: string): unknown;
|
|
25
|
+
/**
|
|
26
|
+
* Parses a `.env` file from disk into a flat key/value record.
|
|
27
|
+
*
|
|
28
|
+
* Processing rules (applied per line):
|
|
29
|
+
* - Lines beginning with `#` (after trimming whitespace) are treated as comments and skipped
|
|
30
|
+
* - Blank lines are skipped
|
|
31
|
+
* - Lines containing `=` are split on the first `=`; the key is trimmed; the value is trimmed
|
|
32
|
+
* and surrounding single- or double-quotes are stripped if present
|
|
33
|
+
* - Inline comments (e.g. `KEY=value # comment`) are stripped from unquoted values
|
|
34
|
+
* - Lines without `=` are skipped
|
|
35
|
+
* - Windows-style `\r\n` line endings are normalized automatically
|
|
36
|
+
* - Paths containing `..` traversal sequences are rejected for security
|
|
37
|
+
* - Symbolic links are not permitted for security (both final component and parent directories)
|
|
38
|
+
*
|
|
39
|
+
* @param path - Path to the `.env` file to read
|
|
40
|
+
* @returns A promise resolving to a record mapping each key to its raw string value
|
|
41
|
+
* @throws {ConfigError} When the path contains `..` directory traversal sequences
|
|
42
|
+
* @throws {ConfigError} When the path is a symbolic link (final component or parent directory)
|
|
43
|
+
* @throws {Error} If the file cannot be read (e.g. not found, permission denied)
|
|
44
|
+
*
|
|
45
|
+
* @remarks
|
|
46
|
+
* Parent directories are canonicalized via realpath to detect symlinked ancestor directories.
|
|
47
|
+
* The final path component is checked via O_NOFOLLOW for atomic symlink rejection.
|
|
48
|
+
* Inline comments must be preceded by a space to be recognized (e.g., `KEY=value # comment`).
|
|
49
|
+
* A `#` that is not preceded by a space is treated as part of the value.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* // .env contents:
|
|
54
|
+
* // # Database configuration
|
|
55
|
+
* // HOST=localhost
|
|
56
|
+
* // PORT=3000
|
|
57
|
+
* // SECRET="my-token"
|
|
58
|
+
* const values = await ParseDotEnvFileAsync('./.env');
|
|
59
|
+
* // → { HOST: 'localhost', PORT: '3000', SECRET: 'my-token' }
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export declare function ParseDotEnvFileAsync(path: string): Promise<Record<string, string>>;
|
|
63
|
+
/**
|
|
64
|
+
* Serializes a configuration value to its `.env` string representation.
|
|
65
|
+
*
|
|
66
|
+
* Conversion rules:
|
|
67
|
+
* - `null` or `undefined` → `''` (blank)
|
|
68
|
+
* - Arrays → JSON-stringified (e.g. `["a","b"]`)
|
|
69
|
+
* - Plain objects → JSON-stringified
|
|
70
|
+
* - `Date` → ISO 8601 string
|
|
71
|
+
* - All other values → `String(value)`
|
|
72
|
+
*
|
|
73
|
+
* @param value - The configuration value to serialize
|
|
74
|
+
* @returns The serialized string ready for inclusion in a `.env` file
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* SerializeConfigValue('hello') // → 'hello'
|
|
79
|
+
* SerializeConfigValue(42) // → '42'
|
|
80
|
+
* SerializeConfigValue(true) // → 'true'
|
|
81
|
+
* SerializeConfigValue(['a', 'b']) // → '["a","b"]'
|
|
82
|
+
* SerializeConfigValue({ key: 'value' }) // → '{"key":"value"}'
|
|
83
|
+
* SerializeConfigValue(new Date('2024-01-01T00:00:00.000Z'))
|
|
84
|
+
* // → '2024-01-01T00:00:00.000Z'
|
|
85
|
+
* SerializeConfigValue(null) // → ''
|
|
86
|
+
* SerializeConfigValue(undefined) // → ''
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export declare function SerializeConfigValue(value: unknown): string;
|
|
90
|
+
//# sourceMappingURL=env-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-utils.d.ts","sourceRoot":"","sources":["../src/env-utils.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAqB7D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CA8ExF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAM3D"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { promises as fs, constants as fsConstants } from 'node:fs';
|
|
2
|
+
import { normalize, dirname, basename, join } from 'node:path';
|
|
3
|
+
import { ConfigError } from '@pawells/config';
|
|
4
|
+
/**
|
|
5
|
+
* Intelligently parses an environment variable string to its native JavaScript type.
|
|
6
|
+
*
|
|
7
|
+
* Attempts `JSON.parse()` to convert encoded booleans (`'true'`), numbers (`'42'`),
|
|
8
|
+
* arrays (`'["a","b"]'`), and `null` to their native JavaScript equivalents.
|
|
9
|
+
* Falls back to returning the original string unchanged if JSON parsing fails.
|
|
10
|
+
* Uses a fast-path optimization to skip `JSON.parse()` for plain strings.
|
|
11
|
+
*
|
|
12
|
+
* @param envVarValue - Raw string value read from `process.env`
|
|
13
|
+
* @returns Parsed value; a native JS type if JSON-parseable, otherwise the original string
|
|
14
|
+
*
|
|
15
|
+
* @remarks
|
|
16
|
+
* The string `"null"` is parsed by `JSON.parse()` and returns the JavaScript `null` value
|
|
17
|
+
* (not the string `"null"`). Schemas that expect a string will reject this value via `safeParse()`.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* ParseEnvVarValue('true') // → true (boolean)
|
|
22
|
+
* ParseEnvVarValue('42') // → 42 (number)
|
|
23
|
+
* ParseEnvVarValue('["a","b"]') // → ['a', 'b'] (string[])
|
|
24
|
+
* ParseEnvVarValue('hello world') // → 'hello world' (string, unchanged)
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export function ParseEnvVarValue(envVarValue) {
|
|
28
|
+
// Fast path: skip JSON.parse for plain strings that cannot be valid JSON
|
|
29
|
+
const firstChar = envVarValue[0];
|
|
30
|
+
if (firstChar !== '{'
|
|
31
|
+
&& firstChar !== '['
|
|
32
|
+
&& firstChar !== '"'
|
|
33
|
+
&& envVarValue !== 'true'
|
|
34
|
+
&& envVarValue !== 'false'
|
|
35
|
+
&& envVarValue !== 'null'
|
|
36
|
+
&& !/^-?\d/.test(envVarValue)) {
|
|
37
|
+
return envVarValue;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
return JSON.parse(envVarValue);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return envVarValue;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Parses a `.env` file from disk into a flat key/value record.
|
|
48
|
+
*
|
|
49
|
+
* Processing rules (applied per line):
|
|
50
|
+
* - Lines beginning with `#` (after trimming whitespace) are treated as comments and skipped
|
|
51
|
+
* - Blank lines are skipped
|
|
52
|
+
* - Lines containing `=` are split on the first `=`; the key is trimmed; the value is trimmed
|
|
53
|
+
* and surrounding single- or double-quotes are stripped if present
|
|
54
|
+
* - Inline comments (e.g. `KEY=value # comment`) are stripped from unquoted values
|
|
55
|
+
* - Lines without `=` are skipped
|
|
56
|
+
* - Windows-style `\r\n` line endings are normalized automatically
|
|
57
|
+
* - Paths containing `..` traversal sequences are rejected for security
|
|
58
|
+
* - Symbolic links are not permitted for security (both final component and parent directories)
|
|
59
|
+
*
|
|
60
|
+
* @param path - Path to the `.env` file to read
|
|
61
|
+
* @returns A promise resolving to a record mapping each key to its raw string value
|
|
62
|
+
* @throws {ConfigError} When the path contains `..` directory traversal sequences
|
|
63
|
+
* @throws {ConfigError} When the path is a symbolic link (final component or parent directory)
|
|
64
|
+
* @throws {Error} If the file cannot be read (e.g. not found, permission denied)
|
|
65
|
+
*
|
|
66
|
+
* @remarks
|
|
67
|
+
* Parent directories are canonicalized via realpath to detect symlinked ancestor directories.
|
|
68
|
+
* The final path component is checked via O_NOFOLLOW for atomic symlink rejection.
|
|
69
|
+
* Inline comments must be preceded by a space to be recognized (e.g., `KEY=value # comment`).
|
|
70
|
+
* A `#` that is not preceded by a space is treated as part of the value.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* // .env contents:
|
|
75
|
+
* // # Database configuration
|
|
76
|
+
* // HOST=localhost
|
|
77
|
+
* // PORT=3000
|
|
78
|
+
* // SECRET="my-token"
|
|
79
|
+
* const values = await ParseDotEnvFileAsync('./.env');
|
|
80
|
+
* // → { HOST: 'localhost', PORT: '3000', SECRET: 'my-token' }
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export async function ParseDotEnvFileAsync(path) {
|
|
84
|
+
if (normalize(path).includes('..'))
|
|
85
|
+
throw new ConfigError(`Path traversal sequences ("..") are not permitted. Received: "${path}"`);
|
|
86
|
+
try {
|
|
87
|
+
// Canonicalize parent directory to detect symlinked ancestors
|
|
88
|
+
const realParent = await fs.realpath(dirname(path));
|
|
89
|
+
const safePath = join(realParent, basename(path));
|
|
90
|
+
// Open file with O_NOFOLLOW to reject symlink final component atomically
|
|
91
|
+
const filehandle = await fs.open(safePath, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW);
|
|
92
|
+
let raw;
|
|
93
|
+
try {
|
|
94
|
+
const buffer = await filehandle.readFile();
|
|
95
|
+
raw = buffer.toString('utf-8');
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
await filehandle.close();
|
|
99
|
+
}
|
|
100
|
+
const result = {};
|
|
101
|
+
for (const rawLine of raw.split('\n')) {
|
|
102
|
+
const line = rawLine.replace(/\r$/, '').trim();
|
|
103
|
+
if (line === '' || line.startsWith('#'))
|
|
104
|
+
continue;
|
|
105
|
+
const eqIdx = line.indexOf('=');
|
|
106
|
+
if (eqIdx === -1)
|
|
107
|
+
continue;
|
|
108
|
+
const key = line.slice(0, eqIdx).trim();
|
|
109
|
+
let value = line.slice(eqIdx + 1).trim();
|
|
110
|
+
if ((value.startsWith('"') && value.endsWith('"'))
|
|
111
|
+
|| (value.startsWith('\'') && value.endsWith('\''))) {
|
|
112
|
+
value = value.slice(1, -1);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
// Strip inline # comments (only if value is not quoted)
|
|
116
|
+
const commentIdx = value.indexOf(' #');
|
|
117
|
+
if (commentIdx !== -1) {
|
|
118
|
+
value = value.slice(0, commentIdx).trim();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (key !== '') {
|
|
122
|
+
result[key] = value;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
// If it's already a ConfigError, rethrow it
|
|
129
|
+
if (error instanceof ConfigError) {
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
// Check error codes from fs.open and realpath
|
|
133
|
+
const errorCode = error instanceof Error ? error.code : undefined;
|
|
134
|
+
// ELOOP (Linux) or ENOTDIR (macOS) indicate symlink in final component
|
|
135
|
+
if (errorCode === 'ELOOP' || errorCode === 'ENOTDIR') {
|
|
136
|
+
throw new ConfigError('Symlink paths are not permitted.');
|
|
137
|
+
}
|
|
138
|
+
// Check if it's ENOENT (file not found) — allow optional dotenv files to return empty
|
|
139
|
+
const isNotFound = errorCode === 'ENOENT';
|
|
140
|
+
// If file is missing, return empty config (dotenv files are optional by nature)
|
|
141
|
+
if (isNotFound) {
|
|
142
|
+
return {};
|
|
143
|
+
}
|
|
144
|
+
// All other errors (permission, etc.) are rethrown
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Serializes a configuration value to its `.env` string representation.
|
|
150
|
+
*
|
|
151
|
+
* Conversion rules:
|
|
152
|
+
* - `null` or `undefined` → `''` (blank)
|
|
153
|
+
* - Arrays → JSON-stringified (e.g. `["a","b"]`)
|
|
154
|
+
* - Plain objects → JSON-stringified
|
|
155
|
+
* - `Date` → ISO 8601 string
|
|
156
|
+
* - All other values → `String(value)`
|
|
157
|
+
*
|
|
158
|
+
* @param value - The configuration value to serialize
|
|
159
|
+
* @returns The serialized string ready for inclusion in a `.env` file
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* ```typescript
|
|
163
|
+
* SerializeConfigValue('hello') // → 'hello'
|
|
164
|
+
* SerializeConfigValue(42) // → '42'
|
|
165
|
+
* SerializeConfigValue(true) // → 'true'
|
|
166
|
+
* SerializeConfigValue(['a', 'b']) // → '["a","b"]'
|
|
167
|
+
* SerializeConfigValue({ key: 'value' }) // → '{"key":"value"}'
|
|
168
|
+
* SerializeConfigValue(new Date('2024-01-01T00:00:00.000Z'))
|
|
169
|
+
* // → '2024-01-01T00:00:00.000Z'
|
|
170
|
+
* SerializeConfigValue(null) // → ''
|
|
171
|
+
* SerializeConfigValue(undefined) // → ''
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
export function SerializeConfigValue(value) {
|
|
175
|
+
if (value === null || value === undefined)
|
|
176
|
+
return '';
|
|
177
|
+
if (value instanceof Date)
|
|
178
|
+
return value.toISOString();
|
|
179
|
+
if (Array.isArray(value))
|
|
180
|
+
return JSON.stringify(value);
|
|
181
|
+
if (value !== null && typeof value === 'object')
|
|
182
|
+
return JSON.stringify(value);
|
|
183
|
+
return String(value);
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=env-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-utils.js","sourceRoot":"","sources":["../src/env-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
|
+
import { ConfigProvider, type ConfigSaveEntry } from '@pawells/config';
|
|
3
|
+
/**
|
|
4
|
+
* Zod schema for validating ConfigEnvironmentProvider constructor options.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* This schema extends the base {@link CONFIG_PROVIDER_OPTIONS_SCHEMA} with environment-specific fields:
|
|
8
|
+
* - `name`: Unique provider identifier (default: `'environment'`)
|
|
9
|
+
* - `path`: Path to the `.env` file to load (default: `.env` in current working directory);
|
|
10
|
+
* paths containing `..` directory traversal sequences are rejected for security
|
|
11
|
+
*/
|
|
12
|
+
export declare const CONFIG_ENV_PROVIDER_OPTIONS_SCHEMA: z.ZodObject<{
|
|
13
|
+
name: z.ZodDefault<z.ZodString>;
|
|
14
|
+
path: z.ZodDefault<z.ZodString>;
|
|
15
|
+
}, z.core.$strip>;
|
|
16
|
+
/**
|
|
17
|
+
* Runtime type extracted from {@link CONFIG_ENV_PROVIDER_OPTIONS_SCHEMA}.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const options: TConfigENVProviderOptions = {
|
|
22
|
+
* name: 'my-env',
|
|
23
|
+
* path: '.env.local'
|
|
24
|
+
* };
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export type TConfigENVProviderOptions = z.infer<typeof CONFIG_ENV_PROVIDER_OPTIONS_SCHEMA>;
|
|
28
|
+
/**
|
|
29
|
+
* Asserts that a value conforms to {@link TConfigENVProviderOptions}.
|
|
30
|
+
*
|
|
31
|
+
* @param options - The value to validate
|
|
32
|
+
* @throws {ZodError} When validation fails
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* AssertConfigENVProviderOptions(userInput);
|
|
37
|
+
* // If no error, userInput is safely typed as TConfigENVProviderOptions
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function AssertConfigENVProviderOptions(options: unknown): asserts options is TConfigENVProviderOptions;
|
|
41
|
+
/**
|
|
42
|
+
* Validates whether a value conforms to {@link TConfigENVProviderOptions}.
|
|
43
|
+
*
|
|
44
|
+
* @param options - The value to validate
|
|
45
|
+
* @returns `true` if valid; `false` otherwise
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* if (ValidateConfigENVProviderOptions(userInput)) {
|
|
50
|
+
* // userInput is valid
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export declare function ValidateConfigENVProviderOptions(options: unknown): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Zod schema for validating ConfigEnvironmentProvider Save options.
|
|
57
|
+
*
|
|
58
|
+
* @remarks
|
|
59
|
+
* Extends the base {@link CONFIG_PROVIDER_SAVE_OPTIONS_SCHEMA} with:
|
|
60
|
+
* - `path`: Optional output file path; overrides constructor path if provided
|
|
61
|
+
*/
|
|
62
|
+
export declare const CONFIG_ENV_PROVIDER_SAVE_OPTIONS_SCHEMA: z.ZodObject<{
|
|
63
|
+
useCurrentValues: z.ZodOptional<z.ZodBoolean>;
|
|
64
|
+
path: z.ZodOptional<z.ZodString>;
|
|
65
|
+
}, z.core.$strip>;
|
|
66
|
+
/**
|
|
67
|
+
* Runtime type extracted from {@link CONFIG_ENV_PROVIDER_SAVE_OPTIONS_SCHEMA}.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* const options: TConfigENVProviderSaveOptions = {
|
|
72
|
+
* path: '.env.example',
|
|
73
|
+
* useCurrentValues: false
|
|
74
|
+
* };
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export type TConfigENVProviderSaveOptions = z.infer<typeof CONFIG_ENV_PROVIDER_SAVE_OPTIONS_SCHEMA>;
|
|
78
|
+
/**
|
|
79
|
+
* Asserts that a value conforms to {@link TConfigENVProviderSaveOptions}.
|
|
80
|
+
*
|
|
81
|
+
* @param options - The value to validate
|
|
82
|
+
* @throws {ZodError} When validation fails
|
|
83
|
+
*/
|
|
84
|
+
export declare function AssertConfigENVProviderSaveOptions(options: unknown): asserts options is TConfigENVProviderSaveOptions;
|
|
85
|
+
/**
|
|
86
|
+
* Validates whether a value conforms to {@link TConfigENVProviderSaveOptions}.
|
|
87
|
+
*
|
|
88
|
+
* @param options - The value to validate
|
|
89
|
+
* @returns `true` if valid; `false` otherwise
|
|
90
|
+
*/
|
|
91
|
+
export declare function ValidateConfigENVProviderSaveOptions(options: unknown): boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Configuration provider for environment variables and `.env` files.
|
|
94
|
+
*
|
|
95
|
+
* Loads configuration from `process.env` and an optional `.env` file. This provider
|
|
96
|
+
* implements the {@link ConfigProvider} interface, supporting both {@link Load} and
|
|
97
|
+
* {@link Save} operations. It is suitable for development environments and containerized
|
|
98
|
+
* deployments where configuration is typically provided via environment variables.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```typescript
|
|
102
|
+
* // Register with defaults
|
|
103
|
+
* const provider = await ConfigEnvironmentProvider.Register();
|
|
104
|
+
*
|
|
105
|
+
* // Register with custom path
|
|
106
|
+
* const provider = await ConfigEnvironmentProvider.Register({
|
|
107
|
+
* path: '.env.production'
|
|
108
|
+
* });
|
|
109
|
+
*
|
|
110
|
+
* // Or instantiate directly
|
|
111
|
+
* const provider = new ConfigEnvironmentProvider({
|
|
112
|
+
* name: 'app-env',
|
|
113
|
+
* path: '.env'
|
|
114
|
+
* });
|
|
115
|
+
* await ConfigManager.RegisterProvider(provider);
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
export declare class ConfigEnvironmentProvider extends ConfigProvider<TConfigENVProviderOptions, unknown, TConfigENVProviderSaveOptions> {
|
|
119
|
+
/**
|
|
120
|
+
* Initialize a configuration provider that loads from environment variables and a `.env` file.
|
|
121
|
+
*
|
|
122
|
+
* @param options - Provider configuration object with `name` and optional `path`
|
|
123
|
+
* @param options.name - Unique provider name (default: `'environment'`)
|
|
124
|
+
* @param options.path - Path to the `.env` file to load (default: `.env` in current working directory)
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```typescript
|
|
128
|
+
* const provider = new ConfigEnvironmentProvider({
|
|
129
|
+
* name: 'my-env-provider',
|
|
130
|
+
* path: './.env.local'
|
|
131
|
+
* });
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
constructor(options: TConfigENVProviderOptions);
|
|
135
|
+
/**
|
|
136
|
+
* Load configuration values from `process.env` and, optionally, a `.env` file.
|
|
137
|
+
*
|
|
138
|
+
* Reads all entries in `process.env` first, then (if `options.path` is set)
|
|
139
|
+
* reads the dotenv file and overwrites any overlapping keys. All values are passed
|
|
140
|
+
* through {@link ParseEnvVarValue} before being returned.
|
|
141
|
+
*
|
|
142
|
+
* If the dotenv file is missing or cannot be read due to permission errors or other
|
|
143
|
+
* file system issues, the file is silently skipped and only environment variables are
|
|
144
|
+
* returned. The dotenv file is optional and its absence or read failure does not prevent
|
|
145
|
+
* configuration loading.
|
|
146
|
+
*
|
|
147
|
+
* Security rejections (symlink detection, path traversal) are propagated as errors.
|
|
148
|
+
*
|
|
149
|
+
* @returns A record of fully-qualified config key names to their parsed values
|
|
150
|
+
* @throws {ConfigError} When the dotenv path is a symlink or contains path traversal sequences
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```typescript
|
|
154
|
+
* // process.env = { KEYCLOAK_HOST: 'prod.example.com' }
|
|
155
|
+
* // .env = { KEYCLOAK_HOST: 'localhost', KEYCLOAK_PORT: '8080' }
|
|
156
|
+
* const provider = new ConfigEnvironmentProvider({
|
|
157
|
+
* name: 'env',
|
|
158
|
+
* path: '.env'
|
|
159
|
+
* });
|
|
160
|
+
* const config = await provider.Load();
|
|
161
|
+
* // → { KEYCLOAK_HOST: 'localhost', KEYCLOAK_PORT: 8080 }
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
Load(): Promise<Record<string, unknown>>;
|
|
165
|
+
/**
|
|
166
|
+
* Save configuration values to a `.env`-format file.
|
|
167
|
+
*
|
|
168
|
+
* In template mode (`useCurrentValues: false`, the default), each entry is
|
|
169
|
+
* written using its registered default value. Secret fields (where
|
|
170
|
+
* `entry.isSecret` is `true`) are always written with a blank value in
|
|
171
|
+
* template mode, regardless of their default — making this suitable for
|
|
172
|
+
* generating `.env.example` files.
|
|
173
|
+
*
|
|
174
|
+
* In current-values mode (`useCurrentValues: true`), the live resolved value
|
|
175
|
+
* is used for every field including secrets — suitable for snapshotting the
|
|
176
|
+
* active runtime configuration.
|
|
177
|
+
*
|
|
178
|
+
* If a field carries a description (from a Zod `.describe()` annotation) it
|
|
179
|
+
* is emitted as a `# comment` line immediately before the key–value pair.
|
|
180
|
+
*
|
|
181
|
+
* @param entries - All registered config entries supplied by {@link ConfigManager.Save}
|
|
182
|
+
* @param options - Save options
|
|
183
|
+
* @param options.path - Output file path; overrides `this.options.path` if provided
|
|
184
|
+
* @param options.useCurrentValues - When `true`, emit current live values; when `false` (default), emit registered defaults
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```typescript
|
|
188
|
+
* const provider = new ConfigEnvironmentProvider({
|
|
189
|
+
* name: 'env',
|
|
190
|
+
* path: '.env'
|
|
191
|
+
* });
|
|
192
|
+
*
|
|
193
|
+
* // Generate a .env.example template (secrets blank)
|
|
194
|
+
* await ConfigManager.Save(provider, { path: '.env.example' });
|
|
195
|
+
*
|
|
196
|
+
* // Snapshot current runtime config (secrets included)
|
|
197
|
+
* await ConfigManager.Save(provider, {
|
|
198
|
+
* path: '.env.snapshot',
|
|
199
|
+
* useCurrentValues: true,
|
|
200
|
+
* });
|
|
201
|
+
* ```
|
|
202
|
+
*/
|
|
203
|
+
Save(entries: readonly ConfigSaveEntry[], options?: TConfigENVProviderSaveOptions): Promise<void>;
|
|
204
|
+
/**
|
|
205
|
+
* Creates a ConfigEnvironmentProvider instance and registers it with ConfigManager.
|
|
206
|
+
*
|
|
207
|
+
* This is a convenience factory method that combines instantiation and registration in one call.
|
|
208
|
+
* Unspecified options use their schema defaults (`name: 'environment'`, `path: '.env'` in cwd).
|
|
209
|
+
*
|
|
210
|
+
* @param options - Optional partial provider configuration; unspecified fields use schema defaults
|
|
211
|
+
* @returns A promise resolving to the registered provider instance
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* ```typescript
|
|
215
|
+
* // Register with all defaults
|
|
216
|
+
* const provider = await ConfigEnvironmentProvider.Register();
|
|
217
|
+
*
|
|
218
|
+
* // Register with custom path
|
|
219
|
+
* const provider = await ConfigEnvironmentProvider.Register({
|
|
220
|
+
* path: '.env.production'
|
|
221
|
+
* });
|
|
222
|
+
* ```
|
|
223
|
+
*/
|
|
224
|
+
static Register(options?: Partial<TConfigENVProviderOptions>): Promise<ConfigEnvironmentProvider>;
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=environment-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environment-provider.d.ts","sourceRoot":"","sources":["../src/environment-provider.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAC3B,OAAO,EAAE,cAAc,EAAuE,KAAK,eAAe,EAA8B,MAAM,iBAAiB,CAAC;AAGxK;;;;;;;;GAQG;AACH,eAAO,MAAM,kCAAkC;;;iBAK7C,CAAC;AAEH;;;;;;;;;;GAUG;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAE3F;;;;;;;;;;;GAWG;AACH,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,yBAAyB,CAE7G;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gCAAgC,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAQ1E;AAED;;;;;;GAMG;AACH,eAAO,MAAM,uCAAuC;;;iBAIlD,CAAC;AAEH;;;;;;;;;;GAUG;AACH,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uCAAuC,CAAC,CAAC;AAEpG;;;;;GAKG;AACH,wBAAgB,kCAAkC,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,6BAA6B,CAErH;AAED;;;;;GAKG;AACH,wBAAgB,oCAAoC,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAQ9E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,yBAA0B,SAAQ,cAAc,CAAC,yBAAyB,EAAE,OAAO,EAAE,6BAA6B,CAAC;IAC/H;;;;;;;;;;;;;;OAcG;gBACS,OAAO,EAAE,yBAAyB;IAK9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACU,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IA6BrD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqCG;IACU,IAAI,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,EAAE,OAAO,CAAC,EAAE,6BAA6B,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB9G;;;;;;;;;;;;;;;;;;;OAmBG;WACiB,QAAQ,CAAC,OAAO,GAAE,OAAO,CAAC,yBAAyB,CAAM,GAAG,OAAO,CAAC,yBAAyB,CAAC;CAKlH"}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { promises as FS } from 'node:fs';
|
|
2
|
+
import * as PATH from 'node:path';
|
|
3
|
+
import { z } from 'zod/v4';
|
|
4
|
+
import { ConfigProvider, CONFIG_PROVIDER_OPTIONS_SCHEMA, CONFIG_PROVIDER_SAVE_OPTIONS_SCHEMA, ConfigManager, ConfigError } from '@pawells/config';
|
|
5
|
+
import { ParseEnvVarValue, ParseDotEnvFileAsync, SerializeConfigValue } from './env-utils.js';
|
|
6
|
+
/**
|
|
7
|
+
* Zod schema for validating ConfigEnvironmentProvider constructor options.
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* This schema extends the base {@link CONFIG_PROVIDER_OPTIONS_SCHEMA} with environment-specific fields:
|
|
11
|
+
* - `name`: Unique provider identifier (default: `'environment'`)
|
|
12
|
+
* - `path`: Path to the `.env` file to load (default: `.env` in current working directory);
|
|
13
|
+
* paths containing `..` directory traversal sequences are rejected for security
|
|
14
|
+
*/
|
|
15
|
+
export const CONFIG_ENV_PROVIDER_OPTIONS_SCHEMA = CONFIG_PROVIDER_OPTIONS_SCHEMA.extend({
|
|
16
|
+
name: z.string().min(1).default('environment'),
|
|
17
|
+
path: z.string().min(1).default(PATH.join(process.cwd(), '.env')).refine((path) => !PATH.normalize(path).includes('..'), {
|
|
18
|
+
message: 'Path traversal sequences ("..") are not permitted.'
|
|
19
|
+
})
|
|
20
|
+
});
|
|
21
|
+
/**
|
|
22
|
+
* Asserts that a value conforms to {@link TConfigENVProviderOptions}.
|
|
23
|
+
*
|
|
24
|
+
* @param options - The value to validate
|
|
25
|
+
* @throws {ZodError} When validation fails
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* AssertConfigENVProviderOptions(userInput);
|
|
30
|
+
* // If no error, userInput is safely typed as TConfigENVProviderOptions
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export function AssertConfigENVProviderOptions(options) {
|
|
34
|
+
CONFIG_ENV_PROVIDER_OPTIONS_SCHEMA.parse(options);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Validates whether a value conforms to {@link TConfigENVProviderOptions}.
|
|
38
|
+
*
|
|
39
|
+
* @param options - The value to validate
|
|
40
|
+
* @returns `true` if valid; `false` otherwise
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* if (ValidateConfigENVProviderOptions(userInput)) {
|
|
45
|
+
* // userInput is valid
|
|
46
|
+
* }
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export function ValidateConfigENVProviderOptions(options) {
|
|
50
|
+
try {
|
|
51
|
+
AssertConfigENVProviderOptions(options);
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Zod schema for validating ConfigEnvironmentProvider Save options.
|
|
60
|
+
*
|
|
61
|
+
* @remarks
|
|
62
|
+
* Extends the base {@link CONFIG_PROVIDER_SAVE_OPTIONS_SCHEMA} with:
|
|
63
|
+
* - `path`: Optional output file path; overrides constructor path if provided
|
|
64
|
+
*/
|
|
65
|
+
export const CONFIG_ENV_PROVIDER_SAVE_OPTIONS_SCHEMA = CONFIG_PROVIDER_SAVE_OPTIONS_SCHEMA.extend({
|
|
66
|
+
path: z.string().min(1).refine((path) => !PATH.normalize(path).includes('..'), {
|
|
67
|
+
message: 'Path traversal sequences ("..") are not permitted.'
|
|
68
|
+
}).optional()
|
|
69
|
+
});
|
|
70
|
+
/**
|
|
71
|
+
* Asserts that a value conforms to {@link TConfigENVProviderSaveOptions}.
|
|
72
|
+
*
|
|
73
|
+
* @param options - The value to validate
|
|
74
|
+
* @throws {ZodError} When validation fails
|
|
75
|
+
*/
|
|
76
|
+
export function AssertConfigENVProviderSaveOptions(options) {
|
|
77
|
+
CONFIG_ENV_PROVIDER_SAVE_OPTIONS_SCHEMA.parse(options);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Validates whether a value conforms to {@link TConfigENVProviderSaveOptions}.
|
|
81
|
+
*
|
|
82
|
+
* @param options - The value to validate
|
|
83
|
+
* @returns `true` if valid; `false` otherwise
|
|
84
|
+
*/
|
|
85
|
+
export function ValidateConfigENVProviderSaveOptions(options) {
|
|
86
|
+
try {
|
|
87
|
+
AssertConfigENVProviderSaveOptions(options);
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Configuration provider for environment variables and `.env` files.
|
|
96
|
+
*
|
|
97
|
+
* Loads configuration from `process.env` and an optional `.env` file. This provider
|
|
98
|
+
* implements the {@link ConfigProvider} interface, supporting both {@link Load} and
|
|
99
|
+
* {@link Save} operations. It is suitable for development environments and containerized
|
|
100
|
+
* deployments where configuration is typically provided via environment variables.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```typescript
|
|
104
|
+
* // Register with defaults
|
|
105
|
+
* const provider = await ConfigEnvironmentProvider.Register();
|
|
106
|
+
*
|
|
107
|
+
* // Register with custom path
|
|
108
|
+
* const provider = await ConfigEnvironmentProvider.Register({
|
|
109
|
+
* path: '.env.production'
|
|
110
|
+
* });
|
|
111
|
+
*
|
|
112
|
+
* // Or instantiate directly
|
|
113
|
+
* const provider = new ConfigEnvironmentProvider({
|
|
114
|
+
* name: 'app-env',
|
|
115
|
+
* path: '.env'
|
|
116
|
+
* });
|
|
117
|
+
* await ConfigManager.RegisterProvider(provider);
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
export class ConfigEnvironmentProvider extends ConfigProvider {
|
|
121
|
+
/**
|
|
122
|
+
* Initialize a configuration provider that loads from environment variables and a `.env` file.
|
|
123
|
+
*
|
|
124
|
+
* @param options - Provider configuration object with `name` and optional `path`
|
|
125
|
+
* @param options.name - Unique provider name (default: `'environment'`)
|
|
126
|
+
* @param options.path - Path to the `.env` file to load (default: `.env` in current working directory)
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* ```typescript
|
|
130
|
+
* const provider = new ConfigEnvironmentProvider({
|
|
131
|
+
* name: 'my-env-provider',
|
|
132
|
+
* path: './.env.local'
|
|
133
|
+
* });
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
constructor(options) {
|
|
137
|
+
super(options);
|
|
138
|
+
AssertConfigENVProviderOptions(options);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Load configuration values from `process.env` and, optionally, a `.env` file.
|
|
142
|
+
*
|
|
143
|
+
* Reads all entries in `process.env` first, then (if `options.path` is set)
|
|
144
|
+
* reads the dotenv file and overwrites any overlapping keys. All values are passed
|
|
145
|
+
* through {@link ParseEnvVarValue} before being returned.
|
|
146
|
+
*
|
|
147
|
+
* If the dotenv file is missing or cannot be read due to permission errors or other
|
|
148
|
+
* file system issues, the file is silently skipped and only environment variables are
|
|
149
|
+
* returned. The dotenv file is optional and its absence or read failure does not prevent
|
|
150
|
+
* configuration loading.
|
|
151
|
+
*
|
|
152
|
+
* Security rejections (symlink detection, path traversal) are propagated as errors.
|
|
153
|
+
*
|
|
154
|
+
* @returns A record of fully-qualified config key names to their parsed values
|
|
155
|
+
* @throws {ConfigError} When the dotenv path is a symlink or contains path traversal sequences
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* ```typescript
|
|
159
|
+
* // process.env = { KEYCLOAK_HOST: 'prod.example.com' }
|
|
160
|
+
* // .env = { KEYCLOAK_HOST: 'localhost', KEYCLOAK_PORT: '8080' }
|
|
161
|
+
* const provider = new ConfigEnvironmentProvider({
|
|
162
|
+
* name: 'env',
|
|
163
|
+
* path: '.env'
|
|
164
|
+
* });
|
|
165
|
+
* const config = await provider.Load();
|
|
166
|
+
* // → { KEYCLOAK_HOST: 'localhost', KEYCLOAK_PORT: 8080 }
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
async Load() {
|
|
170
|
+
const result = {};
|
|
171
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
172
|
+
if (value !== undefined) {
|
|
173
|
+
result[key] = ParseEnvVarValue(value);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (this.options.path !== undefined) {
|
|
177
|
+
try {
|
|
178
|
+
const dotenv = await ParseDotEnvFileAsync(this.options.path);
|
|
179
|
+
for (const [key, value] of Object.entries(dotenv)) {
|
|
180
|
+
result[key] = ParseEnvVarValue(value);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
// Re-throw ConfigError (security rejections: symlink, path traversal)
|
|
185
|
+
if (error instanceof ConfigError) {
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
188
|
+
// Silently skip other file errors (ENOENT, EACCES, etc.);
|
|
189
|
+
// dotenv is optional and fallback uses process.env values already collected
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return result;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Save configuration values to a `.env`-format file.
|
|
196
|
+
*
|
|
197
|
+
* In template mode (`useCurrentValues: false`, the default), each entry is
|
|
198
|
+
* written using its registered default value. Secret fields (where
|
|
199
|
+
* `entry.isSecret` is `true`) are always written with a blank value in
|
|
200
|
+
* template mode, regardless of their default — making this suitable for
|
|
201
|
+
* generating `.env.example` files.
|
|
202
|
+
*
|
|
203
|
+
* In current-values mode (`useCurrentValues: true`), the live resolved value
|
|
204
|
+
* is used for every field including secrets — suitable for snapshotting the
|
|
205
|
+
* active runtime configuration.
|
|
206
|
+
*
|
|
207
|
+
* If a field carries a description (from a Zod `.describe()` annotation) it
|
|
208
|
+
* is emitted as a `# comment` line immediately before the key–value pair.
|
|
209
|
+
*
|
|
210
|
+
* @param entries - All registered config entries supplied by {@link ConfigManager.Save}
|
|
211
|
+
* @param options - Save options
|
|
212
|
+
* @param options.path - Output file path; overrides `this.options.path` if provided
|
|
213
|
+
* @param options.useCurrentValues - When `true`, emit current live values; when `false` (default), emit registered defaults
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* ```typescript
|
|
217
|
+
* const provider = new ConfigEnvironmentProvider({
|
|
218
|
+
* name: 'env',
|
|
219
|
+
* path: '.env'
|
|
220
|
+
* });
|
|
221
|
+
*
|
|
222
|
+
* // Generate a .env.example template (secrets blank)
|
|
223
|
+
* await ConfigManager.Save(provider, { path: '.env.example' });
|
|
224
|
+
*
|
|
225
|
+
* // Snapshot current runtime config (secrets included)
|
|
226
|
+
* await ConfigManager.Save(provider, {
|
|
227
|
+
* path: '.env.snapshot',
|
|
228
|
+
* useCurrentValues: true,
|
|
229
|
+
* });
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
232
|
+
async Save(entries, options) {
|
|
233
|
+
if (options !== undefined)
|
|
234
|
+
AssertConfigENVProviderSaveOptions(options);
|
|
235
|
+
const useCurrentValues = options?.useCurrentValues ?? false;
|
|
236
|
+
const path = options?.path ?? this.options.path;
|
|
237
|
+
const lines = [];
|
|
238
|
+
for (const entry of entries) {
|
|
239
|
+
if (entry.description !== undefined) {
|
|
240
|
+
const safeDescription = entry.description.replace(/[\r\n]/g, ' ').replace(/#/g, '(hash)');
|
|
241
|
+
lines.push(`# ${safeDescription}`);
|
|
242
|
+
}
|
|
243
|
+
if (!useCurrentValues && entry.isSecret) {
|
|
244
|
+
lines.push(`${entry.key}=`);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
lines.push(`${entry.key}=${SerializeConfigValue(entry.value)}`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
await FS.writeFile(path, lines.join('\n'), 'utf-8');
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Creates a ConfigEnvironmentProvider instance and registers it with ConfigManager.
|
|
254
|
+
*
|
|
255
|
+
* This is a convenience factory method that combines instantiation and registration in one call.
|
|
256
|
+
* Unspecified options use their schema defaults (`name: 'environment'`, `path: '.env'` in cwd).
|
|
257
|
+
*
|
|
258
|
+
* @param options - Optional partial provider configuration; unspecified fields use schema defaults
|
|
259
|
+
* @returns A promise resolving to the registered provider instance
|
|
260
|
+
*
|
|
261
|
+
* @example
|
|
262
|
+
* ```typescript
|
|
263
|
+
* // Register with all defaults
|
|
264
|
+
* const provider = await ConfigEnvironmentProvider.Register();
|
|
265
|
+
*
|
|
266
|
+
* // Register with custom path
|
|
267
|
+
* const provider = await ConfigEnvironmentProvider.Register({
|
|
268
|
+
* path: '.env.production'
|
|
269
|
+
* });
|
|
270
|
+
* ```
|
|
271
|
+
*/
|
|
272
|
+
static async Register(options = {}) {
|
|
273
|
+
const provider = new ConfigEnvironmentProvider(CONFIG_ENV_PROVIDER_OPTIONS_SCHEMA.parse(options));
|
|
274
|
+
await ConfigManager.RegisterProvider(provider);
|
|
275
|
+
return provider;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
//# sourceMappingURL=environment-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environment-provider.js","sourceRoot":"","sources":["../src/environment-provider.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pawells/config-provider-env",
|
|
3
|
+
"displayName": "Config ENV Provider",
|
|
4
|
+
"version": "3.0.0-rc1",
|
|
5
|
+
"description": "Environment variable and dotenv file configuration provider for @pawells/config",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"config",
|
|
8
|
+
"configuration",
|
|
9
|
+
"environment",
|
|
10
|
+
"environment-variables",
|
|
11
|
+
"dotenv",
|
|
12
|
+
"typescript",
|
|
13
|
+
"nodejs"
|
|
14
|
+
],
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"author": {
|
|
17
|
+
"name": "Phillip Aaron Wells",
|
|
18
|
+
"email": "69355326+PhillipAWells@users.noreply.github.com"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://github.com/PhillipAWells/config",
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/PhillipAWells/config/issues"
|
|
23
|
+
},
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "https://github.com/PhillipAWells/config.git",
|
|
27
|
+
"directory": "packages/config-provider-env"
|
|
28
|
+
},
|
|
29
|
+
"funding": {
|
|
30
|
+
"type": "github",
|
|
31
|
+
"url": "https://github.com/sponsors/PhillipAWells"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=22.0.0"
|
|
35
|
+
},
|
|
36
|
+
"type": "module",
|
|
37
|
+
"types": "./dist/index.d.ts",
|
|
38
|
+
"exports": {
|
|
39
|
+
"./package.json": "./package.json",
|
|
40
|
+
".": {
|
|
41
|
+
"local": "./src/index.ts",
|
|
42
|
+
"types": "./dist/index.d.ts",
|
|
43
|
+
"import": "./dist/index.js",
|
|
44
|
+
"default": "./dist/index.js"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"files": [
|
|
48
|
+
"dist",
|
|
49
|
+
"!**/*.tsbuildinfo",
|
|
50
|
+
"LICENSE"
|
|
51
|
+
],
|
|
52
|
+
"sideEffects": false,
|
|
53
|
+
"scripts": {
|
|
54
|
+
"test:coverage": "yarn nx test config-provider-env -- --coverage"
|
|
55
|
+
},
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public"
|
|
58
|
+
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"@pawells/config": "workspace:*",
|
|
61
|
+
"tslib": "^2.8.1",
|
|
62
|
+
"zod": "^4.4.3"
|
|
63
|
+
}
|
|
64
|
+
}
|