@incanta/config 2.1.7 → 2.1.8
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 +421 -0
- package/package.json +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
# @incanta/config
|
|
2
|
+
|
|
3
|
+
A hierarchical, folder-based configuration library for Node.js with environment inheritance, variable cross-referencing, kebab-to-camelCase conversion, secrets provider integration, and support for YAML, JSON, JSONC, and JSON5 file formats.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @incanta/config
|
|
9
|
+
# or
|
|
10
|
+
yarn add @incanta/config
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import config from "@incanta/config";
|
|
17
|
+
|
|
18
|
+
const port = config.get<number>("server.port");
|
|
19
|
+
const debug = config.get<boolean>("environment.development");
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
By default, `@incanta/config` looks for a `config` directory in the current working directory and loads the `default` environment. You can override this with the `NODE_CONFIG_DIR` and `NODE_CONFIG_ENV` environment variables, or by creating a new `Config` instance directly.
|
|
23
|
+
|
|
24
|
+
## Core Concepts
|
|
25
|
+
|
|
26
|
+
### Directory-Based Configuration
|
|
27
|
+
|
|
28
|
+
Configuration values are defined by the folder structure, file names, and object keys within those files. Each subfolder and each file (minus its extension) adds a segment to the config key path.
|
|
29
|
+
|
|
30
|
+
Given this structure:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
config/
|
|
34
|
+
default/
|
|
35
|
+
_index.yaml
|
|
36
|
+
server.yaml
|
|
37
|
+
persistence/
|
|
38
|
+
database.yaml
|
|
39
|
+
redis.yaml
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
- `_index.yaml` contents are scoped to the parent folder (no path segment added)
|
|
43
|
+
- `server.yaml` values are accessed under `server.*`
|
|
44
|
+
- `persistence/database.yaml` values are accessed under `persistence.database.*`
|
|
45
|
+
- `persistence/redis.yaml` values are accessed under `persistence.redis.*`
|
|
46
|
+
|
|
47
|
+
For example, if `server.yaml` contains:
|
|
48
|
+
|
|
49
|
+
```yaml
|
|
50
|
+
port: 3000
|
|
51
|
+
host: "localhost"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
You access these values with:
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
config.get<number>("server.port"); // 3000
|
|
58
|
+
config.get<string>("server.host"); // "localhost"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### The `_index.yaml` / `index.yaml` File
|
|
62
|
+
|
|
63
|
+
Index files are special — they do **not** add a path segment. Their contents are scoped to the parent directory. For example, `config/default/_index.yaml`:
|
|
64
|
+
|
|
65
|
+
```yaml
|
|
66
|
+
environment:
|
|
67
|
+
development: true
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
This is accessed as `environment.development`, not `_index.environment.development`. The underscore prefix is optional but recommended to keep the file at the top of file explorers.
|
|
71
|
+
|
|
72
|
+
### Supported File Formats
|
|
73
|
+
|
|
74
|
+
- **YAML** (`.yaml`, `.yml`)
|
|
75
|
+
- **JSON** (`.json`)
|
|
76
|
+
- **JSONC** (`.jsonc`) — JSON with comments
|
|
77
|
+
- **JSON5** (`.json5`)
|
|
78
|
+
|
|
79
|
+
You can mix file formats within the same config directory.
|
|
80
|
+
|
|
81
|
+
## Environments
|
|
82
|
+
|
|
83
|
+
Environments let you override default values for different deployment targets (e.g. `staging`, `production`). Only values that differ from the default need to be specified in the environment folder.
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
config/
|
|
87
|
+
default/ # base values (always loaded first)
|
|
88
|
+
server.yaml
|
|
89
|
+
staging/ # overrides for staging
|
|
90
|
+
server.yaml
|
|
91
|
+
production/ # overrides for production
|
|
92
|
+
server.yaml
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Set the environment via:
|
|
96
|
+
|
|
97
|
+
- **Environment variable**: `NODE_CONFIG_ENV=production`
|
|
98
|
+
- **Constructor option**: `new Config({ configEnv: "production" })`
|
|
99
|
+
- **`config.init()` call**: `config.init({ configEnv: "production" })`
|
|
100
|
+
- **Using [config-env](#environment-variable-injection-config-env)**: `config-env --env=production <command>`
|
|
101
|
+
|
|
102
|
+
When an environment is active, its values are deep merged on top of `default`. Arrays are **replaced** entirely (not merged).
|
|
103
|
+
|
|
104
|
+
### Environment Inheritance
|
|
105
|
+
|
|
106
|
+
Environments inherit from `default` automatically. For more complex setups, you can inherit from additional environments using a `_config.json` file inside the environment folder:
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
config/
|
|
110
|
+
default/
|
|
111
|
+
staging/
|
|
112
|
+
_config.json # { "parentNames": ["kubernetes"] }
|
|
113
|
+
kubernetes/
|
|
114
|
+
production/
|
|
115
|
+
_config.json # { "parentNames": ["staging"] }
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**`production/_config.json`:**
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"parentNames": ["staging"]
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
This creates an inheritance chain: `default` → `kubernetes` → `staging` → `production`. Values are loaded and merged in that order, with later environments taking precedence.
|
|
126
|
+
|
|
127
|
+
You can specify multiple parents for complex inheritance:
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"parentNames": ["production", "cloud-aws"]
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Inheritance is resolved in order: `default` → `production` (and its parents) → `cloud-aws` → current environment.
|
|
136
|
+
|
|
137
|
+
### Override File
|
|
138
|
+
|
|
139
|
+
An `override.json` file placed directly in the config directory root (not inside any environment folder) is loaded last and merges on top of everything:
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
config/
|
|
143
|
+
default/
|
|
144
|
+
production/
|
|
145
|
+
override.json # applied last, on top of the active environment
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Variable Cross-Referencing
|
|
149
|
+
|
|
150
|
+
Config values can reference other config values using `${path.to.variable}` syntax:
|
|
151
|
+
|
|
152
|
+
```yaml
|
|
153
|
+
# docker.yaml
|
|
154
|
+
image-prefix: "myregistry/myapp"
|
|
155
|
+
|
|
156
|
+
# server.yaml
|
|
157
|
+
image: "${docker.imagePrefix}-server"
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`config.get<string>("server.image")` resolves to `"myregistry/myapp-server"`.
|
|
161
|
+
|
|
162
|
+
### Relative References
|
|
163
|
+
|
|
164
|
+
You can use relative paths with a leading dot (`.`):
|
|
165
|
+
|
|
166
|
+
```yaml
|
|
167
|
+
# realm/instances/default.yaml
|
|
168
|
+
key: "default"
|
|
169
|
+
namespace: "realm-${./key}" # resolves to "realm-default"
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Relative paths are resolved from the current object's location in the config tree.
|
|
173
|
+
|
|
174
|
+
### Recursive Resolution
|
|
175
|
+
|
|
176
|
+
References are resolved recursively, so a reference can point to another value that itself contains a reference.
|
|
177
|
+
|
|
178
|
+
## Kebab-Case to camelCase Conversion
|
|
179
|
+
|
|
180
|
+
By default, kebab-case keys are automatically converted to camelCase. Both forms can be used to access the value:
|
|
181
|
+
|
|
182
|
+
```yaml
|
|
183
|
+
# server.yaml
|
|
184
|
+
max-connections: 100
|
|
185
|
+
debug-port: 9229
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
config.get<number>("server.maxConnections"); // 100
|
|
190
|
+
config.get<number>("server.max-connections"); // 100
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Variable Casing Options
|
|
194
|
+
|
|
195
|
+
You can control this behavior with a `variableCasing` setting in `_config.json` (or as a key in any config object):
|
|
196
|
+
|
|
197
|
+
| Value | Behavior |
|
|
198
|
+
|-------|----------|
|
|
199
|
+
| `"camel"` | Only camelCase keys are available (default) |
|
|
200
|
+
| `"original"` | Only original keys are preserved |
|
|
201
|
+
| `"both"` | Both the original and camelCase keys are available |
|
|
202
|
+
|
|
203
|
+
```json
|
|
204
|
+
{
|
|
205
|
+
"variableCasing": "original"
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Config Base (Template Objects)
|
|
210
|
+
|
|
211
|
+
You can define a base/template object that other sibling objects inherit from using `incantaConfigBase`:
|
|
212
|
+
|
|
213
|
+
```yaml
|
|
214
|
+
# instances/_index.yaml
|
|
215
|
+
incantaConfigBase: "base"
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
```yaml
|
|
219
|
+
# instances/base.yaml
|
|
220
|
+
foo: "bar"
|
|
221
|
+
hello: "world"
|
|
222
|
+
num: 42
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
```yaml
|
|
226
|
+
# instances/custom.yaml
|
|
227
|
+
hello: "city"
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
The `custom` object will inherit values from `base` and override only the specified keys:
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
config.get<string>("instances.custom.foo"); // "bar" (inherited)
|
|
234
|
+
config.get<string>("instances.custom.hello"); // "city" (overridden)
|
|
235
|
+
config.get<number>("instances.custom.num"); // 42 (inherited)
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Secrets
|
|
239
|
+
|
|
240
|
+
`@incanta/config` has built-in support for fetching secrets from external providers. Any config value prefixed with `secret|` is treated as a secret reference:
|
|
241
|
+
|
|
242
|
+
```yaml
|
|
243
|
+
password: "secret|my-secret-key"
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Supported Providers
|
|
247
|
+
|
|
248
|
+
| Provider | Config Value |
|
|
249
|
+
|----------|-------------|
|
|
250
|
+
| HashiCorp Vault | `"vault"` |
|
|
251
|
+
| AWS Secrets Manager | `"aws-secrets-manager"` |
|
|
252
|
+
| Azure Key Vault | `"azure-key-vault"` |
|
|
253
|
+
| GCP Secret Manager | `"gcp-secret-manager"` |
|
|
254
|
+
| Local (file-based) | `"local"` |
|
|
255
|
+
| None (disabled) | `"none"` |
|
|
256
|
+
|
|
257
|
+
Configure the provider in your config files:
|
|
258
|
+
|
|
259
|
+
```yaml
|
|
260
|
+
# secrets.yaml
|
|
261
|
+
provider: "none"
|
|
262
|
+
cache-duration-seconds: 300
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Fetching Secrets
|
|
266
|
+
|
|
267
|
+
Use `getWithSecrets()` to resolve secret references:
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
const dbConfig = await config.getWithSecrets<IDatabaseConfig>(
|
|
271
|
+
"persistence.database"
|
|
272
|
+
);
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
This recursively walks the returned object and replaces any `"secret|..."` strings with values fetched from the configured secrets provider. Results are cached according to `secrets.cache-duration-seconds`.
|
|
276
|
+
|
|
277
|
+
## Environment Variable Injection (`config-env`)
|
|
278
|
+
|
|
279
|
+
The package includes a `config-env` CLI tool that injects config values as environment variables into a child process. This tool will also inject the `NODE_CONFIG_ENV` variable to the child process.
|
|
280
|
+
|
|
281
|
+
Define an `environment.yaml` (or `environment.json`, etc.) file in the config directory root mapping environment variable names to config paths:
|
|
282
|
+
|
|
283
|
+
```yaml
|
|
284
|
+
# environment.yaml
|
|
285
|
+
DATABASE_HOST: "persistence.database.host"
|
|
286
|
+
DATABASE_PORT: "persistence.database.port"
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### CLI Usage
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
npx config-env <command>
|
|
293
|
+
|
|
294
|
+
# With a specific config environment:
|
|
295
|
+
npx config-env --env=production <command>
|
|
296
|
+
npx config-env -e=production <command>
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Programmatic Usage
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
import config from "@incanta/config";
|
|
303
|
+
|
|
304
|
+
const envVars = config.getConfiguredEnv();
|
|
305
|
+
// { DATABASE_HOST: "localhost", DATABASE_PORT: "5432" }
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## `config-settings.json`
|
|
309
|
+
|
|
310
|
+
Place a `config-settings.json` file in the working directory root to customize the config system's behavior:
|
|
311
|
+
|
|
312
|
+
```jsonc
|
|
313
|
+
{
|
|
314
|
+
"defaults": {
|
|
315
|
+
"dir": "config/node", // default config directory (default: "config")
|
|
316
|
+
"env": "my-default-env" // default environment name (default: "default")
|
|
317
|
+
},
|
|
318
|
+
"extraDirs": [
|
|
319
|
+
"/path/to/additional/config" // extra directories to search for environments
|
|
320
|
+
]
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Extra Directories
|
|
325
|
+
|
|
326
|
+
`extraDirs` allows environment folders to live outside the main config directory. This is useful when you want to keep project-specific configuration separate from shared configuration:
|
|
327
|
+
|
|
328
|
+
```jsonc
|
|
329
|
+
{
|
|
330
|
+
"extraDirs": [
|
|
331
|
+
"C:\\path\\to\\MyProject\\Config\\Backend"
|
|
332
|
+
]
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
When resolving an environment name, the library first checks the main config directory, then checks each extra directory in order.
|
|
337
|
+
|
|
338
|
+
## API Reference
|
|
339
|
+
|
|
340
|
+
### Default Export (Singleton)
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
import config from "@incanta/config";
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Returns a singleton `Config` instance, initialized with defaults on first access.
|
|
347
|
+
|
|
348
|
+
### `Config` Class
|
|
349
|
+
|
|
350
|
+
```typescript
|
|
351
|
+
import Config from "@incanta/config/config";
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
#### Constructor
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
const config = new Config(options?: IConfigOptions);
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
| Option | Type | Description |
|
|
361
|
+
|--------|------|-------------|
|
|
362
|
+
| `configDir` | `string` | Path to the config directory |
|
|
363
|
+
| `configEnv` | `string` | Environment name to load |
|
|
364
|
+
| `cwd` | `string` | Working directory for resolving relative paths |
|
|
365
|
+
|
|
366
|
+
#### Methods
|
|
367
|
+
|
|
368
|
+
| Method | Returns | Description |
|
|
369
|
+
|--------|---------|-------------|
|
|
370
|
+
| `init(options?)` | `void` | Re-initialize with new options |
|
|
371
|
+
| `get<T>(key)` | `T` | Get a value by dot-separated path; throws if not found |
|
|
372
|
+
| `tryGet<T>(key)` | `T \| null` | Get a value, returning `null` if the key doesn't exist |
|
|
373
|
+
| `getWithSecrets<T>(key)` | `Promise<T>` | Get a value and resolve any `secret\|...` references |
|
|
374
|
+
| `processSecrets<T>(value)` | `Promise<T>` | Recursively resolve secrets in an arbitrary value |
|
|
375
|
+
| `getConfiguredEnv()` | `object` | Get env vars mapped from the `environment.*` config file |
|
|
376
|
+
| `getJson()` | `object` | Get the entire resolved config as a plain object |
|
|
377
|
+
| `set<T>(key, value)` | `void` | Set a runtime config value |
|
|
378
|
+
| `dir()` | `string` | Get the active config directory path |
|
|
379
|
+
| `env()` | `string` | Get the active environment name |
|
|
380
|
+
| `cwd()` | `string` | Get the working directory |
|
|
381
|
+
|
|
382
|
+
### Environment Variables
|
|
383
|
+
|
|
384
|
+
| Variable | Description |
|
|
385
|
+
|----------|-------------|
|
|
386
|
+
| `NODE_CONFIG_DIR` | Override the config directory path |
|
|
387
|
+
| `NODE_CONFIG_ENV` | Override the config environment name |
|
|
388
|
+
| `NODE_CONFIG_SKIP_ENV_WARNING` | Set to `"true"` to suppress missing environment warnings |
|
|
389
|
+
|
|
390
|
+
## Example Project Structure
|
|
391
|
+
|
|
392
|
+
A typical project using `@incanta/config`:
|
|
393
|
+
|
|
394
|
+
```
|
|
395
|
+
my-project/
|
|
396
|
+
config-settings.json # optional: customize defaults and extra dirs
|
|
397
|
+
config/
|
|
398
|
+
environment.yaml # env var mappings for config-env CLI
|
|
399
|
+
override.json # optional: final overrides
|
|
400
|
+
default/ # base configuration
|
|
401
|
+
_index.yaml
|
|
402
|
+
server.yaml
|
|
403
|
+
persistence/
|
|
404
|
+
database.yaml
|
|
405
|
+
redis.yaml
|
|
406
|
+
staging/ # staging overrides
|
|
407
|
+
_config.json # { "parentNames": ["kubernetes"] }
|
|
408
|
+
_index.yaml
|
|
409
|
+
kubernetes/ # kubernetes-specific values
|
|
410
|
+
server.yaml
|
|
411
|
+
production/ # production overrides
|
|
412
|
+
_config.json # { "parentNames": ["staging"] }
|
|
413
|
+
persistence/
|
|
414
|
+
database.yaml
|
|
415
|
+
src/
|
|
416
|
+
index.ts
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
## License
|
|
420
|
+
|
|
421
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@incanta/config",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.8",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./lib/index.js",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"audit": "yarn-audit-fix"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@aws-sdk/client-secrets-manager": "^3.
|
|
22
|
+
"@aws-sdk/client-secrets-manager": "^3.985.0",
|
|
23
23
|
"@azure/identity": "^4.10.1",
|
|
24
24
|
"@azure/keyvault-secrets": "^4.10.0",
|
|
25
25
|
"@google-cloud/secret-manager": "^6.1.1",
|